diff --git a/packages/adapter-utils/src/execution-target-sandbox.test.ts b/packages/adapter-utils/src/execution-target-sandbox.test.ts index 103a77c7b4..3a2af872db 100644 --- a/packages/adapter-utils/src/execution-target-sandbox.test.ts +++ b/packages/adapter-utils/src/execution-target-sandbox.test.ts @@ -237,6 +237,48 @@ describe("sandbox adapter execution targets", () => { }); }); + it("preserves stdin when wrapping sandbox adapter commands for run-log streaming", async () => { + const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-run-log-stdin-")); + cleanupDirs.push(rootDir); + const target: AdapterSandboxExecutionTarget = { + kind: "remote", + transport: "sandbox", + providerKey: "local-test", + remoteCwd: rootDir, + timeoutMs: 30_000, + streamRunLogs: true, + runner: createLocalSandboxRunner(), + }; + const logsDir = path.posix.join(rootDir, ".paperclip-runtime", "bridge", "logs"); + const runLogTail = createSandboxRunLogTailFactory({ + runner: target.runner!, + remoteCwd: rootDir, + logsDir, + shellCommand: "bash", + }).create(); + const events: Array<{ stream: "stdout" | "stderr"; chunk: string }> = []; + + const result = await runAdapterExecutionTargetProcess( + "run-log-stdin", + target, + process.execPath, + ["-e", "process.stdin.setEncoding('utf8'); let s=''; process.stdin.on('data', c => s += c); process.stdin.on('end', () => process.stdout.write('stdin=' + s));"], + { + cwd: rootDir, + env: {}, + stdin: "hello-through-wrapper", + timeoutSec: 5, + graceSec: 1, + runLogTail: { create: () => runLogTail }, + onLog: async (stream, chunk) => { events.push({ stream, chunk }); }, + }, + ); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toBe("stdin=hello-through-wrapper"); + expect(combinedStream(events, "stdout")).toContain("stdin=hello-through-wrapper"); + }); + it("creates the process session directories only in the launch exec, not in upfront makeDir execs", async () => { const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-process-session-makedir-")); cleanupDirs.push(rootDir); diff --git a/packages/adapters/claude-local/src/server/execute.ts b/packages/adapters/claude-local/src/server/execute.ts index ad1bdc253e..d0cbbe8140 100644 --- a/packages/adapters/claude-local/src/server/execute.ts +++ b/packages/adapters/claude-local/src/server/execute.ts @@ -835,7 +835,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise { - const args = ["--print", "-", "--output-format", "stream-json", "--verbose"]; + const args = ["--print", "--output-format", "stream-json", "--verbose"]; if (resumeSessionId) args.push("--resume", resumeSessionId); args.push(...buildClaudeExecutionPermissionArgs({ dangerouslySkipPermissions, diff --git a/server/src/__tests__/claude-local-execute.test.ts b/server/src/__tests__/claude-local-execute.test.ts index 2a1d9065d5..4280ef17a5 100644 --- a/server/src/__tests__/claude-local-execute.test.ts +++ b/server/src/__tests__/claude-local-execute.test.ts @@ -434,6 +434,9 @@ describe("claude execute", () => { onMeta: async () => {}, }); const captured = JSON.parse(await fs.readFile(capturePath, "utf-8")); + expect(captured.argv).toContain("--print"); + expect(captured.argv).not.toContain("-"); + expect(captured.prompt).toContain("Do work."); expect(captured.argv).toContain("--append-system-prompt-file"); } finally { restore();