1import {
2 TurnItemId,
3 NodeId,
4 MessageId,
5 ProviderInstanceId,
6 ProviderThreadId,
7 ProviderSessionId,
8 ProviderDriverKind,
9 RunId,
10 ThreadId,
11 type OrchestrationV2ExecutionNode,
12 type OrchestrationV2RunStatus,
13} from "@t3tools/contracts";
14import * as DateTime from "effect/DateTime";
15import { usageLimitBlockedRun } from "@t3tools/shared/orchestrationV2ThreadError";
16import { describe, expect, it } from "vite-plus/test";
17
18import { v2Projection } from "./orchestrationV2TestFixtures.ts";
19import {
20 presentPendingBackgroundWork,
21 presentProviderGoal,
22 deriveReportedModelSelection,
23 deriveLatestThreadRun,
24 deriveProviderSubagentStatus,
25 formatModelSelectionEffort,
26 formatProviderSubagentStatus,
27 deriveRunlessWorkStartedAt,
28 deriveThreadActivityRun,
29 deriveThreadRuntime,
30 threadRuntimeHasInterruptibleRun,
31} from "./threadExecution.ts";
32import { threadRuntimeCanArchive, type ThreadRuntimeSummary } from "./models.ts";
33
34const now = DateTime.makeUnsafe("2026-07-28T10:00:00.000Z");
35
36function run(id: string, ordinal: number, status: OrchestrationV2RunStatus) {
37 return {
38 id: RunId.make(id),
39 threadId: v2Projection.thread.id,
40 ordinal,
41 providerInstanceId: v2Projection.thread.providerInstanceId,
42 modelSelection: v2Projection.thread.modelSelection,
43 providerThreadId: null,
44 userMessageId: MessageId.make(`message-${id}`),
45 rootNodeId: null,
46 activeAttemptId: null,
47 status,
48 requestedAt: now,
49 startedAt: status === "queued" ? null : now,
50 completedAt: null,
51 checkpointId: null,
52 contextHandoffId: null,
53 };
54}
55
56describe("thread execution presentation", () => {
57 it("derives the current root failure without inheriting errors from children or previous runs", () => {
58 const failed = { ...run("limited", 1, "failed"), rootNodeId: NodeId.make("root") };
59 const item = {
60 id: TurnItemId.make("limit-error"),
61 threadId: v2Projection.thread.id,
62 runId: failed.id,
63 nodeId: failed.rootNodeId,
64 providerThreadId: null,
65 providerTurnId: null,
66 nativeItemRef: null,
67 parentItemId: null,
68 ordinal: 1,
69 type: "error" as const,
70 status: "failed" as const,
71 title: "Usage limit reached",
72 startedAt: now,
73 completedAt: now,
74 updatedAt: now,
75 failure: {
76 class: "usage_limit" as const,
77 message: "Plan limit reached",
78 code: "usageLimitExceeded",
79 retryable: null,
80 },
81 };
82 const projection = { ...v2Projection, runs: [failed], turnItems: [item] };
83 expect(deriveThreadRuntime(projection)).toMatchObject({
84 lastError: "Plan limit reached",
85 lastErrorClass: "usage_limit",
86 });
87 expect(
88 deriveThreadRuntime({
89 ...projection,
90 turnItems: [{ ...item, nodeId: NodeId.make("child") }],
91 }),
92 ).toMatchObject({ lastError: null, lastErrorClass: null });
93 expect(
94 deriveThreadRuntime({
95 ...projection,
96 runs: [{ ...failed, rootNodeId: NodeId.make("new-root") }],
97 }),
98 ).toMatchObject({ lastError: null, lastErrorClass: null });
99 expect(
100 deriveThreadRuntime({ ...projection, runs: [failed, run("new", 2, "running")] }),
101 ).toMatchObject({ status: "running", lastError: null, lastErrorClass: null });
102 });
103
104 it("counts a wake run's activity from the start of the work it continues", () => {
105 const workStartedAt = DateTime.makeUnsafe("2026-07-28T09:20:00.000Z");
106 const wake = { ...run("wake", 2, "running"), workStartedAt };
107 expect(
108 deriveThreadRuntime({ ...v2Projection, runs: [run("prompt", 1, "completed"), wake] })
109 ?.activityStartedAt,
110 ).toBe("2026-07-28T09:20:00.000Z");
111 expect(
112 deriveThreadRuntime({ ...v2Projection, runs: [run("prompt", 1, "running")] })
113 ?.activityStartedAt,
114 ).toBe("2026-07-28T10:00:00.000Z");
115 });
116
117 it("keeps a subscription limit visible while later messages stay queued", () => {
118 const failed = {
119 ...run("limited", 1, "failed"),
120 rootNodeId: NodeId.make("root"),
121 completedAt: now,
122 };
123 const queued = run("queued", 2, "queued");
124 const item = {
125 id: TurnItemId.make("limit-error"),
126 threadId: v2Projection.thread.id,
127 runId: failed.id,
128 nodeId: failed.rootNodeId,
129 providerThreadId: null,
130 providerTurnId: null,
131 nativeItemRef: null,
132 parentItemId: null,
133 ordinal: 1,
134 type: "error" as const,
135 status: "failed" as const,
136 title: "Usage limit reached",
137 startedAt: now,
138 completedAt: now,
139 updatedAt: now,
140 failure: {
141 class: "usage_limit" as const,
142 message: "Plan limit reached",
143 code: "usageLimitExceeded",
144 retryable: null,
145 },
146 };
147 const projection = { ...v2Projection, runs: [failed, queued], turnItems: [item] };
148
149 expect(deriveLatestThreadRun(projection)?.runId).toBe(failed.id);
150 expect(deriveThreadActivityRun(projection)?.runId).toBe(failed.id);
151 expect(deriveThreadRuntime(projection)).toMatchObject({
152 status: "failed",
153 lastError: "Plan limit reached",
154 lastErrorClass: "usage_limit",
155 });
156 const cancelledQueued = {
157 ...run("cancelled-queued", 3, "cancelled"),
158 startedAt: null,
159 completedAt: now,
160 };
161 expect(
162 usageLimitBlockedRun([failed, queued, cancelledQueued], projection.turnItems, null)?.id,
163 ).toBe(failed.id);
164 expect(
165 deriveThreadRuntime({ ...projection, runs: [failed, queued, cancelledQueued] }),
166 ).toMatchObject({ status: "failed", lastErrorClass: "usage_limit" });
167 expect(
168 deriveThreadRuntime({
169 ...projection,
170 turnItems: [{ ...item, failure: { ...item.failure, class: "provider_error" as const } }],
171 }),
172 ).toMatchObject({ status: "queued", lastErrorClass: null });
173 });
174
175 it("keeps live activity attached to an executing run when a newer run is queued", () => {
176 const runningRun = run("run-running", 1, "running");
177 const queuedRun = run("run-queued", 2, "queued");
178 const projection = { ...v2Projection, runs: [queuedRun, runningRun], updatedAt: now };
179
180 expect(deriveLatestThreadRun(projection)?.runId).toBe(queuedRun.id);
181 expect(deriveThreadActivityRun(projection)).toMatchObject({
182 runId: runningRun.id,
183 status: "running",
184 });
185
186 const runtime = deriveThreadRuntime(projection);
187 expect(runtime).toMatchObject({
188 status: "running",
189 activeRunId: runningRun.id,
190 });
191 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(true);
192 });
193
194 it("presents a held queue as the stopped run instead of queued work", () => {
195 const interrupted = { ...run("run-interrupted", 1, "interrupted"), completedAt: now };
196 const held = { ...run("run-held", 2, "queued"), queueHeld: true };
197 const projection = { ...v2Projection, runs: [interrupted, held], updatedAt: now };
198
199 expect(deriveLatestThreadRun(projection)?.runId).toBe(interrupted.id);
200 expect(deriveThreadActivityRun(projection)?.runId).toBe(interrupted.id);
201 expect(deriveThreadRuntime(projection)).toMatchObject({
202 status: "interrupted",
203 activeRunId: null,
204 });
205
206 // Resuming clears the hold, and the run reads as queued until it starts.
207 const resumed = { ...projection, runs: [interrupted, { ...held, queueHeld: false }] };
208 expect(deriveThreadRuntime(resumed)).toMatchObject({ status: "queued" });
209
210 // Recovery can hold a first message before any run executed; it is not work.
211 const onlyHeld = {
212 ...projection,
213 runs: [held],
214 thread: { ...projection.thread, activeProviderThreadId: null },
215 };
216 expect(deriveLatestThreadRun(onlyHeld)).toBeNull();
217 expect(deriveThreadRuntime(onlyHeld)).toBeNull();
218 });
219
220 it("does not expose a queued-only run as interruptible", () => {
221 const queuedRun = run("run-queued", 1, "queued");
222 const projection = { ...v2Projection, runs: [queuedRun], updatedAt: now };
223
224 expect(deriveThreadActivityRun(projection)).toMatchObject({
225 runId: queuedRun.id,
226 status: "queued",
227 });
228
229 const runtime = deriveThreadRuntime(projection);
230 expect(runtime).toMatchObject({
231 status: "queued",
232 activeRunId: null,
233 });
234 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(false);
235 });
236
237 it("keeps checkpoint-wait activity visible without exposing a non-functional interrupt", () => {
238 const waitingRun = run("run-waiting", 1, "waiting");
239 const projection = { ...v2Projection, runs: [waitingRun], updatedAt: now };
240
241 expect(deriveThreadActivityRun(projection)).toMatchObject({
242 runId: waitingRun.id,
243 status: "waiting",
244 });
245
246 const runtime = deriveThreadRuntime(projection);
247 expect(runtime).toMatchObject({
248 status: "waiting",
249 activeRunId: null,
250 });
251 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(false);
252 });
253
254 it("does not expose a stale active run after the runtime parks at idle", () => {
255 const runtime = {
256 status: "idle" as const,
257 activeRunId: RunId.make("run-stale"),
258 providerInstanceId: v2Projection.thread.providerInstanceId,
259 providerName: null,
260 lastError: null,
261 updatedAt: DateTime.formatIso(now),
262 };
263
264 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(false);
265 });
266
267 it.each(["preparing", "starting"] as const)("keeps an active %s run interruptible", (status) => {
268 const runtime = {
269 status,
270 activeRunId: RunId.make(`run-${status}`),
271 providerInstanceId: v2Projection.thread.providerInstanceId,
272 providerName: null,
273 lastError: null,
274 updatedAt: DateTime.formatIso(now),
275 };
276
277 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(true);
278 });
279});
280
281describe("deriveRunlessWorkStartedAt", () => {
282 const later = DateTime.makeUnsafe("2026-07-28T10:05:00.000Z");
283 const rootTurn = (
284 status: OrchestrationV2ExecutionNode["status"],
285 startedAt = now,
286 ): OrchestrationV2ExecutionNode => ({
287 id: NodeId.make("child-root"),
288 threadId: v2Projection.thread.id,
289 runId: null,
290 parentNodeId: null,
291 rootNodeId: NodeId.make("child-root"),
292 kind: "root_turn",
293 status,
294 countsForRun: false,
295 providerThreadId: null,
296 providerTurnId: null,
297 nativeItemRef: null,
298 runtimeRequestId: null,
299 checkpointScopeId: null,
300 startedAt,
301 completedAt: null,
302 });
303
304 const nativeChild = {
305 ...v2Projection,
306 thread: {
307 ...v2Projection.thread,
308 creationSource: "provider" as const,
309 lineage: {
310 parentThreadId: ThreadId.make("parent"),
311 relationshipToParent: "subagent" as const,
312 rootThreadId: ThreadId.make("parent"),
313 },
314 },
315 };
316
317 it("times a provider-native subagent from its runless root turn while it works", () => {
318 const projection = { ...nativeChild, nodes: [rootTurn("running", later)] };
319 expect(deriveRunlessWorkStartedAt(projection)).toBe("2026-07-28T10:05:00.000Z");
320 // The subagent has no run, so it stays unstoppable and unqueueable.
321 expect(deriveThreadRuntime(projection)).toBeNull();
322 });
323
324 it.each(["completed", "cancelled", "failed", "interrupted", "idle"] as const)(
325 "is idle once the subagent is %s",
326 (status) => {
327 expect(deriveRunlessWorkStartedAt({ ...nativeChild, nodes: [rootTurn(status)] })).toBe(null);
328 },
329 );
330
331 it("ignores root turns that belong to a run, and threads the provider does not run", () => {
332 const owned = { ...rootTurn("running"), runId: RunId.make("run-1") };
333 expect(deriveRunlessWorkStartedAt({ ...nativeChild, nodes: [owned] })).toBeNull();
334 expect(
335 deriveRunlessWorkStartedAt({ ...v2Projection, nodes: [rootTurn("running")] }),
336 ).toBeNull();
337 });
338});
339
340describe("deriveProviderSubagentStatus", () => {
341 const root = {
342 id: NodeId.make("child-root"),
343 threadId: v2Projection.thread.id,
344 runId: null,
345 parentNodeId: null,
346 rootNodeId: NodeId.make("child-root"),
347 kind: "root_turn" as const,
348 status: "completed" as const,
349 countsForRun: false,
350 providerThreadId: null,
351 providerTurnId: null,
352 nativeItemRef: null,
353 runtimeRequestId: null,
354 checkpointScopeId: null,
355 startedAt: now,
356 completedAt: now,
357 };
358 const child = (creationSource: "provider" | "mcp") => ({
359 ...v2Projection,
360 thread: {
361 ...v2Projection.thread,
362 creationSource,
363 lineage: {
364 parentThreadId: ThreadId.make("parent"),
365 relationshipToParent: "subagent" as const,
366 rootThreadId: ThreadId.make("parent"),
367 },
368 },
369 nodes: [root],
370 });
371
372 it("reports the provider's own subagent from its runless root turn", () => {
373 expect(deriveProviderSubagentStatus(child("provider"))).toEqual({
374 status: "completed",
375 startedAt: "2026-07-28T10:00:00.000Z",
376 completedAt: "2026-07-28T10:00:00.000Z",
377 });
378 });
379
380 it("says how long the subagent has worked, or took", () => {
381 const startedAt = "2026-07-28T10:00:00.000Z";
382 const at = (iso: string) => Date.parse(iso);
383 expect(
384 formatProviderSubagentStatus(
385 { status: "running", startedAt, completedAt: null },
386 at("2026-07-28T10:01:05.400Z"),
387 ),
388 ).toBe("Working 1m 5s");
389 expect(
390 formatProviderSubagentStatus(
391 { status: "completed", startedAt, completedAt: "2026-07-28T10:00:34.000Z" },
392 at("2026-07-28T11:00:00.000Z"),
393 ),
394 ).toBe("Completed in 34s");
395 expect(
396 formatProviderSubagentStatus(
397 { status: "cancelled", startedAt, completedAt: "2026-07-28T10:00:34.000Z" },
398 0,
399 ),
400 ).toBe("Cancelled");
401 expect(formatProviderSubagentStatus(null, 0)).toBe("Starting");
402 });
403
404 it("leaves T3 delegated tasks and ordinary threads alone", () => {
405 expect(deriveProviderSubagentStatus(child("mcp"))).toBeNull();
406 expect(deriveProviderSubagentStatus({ ...v2Projection, nodes: [root] })).toBeNull();
407 });
408});
409
410describe("formatModelSelectionEffort", () => {
411 const instanceId = ProviderInstanceId.make("claudeAgent");
412 const selection = (options?: ReadonlyArray<{ id: string; value: string }>) => ({
413 instanceId,
414 model: "claude-sonnet-5",
415 ...(options === undefined ? {} : { options }),
416 });
417 const catalog = (descriptor: { currentValue?: string }) => [
418 {
419 slug: "claude-sonnet-5",
420 name: "Claude Sonnet 5",
421 isCustom: false,
422 capabilities: {
423 optionDescriptors: [
424 {
425 id: "effort",
426 label: "Reasoning",
427 type: "select" as const,
428 options: [
429 { id: "medium", label: "Medium" },
430 { id: "high", label: "High", isDefault: true },
431 { id: "xhigh", label: "Extra High" },
432 ],
433 ...descriptor,
434 },
435 ],
436 },
437 },
438 ];
439
440 it("shows the model's default effort when the user never picked one", () => {
441 expect(formatModelSelectionEffort(selection(), catalog({}))).toBe("High");
442 });
443
444 it("names a stored effort the way the catalog does", () => {
445 expect(
446 formatModelSelectionEffort(selection([{ id: "effort", value: "xhigh" }]), catalog({})),
447 ).toBe("Extra High");
448 });
449
450 it("uses the descriptor's current value over the default", () => {
451 expect(formatModelSelectionEffort(selection(), catalog({ currentValue: "medium" }))).toBe(
452 "Medium",
453 );
454 });
455
456 it("shows nothing for a model the catalog does not describe", () => {
457 expect(formatModelSelectionEffort(selection([{ id: "effort", value: "high" }]))).toBeNull();
458 expect(
459 formatModelSelectionEffort(
460 { ...selection(), model: "claude-haiku-4-5" },
461 catalog({ currentValue: "medium" }),
462 ),
463 ).toBeNull();
464 });
465});
466
467describe("threadRuntimeCanArchive", () => {
468 const runtime = (
469 status: ThreadRuntimeSummary["status"],
470 activeRunId: ThreadRuntimeSummary["activeRunId"],
471 ): ThreadRuntimeSummary => ({
472 status,
473 activeRunId,
474 providerInstanceId: v2Projection.thread.providerInstanceId,
475 providerName: null,
476 lastError: null,
477 updatedAt: DateTime.formatIso(now),
478 });
479
480 it.each(["preparing", "starting", "running"] as const)(
481 "blocks archive while a provider is %s",
482 (status) => {
483 expect(threadRuntimeCanArchive(runtime(status, RunId.make(`run-${status}`)))).toBe(false);
484 },
485 );
486
487 it("only blocks a queued runtime when a provider run remains attached", () => {
488 expect(threadRuntimeCanArchive(runtime("queued", RunId.make("run-queued")))).toBe(false);
489 expect(threadRuntimeCanArchive(runtime("queued", null))).toBe(true);
490 });
491
492 it("allows waiting and idle threads", () => {
493 expect(threadRuntimeCanArchive(runtime("waiting", RunId.make("run-finished")))).toBe(true);
494 expect(threadRuntimeCanArchive(runtime("idle", null))).toBe(true);
495 });
496});
497
498describe("presentPendingBackgroundWork", () => {
499 it.each(["Subagent:", "Subagent: "])(
500 "falls back to the subagent noun when %s has no display name",
501 (description) => {
502 const presentation = presentPendingBackgroundWork([
503 { taskId: "unnamed", kind: "subagent", description },
504 ]);
505
506 expect(presentation?.title).toBe("Waiting on a subagent");
507 expect(presentation?.items[0]?.label).toBe("subagent");
508 },
509 );
510
511 it.each([
512 "/root/luna_window_properties",
513 "Subagent: /root/luna_window_properties",
514 "/root/parent/luna_window_properties",
515 ])("uses the subagent display name for %s", (description) => {
516 const childThreadId = ThreadId.make("thread:luna");
517 const presentation = presentPendingBackgroundWork([
518 { taskId: "luna", kind: "subagent", description, childThreadId },
519 ]);
520
521 expect(presentation).toEqual({
522 title: "Waiting on subagent Luna Window Properties",
523 items: [{ taskId: "luna", kind: "subagent", label: "Luna Window Properties", childThreadId }],
524 waiting: true,
525 });
526 });
527
528 it("formats subagent names in a mixed roster and preserves command descriptions", () => {
529 const presentation = presentPendingBackgroundWork([
530 { taskId: "cmd", kind: "command", description: "/root/run_tests" },
531 { taskId: "luna", kind: "subagent", description: "/root/luna_window_properties" },
532 { taskId: "review", kind: "subagent", description: "Review src/math.ts" },
533 ]);
534
535 expect(presentation?.title).toBe("Waiting on 2 subagents and 1 command");
536 expect(presentation?.items.map((item) => item.label)).toEqual([
537 "Luna Window Properties",
538 "Review src/math.ts",
539 "/root/run_tests",
540 ]);
541 });
542
543 it("names a single piece of work by kind", () => {
544 expect(
545 presentPendingBackgroundWork([
546 { taskId: "a", kind: "subagent", description: "Review src/math.ts" },
547 ])?.title,
548 ).toBe("Waiting on subagent Review src/math.ts");
549 expect(presentPendingBackgroundWork([{ taskId: "a", kind: "monitor" }])?.title).toBe(
550 "Waiting on a monitor",
551 );
552 expect(presentPendingBackgroundWork([])).toBeNull();
553 });
554
555 // A command left running, such as a dev server, does not wake the agent.
556 it("says only commands are running, not waited on", () => {
557 expect(
558 presentPendingBackgroundWork([
559 { taskId: "dev", kind: "command", description: "Start the shared dev server" },
560 ]),
561 ).toMatchObject({ title: "Running: Start the shared dev server", waiting: false });
562 expect(presentPendingBackgroundWork([{ taskId: "a", kind: "command" }])).toMatchObject({
563 title: "Running a command",
564 waiting: false,
565 });
566 expect(
567 presentPendingBackgroundWork([
568 { taskId: "a", kind: "command", description: "vp run dev" },
569 { taskId: "b", kind: "command", description: "tailscale serve" },
570 ]),
571 ).toMatchObject({ title: "Running 2 commands", waiting: false });
572 expect(
573 presentPendingBackgroundWork([
574 { taskId: "a", kind: "command", description: "vp run dev" },
575 { taskId: "b", kind: "monitor", description: "Watch PR checks" },
576 ]),
577 ).toMatchObject({ title: "Waiting on 1 command and 1 monitor", waiting: true });
578 });
579
580 it("groups work by kind, subagents first, and keeps each name", () => {
581 const presentation = presentPendingBackgroundWork([
582 { taskId: "cmd", kind: "command", description: "npm test" },
583 {
584 taskId: "b",
585 kind: "subagent",
586 description: "Write tests",
587 childThreadId: ThreadId.make("thread:b"),
588 },
589 { taskId: "a", kind: "subagent", description: "Review src/math.ts" },
590 ]);
591 expect(presentation?.title).toBe("Waiting on 2 subagents and 1 command");
592 expect(presentation?.items.map((item) => [item.kind, item.label, item.childThreadId])).toEqual([
593 ["subagent", "Write tests", "thread:b"],
594 ["subagent", "Review src/math.ts", undefined],
595 ["command", "npm test", undefined],
596 ]);
597 });
598
599 it("names generic work, including rosters from servers that predate kinds", () => {
600 expect(
601 presentPendingBackgroundWork([
602 { taskId: "bash", kind: "command", description: "Background sleep" },
603 { taskId: "watch", kind: "monitor" },
604 { taskId: "other", kind: "background_task" },
605 ])?.title,
606 ).toBe("Waiting on 1 command, 1 monitor and 1 background task");
607 expect(presentPendingBackgroundWork([{ taskId: "old", kind: "background_task" }])?.title).toBe(
608 "Waiting on a background task",
609 );
610 });
611});
612
613describe("provider-reported model selection", () => {
614 const selected = v2Projection.thread.modelSelection;
615 const reported = { ...selected, options: [{ id: "reasoningEffort", value: "default" }] };
616 const providerThread = {
617 id: ProviderThreadId.make("active"),
618 driver: ProviderDriverKind.make("codex"),
619 providerInstanceId: selected.instanceId,
620 providerSessionId: ProviderSessionId.make("session"),
621 appThreadId: v2Projection.thread.id,
622 ownerNodeId: null,
623 nativeThreadRef: null,
624 nativeConversationHeadRef: null,
625 status: "idle" as const,
626 firstRunOrdinal: null,
627 lastRunOrdinal: null,
628 handoffIds: [],
629 forkedFrom: null,
630 createdAt: now,
631 updatedAt: now,
632 nativeMetadata: { modelSelection: reported },
633 };
634 const projection = {
635 ...v2Projection,
636 thread: { ...v2Projection.thread, activeProviderThreadId: providerThread.id },
637 providerThreads: [providerThread],
638 };
639
640 it("reads only the active provider thread's reported selection", () => {
641 expect(deriveReportedModelSelection(projection)).toBe(reported);
642 expect(
643 deriveReportedModelSelection({
644 ...projection,
645 thread: { ...projection.thread, activeProviderThreadId: null },
646 }),
647 ).toBeNull();
648 expect(
649 deriveReportedModelSelection({
650 ...projection,
651 providerThreads: [
652 { ...providerThread, providerInstanceId: ProviderInstanceId.make("other") },
653 ],
654 }),
655 ).toBeNull();
656 });
657
658 it("shows the reported default in a subagent's effort label", () => {
659 const models = [
660 {
661 slug: selected.model,
662 name: selected.model,
663 isCustom: false,
664 capabilities: {
665 optionDescriptors: [
666 {
667 id: "variant",
668 label: "Reasoning",
669 type: "select" as const,
670 options: [{ id: "high", label: "High" }],
671 },
672 ],
673 },
674 },
675 ];
676 const variantReport = { ...selected, options: [{ id: "variant", value: "default" }] };
677 expect(formatModelSelectionEffort(selected, models, variantReport)).toBe("Default");
678 expect(formatModelSelectionEffort(selected, models)).toBe("Unknown");
679 });
680});
681
682describe("presentProviderGoal", () => {
683 it("summarizes Codex accounting and offers resume once the goal stops short", () => {
684 expect(
685 presentProviderGoal(
686 {
687 objective: "Ship the feature",
688 status: "paused",
689 tokensUsed: 12_400,
690 tokenBudget: 50_000,
691 timeUsedSeconds: 245,
692 },
693 false,
694 ),
695 ).toEqual({
696 title: "Goal paused",
697 objective: "Ship the feature",
698 usage: "12k / 50k tokens · 4m 5s",
699 canResume: true,
700 });
701 });
702
703 it("counts Claude evaluator checks and never offers resume", () => {
704 expect(
705 presentProviderGoal({ objective: "All tests pass", status: "active", checks: 1 }, true),
706 ).toEqual({
707 title: "Pursuing goal",
708 objective: "All tests pass",
709 usage: "1 check",
710 canResume: false,
711 });
712 expect(
713 presentProviderGoal({ objective: "All tests pass", status: "complete", checks: 0 }, false)
714 .usage,
715 ).toBeNull();
716 expect(
717 presentProviderGoal({ objective: "All tests pass", status: "active" }, false).title,
718 ).toBe("Goal set");
719 });
720});