| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import { | 1 | import { |
| 2 | EnvironmentId, | 2 | EnvironmentId, |
| 3 | ProjectId, | 3 | ProjectId, |
| | 4 | RuntimeRequestId, |
| 4 | ThreadId, | 5 | ThreadId, |
| 5 | type OrchestrationV2ShellSnapshot, | 6 | type OrchestrationV2ShellSnapshot, |
| 6 | } from "@t3tools/contracts"; | 7 | } from "@t3tools/contracts"; |
| ... | @@ -9,7 +10,7 @@ import { Atom, AtomRegistry } from "effect/reactivity"; | ... | @@ -9,7 +10,7 @@ import { Atom, AtomRegistry } from "effect/reactivity"; |
| 9 | import { describe, expect, it } from "vite-plus/test"; | 10 | import { describe, expect, it } from "vite-plus/test"; |
| 10 | | 11 | |
| 11 | import { PrimaryConnectionTarget } from "../connection/model.ts"; | 12 | import { PrimaryConnectionTarget } from "../connection/model.ts"; |
| 12 | import { v2ShellSnapshot, v2ThreadShell } from "./orchestrationV2TestFixtures.ts"; | 13 | import { v2Now, v2ShellSnapshot, v2ThreadShell } from "./orchestrationV2TestFixtures.ts"; |
| 13 | import { applyShellStreamEvent } from "./shellReducer.ts"; | 14 | import { applyShellStreamEvent } from "./shellReducer.ts"; |
| 14 | import { createEnvironmentThreadShellAtoms } from "./threadShell.ts"; | 15 | import { createEnvironmentThreadShellAtoms } from "./threadShell.ts"; |
| 15 | | 16 | |
| ... | @@ -103,6 +104,53 @@ describe("v2 thread shell lists", () => { | ... | @@ -103,6 +104,53 @@ describe("v2 thread shell lists", () => { |
| 103 | registry.dispose(); | 104 | registry.dispose(); |
| 104 | }); | 105 | }); |
| 105 | | 106 | |
| | 107 | it("counts a subagent question on its parent only while the subagent runs", () => { |
| | 108 | const { registry, threads, snapshotAtom } = makeHarness(); |
| | 109 | const root = v2ThreadShell; |
| | 110 | const child = { |
| | 111 | ...root, |
| | 112 | id: ThreadId.make("child"), |
| | 113 | creationSource: "mcp" as const, |
| | 114 | status: "running" as const, |
| | 115 | activityRunStatus: "running" as const, |
| | 116 | lineage: { |
| | 117 | ...root.lineage, |
| | 118 | parentThreadId: root.id, |
| | 119 | relationshipToParent: "subagent" as const, |
| | 120 | }, |
| | 121 | pendingRuntimeRequest: { |
| | 122 | id: RuntimeRequestId.make("question"), |
| | 123 | kind: "user_input" as const, |
| | 124 | createdAt: v2Now, |
| | 125 | }, |
| | 126 | }; |
| | 127 | const inputs = threads.childThreadInputsAtom({ environmentId, threadId: root.id }); |
| | 128 | const dispose = registry.mount(inputs); |
| | 129 | registry.set(snapshotAtom(environmentId), { ...v2ShellSnapshot, threads: [root, child] }); |
| | 130 | expect(registry.get(inputs).map((thread) => thread.id)).toEqual([child.id]); |
| | 131 | // A follow-up queued behind the blocked run leaves the question live. |
| | 132 | registry.set(snapshotAtom(environmentId), { |
| | 133 | ...v2ShellSnapshot, |
| | 134 | threads: [root, { ...child, status: "queued" as const }], |
| | 135 | }); |
| | 136 | expect(registry.get(inputs).map((thread) => thread.id)).toEqual([child.id]); |
| | 137 | // An async question stays pending after the run that asked it completes. |
| | 138 | registry.set(snapshotAtom(environmentId), { |
| | 139 | ...v2ShellSnapshot, |
| | 140 | threads: [root, { ...child, status: "completed" as const, activityRunStatus: null }], |
| | 141 | }); |
| | 142 | expect(registry.get(inputs)).toEqual([]); |
| | 143 | // Older servers omit the activity run, so the latest run's status decides. |
| | 144 | const { activityRunStatus: _omitted, ...legacyChild } = child; |
| | 145 | registry.set(snapshotAtom(environmentId), { |
| | 146 | ...v2ShellSnapshot, |
| | 147 | threads: [root, { ...legacyChild, status: "completed" as const }], |
| | 148 | }); |
| | 149 | expect(registry.get(inputs)).toEqual([]); |
| | 150 | dispose(); |
| | 151 | registry.dispose(); |
| | 152 | }); |
| | 153 | |
| 106 | it("leaves switched-off environments out of navigation", () => { | 154 | it("leaves switched-off environments out of navigation", () => { |
| 107 | const { registry, threads, catalogValueAtom } = makeHarness([ | 155 | const { registry, threads, catalogValueAtom } = makeHarness([ |
| 108 | environmentId, | 156 | environmentId, |