From 7df0db14fc93bd020bca6ccbaaec954e6c20b7d8 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 24 Sep 2026 11:33:44 -0700 Subject: [PATCH 1/2] fix(server): Claude V2 sessions expand ~ in the binary path The Claude driver expands a leading ~ in binaryPath for its own probes, but createClaudeAdapterV2 spread the raw config into the adapter's settings, so the Agent SDK received a literal ~/... as pathToClaudeCodeExecutable. The SDK does not expand it and fails with "Claude Code native binary not found". Expand it where the adapter builds its settings. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../Adapters/ClaudeAdapterV2.test.ts | 71 +++++++++++++++++++ .../Adapters/ClaudeAdapterV2.ts | 3 +- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 48b64dbd5d66..d76e6668cd09 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -1,3 +1,6 @@ +// @effect-diagnostics-next-line nodeBuiltinImport:off +import * as NodeOS from "node:os"; + import type { Query as ClaudeQuery, SDKMessage, @@ -46,6 +49,7 @@ import { Tool } from "effect/unstable/ai"; import { formatClaudeResumeCompactionQuestion } from "@t3tools/shared/claudeCompaction"; import { attachmentRelativePath } from "../../attachmentStore.ts"; +import { ServerConfig } from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import { PreviewControlsToolkit } from "../../mcp/toolkits/previewControls/tools.ts"; import { EnvironmentToolkit } from "../../mcp/toolkits/environment/tools.ts"; @@ -68,6 +72,7 @@ import { CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, CLAUDE_T3_MCP_TOOL_WILDCARD, ClaudeProviderCapabilitiesV2, + ClaudeAgentSdkQueryRunner, ClaudeAgentSdkQueryRunnerError, claudeEffectiveQueryPolicyKey, claudeProviderTurnTokenUsage, @@ -79,6 +84,7 @@ import { claudeTodoSteps, claudeProposedPlan, awaitClaudeApprovalDecision, + createClaudeAdapterV2, loggedClaudeQueryOptions, makeClaudeAdapterV2, makeClaudeAgentSdkProtocolLogger, @@ -961,6 +967,71 @@ describe("ClaudeAdapterV2 approval cancellation", () => { ); }); +describe("ClaudeAdapterV2 executable path", () => { + it.effect("expands ~ in the configured binary path for the SDK", () => + Effect.scoped( + Effect.gen(function* () { + const path = yield* Path.Path; + const executablePaths: Array = []; + const adapter = yield* createClaudeAdapterV2( + { + instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + displayName: undefined, + environment: [], + enabled: true, + config: { ...DEFAULT_CLAUDE_SETTINGS, binaryPath: "~/bin/claude" }, + }, + {}, + ).pipe( + Effect.provide( + ServerConfig.layerTest(process.cwd(), { prefix: "t3-claude-binary-home-" }), + ), + Effect.provideService(ClaudeAgentSdkQueryRunner, { + allocateSessionId: Effect.succeed("native-thread-claude-binary-home"), + open: (input) => + Effect.sync(() => { + executablePaths.push(input.options.pathToClaudeCodeExecutable); + return { + messages: Stream.never, + offer: () => Effect.void, + setModel: () => Effect.void, + interrupt: Effect.void, + close: Effect.void, + }; + }), + forkSession: () => Effect.die("unused"), + assertComplete: Effect.void, + }), + ); + const threadId = ThreadId.make("thread-claude-binary-home"); + const runtime = yield* adapter.openSession({ + threadId, + providerSessionId: ProviderSessionId.make("provider-session-claude-binary-home"), + modelSelection: CLAUDE_TEST_MODEL_SELECTION, + runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY, + }); + const providerThread = yield* runtime.ensureThread({ + threadId, + modelSelection: CLAUDE_TEST_MODEL_SELECTION, + runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY, + }); + yield* runtime.startTurn( + makeClaudeTestTurnInput({ + threadId, + providerThread, + now: yield* DateTime.now, + attemptId: RunAttemptId.make("attempt-claude-binary-home"), + text: "hello", + attachments: [], + }), + ); + + assert.deepEqual(executablePaths, [path.join(NodeOS.homedir(), "bin", "claude")]); + }), + ).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + ); +}); + describe("ClaudeAdapterV2 resume compaction", () => { it.effect("resolves and cancels the SDK resume dialog through structured runtime input", () => Effect.scoped( diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 7ba2b376ac83..1bcb7716dd4e 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -82,6 +82,7 @@ import { planClaudeSkillDispatch } from "../../provider/Drivers/ClaudeSkillDispa import { discoverClaudeSkills } from "../../provider/Drivers/ClaudeSkills.ts"; import { compileClaudeModelSelection } from "../../claudeModelOptions.ts"; import { ServerConfig } from "../../config.ts"; +import { expandHomePath } from "../../pathExpansion.ts"; import { claudeSignedOutMessage, makeClaudeEnvironment, @@ -6558,7 +6559,7 @@ export const createClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2Driver.create")( const path = yield* Path.Path; return makeClaudeAdapterV2({ instanceId, - settings: { ...config, enabled }, + settings: { ...config, enabled, binaryPath: expandHomePath(config.binaryPath) }, environment: claudeEnvironment, attachmentsDir: serverConfig.attachmentsDir, fileSystem, From a9d2cde410da5172ad08c02602fd72267470ebe9 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 24 Sep 2026 12:49:08 -0700 Subject: [PATCH 2/2] test(server): drop an unused diagnostics directive The nodeBuiltinImport rule does not apply to this test file, so the directive tripped TS377000 in the typecheck step. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index d76e6668cd09..124b044387cc 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -1,4 +1,3 @@ -// @effect-diagnostics-next-line nodeBuiltinImport:off import * as NodeOS from "node:os"; import type {