| ... | @@ -947,6 +947,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { | ... | @@ -947,6 +947,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { |
| 947 | messages: Stream.never, | 947 | messages: Stream.never, |
| 948 | offer: () => Effect.void, | 948 | offer: () => Effect.void, |
| 949 | setModel: () => Effect.void, | 949 | setModel: () => Effect.void, |
| | 950 | setPermissionMode: () => Effect.void, |
| 950 | interrupt: Effect.void, | 951 | interrupt: Effect.void, |
| 951 | close: Effect.void, | 952 | close: Effect.void, |
| 952 | }; | 953 | }; |
| ... | @@ -1097,6 +1098,7 @@ const captureSdkExecutablePaths = Effect.fn("captureSdkExecutablePaths")(functio | ... | @@ -1097,6 +1098,7 @@ const captureSdkExecutablePaths = Effect.fn("captureSdkExecutablePaths")(functio |
| 1097 | messages: Stream.never, | 1098 | messages: Stream.never, |
| 1098 | offer: () => Effect.void, | 1099 | offer: () => Effect.void, |
| 1099 | setModel: () => Effect.void, | 1100 | setModel: () => Effect.void, |
| | 1101 | setPermissionMode: () => Effect.void, |
| 1100 | interrupt: Effect.void, | 1102 | interrupt: Effect.void, |
| 1101 | close: Effect.void, | 1103 | close: Effect.void, |
| 1102 | }; | 1104 | }; |
| ... | @@ -1187,6 +1189,7 @@ describe("ClaudeAdapterV2 resume compaction", () => { | ... | @@ -1187,6 +1189,7 @@ describe("ClaudeAdapterV2 resume compaction", () => { |
| 1187 | messages: Stream.never, | 1189 | messages: Stream.never, |
| 1188 | offer: () => Effect.void, | 1190 | offer: () => Effect.void, |
| 1189 | setModel: () => Effect.void, | 1191 | setModel: () => Effect.void, |
| | 1192 | setPermissionMode: () => Effect.void, |
| 1190 | interrupt: Effect.void, | 1193 | interrupt: Effect.void, |
| 1191 | close: Effect.void, | 1194 | close: Effect.void, |
| 1192 | }; | 1195 | }; |
| ... | @@ -1406,6 +1409,7 @@ describe("ClaudeAdapterV2 attachments", () => { | ... | @@ -1406,6 +1409,7 @@ describe("ClaudeAdapterV2 attachments", () => { |
| 1406 | offeredMessages.push(message); | 1409 | offeredMessages.push(message); |
| 1407 | }), | 1410 | }), |
| 1408 | setModel: () => Effect.void, | 1411 | setModel: () => Effect.void, |
| | 1412 | setPermissionMode: () => Effect.void, |
| 1409 | interrupt: Effect.void, | 1413 | interrupt: Effect.void, |
| 1410 | close: Effect.void, | 1414 | close: Effect.void, |
| 1411 | }), | 1415 | }), |
| ... | @@ -1544,6 +1548,7 @@ describe("ClaudeAdapterV2 attachments", () => { | ... | @@ -1544,6 +1548,7 @@ describe("ClaudeAdapterV2 attachments", () => { |
| 1544 | messages: Stream.never, | 1548 | messages: Stream.never, |
| 1545 | offer: () => Effect.void, | 1549 | offer: () => Effect.void, |
| 1546 | setModel: () => Effect.void, | 1550 | setModel: () => Effect.void, |
| | 1551 | setPermissionMode: () => Effect.void, |
| 1547 | interrupt: Effect.void, | 1552 | interrupt: Effect.void, |
| 1548 | close: Effect.void, | 1553 | close: Effect.void, |
| 1549 | }; | 1554 | }; |
| ... | @@ -1632,6 +1637,7 @@ describe("ClaudeAdapterV2 native fork", () => { | ... | @@ -1632,6 +1637,7 @@ describe("ClaudeAdapterV2 native fork", () => { |
| 1632 | messages: Stream.empty, | 1637 | messages: Stream.empty, |
| 1633 | offer: () => Effect.void, | 1638 | offer: () => Effect.void, |
| 1634 | setModel: () => Effect.void, | 1639 | setModel: () => Effect.void, |
| | 1640 | setPermissionMode: () => Effect.void, |
| 1635 | interrupt: Effect.void, | 1641 | interrupt: Effect.void, |
| 1636 | close: Effect.void, | 1642 | close: Effect.void, |
| 1637 | }; | 1643 | }; |
| ... | @@ -1803,6 +1809,7 @@ describe("ClaudeAdapterV2 native session identity", () => { | ... | @@ -1803,6 +1809,7 @@ describe("ClaudeAdapterV2 native session identity", () => { |
| 1803 | messages: Stream.empty, | 1809 | messages: Stream.empty, |
| 1804 | offer: () => Effect.void, | 1810 | offer: () => Effect.void, |
| 1805 | setModel: () => Effect.void, | 1811 | setModel: () => Effect.void, |
| | 1812 | setPermissionMode: () => Effect.void, |
| 1806 | interrupt: Effect.void, | 1813 | interrupt: Effect.void, |
| 1807 | close: Effect.void, | 1814 | close: Effect.void, |
| 1808 | }; | 1815 | }; |
| ... | @@ -2058,6 +2065,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2058,6 +2065,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2058 | yield* Deferred.await(processed); | 2065 | yield* Deferred.await(processed); |
| 2059 | }); | 2066 | }); |
| 2060 | const offeredMessages: Array<SDKUserMessage> = []; | 2067 | const offeredMessages: Array<SDKUserMessage> = []; |
| | 2068 | const permissionModeChanges: Array<string> = []; |
| 2061 | const continuationRequests: Array<ProviderContinuationRequest> = []; | 2069 | const continuationRequests: Array<ProviderContinuationRequest> = []; |
| 2062 | const terminalReceipts = | 2070 | const terminalReceipts = |
| 2063 | yield* Queue.unbounded<Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>>(); | 2071 | yield* Queue.unbounded<Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>>(); |
| ... | @@ -2106,6 +2114,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2106,6 +2114,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2106 | offeredMessages.push(message); | 2114 | offeredMessages.push(message); |
| 2107 | }), | 2115 | }), |
| 2108 | setModel: () => Effect.void, | 2116 | setModel: () => Effect.void, |
| | 2117 | setPermissionMode: (mode) => |
| | 2118 | Effect.sync(() => { |
| | 2119 | permissionModeChanges.push(mode); |
| | 2120 | }), |
| 2109 | interrupt: options?.interrupt ?? Effect.void, | 2121 | interrupt: options?.interrupt ?? Effect.void, |
| 2110 | close: options?.close?.(sdkMessages) ?? Effect.void, | 2122 | close: options?.close?.(sdkMessages) ?? Effect.void, |
| 2111 | }; | 2123 | }; |
| ... | @@ -2158,6 +2170,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2158,6 +2170,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2158 | sdkMessages, | 2170 | sdkMessages, |
| 2159 | offerAndWait, | 2171 | offerAndWait, |
| 2160 | offeredMessages, | 2172 | offeredMessages, |
| | 2173 | permissionModeChanges, |
| 2161 | continuationRequests, | 2174 | continuationRequests, |
| 2162 | events, | 2175 | events, |
| 2163 | terminalReceipts, | 2176 | terminalReceipts, |
| ... | @@ -2985,6 +2998,18 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2985,6 +2998,18 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2985 | harness.sdkMessages, | 2998 | harness.sdkMessages, |
| 2986 | toolResults("00000000-0000-4000-8000-000000000502", ["tool-todo-1"]), | 2999 | toolResults("00000000-0000-4000-8000-000000000502", ["tool-todo-1"]), |
| 2987 | ); | 3000 | ); |
| | 3001 | // Claude entered plan mode on its own (EnterPlanMode). |
| | 3002 | yield* Queue.offer( |
| | 3003 | harness.sdkMessages, |
| | 3004 | claudeSdkFrame({ |
| | 3005 | type: "system", |
| | 3006 | subtype: "status", |
| | 3007 | status: null, |
| | 3008 | permissionMode: "plan", |
| | 3009 | uuid: "00000000-0000-4000-8000-000000000508", |
| | 3010 | session_id: WAKE_NATIVE_SESSION, |
| | 3011 | }), |
| | 3012 | ); |
| 2988 | yield* Queue.offer( | 3013 | yield* Queue.offer( |
| 2989 | harness.sdkMessages, | 3014 | harness.sdkMessages, |
| 2990 | makeResultFrame({ | 3015 | makeResultFrame({ |
| ... | @@ -3095,6 +3120,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3095,6 +3120,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3095 | ); | 3120 | ); |
| 3096 | const proposedPlan = [...plans.values()].find((plan) => plan.kind === "proposed_plan"); | 3121 | const proposedPlan = [...plans.values()].find((plan) => plan.kind === "proposed_plan"); |
| 3097 | assert.equal(proposedPlan?.status, "active"); | 3122 | assert.equal(proposedPlan?.status, "active"); |
| | 3123 | // The second prompt reuses the live process, which is still in the |
| | 3124 | // plan mode Claude entered, so it is put back in the thread's mode. |
| | 3125 | assert.deepEqual(harness.permissionModeChanges, ["bypassPermissions"]); |
| 3098 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3126 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| 3099 | ), | 3127 | ), |
| 3100 | ); | 3128 | ); |
| ... | @@ -3615,6 +3643,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3615,6 +3643,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3615 | messages: Stream.fromQueue(sdkMessages), | 3643 | messages: Stream.fromQueue(sdkMessages), |
| 3616 | offer: () => Effect.void, | 3644 | offer: () => Effect.void, |
| 3617 | setModel: () => Effect.void, | 3645 | setModel: () => Effect.void, |
| | 3646 | setPermissionMode: () => Effect.void, |
| 3618 | interrupt: Effect.void, | 3647 | interrupt: Effect.void, |
| 3619 | // The first CLI process keeps streaming until the test ends | 3648 | // The first CLI process keeps streaming until the test ends |
| 3620 | // it, so Stop stays parked waiting for it to exit. | 3649 | // it, so Stop stays parked waiting for it to exit. |
| ... | @@ -3864,6 +3893,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3864,6 +3893,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3864 | messages: Stream.fromQueue(queue), | 3893 | messages: Stream.fromQueue(queue), |
| 3865 | offer: () => Effect.void, | 3894 | offer: () => Effect.void, |
| 3866 | setModel: () => Effect.void, | 3895 | setModel: () => Effect.void, |
| | 3896 | setPermissionMode: () => Effect.void, |
| 3867 | interrupt: Effect.void, | 3897 | interrupt: Effect.void, |
| 3868 | close: Queue.shutdown(queue), | 3898 | close: Queue.shutdown(queue), |
| 3869 | }; | 3899 | }; |
| ... | @@ -6655,6 +6685,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6655,6 +6685,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6655 | messages: Stream.fromQueue(sdkMessages), | 6685 | messages: Stream.fromQueue(sdkMessages), |
| 6656 | offer: () => Effect.void, | 6686 | offer: () => Effect.void, |
| 6657 | setModel: () => Effect.void, | 6687 | setModel: () => Effect.void, |
| | 6688 | setPermissionMode: () => Effect.void, |
| 6658 | interrupt: Effect.void, | 6689 | interrupt: Effect.void, |
| 6659 | // End this process stream so openQuery can replace it. | 6690 | // End this process stream so openQuery can replace it. |
| 6660 | close: Queue.shutdown(sdkMessages), | 6691 | close: Queue.shutdown(sdkMessages), |
| ... | @@ -6837,6 +6868,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6837,6 +6868,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6837 | messages: Stream.fromQueue(sdkMessages), | 6868 | messages: Stream.fromQueue(sdkMessages), |
| 6838 | offer: () => Effect.void, | 6869 | offer: () => Effect.void, |
| 6839 | setModel: () => Effect.void, | 6870 | setModel: () => Effect.void, |
| | 6871 | setPermissionMode: () => Effect.void, |
| 6840 | interrupt: Effect.void, | 6872 | interrupt: Effect.void, |
| 6841 | close: Queue.shutdown(sdkMessages), | 6873 | close: Queue.shutdown(sdkMessages), |
| 6842 | }; | 6874 | }; |
| ... | @@ -7071,6 +7103,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7071,6 +7103,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7071 | messages: Stream.fromQueue(sdkMessages), | 7103 | messages: Stream.fromQueue(sdkMessages), |
| 7072 | offer: () => Effect.void, | 7104 | offer: () => Effect.void, |
| 7073 | setModel: () => Effect.void, | 7105 | setModel: () => Effect.void, |
| | 7106 | setPermissionMode: () => Effect.void, |
| 7074 | interrupt: Effect.void, | 7107 | interrupt: Effect.void, |
| 7075 | close: Queue.shutdown(sdkMessages), | 7108 | close: Queue.shutdown(sdkMessages), |
| 7076 | }; | 7109 | }; |
| ... | @@ -7257,6 +7290,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7257,6 +7290,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7257 | messages: Stream.fromQueue(sdkMessages), | 7290 | messages: Stream.fromQueue(sdkMessages), |
| 7258 | offer: () => Effect.void, | 7291 | offer: () => Effect.void, |
| 7259 | setModel: () => Effect.void, | 7292 | setModel: () => Effect.void, |
| | 7293 | setPermissionMode: () => Effect.void, |
| 7260 | interrupt: Effect.void, | 7294 | interrupt: Effect.void, |
| 7261 | close: Queue.shutdown(sdkMessages), | 7295 | close: Queue.shutdown(sdkMessages), |
| 7262 | }; | 7296 | }; |
| ... | @@ -7433,6 +7467,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7433,6 +7467,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7433 | messages: Stream.fromQueue(sdkMessages), | 7467 | messages: Stream.fromQueue(sdkMessages), |
| 7434 | offer: () => Effect.void, | 7468 | offer: () => Effect.void, |
| 7435 | setModel: () => Effect.void, | 7469 | setModel: () => Effect.void, |
| | 7470 | setPermissionMode: () => Effect.void, |
| 7436 | interrupt: Effect.void, | 7471 | interrupt: Effect.void, |
| 7437 | close: Queue.shutdown(sdkMessages), | 7472 | close: Queue.shutdown(sdkMessages), |
| 7438 | }; | 7473 | }; |
| ... | @@ -7564,6 +7599,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7564,6 +7599,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7564 | messages: Stream.fromQueue(sdkMessages), | 7599 | messages: Stream.fromQueue(sdkMessages), |
| 7565 | offer: () => Effect.void, | 7600 | offer: () => Effect.void, |
| 7566 | setModel: () => Effect.void, | 7601 | setModel: () => Effect.void, |
| | 7602 | setPermissionMode: () => Effect.void, |
| 7567 | interrupt: Effect.void, | 7603 | interrupt: Effect.void, |
| 7568 | close: Queue.shutdown(sdkMessages), | 7604 | close: Queue.shutdown(sdkMessages), |
| 7569 | }; | 7605 | }; |