Skip to content
Source
Sign inSign up

threadAgentsPresentation.test.ts

352 lines11.0 KB

1import { describe, expect, it } from "vite-plus/test";
2import {
3 deriveWorkflowGroups,
4 projectedSubagentsToRuntime,
5} from "@t3tools/client-runtime/state/subagentRuntime";
6import {
7 NodeId,
8 ProviderDriverKind,
9 RunId,
10 ThreadId,
11 type OrchestrationV2SubagentWorkflow,
12} from "@t3tools/contracts";
13import * as DateTime from "effect/DateTime";
14​
15import {
16 nextWorkflowSelection,
17 resolveSubagentRowPresentation,
18 resolveSubagentStopTarget,
19 workflowMemberDetail,
20 workflowMemberMetadata,
21 workflowRail,
22} from "./threadAgentsPresentation";
23​
24const base = {
25 title: null,
26 prompt: "Audit the timestamps",
27 status: "running" as const,
28 result: null,
29 childThreadId: "thread-child" as never,
30};
31​
32describe("resolveSubagentRowPresentation", () => {
33 it("leads with progress while the agent is working", () => {
34 const row = resolveSubagentRowPresentation({
35 ...base,
36 progress: "Reading files",
37 result: "stale result",
38 });
39​
40 expect(row.detail).toBe("Reading files");
41 expect(row.tone).toBe("working");
42 expect(row.live).toBe(true);
43 });
44​
45 it("leads with the result once the agent has settled", () => {
46 const row = resolveSubagentRowPresentation({
47 ...base,
48 status: "completed",
49 progress: "Reading files",
50 result: "Found two\n problems",
51 });
52​
53 expect(row.detail).toBe("Found two problems");
54 expect(row.tone).toBe("completed");
55 expect(row.statusLabel).toBe("Completed");
56 });
57​
58 it("shows a failure's text, since that is where the error lands", () => {
59 const row = resolveSubagentRowPresentation({ ...base, status: "failed", result: "Timed out" });
60​
61 expect(row.detail).toBe("Timed out");
62 expect(row.tone).toBe("failed");
63 });
64​
65 it("falls back to a trimmed prompt when the agent has no title", () => {
66 const long = resolveSubagentRowPresentation({ ...base, prompt: "x".repeat(200) });
67 const titled = resolveSubagentRowPresentation({ ...base, title: "Subagent: /root/my_worker" });
68​
69 expect(long.title).toHaveLength(80);
70 expect(long.title.endsWith("...")).toBe(true);
71 expect(titled.title).toBe("My Worker");
72 });
73​
74 it("only offers a thread to open when the agent has one", () => {
75 expect(resolveSubagentRowPresentation(base).canOpenThread).toBe(true);
76 expect(resolveSubagentRowPresentation({ ...base, childThreadId: null }).canOpenThread).toBe(
77 false,
78 );
79 });
80​
81 it("uses the status label when there is nothing to report yet", () => {
82 const row = resolveSubagentRowPresentation({ ...base, status: "pending" });
83​
84 expect(row.detail).toBeNull();
85 expect(row.statusLabel).toBe("Working");
86 });
87​
88 it("bounds long result previews without dropping the agent's status", () => {
89 const row = resolveSubagentRowPresentation({
90 ...base,
91 status: "failed",
92 result: "x".repeat(400),
93 });
94 expect(row.detail).toBe(`${"x".repeat(280)}…`);
95 expect(row.statusLabel).toBe("Failed");
96 });
97});
98​
99type WorkflowAgent = OrchestrationV2SubagentWorkflow["agents"][number];
100​
101const workflowRailOf = (
102 agents: ReadonlyArray<WorkflowAgent>,
103 status: "running" | "completed" = "running",
104) =>
105 workflowRail(
106 deriveWorkflowGroups(
107 projectedSubagentsToRuntime([
108 {
109 id: "workflow",
110 title: "Mobile Stop review",
111 prompt: "",
112 model: null,
113 status,
114 result: null,
115 startedAt: DateTime.makeUnsafe("2026-08-01T10:00:00.000Z"),
116 completedAt: null,
117 updatedAt: DateTime.makeUnsafe("2026-08-01T10:00:05.000Z"),
118 workflow: {
119 phases: [
120 { index: 0, title: "Inspect" },
121 { index: 1, title: "Verify" },
122 { index: 2, title: "Report" },
123 ],
124 agents,
125 },
126 },
127 ]),
128 )[0]!,
129 );
130​
131const agent = (
132 index: number,
133 phaseIndex: number | undefined,
134 state: WorkflowAgent["state"],
135 overrides: Partial<WorkflowAgent> = {},
136): WorkflowAgent => ({
137 index,
138 label: `agent ${index}`,
139 state,
140 ...(phaseIndex === undefined ? {} : { phaseIndex }),
141 ...overrides,
142});
143​
144describe("workflowRail", () => {
145 it("keeps declared phases without agents and gives unphased agents a stop", () => {
146 const rail = workflowRailOf([
147 agent(1, 0, "completed"),
148 agent(2, 1, "running"),
149 agent(3, undefined, "queued"),
150 ]);
151​
152 expect(rail.map((item) => [item.title, item.tone, item.members.length])).toEqual([
153 ["Inspect", "completed", 1],
154 ["Verify", "working", 1],
155 ["Report", "queued", 0],
156 ["Other", "queued", 1],
157 ]);
158 });
159​
160 it("counts only completed agents toward a failed phase's progress", () => {
161 const [phase] = workflowRailOf([agent(1, 0, "failed"), agent(2, 0, "completed")], "completed");
162​
163 expect([phase!.tone, phase!.completedCount, phase!.members.length]).toEqual(["failed", 1, 2]);
164 });
165});
166​
167describe("nextWorkflowSelection", () => {
168 const running = workflowRailOf([agent(1, 0, "completed"), agent(2, 1, "running")]);
169​
170 it("opens the running phase from the collapsed title, then collapses", () => {
171 expect(nextWorkflowSelection(running, null)).toBe("1");
172 expect(nextWorkflowSelection(running, "1")).toBeNull();
173 });
174​
175 it("prefers the failed phase, then the latest phase with agents", () => {
176 const failed = workflowRailOf([agent(1, 0, "failed"), agent(2, 1, "completed")], "completed");
177 const done = workflowRailOf([agent(1, 0, "completed"), agent(2, 1, "completed")], "completed");
178​
179 expect(nextWorkflowSelection(failed, null)).toBe("0");
180 expect(nextWorkflowSelection(done, null)).toBe("1");
181 expect(nextWorkflowSelection(workflowRailOf([]), null)).toBeNull();
182 });
183​
184 it("keeps one phase open: another replaces it and the open one collapses the card", () => {
185 expect(nextWorkflowSelection(running, null, "0")).toBe("0");
186 expect(nextWorkflowSelection(running, "1", "0")).toBe("0");
187 expect(nextWorkflowSelection(running, "0", "0")).toBeNull();
188 });
189});
190​
191describe("workflow member rows", () => {
192 it("keeps reported model, effort, and speed once an agent completes", () => {
193 const [completed] = workflowRailOf(
194 [
195 agent(1, 0, "completed", {
196 model: "claude-opus-5-5",
197 effort: "high",
198 speed: "fast",
199 result: "Checked controls",
200 }),
201 ],
202 "completed",
203 )[0]!.members;
204​
205 expect(workflowMemberMetadata(completed!, undefined)).toEqual({
206 model: "Claude Opus 5.5",
207 effort: "High",
208 speed: "Fast",
209 fast: true,
210 });
211 expect(workflowMemberDetail(completed!)).toEqual({ detail: "Checked controls", error: null });
212 });
213​
214 it("reports nothing the provider did not send", () => {
215 expect(workflowMemberMetadata({ model: null, effort: null }, undefined)).toEqual({
216 model: null,
217 effort: null,
218 speed: null,
219 fast: false,
220 });
221 });
222​
223 it("labels effort from the catalog model a reported alias resolves to", () => {
224 expect(
225 workflowMemberMetadata(
226 { model: "opus", effort: "xhigh" },
227 {
228 driver: ProviderDriverKind.make("claudeAgent"),
229 models: [
230 {
231 slug: "claude-opus-5-5",
232 name: "Claude Opus 5.5",
233 shortName: "Opus 5.5",
234 aliases: ["opus"],
235 isCustom: false,
236 capabilities: {
237 optionDescriptors: [
238 {
239 id: "effort",
240 label: "Reasoning",
241 type: "select",
242 options: [{ id: "xhigh", label: "Extra High" }],
243 },
244 ],
245 },
246 },
247 ],
248 },
249 ),
250 ).toMatchObject({ model: "Opus 5.5", effort: "Extra High" });
251 });
252​
253 it("shows a failed agent's error under its prompt", () => {
254 const [failed] = workflowRailOf([
255 agent(1, 0, "failed", { prompt: "Update session store", result: "Type error" }),
256 ])[0]!.members;
257​
258 expect(workflowMemberDetail(failed!)).toEqual({
259 detail: "Update session store",
260 error: "Type error",
261 });
262 });
263});
264​
265const stopAgent = {
266 id: NodeId.make("agent"),
267 threadId: ThreadId.make("parent"),
268 childThreadId: ThreadId.make("child"),
269 status: "running" as const,
270 origin: "app_owned" as const,
271 driver: ProviderDriverKind.make("codex"),
272 runId: RunId.make("run"),
273 nativeTaskRef: null,
274};
275​
276describe("resolveSubagentStopTarget", () => {
277 it.each(["pending", "running", "waiting"] as const)(
278 "stops the parent task and child of an app-owned %s agent",
279 (status) => {
280 expect(resolveSubagentStopTarget({ ...stopAgent, status })).toEqual({
281 kind: "subagent",
282 input: { threadId: stopAgent.threadId, subagentId: stopAgent.id },
283 });
284 expect(resolveSubagentStopTarget({ ...stopAgent, status, childThreadId: null })).toBeNull();
285 },
286 );
287​
288 it.each(["idle", "completed", "failed", "cancelled", "interrupted"] as const)(
289 "offers no Stop for a %s agent",
290 (status) => {
291 expect(resolveSubagentStopTarget({ ...stopAgent, status })).toBeNull();
292 },
293 );
294​
295 const native = {
296 ...stopAgent,
297 origin: "provider_native" as const,
298 driver: ProviderDriverKind.make("claudeAgent"),
299 childThreadId: null,
300 nativeTaskRef: {
301 driver: ProviderDriverKind.make("claudeAgent"),
302 strength: "strong" as const,
303 nativeId: "native-task",
304 },
305 };
306​
307 it("targets a supported native task without interrupting its parent run", () => {
308 expect(resolveSubagentStopTarget(native)).toEqual({
309 kind: "subagent",
310 input: { threadId: native.threadId, subagentId: native.id },
311 });
312 });
313​
314 it("hides native Stop when the task cannot be identified or stopped", () => {
315 expect(resolveSubagentStopTarget({ ...native, nativeTaskRef: null })).toBeNull();
316 expect(
317 resolveSubagentStopTarget({
318 ...native,
319 nativeTaskRef: { ...native.nativeTaskRef, strength: "weak" },
320 }),
321 ).toBeNull();
322 expect(
323 resolveSubagentStopTarget({
324 ...native,
325 nativeTaskRef: { ...native.nativeTaskRef, nativeId: null },
326 }),
327 ).toBeNull();
328 expect(resolveSubagentStopTarget({ ...native, runId: null })).toBeNull();
329 expect(resolveSubagentStopTarget({ ...native, status: "completed" })).toBeNull();
330 expect(
331 resolveSubagentStopTarget({ ...native, driver: ProviderDriverKind.make("codex") }),
332 ).toBeNull();
333 });
334​
335 it("stops the whole workflow through its own command", () => {
336 const workflow = { name: "Review", phases: [], agents: [] };
337 expect(resolveSubagentStopTarget({ ...native, workflow })).toEqual({
338 kind: "workflow",
339 input: { threadId: native.threadId, subagentId: native.id },
340 });
341 expect(resolveSubagentStopTarget({ ...native, workflow, status: "waiting" })).toBeNull();
342 expect(resolveSubagentStopTarget({ ...stopAgent, workflow })).toBeNull();
343 expect(resolveSubagentStopTarget({ ...native, workflow, nativeTaskRef: null })).toBeNull();
344 expect(
345 resolveSubagentStopTarget({
346 ...native,
347 workflow,
348 nativeTaskRef: { ...native.nativeTaskRef, nativeId: "" },
349 }),
350 ).toBeNull();
351 });
352});