1import {
2 TurnItemId,
3 NodeId,
4 MessageId,
5 ProviderInstanceId,
6 ProviderThreadId,
7 ProviderSessionId,
8 ProviderDriverKind,
9 ProviderTurnId,
10 RunAttemptId,
11 RunId,
12 ThreadId,
13 type OrchestrationV2ExecutionNode,
14 type OrchestrationV2RunStatus,
15} from "@t3tools/contracts";
16import * as DateTime from "effect/DateTime";
17import { createModelCapabilities, createModelSelection } from "@t3tools/shared/model";
18import { usageLimitBlockedRun } from "@t3tools/shared/orchestrationV2ThreadError";
19import { describe, expect, it } from "vite-plus/test";
20
21import { v2Projection } from "./orchestrationV2TestFixtures.ts";
22import {
23 deriveReportedModelSelection,
24 deriveLatestThreadRun,
25 deriveProviderSubagentStatus,
26 deriveProviderExtensionStatuses,
27 deriveResumableRunId,
28 deriveRunlessWorkStartedAt,
29 deriveThreadActivityRun,
30 deriveThreadRuntime,
31 formatModelSelectionEffort,
32 isBackgroundWorkStopPending,
33 presentPendingBackgroundWork,
34 presentProviderGoal,
35 presentRunStop,
36 threadRuntimeHasInterruptibleRun,
37} from "./threadExecution.ts";
38
39const now = DateTime.makeUnsafe("2026-07-28T10:00:00.000Z");
40
41function run(id: string, ordinal: number, status: OrchestrationV2RunStatus) {
42 return {
43 id: RunId.make(id),
44 threadId: v2Projection.thread.id,
45 ordinal,
46 providerInstanceId: v2Projection.thread.providerInstanceId,
47 modelSelection: v2Projection.thread.modelSelection,
48 providerThreadId: null,
49 userMessageId: MessageId.make(`message-${id}`),
50 rootNodeId: null,
51 activeAttemptId: null,
52 status,
53 requestedAt: now,
54 startedAt: status === "queued" ? null : now,
55 completedAt: null,
56 checkpointId: null,
57 contextHandoffId: null,
58 };
59}
60
61describe("thread execution presentation", () => {
62 it("shows extension statuses only from the selected provider's current live session", () => {
63 const instanceId = v2Projection.thread.modelSelection.instanceId;
64 const providerThreadId = ProviderThreadId.make("native-status-thread");
65 const sessionId = ProviderSessionId.make("native-status-session");
66 const statuses = [{ key: "watch", text: "Watching" }];
67 const projection = {
68 thread: { ...v2Projection.thread, activeProviderThreadId: providerThreadId },
69 providerThreads: [
70 { id: providerThreadId, providerSessionId: sessionId, providerInstanceId: instanceId },
71 ],
72 providerSessions: [
73 {
74 id: sessionId,
75 providerInstanceId: instanceId,
76 status: "ready" as const,
77 extensionStatuses: statuses,
78 },
79 ],
80 };
81 expect(deriveProviderExtensionStatuses(projection)).toEqual(statuses);
82 expect(
83 deriveProviderExtensionStatuses({
84 ...projection,
85 providerSessions: [
86 ...projection.providerSessions,
87 {
88 ...projection.providerSessions[0]!,
89 id: ProviderSessionId.make("unrelated"),
90 extensionStatuses: [{ key: "wrong", text: "Wrong session" }],
91 },
92 ],
93 }),
94 ).toEqual(statuses);
95 expect(
96 deriveProviderExtensionStatuses({
97 ...projection,
98 providerSessions: [{ ...projection.providerSessions[0]!, status: "stopped" }],
99 }),
100 ).toEqual([]);
101 expect(
102 deriveProviderExtensionStatuses({
103 ...projection,
104 providerSessions: [{ ...projection.providerSessions[0]!, status: "error" }],
105 }),
106 ).toEqual(statuses);
107 expect(
108 deriveProviderExtensionStatuses({
109 ...projection,
110 providerSessions: [
111 { ...projection.providerSessions[0]!, status: "error", extensionStatuses: [] },
112 ],
113 }),
114 ).toEqual([]);
115 expect(
116 deriveProviderExtensionStatuses({
117 ...projection,
118 providerSessions: [{ ...projection.providerSessions[0]!, extensionStatuses: undefined }],
119 }),
120 ).toEqual([]);
121 expect(
122 deriveProviderExtensionStatuses({
123 ...projection,
124 thread: {
125 ...projection.thread,
126 modelSelection: { instanceId: ProviderInstanceId.make("other"), model: "default" },
127 },
128 }),
129 ).toEqual([]);
130 expect(deriveProviderExtensionStatuses(null)).toEqual([]);
131 });
132
133 it("derives the current root failure without inheriting errors from children or previous runs", () => {
134 const failed = { ...run("limited", 1, "failed"), rootNodeId: NodeId.make("root") };
135 const item = {
136 id: TurnItemId.make("limit-error"),
137 threadId: v2Projection.thread.id,
138 runId: failed.id,
139 nodeId: failed.rootNodeId,
140 providerThreadId: null,
141 providerTurnId: null,
142 nativeItemRef: null,
143 parentItemId: null,
144 ordinal: 1,
145 type: "error" as const,
146 status: "failed" as const,
147 title: "Usage limit reached",
148 startedAt: now,
149 completedAt: now,
150 updatedAt: now,
151 failure: {
152 class: "usage_limit" as const,
153 message: "Plan limit reached",
154 code: "usageLimitExceeded",
155 retryable: null,
156 },
157 };
158 const projection = { ...v2Projection, runs: [failed], turnItems: [item] };
159 expect(deriveThreadRuntime(projection)).toMatchObject({
160 lastError: "Plan limit reached",
161 lastErrorClass: "usage_limit",
162 });
163 expect(deriveResumableRunId(projection, deriveThreadRuntime(projection))).toBe(failed.id);
164 expect(
165 deriveThreadRuntime({
166 ...projection,
167 turnItems: [{ ...item, nodeId: NodeId.make("child") }],
168 }),
169 ).toMatchObject({ lastError: null, lastErrorClass: null });
170 expect(
171 deriveThreadRuntime({
172 ...projection,
173 runs: [{ ...failed, rootNodeId: NodeId.make("new-root") }],
174 }),
175 ).toMatchObject({ lastError: null, lastErrorClass: null });
176 expect(
177 deriveThreadRuntime({ ...projection, runs: [failed, run("new", 2, "running")] }),
178 ).toMatchObject({ status: "running", lastError: null, lastErrorClass: null });
179 });
180
181 // regression: #15029 working timers no longer reset on every background wake
182 it("counts a wake run's activity from the start of the work it continues", () => {
183 const workStartedAt = DateTime.makeUnsafe("2026-07-28T09:20:00.000Z");
184 const wake = { ...run("wake", 2, "running"), workStartedAt };
185 expect(
186 deriveThreadRuntime({ ...v2Projection, runs: [run("prompt", 1, "completed"), wake] })
187 ?.activityStartedAt,
188 ).toBe("2026-07-28T09:20:00.000Z");
189 expect(
190 deriveThreadRuntime({ ...v2Projection, runs: [run("prompt", 1, "running")] })
191 ?.activityStartedAt,
192 ).toBe("2026-07-28T10:00:00.000Z");
193 });
194
195 it("keeps a subscription limit visible while later messages stay queued", () => {
196 const failed = {
197 ...run("limited", 1, "failed"),
198 rootNodeId: NodeId.make("root"),
199 completedAt: now,
200 };
201 const queued = run("queued", 2, "queued");
202 const item = {
203 id: TurnItemId.make("limit-error"),
204 threadId: v2Projection.thread.id,
205 runId: failed.id,
206 nodeId: failed.rootNodeId,
207 providerThreadId: null,
208 providerTurnId: null,
209 nativeItemRef: null,
210 parentItemId: null,
211 ordinal: 1,
212 type: "error" as const,
213 status: "failed" as const,
214 title: "Usage limit reached",
215 startedAt: now,
216 completedAt: now,
217 updatedAt: now,
218 failure: {
219 class: "usage_limit" as const,
220 message: "Plan limit reached",
221 code: "usageLimitExceeded",
222 retryable: null,
223 },
224 };
225 const projection = { ...v2Projection, runs: [failed, queued], turnItems: [item] };
226
227 expect(deriveLatestThreadRun(projection)?.runId).toBe(failed.id);
228 expect(deriveThreadActivityRun(projection)?.runId).toBe(failed.id);
229 expect(deriveThreadRuntime(projection)).toMatchObject({
230 status: "failed",
231 lastError: "Plan limit reached",
232 lastErrorClass: "usage_limit",
233 });
234 expect(deriveResumableRunId(projection, deriveThreadRuntime(projection))).toBe(failed.id);
235 const cancelledQueued = {
236 ...run("cancelled-queued", 3, "cancelled"),
237 startedAt: null,
238 completedAt: now,
239 };
240 expect(
241 usageLimitBlockedRun([failed, queued, cancelledQueued], projection.turnItems, null)?.id,
242 ).toBe(failed.id);
243 expect(
244 deriveThreadRuntime({ ...projection, runs: [failed, queued, cancelledQueued] }),
245 ).toMatchObject({ status: "failed", lastErrorClass: "usage_limit" });
246 const providerError = {
247 ...projection,
248 turnItems: [{ ...item, failure: { ...item.failure, class: "provider_error" as const } }],
249 };
250 expect(deriveThreadRuntime(providerError)).toMatchObject({
251 status: "queued",
252 lastErrorClass: null,
253 });
254 expect(deriveResumableRunId(providerError, deriveThreadRuntime(providerError))).toBeNull();
255 });
256
257 it("keeps live activity attached to an executing run when a newer run is queued", () => {
258 const runningRun = run("run-running", 1, "running");
259 const queuedRun = run("run-queued", 2, "queued");
260 const projection = { ...v2Projection, runs: [queuedRun, runningRun], updatedAt: now };
261
262 expect(deriveLatestThreadRun(projection)?.runId).toBe(queuedRun.id);
263 expect(deriveThreadActivityRun(projection)).toMatchObject({
264 runId: runningRun.id,
265 status: "running",
266 });
267
268 const runtime = deriveThreadRuntime(projection);
269 expect(runtime).toMatchObject({
270 status: "running",
271 activeRunId: runningRun.id,
272 });
273 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(true);
274 });
275
276 it("marks an open run whose provider turn already ended", () => {
277 const attemptId = RunAttemptId.make("attempt-open");
278 const openRun = { ...run("run-open", 1, "running"), activeAttemptId: attemptId };
279 const providerTurn = (status: "running" | "failed") => ({
280 id: ProviderTurnId.make("provider-turn-open"),
281 providerThreadId: ProviderThreadId.make("provider-thread-open"),
282 nodeId: NodeId.make("node-open"),
283 runAttemptId: attemptId,
284 nativeTurnRef: null,
285 ordinal: 1,
286 status,
287 startedAt: now,
288 completedAt: status === "running" ? null : now,
289 });
290 const projection = (status: "running" | "failed") => ({
291 ...v2Projection,
292 runs: [openRun],
293 providerTurns: [providerTurn(status)],
294 updatedAt: now,
295 });
296
297 expect(deriveThreadActivityRun(projection("running"))?.turnEnded).toBeUndefined();
298 expect(deriveThreadActivityRun(projection("failed"))).toMatchObject({
299 status: "running",
300 turnEnded: true,
301 });
302 });
303
304 // The server marks a run running before the provider opens, which can take minutes.
305 it("labels an open run whose provider has not started a turn", () => {
306 const attemptId = RunAttemptId.make("attempt-starting");
307 const openRun = { ...run("run-starting", 1, "running"), activeAttemptId: attemptId };
308 const session = {
309 id: ProviderSessionId.make("session-starting"),
310 driver: ProviderDriverKind.make("claudeAgent"),
311 providerInstanceId: openRun.providerInstanceId,
312 status: "starting" as const,
313 cwd: "/repo",
314 model: null,
315 // The label reads only the driver.
316 capabilities: {} as never,
317 createdAt: now,
318 updatedAt: now,
319 lastError: null,
320 };
321 const starting = { ...v2Projection, runs: [openRun], providerSessions: [session] };
322
323 expect(deriveThreadActivityRun(starting)?.providerStartingLabel).toBe("Starting Claude");
324 expect(
325 deriveThreadActivityRun({ ...starting, providerSessions: [] })?.providerStartingLabel,
326 ).toBe("Starting");
327 expect(
328 deriveThreadActivityRun({
329 ...starting,
330 providerTurns: [
331 {
332 id: ProviderTurnId.make("provider-turn-starting"),
333 providerThreadId: ProviderThreadId.make("provider-thread-starting"),
334 nodeId: NodeId.make("node-starting"),
335 runAttemptId: attemptId,
336 nativeTurnRef: null,
337 ordinal: 1,
338 status: "running",
339 startedAt: now,
340 completedAt: null,
341 },
342 ],
343 })?.providerStartingLabel,
344 ).toBeUndefined();
345 });
346
347 it("presents a held queue as the stopped run instead of queued work", () => {
348 const interrupted = { ...run("run-interrupted", 1, "interrupted"), completedAt: now };
349 const held = { ...run("run-held", 2, "queued"), queueHeld: true };
350 const projection = { ...v2Projection, runs: [interrupted, held], updatedAt: now };
351
352 expect(deriveLatestThreadRun(projection)?.runId).toBe(interrupted.id);
353 expect(deriveThreadActivityRun(projection)?.runId).toBe(interrupted.id);
354 expect(deriveThreadRuntime(projection)).toMatchObject({
355 status: "interrupted",
356 activeRunId: null,
357 });
358 expect(deriveResumableRunId(projection, deriveThreadRuntime(projection))).toBe(interrupted.id);
359
360 const resumed = { ...projection, runs: [interrupted, { ...held, queueHeld: false }] };
361 expect(deriveThreadRuntime(resumed)).toMatchObject({ status: "queued" });
362
363 const onlyHeld = {
364 ...projection,
365 runs: [held],
366 thread: { ...projection.thread, activeProviderThreadId: null },
367 };
368 expect(deriveLatestThreadRun(onlyHeld)).toBeNull();
369 expect(deriveThreadRuntime(onlyHeld)).toBeNull();
370 });
371
372 it("does not expose a queued-only run as interruptible", () => {
373 const queuedRun = run("run-queued", 1, "queued");
374 const projection = { ...v2Projection, runs: [queuedRun], updatedAt: now };
375
376 expect(deriveThreadActivityRun(projection)).toMatchObject({
377 runId: queuedRun.id,
378 status: "queued",
379 });
380
381 const runtime = deriveThreadRuntime(projection);
382 expect(runtime).toMatchObject({
383 status: "queued",
384 activeRunId: null,
385 });
386 expect(threadRuntimeHasInterruptibleRun(runtime)).toBe(false);
387 });
388});
389
390describe("presentRunStop", () => {
391 const signal = (type: "run_interrupt_request" | "run_interrupt_result", runId: RunId) => ({
392 id: TurnItemId.make(`${type}:${runId}`),
393 threadId: v2Projection.thread.id,
394 runId,
395 nodeId: null,
396 providerThreadId: null,
397 providerTurnId: null,
398 nativeItemRef: null,
399 parentItemId: null,
400 ordinal: 1,
401 status: type === "run_interrupt_result" ? ("interrupted" as const) : ("completed" as const),
402 title: "Interrupt",
403 startedAt: now,
404 completedAt: now,
405 updatedAt: now,
406 type,
407 message: "Stopped by user",
408 });
409 const running = run("run-stopping", 1, "running");
410 const request = signal("run_interrupt_request", running.id);
411
412 it.each([
413 { name: "no Stop", runs: [running], turnItems: [], state: null },
414 {
415 name: "a Stop the agent is winding down",
416 runs: [running],
417 turnItems: [request],
418 state: "stopping",
419 },
420 {
421 name: "a Stop the provider never confirmed",
422 runs: [running],
423 turnItems: [{ ...request, unresponsiveAt: now }],
424 state: "unresponsive",
425 },
426 {
427 name: "a Stop that ended the run",
428 runs: [running],
429 turnItems: [request, signal("run_interrupt_result", running.id)],
430 state: null,
431 },
432 {
433 name: "a settled run's background work still stopping",
434 runs: [{ ...running, status: "completed" as const, completedAt: now }],
435 turnItems: [request],
436 state: null,
437 },
438 {
439 name: "a settled run whose background work ignored Stop",
440 runs: [{ ...running, status: "completed" as const, completedAt: now }],
441 turnItems: [{ ...request, unresponsiveAt: now }],
442 state: "unresponsive",
443 },
444 {
445 name: "a settled run whose background work ignored Stop, with a queued follow-up",
446 runs: [
447 { ...running, status: "completed" as const, completedAt: now },
448 run("run-queued", 2, "queued"),
449 ],
450 turnItems: [{ ...request, unresponsiveAt: now }],
451 state: "unresponsive",
452 },
453 ])("presents $name", ({ runs, turnItems, state }) => {
454 expect(presentRunStop({ runs, turnItems })?.state ?? null).toBe(state);
455 });
456});
457
458describe("deriveRunlessWorkStartedAt", () => {
459 const later = DateTime.makeUnsafe("2026-07-28T10:05:00.000Z");
460 const rootTurn = (
461 status: OrchestrationV2ExecutionNode["status"],
462 startedAt = now,
463 ): OrchestrationV2ExecutionNode => ({
464 id: NodeId.make("child-root"),
465 threadId: v2Projection.thread.id,
466 runId: null,
467 parentNodeId: null,
468 rootNodeId: NodeId.make("child-root"),
469 kind: "root_turn",
470 status,
471 countsForRun: false,
472 providerThreadId: null,
473 providerTurnId: null,
474 nativeItemRef: null,
475 runtimeRequestId: null,
476 checkpointScopeId: null,
477 startedAt,
478 completedAt: null,
479 });
480
481 const nativeChild = {
482 ...v2Projection,
483 thread: {
484 ...v2Projection.thread,
485 creationSource: "provider" as const,
486 lineage: {
487 parentThreadId: ThreadId.make("parent"),
488 relationshipToParent: "subagent" as const,
489 rootThreadId: ThreadId.make("parent"),
490 },
491 },
492 };
493
494 it("times a provider-native subagent from its runless root turn while it works", () => {
495 const projection = { ...nativeChild, nodes: [rootTurn("running", later)] };
496 expect(deriveRunlessWorkStartedAt(projection)).toBe("2026-07-28T10:05:00.000Z");
497 expect(deriveThreadRuntime(projection)).toBeNull();
498 });
499
500 it("ignores root turns that belong to a run, and threads the provider does not run", () => {
501 const owned = { ...rootTurn("running"), runId: RunId.make("run-1") };
502 expect(deriveRunlessWorkStartedAt({ ...nativeChild, nodes: [owned] })).toBeNull();
503 expect(
504 deriveRunlessWorkStartedAt({ ...v2Projection, nodes: [rootTurn("running")] }),
505 ).toBeNull();
506 });
507});
508
509describe("deriveProviderSubagentStatus", () => {
510 const root = {
511 id: NodeId.make("child-root"),
512 threadId: v2Projection.thread.id,
513 runId: null,
514 parentNodeId: null,
515 rootNodeId: NodeId.make("child-root"),
516 kind: "root_turn" as const,
517 status: "completed" as const,
518 countsForRun: false,
519 providerThreadId: null,
520 providerTurnId: null,
521 nativeItemRef: null,
522 runtimeRequestId: null,
523 checkpointScopeId: null,
524 startedAt: now,
525 completedAt: now,
526 };
527 const child = (creationSource: "provider" | "mcp") => ({
528 ...v2Projection,
529 thread: {
530 ...v2Projection.thread,
531 creationSource,
532 lineage: {
533 parentThreadId: ThreadId.make("parent"),
534 relationshipToParent: "subagent" as const,
535 rootThreadId: ThreadId.make("parent"),
536 },
537 },
538 nodes: [root],
539 });
540
541 it("leaves T3 delegated tasks and ordinary threads alone", () => {
542 expect(deriveProviderSubagentStatus(child("mcp"))).toBeNull();
543 expect(deriveProviderSubagentStatus({ ...v2Projection, nodes: [root] })).toBeNull();
544 });
545});
546
547describe("provider-reported model selection", () => {
548 const selected = v2Projection.thread.modelSelection;
549 const reported = { ...selected, options: [{ id: "reasoningEffort", value: "default" }] };
550 const providerThread = {
551 id: ProviderThreadId.make("active"),
552 driver: ProviderDriverKind.make("codex"),
553 providerInstanceId: selected.instanceId,
554 providerSessionId: ProviderSessionId.make("session"),
555 appThreadId: v2Projection.thread.id,
556 ownerNodeId: null,
557 nativeThreadRef: null,
558 nativeConversationHeadRef: null,
559 status: "idle" as const,
560 firstRunOrdinal: null,
561 lastRunOrdinal: null,
562 handoffIds: [],
563 forkedFrom: null,
564 createdAt: now,
565 updatedAt: now,
566 nativeMetadata: { modelSelection: reported },
567 };
568 const projection = {
569 ...v2Projection,
570 thread: { ...v2Projection.thread, activeProviderThreadId: providerThread.id },
571 providerThreads: [providerThread],
572 };
573
574 it("reads only the active provider thread's reported selection", () => {
575 expect(deriveReportedModelSelection(projection)).toBe(reported);
576 expect(
577 deriveReportedModelSelection({
578 ...projection,
579 thread: { ...projection.thread, activeProviderThreadId: null },
580 }),
581 ).toBeNull();
582 expect(
583 deriveReportedModelSelection({
584 ...projection,
585 providerThreads: [
586 { ...providerThread, providerInstanceId: ProviderInstanceId.make("other") },
587 ],
588 }),
589 ).toBeNull();
590 });
591});
592
593describe("formatModelSelectionEffort", () => {
594 it.each([
595 { id: "reasoningEffort" },
596 { id: "effort" },
597 { id: "reasoning_effort" },
598 { id: "reasoning" },
599 { id: "variant" },
600 { id: "thinking_level" },
601 { id: "thinking" },
602 { id: "agent/thought-level", role: "reasoning" as const },
603 ])("labels a saved $id without loading thread details", (option) => {
604 const selection = createModelSelection(ProviderInstanceId.make("provider"), "model", [
605 { id: option.id, value: "medium" },
606 ]);
607 const models = [
608 {
609 slug: "model",
610 name: "Model",
611 isCustom: false,
612 capabilities: createModelCapabilities({
613 optionDescriptors: [
614 {
615 ...option,
616 label: "Reasoning",
617 type: "select",
618 options: [
619 { id: "medium", label: "Medium" },
620 { id: "high", label: "High", isDefault: true },
621 ],
622 },
623 ],
624 }),
625 },
626 ];
627
628 expect(formatModelSelectionEffort(selection, models)).toBe("Medium");
629 if (option.id !== "variant") {
630 expect(
631 formatModelSelectionEffort(
632 createModelSelection(selection.instanceId, selection.model),
633 models,
634 ),
635 ).toBeNull();
636 }
637 });
638
639 it("labels an old Ultracode effort by the effort it runs at", () => {
640 const models = [
641 {
642 slug: "claude-opus-5-5",
643 name: "Claude Opus 5.5",
644 isCustom: false,
645 capabilities: createModelCapabilities({
646 optionDescriptors: [
647 {
648 id: "effort",
649 label: "Reasoning",
650 type: "select",
651 options: [
652 { id: "high", label: "High", isDefault: true },
653 { id: "xhigh", label: "Extra High" },
654 ],
655 },
656 { id: "ultracode", label: "Ultracode", type: "boolean" },
657 ],
658 }),
659 },
660 ];
661 const selection = createModelSelection(
662 ProviderInstanceId.make("claudeAgent"),
663 "claude-opus-5-5",
664 [{ id: "effort", value: "ultracode" }],
665 );
666
667 expect(formatModelSelectionEffort(selection, models)).toBe("Extra High");
668 });
669});
670
671describe("isBackgroundWorkStopPending", () => {
672 const tasks = [{ taskId: "dev-server" }];
673 const sent = { threadKey: "env:thread", taskIds: ["dev-server"], retryAt: null };
674 const accepted = { ...sent, retryAt: 10_000 };
675
676 it("stays pending after acceptance while a stopped task is still listed", () => {
677 expect(isBackgroundWorkStopPending(sent, "env:thread", tasks, false)).toBe(true);
678 expect(isBackgroundWorkStopPending(accepted, "env:thread", tasks, false)).toBe(true);
679 });
680
681 it("ends once the stopped tasks leave, even if new work starts", () => {
682 expect(isBackgroundWorkStopPending(accepted, "env:thread", [], false)).toBe(false);
683 expect(isBackgroundWorkStopPending(accepted, "env:thread", [{ taskId: "next" }], false)).toBe(
684 false,
685 );
686 });
687
688 it("allows retry at the deadline even if a stopped task remains", () => {
689 const request = { ...accepted, retryAt: 10_000 };
690 expect(isBackgroundWorkStopPending(request, "env:thread", tasks, false)).toBe(true);
691 expect(isBackgroundWorkStopPending(request, "env:thread", tasks, true)).toBe(false);
692 });
693
694 it("only applies to the thread that sent the Stop", () => {
695 expect(isBackgroundWorkStopPending(null, "env:thread", tasks, false)).toBe(false);
696 expect(isBackgroundWorkStopPending(sent, "env:other", tasks, false)).toBe(false);
697 });
698});
699
700describe("presentProviderGoal", () => {
701 it("rolls token counts that round to 1000k over to 1m", () => {
702 const usage = (tokensUsed: number) =>
703 presentProviderGoal(
704 { objective: "Ship the feature", status: "active", tokensUsed, tokenBudget: 2_000_000 },
705 true,
706 ).usage;
707 expect(usage(999_499)).toBe("999k / 2m tokens");
708 expect(usage(999_500)).toBe("1m / 2m tokens");
709 expect(usage(999_999)).toBe("1m / 2m tokens");
710 expect(usage(1_000_000)).toBe("1m / 2m tokens");
711 expect(
712 presentProviderGoal(
713 {
714 objective: "Ship the feature",
715 status: "paused",
716 tokensUsed: 999_500,
717 tokenBudget: 999_500,
718 },
719 false,
720 ).usage,
721 ).toBe("1m / 1m tokens");
722 });
723});
724
725describe("presentPendingBackgroundWork", () => {
726 it("names a command by its script, not the shell that wraps it", () => {
727 const presentation = presentPendingBackgroundWork([
728 {
729 taskId: "call_1",
730 kind: "command",
731 description: `/bin/zsh -lc "mv a.ts b.ts\ncat > b.ts <<'EOF'\nexport {};\nEOF"`,
732 },
733 ]);
734 expect(presentation?.items[0]?.label).toBe("mv a.ts b.ts\ncat > b.ts <<'EOF'\nexport {};\nEOF");
735 });
736});