| ... | @@ -2229,6 +2229,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2229,6 +2229,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2229 | processQueues, | 2229 | processQueues, |
| 2230 | offerAndWait, | 2230 | offerAndWait, |
| 2231 | offeredMessages, | 2231 | offeredMessages, |
| | 2232 | // The uuid Claude echoes on the turn answering the nth offered prompt. |
| | 2233 | promptUuid: (index: number) => { |
| | 2234 | const uuid = offeredMessages[index]?.uuid; |
| | 2235 | if (uuid === undefined) throw new Error(`No prompt offered at index ${index}.`); |
| | 2236 | return uuid; |
| | 2237 | }, |
| 2232 | permissionModeChanges, | 2238 | permissionModeChanges, |
| 2233 | continuationRequests, | 2239 | continuationRequests, |
| 2234 | events, | 2240 | events, |
| ... | @@ -4421,12 +4427,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4421,12 +4427,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4421 | const continuationAttempt = RunAttemptId.make("attempt-claude-wake-callback-3"); | 4427 | const continuationAttempt = RunAttemptId.make("attempt-claude-wake-callback-3"); |
| 4422 | const planToolUseId = "toolu_01WakePlanExitPlanMode"; | 4428 | const planToolUseId = "toolu_01WakePlanExitPlanMode"; |
| 4423 | const planMarkdown = "# Wake plan\n\n1. Report the background result."; | 4429 | const planMarkdown = "# Wake plan\n\n1. Report the background result."; |
| 4424 | const stamp = Effect.fnUntraced(function* (frame: SDKMessage, attemptId: RunAttemptId) { | 4430 | const stamp = (frame: SDKMessage, promptIndex: number) => |
| 4425 | return claudeSdkFrame({ | 4431 | claudeSdkFrame({ |
| 4426 | ...frame, | 4432 | ...frame, |
| 4427 | user_message_uuid: yield* ClaudeAdapterV2.claudePromptUuid(attemptId), | 4433 | user_message_uuid: harness.promptUuid(promptIndex), |
| 4428 | }); | 4434 | }); |
| 4429 | }); | | |
| 4430 | const runOf = (attemptId: RunAttemptId) => RunId.make(`run-${attemptId}`); | 4435 | const runOf = (attemptId: RunAttemptId) => RunId.make(`run-${attemptId}`); |
| 4431 | const planToolUse = claudeSdkFrame({ | 4436 | const planToolUse = claudeSdkFrame({ |
| 4432 | type: "assistant", | 4437 | type: "assistant", |
| ... | @@ -4479,12 +4484,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4479,12 +4484,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4479 | runtimePolicy, | 4484 | runtimePolicy, |
| 4480 | }), | 4485 | }), |
| 4481 | ); | 4486 | ); |
| 4482 | yield* Queue.offer(harness.sdkMessages, yield* stamp(wakeTaskStarted, firstAttempt)); | 4487 | yield* Queue.offer(harness.sdkMessages, stamp(wakeTaskStarted, 0)); |
| 4483 | yield* Queue.offer( | 4488 | yield* Queue.offer( |
| 4484 | harness.sdkMessages, | 4489 | harness.sdkMessages, |
| 4485 | yield* stamp( | 4490 | stamp( |
| 4486 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000740", result: "STARTED" }), | 4491 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000740", result: "STARTED" }), |
| 4487 | firstAttempt, | 4492 | 0, |
| 4488 | ), | 4493 | ), |
| 4489 | ); | 4494 | ); |
| 4490 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); | 4495 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); |
| ... | @@ -4541,22 +4546,22 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4541,22 +4546,22 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4541 | // The prompt's own turn follows and echoes its uuid. | 4546 | // The prompt's own turn follows and echoes its uuid. |
| 4542 | yield* Queue.offer( | 4547 | yield* Queue.offer( |
| 4543 | harness.sdkMessages, | 4548 | harness.sdkMessages, |
| 4544 | yield* stamp( | 4549 | stamp( |
| 4545 | makeAssistantTextFrame({ | 4550 | makeAssistantTextFrame({ |
| 4546 | uuid: "00000000-0000-4000-8000-000000000743", | 4551 | uuid: "00000000-0000-4000-8000-000000000743", |
| 4547 | text: "USER_REPLY", | 4552 | text: "USER_REPLY", |
| 4548 | }), | 4553 | }), |
| 4549 | userAttempt, | 4554 | 1, |
| 4550 | ), | 4555 | ), |
| 4551 | ); | 4556 | ); |
| 4552 | yield* Queue.offer( | 4557 | yield* Queue.offer( |
| 4553 | harness.sdkMessages, | 4558 | harness.sdkMessages, |
| 4554 | yield* stamp( | 4559 | stamp( |
| 4555 | makeResultFrame({ | 4560 | makeResultFrame({ |
| 4556 | uuid: "00000000-0000-4000-8000-000000000744", | 4561 | uuid: "00000000-0000-4000-8000-000000000744", |
| 4557 | result: "USER_REPLY", | 4562 | result: "USER_REPLY", |
| 4558 | }), | 4563 | }), |
| 4559 | userAttempt, | 4564 | 1, |
| 4560 | ), | 4565 | ), |
| 4561 | ); | 4566 | ); |
| 4562 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "user turn terminal"); | 4567 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "user turn terminal"); |
| ... | @@ -4721,12 +4726,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4721,12 +4726,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4721 | const firstAttempt = RunAttemptId.make("attempt-claude-wake-approval-1"); | 4726 | const firstAttempt = RunAttemptId.make("attempt-claude-wake-approval-1"); |
| 4722 | const userAttempt = RunAttemptId.make("attempt-claude-wake-approval-2"); | 4727 | const userAttempt = RunAttemptId.make("attempt-claude-wake-approval-2"); |
| 4723 | const bashToolUseId = "toolu_01WakeApprovalBash"; | 4728 | const bashToolUseId = "toolu_01WakeApprovalBash"; |
| 4724 | const stamp = Effect.fnUntraced(function* (frame: SDKMessage, attemptId: RunAttemptId) { | 4729 | const stamp = (frame: SDKMessage, promptIndex: number) => |
| 4725 | return claudeSdkFrame({ | 4730 | claudeSdkFrame({ |
| 4726 | ...frame, | 4731 | ...frame, |
| 4727 | user_message_uuid: yield* ClaudeAdapterV2.claudePromptUuid(attemptId), | 4732 | user_message_uuid: harness.promptUuid(promptIndex), |
| 4728 | }); | 4733 | }); |
| 4729 | }); | | |
| 4730 | | 4734 | |
| 4731 | yield* harness.runtime.startTurn( | 4735 | yield* harness.runtime.startTurn( |
| 4732 | makeClaudeTestTurnInput({ | 4736 | makeClaudeTestTurnInput({ |
| ... | @@ -4741,16 +4745,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4741,16 +4745,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4741 | ); | 4745 | ); |
| 4742 | yield* Queue.offer( | 4746 | yield* Queue.offer( |
| 4743 | harness.sdkMessages, | 4747 | harness.sdkMessages, |
| 4744 | yield* stamp( | 4748 | stamp( |
| 4745 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000780", text: "One." }), | 4749 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000780", text: "One." }), |
| 4746 | firstAttempt, | 4750 | 0, |
| 4747 | ), | 4751 | ), |
| 4748 | ); | 4752 | ); |
| 4749 | yield* Queue.offer( | 4753 | yield* Queue.offer( |
| 4750 | harness.sdkMessages, | 4754 | harness.sdkMessages, |
| 4751 | yield* stamp( | 4755 | stamp( |
| 4752 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000781", result: "One." }), | 4756 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000781", result: "One." }), |
| 4753 | firstAttempt, | 4757 | 0, |
| 4754 | ), | 4758 | ), |
| 4755 | ); | 4759 | ); |
| 4756 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); | 4760 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); |
| ... | @@ -4845,12 +4849,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4845,12 +4849,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4845 | const now = yield* DateTime.now; | 4849 | const now = yield* DateTime.now; |
| 4846 | const firstAttempt = RunAttemptId.make("attempt-claude-echo-debris-1"); | 4850 | const firstAttempt = RunAttemptId.make("attempt-claude-echo-debris-1"); |
| 4847 | const secondAttempt = RunAttemptId.make("attempt-claude-echo-debris-2"); | 4851 | const secondAttempt = RunAttemptId.make("attempt-claude-echo-debris-2"); |
| 4848 | const stamp = Effect.fnUntraced(function* (frame: SDKMessage, attemptId: RunAttemptId) { | 4852 | const stamp = (frame: SDKMessage, promptIndex: number) => |
| 4849 | return claudeSdkFrame({ | 4853 | claudeSdkFrame({ |
| 4850 | ...frame, | 4854 | ...frame, |
| 4851 | user_message_uuid: yield* ClaudeAdapterV2.claudePromptUuid(attemptId), | 4855 | user_message_uuid: harness.promptUuid(promptIndex), |
| 4852 | }); | 4856 | }); |
| 4853 | }); | | |
| 4854 | | 4857 | |
| 4855 | yield* harness.runtime.startTurn( | 4858 | yield* harness.runtime.startTurn( |
| 4856 | makeClaudeTestTurnInput({ | 4859 | makeClaudeTestTurnInput({ |
| ... | @@ -4865,16 +4868,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4865,16 +4868,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4865 | // The first turn echoes on its first frame: this process echoes early. | 4868 | // The first turn echoes on its first frame: this process echoes early. |
| 4866 | yield* Queue.offer( | 4869 | yield* Queue.offer( |
| 4867 | harness.sdkMessages, | 4870 | harness.sdkMessages, |
| 4868 | yield* stamp( | 4871 | stamp( |
| 4869 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000760", text: "One." }), | 4872 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000760", text: "One." }), |
| 4870 | firstAttempt, | 4873 | 0, |
| 4871 | ), | 4874 | ), |
| 4872 | ); | 4875 | ); |
| 4873 | yield* Queue.offer( | 4876 | yield* Queue.offer( |
| 4874 | harness.sdkMessages, | 4877 | harness.sdkMessages, |
| 4875 | yield* stamp( | 4878 | stamp( |
| 4876 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000761", result: "One." }), | 4879 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000761", result: "One." }), |
| 4877 | firstAttempt, | 4880 | 0, |
| 4878 | ), | 4881 | ), |
| 4879 | ); | 4882 | ); |
| 4880 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); | 4883 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); |
| ... | @@ -4899,16 +4902,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4899,16 +4902,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4899 | yield* Queue.offer(harness.sdkMessages, staleTaskNotificationResult); | 4902 | yield* Queue.offer(harness.sdkMessages, staleTaskNotificationResult); |
| 4900 | yield* Queue.offer( | 4903 | yield* Queue.offer( |
| 4901 | harness.sdkMessages, | 4904 | harness.sdkMessages, |
| 4902 | yield* stamp( | 4905 | stamp( |
| 4903 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000762", text: "Two." }), | 4906 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000762", text: "Two." }), |
| 4904 | secondAttempt, | 4907 | 1, |
| 4905 | ), | 4908 | ), |
| 4906 | ); | 4909 | ); |
| 4907 | yield* Queue.offer( | 4910 | yield* Queue.offer( |
| 4908 | harness.sdkMessages, | 4911 | harness.sdkMessages, |
| 4909 | yield* stamp( | 4912 | stamp( |
| 4910 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000763", result: "Two." }), | 4913 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000763", result: "Two." }), |
| 4911 | secondAttempt, | 4914 | 1, |
| 4912 | ), | 4915 | ), |
| 4913 | ); | 4916 | ); |
| 4914 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "second turn terminal"); | 4917 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "second turn terminal"); |
| ... | @@ -4944,12 +4947,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4944,12 +4947,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4944 | const now = yield* DateTime.now; | 4947 | const now = yield* DateTime.now; |
| 4945 | const firstAttempt = RunAttemptId.make("attempt-claude-echo-1"); | 4948 | const firstAttempt = RunAttemptId.make("attempt-claude-echo-1"); |
| 4946 | const secondAttempt = RunAttemptId.make("attempt-claude-echo-2"); | 4949 | const secondAttempt = RunAttemptId.make("attempt-claude-echo-2"); |
| 4947 | const stamp = Effect.fnUntraced(function* (frame: SDKMessage, attemptId: RunAttemptId) { | 4950 | const stamp = (frame: SDKMessage, promptIndex: number) => |
| 4948 | return claudeSdkFrame({ | 4951 | claudeSdkFrame({ |
| 4949 | ...frame, | 4952 | ...frame, |
| 4950 | user_message_uuid: yield* ClaudeAdapterV2.claudePromptUuid(attemptId), | 4953 | user_message_uuid: harness.promptUuid(promptIndex), |
| 4951 | }); | 4954 | }); |
| 4952 | }); | | |
| 4953 | const assistantTexts = () => | 4955 | const assistantTexts = () => |
| 4954 | harness.events.flatMap((event) => | 4956 | harness.events.flatMap((event) => |
| 4955 | event.type === "message.updated" && event.message.role === "assistant" | 4957 | event.type === "message.updated" && event.message.role === "assistant" |
| ... | @@ -4969,22 +4971,18 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4969,22 +4971,18 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4969 | attachments: [], | 4971 | attachments: [], |
| 4970 | }), | 4972 | }), |
| 4971 | ); | 4973 | ); |
| 4972 | assert.equal( | | |
| 4973 | harness.offeredMessages[0]?.uuid, | | |
| 4974 | yield* ClaudeAdapterV2.claudePromptUuid(firstAttempt), | | |
| 4975 | ); | | |
| 4976 | yield* Queue.offer( | 4974 | yield* Queue.offer( |
| 4977 | harness.sdkMessages, | 4975 | harness.sdkMessages, |
| 4978 | yield* stamp( | 4976 | stamp( |
| 4979 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000701", text: "One." }), | 4977 | makeAssistantTextFrame({ uuid: "00000000-0000-4000-8000-000000000701", text: "One." }), |
| 4980 | firstAttempt, | 4978 | 0, |
| 4981 | ), | 4979 | ), |
| 4982 | ); | 4980 | ); |
| 4983 | yield* Queue.offer( | 4981 | yield* Queue.offer( |
| 4984 | harness.sdkMessages, | 4982 | harness.sdkMessages, |
| 4985 | yield* stamp( | 4983 | stamp( |
| 4986 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000702", result: "One." }), | 4984 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000702", result: "One." }), |
| 4987 | firstAttempt, | 4985 | 0, |
| 4988 | ), | 4986 | ), |
| 4989 | ); | 4987 | ); |
| 4990 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); | 4988 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); |
| ... | @@ -5018,6 +5016,118 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5018,6 +5016,118 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5018 | ), | 5016 | ), |
| 5019 | ); | 5017 | ); |
| 5020 | | 5018 | |
| | 5019 | it.effect("gives the same run attempt a fresh prompt uuid on every offer", () => |
| | 5020 | Effect.scoped( |
| | 5021 | Effect.gen(function* () { |
| | 5022 | // Two servers sharing a database copy allocate identical run attempt |
| | 5023 | // ids and resume the same Claude session. Claude acks a prompt whose |
| | 5024 | // uuid its transcript already holds without ever running a turn. |
| | 5025 | const original = yield* makeWakeHarness; |
| | 5026 | const copy = yield* makeWakeHarness; |
| | 5027 | const now = yield* DateTime.now; |
| | 5028 | for (const harness of [original, copy]) { |
| | 5029 | yield* harness.runtime.startTurn( |
| | 5030 | makeClaudeTestTurnInput({ |
| | 5031 | threadId: harness.threadId, |
| | 5032 | providerThread: harness.providerThread, |
| | 5033 | now, |
| | 5034 | attemptId: RunAttemptId.make("attempt-claude-shared-copy"), |
| | 5035 | text: "Continue where you left off.", |
| | 5036 | attachments: [], |
| | 5037 | }), |
| | 5038 | ); |
| | 5039 | } |
| | 5040 | assert.notEqual(original.promptUuid(0), copy.promptUuid(0)); |
| | 5041 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| | 5042 | ), |
| | 5043 | ); |
| | 5044 | |
| | 5045 | it.effect("fails a prompt Claude completes without starting a turn for it", () => |
| | 5046 | Effect.scoped( |
| | 5047 | Effect.gen(function* () { |
| | 5048 | // Claude answers a prompt whose uuid its transcript already holds with |
| | 5049 | // a lone completed lifecycle frame and never runs a turn for it. |
| | 5050 | const harness = yield* makeWakeHarness; |
| | 5051 | const now = yield* DateTime.now; |
| | 5052 | yield* harness.runtime.startTurn( |
| | 5053 | makeClaudeTestTurnInput({ |
| | 5054 | threadId: harness.threadId, |
| | 5055 | providerThread: harness.providerThread, |
| | 5056 | now, |
| | 5057 | attemptId: RunAttemptId.make("attempt-claude-duplicate-prompt"), |
| | 5058 | text: "Bump this PR to the latest main.", |
| | 5059 | attachments: [], |
| | 5060 | }), |
| | 5061 | ); |
| | 5062 | yield* Queue.offer( |
| | 5063 | harness.sdkMessages, |
| | 5064 | claudeSdkFrame({ |
| | 5065 | type: "command_lifecycle", |
| | 5066 | command_uuid: harness.promptUuid(0), |
| | 5067 | state: "completed", |
| | 5068 | uuid: "00000000-0000-4000-8000-000000000790", |
| | 5069 | session_id: WAKE_NATIVE_SESSION, |
| | 5070 | }), |
| | 5071 | ); |
| | 5072 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); |
| | 5073 | const [terminal] = harness.terminalEvents(); |
| | 5074 | assert.equal(terminal?.status, "failed"); |
| | 5075 | if (terminal?.status !== "failed") return; |
| | 5076 | assert.equal(terminal.threadDisposition, "reusable"); |
| | 5077 | assert.include(terminal.failure.message, "never started a turn"); |
| | 5078 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| | 5079 | ), |
| | 5080 | ); |
| | 5081 | |
| | 5082 | it.effect("keeps a started prompt running until its result", () => |
| | 5083 | Effect.scoped( |
| | 5084 | Effect.gen(function* () { |
| | 5085 | const harness = yield* makeWakeHarness; |
| | 5086 | const now = yield* DateTime.now; |
| | 5087 | yield* harness.runtime.startTurn( |
| | 5088 | makeClaudeTestTurnInput({ |
| | 5089 | threadId: harness.threadId, |
| | 5090 | providerThread: harness.providerThread, |
| | 5091 | now, |
| | 5092 | attemptId: RunAttemptId.make("attempt-claude-started-prompt"), |
| | 5093 | text: "Bump this PR to the latest main.", |
| | 5094 | attachments: [], |
| | 5095 | }), |
| | 5096 | ); |
| | 5097 | const lifecycle = (state: string, uuid: string) => |
| | 5098 | claudeSdkFrame({ |
| | 5099 | type: "command_lifecycle", |
| | 5100 | command_uuid: harness.promptUuid(0), |
| | 5101 | state, |
| | 5102 | uuid, |
| | 5103 | session_id: WAKE_NATIVE_SESSION, |
| | 5104 | }); |
| | 5105 | yield* Queue.offer( |
| | 5106 | harness.sdkMessages, |
| | 5107 | lifecycle("queued", "00000000-0000-4000-8000-000000000791"), |
| | 5108 | ); |
| | 5109 | yield* Queue.offer( |
| | 5110 | harness.sdkMessages, |
| | 5111 | lifecycle("started", "00000000-0000-4000-8000-000000000792"), |
| | 5112 | ); |
| | 5113 | // The CLI can report the prompt completed before its turn's result. |
| | 5114 | yield* Queue.offer( |
| | 5115 | harness.sdkMessages, |
| | 5116 | lifecycle("completed", "00000000-0000-4000-8000-000000000793"), |
| | 5117 | ); |
| | 5118 | yield* Queue.offer( |
| | 5119 | harness.sdkMessages, |
| | 5120 | claudeSdkFrame({ |
| | 5121 | ...makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000794", result: "Done." }), |
| | 5122 | user_message_uuid: harness.promptUuid(0), |
| | 5123 | }), |
| | 5124 | ); |
| | 5125 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); |
| | 5126 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| | 5127 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| | 5128 | ), |
| | 5129 | ); |
| | 5130 | |
| 5021 | it.effect("buffers wake output and requests a single continuation run", () => | 5131 | it.effect("buffers wake output and requests a single continuation run", () => |
| 5022 | Effect.scoped( | 5132 | Effect.scoped( |
| 5023 | Effect.gen(function* () { | 5133 | Effect.gen(function* () { |
| ... | @@ -6031,12 +6141,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6031,12 +6141,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6031 | const userAttempt = RunAttemptId.make("attempt-claude-wake-subagent-2"); | 6141 | const userAttempt = RunAttemptId.make("attempt-claude-wake-subagent-2"); |
| 6032 | const continuationAttempt = RunAttemptId.make("attempt-claude-wake-subagent-3"); | 6142 | const continuationAttempt = RunAttemptId.make("attempt-claude-wake-subagent-3"); |
| 6033 | const runOf = (attemptId: RunAttemptId) => RunId.make(`run-${attemptId}`); | 6143 | const runOf = (attemptId: RunAttemptId) => RunId.make(`run-${attemptId}`); |
| 6034 | const stamp = Effect.fnUntraced(function* (frame: SDKMessage, attemptId: RunAttemptId) { | 6144 | const stamp = (frame: SDKMessage, promptIndex: number) => |
| 6035 | return claudeSdkFrame({ | 6145 | claudeSdkFrame({ |
| 6036 | ...frame, | 6146 | ...frame, |
| 6037 | user_message_uuid: yield* ClaudeAdapterV2.claudePromptUuid(attemptId), | 6147 | user_message_uuid: harness.promptUuid(promptIndex), |
| 6038 | }); | 6148 | }); |
| 6039 | }); | | |
| 6040 | | 6149 | |
| 6041 | yield* harness.runtime.startTurn( | 6150 | yield* harness.runtime.startTurn( |
| 6042 | makeClaudeTestTurnInput({ | 6151 | makeClaudeTestTurnInput({ |
| ... | @@ -6048,12 +6157,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6048,12 +6157,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6048 | attachments: [], | 6157 | attachments: [], |
| 6049 | }), | 6158 | }), |
| 6050 | ); | 6159 | ); |
| 6051 | yield* Queue.offer(harness.sdkMessages, yield* stamp(wakeTaskStarted, firstAttempt)); | 6160 | yield* Queue.offer(harness.sdkMessages, stamp(wakeTaskStarted, 0)); |
| 6052 | yield* Queue.offer( | 6161 | yield* Queue.offer( |
| 6053 | harness.sdkMessages, | 6162 | harness.sdkMessages, |
| 6054 | yield* stamp( | 6163 | stamp( |
| 6055 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000770", result: "STARTED" }), | 6164 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000770", result: "STARTED" }), |
| 6056 | firstAttempt, | 6165 | 0, |
| 6057 | ), | 6166 | ), |
| 6058 | ); | 6167 | ); |
| 6059 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); | 6168 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "first turn terminal"); |
| ... | @@ -6120,16 +6229,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6120,16 +6229,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6120 | session_id: WAKE_NATIVE_SESSION, | 6229 | session_id: WAKE_NATIVE_SESSION, |
| 6121 | }), | 6230 | }), |
| 6122 | wakeResult, | 6231 | wakeResult, |
| 6123 | yield* stamp( | 6232 | stamp( |
| 6124 | makeAssistantTextFrame({ | 6233 | makeAssistantTextFrame({ |
| 6125 | uuid: "00000000-0000-4000-8000-000000000776", | 6234 | uuid: "00000000-0000-4000-8000-000000000776", |
| 6126 | text: "USER_REPLY", | 6235 | text: "USER_REPLY", |
| 6127 | }), | 6236 | }), |
| 6128 | userAttempt, | 6237 | 1, |
| 6129 | ), | 6238 | ), |
| 6130 | yield* stamp( | 6239 | stamp( |
| 6131 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000777", result: "USER_REPLY" }), | 6240 | makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000777", result: "USER_REPLY" }), |
| 6132 | userAttempt, | 6241 | 1, |
| 6133 | ), | 6242 | ), |
| 6134 | ]; | 6243 | ]; |
| 6135 | for (const frame of wakeFrames) { | 6244 | for (const frame of wakeFrames) { |