1import type { ThreadActivitySnapshot } from "@t3tools/client-runtime/state/thread-activity-announcement";
2import type { RunId, OrchestrationV2RunStatus } from "@t3tools/contracts";
3
4export interface ThreadActivityInput {
5 readonly approvalRequestId: string | null;
6 readonly userInputRequestId: string | null;
7 /** The thread shell's pending request, which secret and link cards have only there. */
8 readonly shellRequestId: string | null;
9 readonly shellPendingUserInput: boolean;
10 readonly working: boolean;
11 readonly run: { readonly runId: RunId; readonly status: OrchestrationV2RunStatus } | null;
12}
13
14export interface ThreadActivityMemory {
15 readonly snapshot: ThreadActivitySnapshot;
16 /** Requests the thread screen has shown as a card. */
17 readonly cardRequestIds: ReadonlySet<string>;
18}
19
20/** What the open thread's screen reader status follows, from the same sources the thread screen shows. */
21export function nextThreadActivitySnapshot(
22 previous: ThreadActivityMemory | null,
23 input: ThreadActivityInput,
24): ThreadActivityMemory {
25 const { approvalRequestId, userInputRequestId, shellRequestId } = input;
26 const cardRequestId = approvalRequestId ?? userInputRequestId;
27 const seen = previous?.cardRequestIds ?? new Set<string>();
28 // A card leaves as soon as it is answered, before the shell; a shell request the card
29 // already showed is that answered one, so the request stays the one last announced.
30 // Secret and link requests never show as a card and come from the shell alone.
31 const requestId =
32 cardRequestId ??
33 (shellRequestId !== null && seen.has(shellRequestId)
34 ? (previous?.snapshot.requestId ?? null)
35 : shellRequestId);
36 return {
37 snapshot: {
38 status:
39 approvalRequestId !== null
40 ? "approval"
41 : userInputRequestId !== null || input.shellPendingUserInput
42 ? "input"
43 : input.working
44 ? "working"
45 : "idle",
46 requestId,
47 runId: input.run?.runId ?? null,
48 runStatus: input.run?.status ?? null,
49 },
50 cardRequestIds:
51 cardRequestId === null || seen.has(cardRequestId) ? seen : new Set([...seen, cardRequestId]),
52 };
53}