authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 20:41:58-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 20:41:58-07:00
log0debebafbb7d98a3c77d3e636fa54616a98d26da
tree40a00aa72741c8234e92555af9776d418e004921
parent2a29de7502cdd02686bda7d12fb2671060fd1979
signature Signed by PGP key B5690EEEBB952194

[codex] Structure thread archive blocked error (#3451)


2 files changed, 34 insertions(+), 6 deletions(-)

apps/web/src/hooks/useThreadActions.test.ts created+19
...@@ -0,0 +1,19 @@
1import { EnvironmentId, ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { ThreadArchiveBlockedError } from "./useThreadActions";
5
6describe("ThreadArchiveBlockedError", () => {
7 it("keeps the blocked thread context with the fixed message", () => {
8 const error = new ThreadArchiveBlockedError({
9 environmentId: EnvironmentId.make("environment-1"),
10 threadId: ThreadId.make("thread-1"),
11 });
12
13 expect(error).toMatchObject({
14 environmentId: "environment-1",
15 threadId: "thread-1",
16 });
17 expect(error.message).toBe("Cannot archive a running thread.");
18 });
19});
apps/web/src/hooks/useThreadActions.ts+15-6
...@@ -4,9 +4,9 @@ import {...@@ -4,9 +4,9 @@ import {
4 scopeThreadRef,4 scopeThreadRef,
5} from "@t3tools/client-runtime/environment";5} from "@t3tools/client-runtime/environment";
6import { settlePromise, squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime";6import { settlePromise, squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime";
7import { type ScopedThreadRef, ThreadId } from "@t3tools/contracts";7import { EnvironmentId, type ScopedThreadRef, ThreadId } from "@t3tools/contracts";
8import * as Cause from "effect/Cause";8import * as Cause from "effect/Cause";
9import * as Data from "effect/Data";9import * as Schema from "effect/Schema";
10import { AsyncResult } from "effect/unstable/reactivity";10import { AsyncResult } from "effect/unstable/reactivity";
11import { useRouter } from "@tanstack/react-router";11import { useRouter } from "@tanstack/react-router";
12import { useCallback, useMemo, useRef } from "react";12import { useCallback, useMemo, useRef } from "react";
...@@ -27,9 +27,17 @@ import { stackedThreadToast, toastManager } from "../components/ui/toast";...@@ -27,9 +27,17 @@ import { stackedThreadToast, toastManager } from "../components/ui/toast";
27import { useClientSettings } from "./useSettings";27import { useClientSettings } from "./useSettings";
28import { useAtomCommand } from "../state/use-atom-command";28import { useAtomCommand } from "../state/use-atom-command";
2929
30export class ThreadArchiveBlockedError extends Data.TaggedError("ThreadArchiveBlockedError")<{30export class ThreadArchiveBlockedError extends Schema.TaggedErrorClass<ThreadArchiveBlockedError>()(
31 readonly message: string;31 "ThreadArchiveBlockedError",
32}> {}32 {
33 environmentId: EnvironmentId,
34 threadId: ThreadId,
35 },
36) {
37 override get message(): string {
38 return "Cannot archive a running thread.";
39 }
40}
3341
34export function useThreadActions() {42export function useThreadActions() {
35 const closeTerminal = useAtomCommand(terminalEnvironment.close);43 const closeTerminal = useAtomCommand(terminalEnvironment.close);
...@@ -89,7 +97,8 @@ export function useThreadActions() {...@@ -89,7 +97,8 @@ export function useThreadActions() {
89 return AsyncResult.failure(97 return AsyncResult.failure(
90 Cause.fail(98 Cause.fail(
91 new ThreadArchiveBlockedError({99 new ThreadArchiveBlockedError({
92 message: "Cannot archive a running thread.",100 environmentId: threadRef.environmentId,
101 threadId: threadRef.threadId,
93 }),102 }),
94 ),103 ),
95 );104 );