Skip to content
Source
Sign inSign up

orchestrationV2.test.ts

641 lines22.4 KB

1import { describe, expect, it } from "@effect/vitest";
2import * as DateTime from "effect/DateTime";
3import * as Schema from "effect/Schema";
4​
5import { NodeId, RunAttemptId, TrimmedNonEmptyString } from "./index.ts";
6import {
7 latestProviderTurnForAttempt,
8 OrchestrationV2ProviderCapabilities,
9 OrchestrationV2ProviderThread,
10 OrchestrationV2ProviderThreadJson,
11 OrchestrationV2RpcSchemas,
12 OrchestrationV2Subagent,
13 OrchestrationV2ThreadShellJson,
14 OrchestrationV2SubagentJson,
15 OrchestrationV2ThreadHistoryPage,
16 OrchestrationV2TurnItem,
17 OrchestrationV2TurnItemJson,
18} from "./orchestrationV2.ts";
19​
20const now = DateTime.makeUnsafe("2026-04-20T00:00:00.000Z");
21​
22const decodeOrchestrationV2TurnItemJson = Schema.decodeUnknownSync(OrchestrationV2TurnItemJson);
23const encodeOrchestrationV2TurnItemJson = Schema.encodeSync(OrchestrationV2TurnItemJson);
24const decodeOrchestrationV2Subagent = Schema.decodeUnknownSync(OrchestrationV2Subagent);
25const encodeOrchestrationV2SubagentJson = Schema.encodeSync(OrchestrationV2SubagentJson);
26const decodeOrchestrationV2ProviderThreadJson = Schema.decodeUnknownSync(
27 OrchestrationV2ProviderThreadJson,
28);
29const encodeOrchestrationV2ProviderThreadJson = Schema.encodeSync(
30 OrchestrationV2ProviderThreadJson,
31);
32const decodeOrchestrationV2ProviderThread = Schema.decodeUnknownSync(OrchestrationV2ProviderThread);
33const decodeOrchestrationV2ProviderCapabilities = Schema.decodeUnknownSync(
34 OrchestrationV2ProviderCapabilities,
35);
36​
37describe("orchestration V2 contracts", () => {
38 it("decodes thread event types from a newer server as skippable items", () => {
39 const decodeWireItems = Schema.decodeUnknownSync(
40 Schema.toCodecJson(Schema.Array(OrchestrationV2RpcSchemas.subscribeThread.output)),
41 );
42 const detached = (id: string, sequence: number) => ({
43 kind: "event",
44 sequence,
45 event: {
46 id,
47 type: "provider-session.detached",
48 threadId: "thread-1",
49 occurredAt: DateTime.formatIso(now),
50 payload: { providerSessionId: "provider-session-1", detachedAt: DateTime.formatIso(now) },
51 },
52 });
53​
54 const items = decodeWireItems([
55 detached("event-1", 1),
56 {
57 kind: "event",
58 sequence: 2,
59 event: {
60 id: "event-2",
61 type: "run.from-a-future-server",
62 threadId: "thread-1",
63 occurredAt: DateTime.formatIso(now),
64 payload: { runId: "run-1" },
65 },
66 },
67 detached("event-3", 3),
68 ]);
69​
70 expect(items.map((item) => item.kind)).toEqual(["event", "unknown-event", "event"]);
71 expect(items[1]).toEqual({
72 kind: "unknown-event",
73 sequence: 2,
74 eventType: "run.from-a-future-server",
75 });
76 expect(() =>
77 decodeWireItems([
78 { ...detached("event-4", 4), event: { ...detached("event-4", 4).event, payload: {} } },
79 ]),
80 ).toThrow();
81 });
82​
83 it("skips turn item types from a newer server in snapshots and turn-item events", () => {
84 const decodeWireItems = Schema.decodeUnknownSync(
85 Schema.toCodecJson(Schema.Array(OrchestrationV2RpcSchemas.subscribeThread.output)),
86 );
87 const decodeHistoryPage = Schema.decodeUnknownSync(
88 Schema.toCodecJson(OrchestrationV2ThreadHistoryPage),
89 );
90 const item = (id: string, type: string, extra: Record<string, unknown>) => ({
91 id,
92 type,
93 threadId: "thread-1",
94 runId: null,
95 nodeId: null,
96 providerThreadId: null,
97 providerTurnId: null,
98 nativeItemRef: null,
99 parentItemId: null,
100 ordinal: 1,
101 status: "completed",
102 title: null,
103 startedAt: null,
104 completedAt: null,
105 updatedAt: DateTime.formatIso(now),
106 ...extra,
107 });
108 const known = item("item-known", "system_notice", { message: "Hello" });
109 const future = item("item-future", "hologram", { beam: "ref-1" });
110 const projected = (position: number, turnItem: { readonly id: string }) => ({
111 position,
112 visibility: "local",
113 sourceThreadId: "thread-1",
114 sourceItemId: turnItem.id,
115 item: turnItem,
116 });
117 const projection = {
118 thread: {
119 createdBy: "user",
120 creationSource: "web",
121 id: "thread-1",
122 projectId: "project-1",
123 title: "Thread",
124 providerInstanceId: "codex",
125 modelSelection: { instanceId: "codex", model: "gpt-5-codex" },
126 runtimeMode: "full-access",
127 interactionMode: "default",
128 branch: null,
129 worktreePath: null,
130 activeProviderThreadId: null,
131 lineage: { parentThreadId: null, relationshipToParent: null, rootThreadId: "thread-1" },
132 forkedFrom: null,
133 createdAt: DateTime.formatIso(now),
134 updatedAt: DateTime.formatIso(now),
135 archivedAt: null,
136 deletedAt: null,
137 },
138 runs: [],
139 attempts: [],
140 nodes: [],
141 subagents: [],
142 providerSessions: [],
143 providerThreads: [],
144 providerTurns: [],
145 runtimeRequests: [],
146 messages: [],
147 plans: [],
148 turnItems: [future, known],
149 checkpointScopes: [],
150 checkpoints: [],
151 contextHandoffs: [],
152 contextTransfers: [],
153 visibleTurnItems: [projected(0, future), projected(1, known)],
154 updatedAt: DateTime.formatIso(now),
155 };
156 const turnItemEvent = (sequence: number, payload: unknown) => ({
157 kind: "event",
158 sequence,
159 event: {
160 id: `event-${sequence}`,
161 type: "turn-item.updated",
162 threadId: "thread-1",
163 occurredAt: DateTime.formatIso(now),
164 payload,
165 },
166 });
167​
168 const [snapshot, futureEvent, knownEvent] = decodeWireItems([
169 { kind: "snapshot", snapshotSequence: 1, projection },
170 turnItemEvent(2, future),
171 turnItemEvent(3, known),
172 ]);
173​
174 expect(snapshot).toMatchObject({ kind: "snapshot" });
175 if (snapshot?.kind !== "snapshot") throw new Error("expected a snapshot");
176 expect(snapshot.projection.turnItems.map((turnItem) => turnItem.id)).toEqual(["item-known"]);
177 expect(snapshot.projection.visibleTurnItems.map((row) => row.item.id)).toEqual(["item-known"]);
178 // The same snapshot with an ID table: refs resolve before unknown items are skipped.
179 const tabled = (turnItem: { readonly id: string }, ref: number) => ({ ...turnItem, id: ref });
180 const [tabledSnapshot] = decodeWireItems([
181 {
182 kind: "snapshot",
183 snapshotSequence: 1,
184 projection: {
185 ...projection,
186 turnItems: [tabled(future, 0), tabled(known, 1)],
187 visibleTurnItems: [
188 { ...projected(0, future), sourceItemId: 0, item: tabled(future, 0) },
189 { ...projected(1, known), sourceItemId: 1, item: tabled(known, 1) },
190 ],
191 },
192 ids: ["item-future", "item-known"],
193 },
194 ]);
195 expect(tabledSnapshot).toEqual(snapshot);
196 expect(futureEvent).toEqual({
197 kind: "unknown-event",
198 sequence: 2,
199 eventType: "turn-item.updated",
200 });
201 expect(knownEvent).toMatchObject({
202 kind: "event",
203 event: { type: "turn-item.updated", payload: { id: "item-known", type: "system_notice" } },
204 });
205 expect(
206 decodeHistoryPage({
207 snapshotSequence: 1,
208 items: [projected(0, future), projected(1, known)],
209 nextCursor: null,
210 hasMoreHistory: false,
211 }).items.map((row) => row.item.id),
212 ).toEqual(["item-known"]);
213​
214 const broken = item("item-broken", "system_notice", {});
215 expect(() => decodeWireItems([turnItemEvent(4, broken)])).toThrow();
216 expect(() =>
217 decodeWireItems([
218 {
219 kind: "snapshot",
220 snapshotSequence: 1,
221 projection: { ...projection, turnItems: [broken] },
222 },
223 ]),
224 ).toThrow();
225 });
226​
227 it("decodes persisted capability snapshots that predate runtimePolicy", () => {
228 const legacyCapabilities = {
229 sessions: {
230 supportsMultipleProviderThreadsPerSession: false,
231 supportsModelSwitchInSession: false,
232 supportsProviderSwitchingViaHandoff: true,
233 supportsRuntimeModeSwitchInSession: false,
234 pendingRequestsSurviveRestart: false,
235 },
236 threads: {
237 canCreateEmptyThread: true,
238 canReadThreadSnapshot: false,
239 canRollbackThread: true,
240 canForkThread: false,
241 canForkFromTurn: false,
242 canForkFromSubagentThread: false,
243 exposesNativeThreadId: true,
244 },
245 turns: {
246 exposesNativeTurnId: false,
247 emitsTurnStarted: true,
248 emitsTurnCompleted: true,
249 supportsInterrupt: true,
250 supportsActiveSteering: false,
251 supportsSteeringByInterruptRestart: true,
252 supportsQueuedMessages: true,
253 terminalStatusQuality: "strong",
254 },
255 streaming: {
256 streamsAssistantText: true,
257 streamsReasoning: true,
258 streamsToolOutput: true,
259 streamsPlanText: false,
260 emitsMessageCompleted: true,
261 },
262 tools: {
263 exposesToolItemIds: true,
264 emitsToolStarted: true,
265 emitsToolCompleted: true,
266 emitsToolOutput: true,
267 supportsMcpTools: false,
268 supportsDynamicToolCallbacks: false,
269 },
270 approvals: {
271 supportsCommandApproval: true,
272 supportsFileReadApproval: true,
273 supportsFileChangeApproval: true,
274 supportsApplyPatchApproval: false,
275 approvalsHaveNativeRequestIds: false,
276 approvalCallbacksAreLiveOnly: true,
277 approvalsCanOriginateFromSubagents: false,
278 },
279 planning: {
280 emitsPlanUpdated: true,
281 emitsTodoList: true,
282 emitsProposedPlan: false,
283 supportsStructuredQuestions: true,
284 planDeltasHaveItemIds: false,
285 },
286 subagents: {
287 supportsSubagents: false,
288 exposesSubagentThreadIds: false,
289 emitsSubagentLifecycle: false,
290 canWaitForSubagents: false,
291 canCloseSubagents: false,
292 canForkSubagentThread: false,
293 },
294 context: {
295 acceptsSystemContext: false,
296 acceptsDeveloperContext: false,
297 acceptsSyntheticUserContext: true,
298 canGenerateSummaries: true,
299 canConsumeHandoffSummaries: true,
300 supportsDeltaHandoff: true,
301 supportsFullThreadHandoff: true,
302 maxRecommendedHandoffChars: null,
303 },
304 checkpointing: {
305 appCanCheckpointFilesystem: true,
306 supportsNestedCheckpointScopes: true,
307 providerCanRollbackConversation: true,
308 providerRollbackReturnsSnapshot: true,
309 providerCanReadConversationSnapshot: false,
310 },
311 identity: {
312 nativeThreadIds: "strong",
313 nativeTurnIds: "weak",
314 nativeItemIds: "weak",
315 nativeRequestIds: "weak",
316 },
317 };
318​
319 const decoded = decodeOrchestrationV2ProviderCapabilities(legacyCapabilities);
320 expect(decoded.runtimePolicy).toEqual({ enforcement: "client-boundary" });
321​
322 const explicit = decodeOrchestrationV2ProviderCapabilities({
323 ...legacyCapabilities,
324 runtimePolicy: { enforcement: "native" },
325 });
326 expect(explicit.runtimePolicy).toEqual({ enforcement: "native" });
327 });
328​
329 it.each([" indented prompt\n", "\n \t\n"])(
330 "preserves workflow prompt and result %j in stored and wire JSON",
331 (preview) => {
332 const subagent = {
333 ...decodeOrchestrationV2Subagent({
334 id: "workflow-coordinator",
335 threadId: "thread-1",
336 runId: "run-1",
337 parentNodeId: "node-root-1",
338 origin: "provider_native",
339 createdBy: "agent",
340 driver: "claudeAgent",
341 providerInstanceId: "claudeAgent",
342 providerThreadId: null,
343 childThreadId: null,
344 nativeTaskRef: null,
345 prompt: "Run workflow",
346 title: null,
347 model: null,
348 status: "running",
349 result: null,
350 startedAt: now,
351 completedAt: null,
352 updatedAt: now,
353 }),
354 workflow: {
355 phases: [],
356 agents: [
357 {
358 index: 1,
359 label: "Member",
360 state: "completed" as const,
361 model: "claude-opus-5[1m]",
362 effort: "xhigh",
363 speed: "standard",
364 prompt: preview,
365 result: preview,
366 },
367 ],
368 },
369 };
370 const { workflow, ...coordinator } = subagent;
371 const incoming = { ...encodeOrchestrationV2SubagentJson(coordinator), workflow };
372 for (const codec of [
373 OrchestrationV2SubagentJson,
374 Schema.toCodecJson(OrchestrationV2Subagent),
375 ]) {
376 const encode = Schema.encodeSync(codec);
377 const decode = Schema.decodeUnknownSync(codec);
378 const json: unknown = JSON.parse(JSON.stringify(encode(subagent)));
379 expect(json).toMatchObject({
380 workflow: { agents: [{ prompt: preview, result: preview }] },
381 });
382 expect(decode(json).workflow?.agents[0]).toMatchObject({
383 prompt: preview,
384 result: preview,
385 model: "claude-opus-5[1m]",
386 effort: "xhigh",
387 speed: "standard",
388 });
389 expect(decode(incoming).workflow?.agents[0]).toMatchObject({
390 prompt: preview,
391 result: preview,
392 model: "claude-opus-5[1m]",
393 effort: "xhigh",
394 speed: "standard",
395 });
396 }
397 },
398 );
399​
400 it("decodes historical provider-thread JSON without pendingBackgroundTasks as empty roster", () => {
401 const providerThread = decodeOrchestrationV2ProviderThreadJson({
402 id: "provider-thread-1",
403 driver: "claude",
404 providerInstanceId: "claudeAgent",
405 providerSessionId: "provider-session-1",
406 appThreadId: "thread-1",
407 ownerNodeId: null,
408 nativeThreadRef: {
409 driver: "claude",
410 nativeId: "native-session-1",
411 strength: "strong",
412 },
413 nativeConversationHeadRef: null,
414 status: "idle",
415 firstRunOrdinal: 1,
416 lastRunOrdinal: 1,
417 handoffIds: [],
418 forkedFrom: null,
419 createdAt: "2026-04-20T00:00:00.000Z",
420 updatedAt: "2026-04-20T00:00:00.000Z",
421 });
422​
423 expect(providerThread.pendingBackgroundTasks).toEqual([]);
424 expect(providerThread.contextUsage).toBeNull();
425 expect(providerThread.nativeMetadata).toBeNull();
426​
427 const runtimeThread = decodeOrchestrationV2ProviderThread({
428 id: "provider-thread-2",
429 driver: "claude",
430 providerInstanceId: "claudeAgent",
431 providerSessionId: null,
432 appThreadId: "thread-2",
433 ownerNodeId: null,
434 nativeThreadRef: null,
435 nativeConversationHeadRef: null,
436 status: "idle",
437 firstRunOrdinal: null,
438 lastRunOrdinal: null,
439 handoffIds: [],
440 forkedFrom: null,
441 createdAt: now,
442 updatedAt: now,
443 });
444 expect(runtimeThread.pendingBackgroundTasks).toEqual([]);
445 expect(runtimeThread.contextUsage).toBeNull();
446 expect(runtimeThread.nativeMetadata).toBeNull();
447 });
448});
449​
450describe("thread open stats", () => {
451 it("reads a thread whose open stats this build cannot read, without them", () => {
452 const shell = {
453 id: "thread-test",
454 projectId: "project-test",
455 title: "Thread",
456 providerInstanceId: "codex",
457 modelSelection: { instanceId: "codex", model: "gpt-5.4" },
458 runtimeMode: "full-access",
459 interactionMode: "default",
460 branch: null,
461 worktreePath: null,
462 activeProviderThreadId: null,
463 lineage: { rootThreadId: "thread-test", parentThreadId: null, relationshipToParent: null },
464 forkedFrom: null,
465 createdBy: "user",
466 creationSource: "mobile",
467 latestRunId: null,
468 activeRunId: null,
469 status: "idle",
470 pendingRuntimeRequest: null,
471 latestVisibleMessage: null,
472 latestUserMessageAt: null,
473 hasActionableProposedPlan: false,
474 itemCount: 0,
475 visibleItemCount: 0,
476 createdAt: "2026-10-01T00:00:00.000Z",
477 updatedAt: "2026-10-01T00:00:00.000Z",
478 archivedAt: null,
479 settledOverride: null,
480 settledAt: null,
481 deletedAt: null,
482 };
483 const decode = Schema.decodeUnknownSync(OrchestrationV2ThreadShellJson);
484 const opens = { openedAt: 1, score: 1, picks: [{ query: "upl", at: 1 }] };
485 expect(decode({ ...shell, opens }).opens).toEqual(opens);
486 expect(decode({ ...shell, opens: { openedAt: "2026-10-01", score: 1 } }).opens).toBeNull();
487 });
488});
489​
490describe("background work kinds from older or newer servers", () => {
491 const storedNotification = (source: unknown) => ({
492 id: "notification",
493 threadId: "parent",
494 runId: null,
495 nodeId: null,
496 providerThreadId: null,
497 providerTurnId: null,
498 nativeItemRef: null,
499 parentItemId: null,
500 ordinal: 1,
501 status: "completed",
502 title: null,
503 startedAt: null,
504 completedAt: null,
505 updatedAt: "2026-09-09T00:00:00.000Z",
506 type: "notification",
507 outcome: "completed",
508 summary: "Background activity updated",
509 source,
510 });
511​
512 it("decodes notification sources stored before specific kinds existed", () => {
513 const sourceOf = (source: unknown) => {
514 const item = decodeOrchestrationV2TurnItemJson(storedNotification(source));
515 return item.type === "notification" ? item.source : undefined;
516 };
517 expect(
518 sourceOf({ kind: "background_task", nativeRef: { driver: "claude", nativeId: "task-1" } }),
519 ).toEqual({ kind: "background_task" });
520 expect(sourceOf({ kind: "background_command" })).toEqual({ kind: "command" });
521 expect(sourceOf({ kind: "monitor" })).toEqual({ kind: "monitor" });
522 });
523​
524 it("sends and stores sources that clients from before specific kinds still decode", () => {
525 const PreSpecificKindsNotification = Schema.Struct({
526 type: Schema.Literal("notification"),
527 source: Schema.Union([
528 Schema.Struct({ kind: Schema.Literal("delegated_task"), taskIds: Schema.Array(NodeId) }),
529 Schema.Struct({
530 kind: Schema.Literals(["background_task", "background_command", "monitor"]),
531 nativeRef: Schema.optional(Schema.Unknown),
532 }),
533 ]),
534 outcome: Schema.Literals(["completed", "failed", "cancelled", "updated", "unknown"]),
535 summary: Schema.String,
536 detail: Schema.optional(Schema.String),
537 });
538 const decodePreSpecificKinds = Schema.decodeUnknownSync(PreSpecificKindsNotification);
539 const sendOverWire = Schema.encodeSync(Schema.toCodecJson(OrchestrationV2TurnItem));
540 const cases = [
541 [{ kind: "subagent", childThreadId: "child" }, { kind: "background_task" }],
542 [{ kind: "subagent" }, { kind: "background_task" }],
543 [{ kind: "command" }, { kind: "background_command" }],
544 [{ kind: "monitor" }, { kind: "monitor" }],
545 [{ kind: "background_task" }, { kind: "background_task" }],
546 [
547 { kind: "delegated_task", taskIds: ["task-1"], childThreadId: "child" },
548 { kind: "delegated_task", taskIds: ["task-1"] },
549 ],
550 ] as const;
551 for (const [source, preSpecificKindsSource] of cases) {
552 const item = decodeOrchestrationV2TurnItemJson(storedNotification(source));
553 for (const encoded of [encodeOrchestrationV2TurnItemJson(item), sendOverWire(item)]) {
554 expect(decodePreSpecificKinds(encoded).source).toEqual(preSpecificKindsSource);
555 expect(decodeOrchestrationV2TurnItemJson(encoded)).toEqual(item);
556 }
557 expect(item).toMatchObject({ source });
558 }
559 });
560​
561 it("decodes a notification source kind from a newer server as generic background work", () => {
562 const item = decodeOrchestrationV2TurnItemJson(
563 storedNotification({ kind: "workflow", workflowId: "wf-1" }),
564 );
565 expect(item).toMatchObject({ type: "notification", source: { kind: "background_task" } });
566 });
567​
568 it("decodes rosters stored before kinds existed, and kinds from a newer server, as generic tasks", () => {
569 const providerThread = decodeOrchestrationV2ProviderThreadJson({
570 id: "provider-thread-1",
571 driver: "claude",
572 providerInstanceId: "claudeAgent",
573 providerSessionId: null,
574 appThreadId: "thread-1",
575 ownerNodeId: null,
576 nativeThreadRef: null,
577 nativeConversationHeadRef: null,
578 status: "idle",
579 firstRunOrdinal: 1,
580 lastRunOrdinal: 1,
581 handoffIds: [],
582 forkedFrom: null,
583 createdAt: "2026-04-20T00:00:00.000Z",
584 updatedAt: "2026-04-20T00:00:00.000Z",
585 pendingBackgroundTasks: [
586 { taskId: "bg-1", description: "Background sleep", taskType: "local_bash" },
587 { taskId: "bg-2" },
588 { taskId: "bg-3", description: "Nightly", kind: "workflow", schedule: "0 3 * * *" },
589 { taskId: "bg-4", description: "npm test", kind: "command" },
590 { taskId: "bg-5", kind: "subagent", childThreadId: "thread-child" },
591 { taskId: "bg-6", kind: "subagent", providerInstanceId: "claudeAgent" },
592 ],
593 });
594 expect(providerThread.pendingBackgroundTasks).toEqual([
595 { taskId: "bg-1", description: "Background sleep", kind: "background_task" },
596 { taskId: "bg-2", kind: "background_task" },
597 { taskId: "bg-3", description: "Nightly", kind: "background_task" },
598 { taskId: "bg-4", description: "npm test", kind: "command" },
599 { taskId: "bg-5", kind: "subagent", childThreadId: "thread-child" },
600 { taskId: "bg-6", kind: "subagent", providerInstanceId: "claudeAgent" },
601 ]);
602 const encoded = encodeOrchestrationV2ProviderThreadJson(providerThread).pendingBackgroundTasks;
603 expect(encoded).toEqual(providerThread.pendingBackgroundTasks);
604 const decodePreKindsRoster = Schema.decodeUnknownSync(
605 Schema.Array(
606 Schema.Struct({
607 taskId: TrimmedNonEmptyString,
608 description: Schema.optional(TrimmedNonEmptyString),
609 taskType: Schema.optional(TrimmedNonEmptyString),
610 }),
611 ),
612 );
613 expect(decodePreKindsRoster(encoded).map((task) => task.taskId)).toEqual([
614 "bg-1",
615 "bg-2",
616 "bg-3",
617 "bg-4",
618 "bg-5",
619 "bg-6",
620 ]);
621 expect(() =>
622 decodeOrchestrationV2ProviderThreadJson({
623 ...encodeOrchestrationV2ProviderThreadJson(providerThread),
624 pendingBackgroundTasks: [{ taskId: "", kind: "command" }],
625 }),
626 ).toThrow();
627 });
628});
629​
630describe("latestProviderTurnForAttempt", () => {
631 it("returns the attempt's highest-ordinal turn, as a Codex goal run spans several", () => {
632 const turns = [
633 { id: "first", runAttemptId: RunAttemptId.make("goal-attempt"), ordinal: 3 },
634 { id: "other", runAttemptId: RunAttemptId.make("other-attempt"), ordinal: 9 },
635 { id: "last", runAttemptId: RunAttemptId.make("goal-attempt"), ordinal: 5 },
636 { id: "subagent", runAttemptId: null, ordinal: 7 },
637 ];
638 expect(latestProviderTurnForAttempt(turns, RunAttemptId.make("goal-attempt"))?.id).toBe("last");
639 expect(latestProviderTurnForAttempt(turns, null)).toBeUndefined();
640 });
641});