authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-03 08:55:10-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 08:55:10-07:00
logaad732901e4b7d485574eaef5a9c1fb388c4291a
tree9548ed0ea2bd2ddcb4d8a0788d1bf12afd1ad43a
parent0080e80c00a3e333e3dcafd85b2615d56f84e3b8
signature Signed by PGP key B5690EEEBB952194

fix(server): Claude threads no longer stay stuck in plan mode Claude entered itself (#15224)


3 files changed, 131 insertions(+), 25 deletions(-)

apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts+36
...@@ -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 ends3648 // 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 };
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts+13
...@@ -184,6 +184,11 @@ interface ClaudeQuerySetModelFrame {...@@ -184,6 +184,11 @@ interface ClaudeQuerySetModelFrame {
184 readonly model: string;184 readonly model: string;
185}185}
186186
187interface ClaudeQuerySetPermissionModeFrame {
188 readonly type: "query.set_permission_mode";
189 readonly mode: string;
190}
191
187interface ClaudeQueryInterruptFrame {192interface ClaudeQueryInterruptFrame {
188 readonly type: "query.interrupt";193 readonly type: "query.interrupt";
189}194}
...@@ -239,6 +244,7 @@ type ClaudeOutboundFrame =...@@ -239,6 +244,7 @@ type ClaudeOutboundFrame =
239 | ClaudeQueryOpenFrame244 | ClaudeQueryOpenFrame
240 | ClaudePromptOfferFrame245 | ClaudePromptOfferFrame
241 | ClaudeQuerySetModelFrame246 | ClaudeQuerySetModelFrame
247 | ClaudeQuerySetPermissionModeFrame
242 | ClaudeQueryInterruptFrame248 | ClaudeQueryInterruptFrame
243 | ClaudePermissionResponseFrame249 | ClaudePermissionResponseFrame
244 | ClaudeSessionForkFrame250 | ClaudeSessionForkFrame
...@@ -811,6 +817,13 @@ function makeReplayQueryRunner(...@@ -811,6 +817,13 @@ function makeReplayQueryRunner(
811 model,817 model,
812 });818 });
813 }),819 }),
820 setPermissionMode: (mode) =>
821 replayEffect(() => {
822 assertNextOutboundFrame({
823 type: "query.set_permission_mode",
824 mode,
825 });
826 }),
814 interrupt: replayEffect(() => {827 interrupt: replayEffect(() => {
815 assertNextOutboundFrame({ type: "query.interrupt" });828 assertNextOutboundFrame({ type: "query.interrupt" });
816 }),829 }),
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+82-25
...@@ -327,6 +327,9 @@ export interface ClaudeAgentSdkQuerySession {...@@ -327,6 +327,9 @@ export interface ClaudeAgentSdkQuerySession {
327 readonly messages: Stream.Stream<SDKMessage, ClaudeAgentSdkQueryRunnerError>;327 readonly messages: Stream.Stream<SDKMessage, ClaudeAgentSdkQueryRunnerError>;
328 readonly offer: (message: SDKUserMessage) => Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;328 readonly offer: (message: SDKUserMessage) => Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;
329 readonly setModel: (model: string) => Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;329 readonly setModel: (model: string) => Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;
330 readonly setPermissionMode: (
331 mode: PermissionMode,
332 ) => Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;
330 readonly interrupt: Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;333 readonly interrupt: Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;
331 readonly close: Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;334 readonly close: Effect.Effect<void, ClaudeAgentSdkQueryRunnerError>;
332}335}
...@@ -466,6 +469,14 @@ export type ClaudeAgentSdkProtocolLogEvent =...@@ -466,6 +469,14 @@ export type ClaudeAgentSdkProtocolLogEvent =
466 readonly model: string;469 readonly model: string;
467 };470 };
468 }471 }
472 | {
473 readonly direction: "outgoing";
474 readonly stage: "decoded";
475 readonly payload: {
476 readonly type: "query.set_permission_mode";
477 readonly mode: PermissionMode;
478 };
479 }
469 | {480 | {
470 readonly direction: "outgoing";481 readonly direction: "outgoing";
471 readonly stage: "decoded";482 readonly stage: "decoded";
...@@ -670,6 +681,22 @@ export const claudeAgentSdkQueryRunnerLiveLayer: Layer.Layer<...@@ -670,6 +681,22 @@ export const claudeAgentSdkQueryRunnerLiveLayer: Layer.Layer<
670 }),681 }),
671 ),682 ),
672 ),683 ),
684 setPermissionMode: (mode) =>
685 Effect.tryPromise({
686 try: () => queryRuntime.setPermissionMode(mode),
687 catch: (cause) => queryRunnerError(cause, "setPermissionMode"),
688 }).pipe(
689 Effect.tap(() =>
690 logProtocolEvent({
691 direction: "outgoing",
692 stage: "decoded",
693 payload: {
694 type: "query.set_permission_mode",
695 mode,
696 },
697 }),
698 ),
699 ),
673 interrupt: Effect.tryPromise({700 interrupt: Effect.tryPromise({
674 try: () => queryRuntime.interrupt(),701 try: () => queryRuntime.interrupt(),
675 catch: (cause) => queryRunnerError(cause, "interrupt"),702 catch: (cause) => queryRunnerError(cause, "interrupt"),
...@@ -2688,6 +2715,11 @@ interface ClaudeLiveQueryContext {...@@ -2688,6 +2715,11 @@ interface ClaudeLiveQueryContext {
2688 // uuid before any echo, so it echoes, but a resume's own turns can still2715 // uuid before any echo, so it echoes, but a resume's own turns can still
2689 // run ahead of that prompt.2716 // run ahead of that prompt.
2690 promptEchoMode: "unknown" | "acknowledged" | "early" | "result_only";2717 promptEchoMode: "unknown" | "acknowledged" | "early" | "result_only";
2718 // The mode this process was opened in, and the mode the CLI last reported
2719 // (init and status frames). Claude changes the latter itself through
2720 // EnterPlanMode.
2721 readonly openedPermissionMode: PermissionMode;
2722 permissionMode: PermissionMode;
2691 // Stop, rollback or fork is closing this process; its work is ending.2723 // Stop, rollback or fork is closing this process; its work is ending.
2692 stopping: boolean;2724 stopping: boolean;
2693 // Registry entries still running when this process opened. Their process2725 // Registry entries still running when this process opened. Their process
...@@ -6843,10 +6875,23 @@ export function makeClaudeAdapterV2(...@@ -6843,10 +6875,23 @@ export function makeClaudeAdapterV2(
6843 if (6875 if (
6844 existing !== null &&6876 existing !== null &&
6845 existing.nativeThreadId === nativeThreadId &&6877 existing.nativeThreadId === nativeThreadId &&
6846 (isClaudeProviderContinuationTurn(turnInput) ||6878 isClaudeProviderContinuationTurn(turnInput)
6847 (existing.queryPolicyKey === queryPolicyKey &&6879 ) {
6848 existing.selectionKey === compiledSelection.queryIdentity))6880 return existing;
6881 }
6882 if (
6883 existing !== null &&
6884 existing.nativeThreadId === nativeThreadId &&
6885 existing.queryPolicyKey === queryPolicyKey &&
6886 existing.selectionKey === compiledSelection.queryIdentity
6849 ) {6887 ) {
6888 // Claude can switch its own mode mid-session (EnterPlanMode), and
6889 // a denied ExitPlanMode leaves it there. Put the live process back
6890 // in the thread's mode before the next prompt.
6891 if (existing.permissionMode !== existing.openedPermissionMode) {
6892 yield* existing.query.setPermissionMode(existing.openedPermissionMode);
6893 existing.permissionMode = existing.openedPermissionMode;
6894 }
6850 return existing;6895 return existing;
6851 }6896 }
68526897
...@@ -6889,31 +6934,32 @@ export function makeClaudeAdapterV2(...@@ -6889,31 +6934,32 @@ export function makeClaudeAdapterV2(
6889 const hasPersistedProviderTurn = turnInput.providerTurnOrdinal > 1;6934 const hasPersistedProviderTurn = turnInput.providerTurnOrdinal > 1;
6890 const shouldResume =6935 const shouldResume =
6891 resumeSessionAt !== undefined || openedWithResume || hasPersistedProviderTurn;6936 resumeSessionAt !== undefined || openedWithResume || hasPersistedProviderTurn;
6937 const queryOptions = makeClaudeQueryOptions({
6938 modelSelection: turnInput.modelSelection,
6939 nativeThreadId,
6940 resume: shouldResume,
6941 ...(resumeSessionAt === undefined ? {} : { resumeSessionAt }),
6942 cwd: turnInput.runtimePolicy.cwd,
6943 attachmentsDir,
6944 settings: adapterOptions.settings,
6945 environment: adapterOptions.environment,
6946 tools: queryPolicy.tools ?? CLAUDE_CODE_PRESET_TOOLS,
6947 ...mcpOverrides,
6948 permissionMode: queryPolicy.permissionMode,
6949 ...(queryPolicy.allowDangerouslySkipPermissions === undefined
6950 ? {}
6951 : {
6952 allowDangerouslySkipPermissions: queryPolicy.allowDangerouslySkipPermissions,
6953 }),
6954 canUseTool,
6955 onUserDialog,
6956 supportedDialogKinds: ["resume_return"],
6957 });
6892 const querySession = yield* queryRunner6958 const querySession = yield* queryRunner
6893 .open({6959 .open({
6894 threadId: turnInput.threadId,6960 threadId: turnInput.threadId,
6895 providerSessionId: input.providerSessionId,6961 providerSessionId: input.providerSessionId,
6896 options: makeClaudeQueryOptions({6962 options: queryOptions,
6897 modelSelection: turnInput.modelSelection,
6898 nativeThreadId,
6899 resume: shouldResume,
6900 ...(resumeSessionAt === undefined ? {} : { resumeSessionAt }),
6901 cwd: turnInput.runtimePolicy.cwd,
6902 attachmentsDir,
6903 settings: adapterOptions.settings,
6904 environment: adapterOptions.environment,
6905 tools: queryPolicy.tools ?? CLAUDE_CODE_PRESET_TOOLS,
6906 ...mcpOverrides,
6907 permissionMode: queryPolicy.permissionMode,
6908 ...(queryPolicy.allowDangerouslySkipPermissions === undefined
6909 ? {}
6910 : {
6911 allowDangerouslySkipPermissions: queryPolicy.allowDangerouslySkipPermissions,
6912 }),
6913 canUseTool,
6914 onUserDialog,
6915 supportedDialogKinds: ["resume_return"],
6916 }),
6917 })6963 })
6918 .pipe(6964 .pipe(
6919 Effect.tapError(() =>6965 Effect.tapError(() =>
...@@ -6960,6 +7006,8 @@ export function makeClaudeAdapterV2(...@@ -6960,6 +7006,8 @@ export function makeClaudeAdapterV2(
6960 selectionKey: compiledSelection.queryIdentity,7006 selectionKey: compiledSelection.queryIdentity,
6961 closed,7007 closed,
6962 promptEchoMode: "unknown",7008 promptEchoMode: "unknown",
7009 openedPermissionMode: queryOptions.permissionMode,
7010 permissionMode: queryOptions.permissionMode,
6963 stopping: false,7011 stopping: false,
6964 subagentsFromEarlierProcesses: new Set(7012 subagentsFromEarlierProcesses: new Set(
6965 [...(yield* Ref.get(sessionSubagentsByTaskId)).values()].filter(7013 [...(yield* Ref.get(sessionSubagentsByTaskId)).values()].filter(
...@@ -6969,7 +7017,16 @@ export function makeClaudeAdapterV2(...@@ -6969,7 +7017,16 @@ export function makeClaudeAdapterV2(
6969 };7017 };
6970 yield* Ref.set(queryContext, context);7018 yield* Ref.set(queryContext, context);
6971 yield* querySession.messages.pipe(7019 yield* querySession.messages.pipe(
6972 Stream.runForEach((message) => handleSdkMessage({ query: querySession, message })),7020 Stream.runForEach((message) => {
7021 if (
7022 message.type === "system" &&
7023 (message.subtype === "init" || message.subtype === "status") &&
7024 message.permissionMode !== undefined
7025 ) {
7026 context.permissionMode = message.permissionMode;
7027 }
7028 return handleSdkMessage({ query: querySession, message });
7029 }),
6973 Effect.exit,7030 Effect.exit,
6974 Effect.flatMap(7031 Effect.flatMap(
6975 Effect.fnUntraced(function* (exit: ClaudeQueryStreamExit) {7032 Effect.fnUntraced(function* (exit: ClaudeQueryStreamExit) {