@clanker-cleanroom/claude 0.2.7-build.37 → 0.2.7-build.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,8 @@ RUN curl -fsSL https://claude.ai/install.sh | bash \
18
18
 
19
19
  # Empty home for arbitrary host UIDs. ~/.claude is pre-created so a read-only
20
20
  # .credentials.json bind mount does not leave the directory root-owned.
21
- RUN mkdir -p /home/agent/.claude \
21
+ # Pre-create projects/-workspace for the narrow session mount (cwd=/workspace).
22
+ RUN mkdir -p /home/agent/.claude/projects/-workspace \
22
23
  && chmod -R 0777 /home/agent
23
24
 
24
25
  ENV HOME=/home/agent
@@ -5,6 +5,8 @@ let node_os = require("node:os");
5
5
  //#region src/constants.ts
6
6
  const CLAUDE_IMAGE = "clanker-cleanroom/claude";
7
7
  const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${clanker_cleanroom.CONTAINER_HOME}/.claude/.credentials.json`;
8
+ /** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */
9
+ const CLAUDE_CONTAINER_SESSION_PATH = `${clanker_cleanroom.CONTAINER_HOME}/.claude/projects/-workspace`;
8
10
  /** Long-lived OAuth token from `claude setup-token` (Claude subscription). */
9
11
  const CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
10
12
  /** Anthropic API key (pay-as-you-go). */
@@ -158,7 +160,8 @@ const claudeBinding = {
158
160
  displayName: "Claude",
159
161
  trajectoryKind: "claude",
160
162
  adaptEvents: adaptClaudeEvents,
161
- command: ({ prompt, model }) => {
163
+ sessionDataPath: CLAUDE_CONTAINER_SESSION_PATH,
164
+ command: ({ prompt, model, sessionId }) => {
162
165
  const claudeArgs = [
163
166
  "claude",
164
167
  "-p",
@@ -167,6 +170,7 @@ const claudeBinding = {
167
170
  "--verbose",
168
171
  "--dangerously-skip-permissions"
169
172
  ];
173
+ if (sessionId !== void 0 && sessionId !== "") claudeArgs.push("--resume", sessionId);
170
174
  if (model !== void 0 && model !== "") claudeArgs.push("--model", model);
171
175
  claudeArgs.push("--", prompt);
172
176
  return claudeArgs;
@@ -233,6 +237,12 @@ Object.defineProperty(exports, "CLAUDE_CONTAINER_CREDENTIALS_PATH", {
233
237
  return CLAUDE_CONTAINER_CREDENTIALS_PATH;
234
238
  }
235
239
  });
240
+ Object.defineProperty(exports, "CLAUDE_CONTAINER_SESSION_PATH", {
241
+ enumerable: true,
242
+ get: function() {
243
+ return CLAUDE_CONTAINER_SESSION_PATH;
244
+ }
245
+ });
236
246
  Object.defineProperty(exports, "CLAUDE_IMAGE", {
237
247
  enumerable: true,
238
248
  get: function() {
@@ -276,4 +286,4 @@ Object.defineProperty(exports, "resolveClaudeCredentials", {
276
286
  }
277
287
  });
278
288
 
279
- //# sourceMappingURL=binding-C2kSQy-B.cjs.map
289
+ //# sourceMappingURL=binding-BZPFUSsx.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-BZPFUSsx.cjs","names":["CONTAINER_HOME","homedir","access","fsConstants","asRecord","readString","readTokenCount","extractTextContent","parseAgentJsonOutput","asRecord","readTokenCount"],"sources":["../src/constants.ts","../src/credentials.ts","../src/trajectory.ts","../src/binding.ts"],"sourcesContent":["import { CONTAINER_HOME } from \"clanker-cleanroom\";\n\nexport const CLAUDE_IMAGE = \"clanker-cleanroom/claude\";\nexport const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${CONTAINER_HOME}/.claude/.credentials.json`;\n/** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */\nexport const CLAUDE_CONTAINER_SESSION_PATH = `${CONTAINER_HOME}/.claude/projects/-workspace`;\n\n/** Long-lived OAuth token from `claude setup-token` (Claude subscription). */\nexport const CLAUDE_OAUTH_TOKEN_ENV = \"CLAUDE_CODE_OAUTH_TOKEN\";\n/** Anthropic API key (pay-as-you-go). */\nexport const CLAUDE_API_KEY_ENV = \"ANTHROPIC_API_KEY\";\n\n/** Linux hosts persist OAuth credentials here; macOS uses the Keychain instead. */\nexport const defaultClaudeHostCredentialsFile = (home: string): string =>\n `${home}/.claude/.credentials.json`;\n","import { access } from \"node:fs/promises\";\nimport { constants as fsConstants } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n CLAUDE_API_KEY_ENV,\n CLAUDE_OAUTH_TOKEN_ENV,\n defaultClaudeHostCredentialsFile,\n} from \"./constants.js\";\n\n/**\n * How Claude Code authenticates inside the container. Env-backed kinds are forwarded\n * to `docker run` by name (never on argv); the file kind is bind-mounted read-only.\n * See `constants.ts` for which host setups produce each.\n */\nexport type ClaudeCredentials =\n | { kind: \"oauth-token\"; token: string }\n | { kind: \"api-key\"; apiKey: string }\n | { kind: \"credentials-file\"; file: string };\n\n/** Env OAuth token, then env API key, then a readable host credentials file. */\nexport async function resolveClaudeCredentials(\n options: { env?: NodeJS.ProcessEnv; home?: string } = {},\n): Promise<ClaudeCredentials> {\n const env = options.env ?? process.env;\n const home = options.home ?? homedir();\n\n const token = env[CLAUDE_OAUTH_TOKEN_ENV];\n if (token !== undefined && token !== \"\") {\n return { kind: \"oauth-token\", token };\n }\n\n const apiKey = env[CLAUDE_API_KEY_ENV];\n if (apiKey !== undefined && apiKey !== \"\") {\n return { kind: \"api-key\", apiKey };\n }\n\n const file = defaultClaudeHostCredentialsFile(home);\n try {\n await access(file, fsConstants.R_OK);\n } catch {\n throw new Error(\n `Claude Code credentials not found. Set ${CLAUDE_OAUTH_TOKEN_ENV} (run \\`claude setup-token\\` on the host) ` +\n `or ${CLAUDE_API_KEY_ENV}, or provide ${file} (Linux hosts; macOS keeps credentials in the Keychain).`,\n );\n }\n\n return { kind: \"credentials-file\", file };\n}\n\n/** Secret values for the docker CLI process, keyed by env names forwarded via envPassthrough. */\nexport function credentialsEnv(credentials: ClaudeCredentials): Record<string, string> {\n switch (credentials.kind) {\n case \"oauth-token\":\n return { [CLAUDE_OAUTH_TOKEN_ENV]: credentials.token };\n case \"api-key\":\n return { [CLAUDE_API_KEY_ENV]: credentials.apiKey };\n case \"credentials-file\":\n return {};\n }\n}\n","import {\n asRecord,\n extractTextContent,\n readString,\n readTokenCount,\n type TrajectoryEvent,\n} from \"clanker-cleanroom\";\n\nexport function adaptClaudeEvents(rawEvents: readonly unknown[]): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n\n for (const raw of rawEvents) {\n const record = asRecord(raw);\n if (record === undefined) {\n events.push({ kind: \"other\", type: \"unknown\", raw });\n continue;\n }\n\n const type = readString(record.type) ?? \"unknown\";\n\n switch (type) {\n case \"system\":\n events.push({\n kind: \"system\",\n sessionId: readString(record.session_id),\n model: readString(record.model),\n raw,\n });\n break;\n case \"user\":\n events.push(...adaptClaudeUser(record, raw));\n break;\n case \"assistant\":\n events.push(...adaptClaudeAssistant(record, raw));\n break;\n case \"result\":\n events.push({\n kind: \"result\",\n text: readString(record.result) ?? \"\",\n isError: record.is_error === true,\n durationMs: readTokenCount(record.duration_ms),\n costUsd: readTokenCount(record.total_cost_usd),\n raw,\n });\n break;\n default:\n events.push({ kind: \"other\", type, raw });\n }\n }\n\n return events;\n}\n\nfunction adaptClaudeAssistant(\n record: Record<string, unknown>,\n raw: unknown,\n): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n if (text !== \"\") {\n events.push({ kind: \"assistant\", text, raw });\n }\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_use\") {\n continue;\n }\n events.push({\n kind: \"tool\",\n phase: \"started\",\n callId: readString(block.id) ?? \"\",\n name: readString(block.name) ?? \"unknown\",\n args: block.input ?? null,\n result: null,\n raw,\n });\n }\n\n if (events.length === 0) {\n events.push({ kind: \"assistant\", text: \"\", raw });\n }\n\n return events;\n}\n\nfunction adaptClaudeUser(record: Record<string, unknown>, raw: unknown): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n const toolResults: TrajectoryEvent[] = [];\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_result\") {\n continue;\n }\n toolResults.push({\n kind: \"tool\",\n phase: \"completed\",\n callId: readString(block.tool_use_id) ?? \"\",\n name: \"tool_result\",\n args: null,\n result: block.content ?? null,\n raw,\n });\n }\n\n if (text !== \"\" || toolResults.length === 0) {\n events.push({ kind: \"user\", text, raw });\n }\n events.push(...toolResults);\n return events;\n}\n\nfunction contentBlocks(message: unknown): Record<string, unknown>[] {\n const content = asRecord(message)?.content;\n if (!Array.isArray(content)) {\n return [];\n }\n return content.map((block) => asRecord(block)).filter((block) => block !== undefined);\n}\n","import {\n asRecord,\n parseAgentJsonOutput,\n readTokenCount,\n type AgentBinding,\n type AgentRunResult,\n type DockerVolumeMount,\n} from \"clanker-cleanroom\";\n\nimport { CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_CONTAINER_SESSION_PATH, CLAUDE_IMAGE } from \"./constants.js\";\nimport { credentialsEnv, resolveClaudeCredentials } from \"./credentials.js\";\nimport { adaptClaudeEvents } from \"./trajectory.js\";\n\nexport const claudeBinding: AgentBinding = {\n image: CLAUDE_IMAGE,\n displayName: \"Claude\",\n trajectoryKind: \"claude\",\n adaptEvents: adaptClaudeEvents,\n sessionDataPath: CLAUDE_CONTAINER_SESSION_PATH,\n command: ({ prompt, model, sessionId }) => {\n const claudeArgs = [\n \"claude\",\n \"-p\",\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n \"--dangerously-skip-permissions\",\n ];\n if (sessionId !== undefined && sessionId !== \"\") {\n claudeArgs.push(\"--resume\", sessionId);\n }\n if (model !== undefined && model !== \"\") {\n claudeArgs.push(\"--model\", model);\n }\n claudeArgs.push(\"--\", prompt);\n return claudeArgs;\n },\n prepare: async () => {\n const credentials = await resolveClaudeCredentials();\n const volumes: DockerVolumeMount[] = [];\n if (credentials.kind === \"credentials-file\") {\n volumes.push({\n host: credentials.file,\n container: CLAUDE_CONTAINER_CREDENTIALS_PATH,\n mode: \"ro\",\n });\n }\n return {\n volumes,\n env: credentialsEnv(credentials),\n };\n },\n parseResult: parseClaudeResult,\n describeFailure: describeClaudeFailure,\n};\n\nfunction parseClaudeResult(trajectory: string): AgentRunResult {\n const parsed = parseAgentJsonOutput(trajectory);\n if (isErrorResult(parsed)) {\n throw new Error(`Claude agent reported an error: ${describeErrorResult(parsed)}`);\n }\n\n const record = asRecord(parsed);\n const usage = asRecord(record?.usage);\n\n return {\n durationMs: readTokenCount(record?.duration_ms),\n costUsd: readTokenCount(record?.total_cost_usd),\n usage: {\n inputTokens: readTokenCount(usage?.input_tokens ?? usage?.inputTokens),\n outputTokens: readTokenCount(usage?.output_tokens ?? usage?.outputTokens),\n cacheReadTokens: readTokenCount(usage?.cache_read_input_tokens ?? usage?.cacheReadTokens),\n cacheWriteTokens: readTokenCount(\n usage?.cache_creation_input_tokens ?? usage?.cacheWriteTokens,\n ),\n },\n };\n}\n\nfunction describeClaudeFailure(trajectory: string): string | undefined {\n try {\n const parsed = parseAgentJsonOutput(trajectory);\n return isErrorResult(parsed) ? describeErrorResult(parsed) : undefined;\n } catch {\n return undefined;\n }\n}\n\ntype ClaudeErrorResult = {\n is_error: true;\n result: string;\n subtype?: string;\n terminal_reason?: string;\n};\n\nfunction isErrorResult(value: unknown): value is ClaudeErrorResult {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { is_error?: unknown }).is_error === true\n );\n}\n\nfunction describeErrorResult(result: ClaudeErrorResult): string {\n const message = String(result.result);\n\n if (result.terminal_reason !== undefined && result.terminal_reason !== \"completed\") {\n return `${result.terminal_reason}: ${message}`;\n }\n\n if (result.subtype !== undefined && result.subtype !== \"success\") {\n return `${result.subtype}: ${message}`;\n }\n\n return message;\n}\n"],"mappings":"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"}
@@ -5,6 +5,8 @@ import { homedir } from "node:os";
5
5
  //#region src/constants.ts
6
6
  const CLAUDE_IMAGE = "clanker-cleanroom/claude";
7
7
  const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${CONTAINER_HOME}/.claude/.credentials.json`;
8
+ /** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */
9
+ const CLAUDE_CONTAINER_SESSION_PATH = `${CONTAINER_HOME}/.claude/projects/-workspace`;
8
10
  /** Long-lived OAuth token from `claude setup-token` (Claude subscription). */
9
11
  const CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
10
12
  /** Anthropic API key (pay-as-you-go). */
@@ -158,7 +160,8 @@ const claudeBinding = {
158
160
  displayName: "Claude",
159
161
  trajectoryKind: "claude",
160
162
  adaptEvents: adaptClaudeEvents,
161
- command: ({ prompt, model }) => {
163
+ sessionDataPath: CLAUDE_CONTAINER_SESSION_PATH,
164
+ command: ({ prompt, model, sessionId }) => {
162
165
  const claudeArgs = [
163
166
  "claude",
164
167
  "-p",
@@ -167,6 +170,7 @@ const claudeBinding = {
167
170
  "--verbose",
168
171
  "--dangerously-skip-permissions"
169
172
  ];
173
+ if (sessionId !== void 0 && sessionId !== "") claudeArgs.push("--resume", sessionId);
170
174
  if (model !== void 0 && model !== "") claudeArgs.push("--model", model);
171
175
  claudeArgs.push("--", prompt);
172
176
  return claudeArgs;
@@ -221,6 +225,6 @@ function describeErrorResult(result) {
221
225
  return message;
222
226
  }
223
227
  //#endregion
224
- export { CLAUDE_API_KEY_ENV as a, CLAUDE_OAUTH_TOKEN_ENV as c, resolveClaudeCredentials as i, defaultClaudeHostCredentialsFile as l, adaptClaudeEvents as n, CLAUDE_CONTAINER_CREDENTIALS_PATH as o, credentialsEnv as r, CLAUDE_IMAGE as s, claudeBinding as t };
228
+ export { CLAUDE_API_KEY_ENV as a, CLAUDE_IMAGE as c, resolveClaudeCredentials as i, CLAUDE_OAUTH_TOKEN_ENV as l, adaptClaudeEvents as n, CLAUDE_CONTAINER_CREDENTIALS_PATH as o, credentialsEnv as r, CLAUDE_CONTAINER_SESSION_PATH as s, claudeBinding as t, defaultClaudeHostCredentialsFile as u };
225
229
 
226
- //# sourceMappingURL=binding-DAPBfelH.mjs.map
230
+ //# sourceMappingURL=binding-DRnzgDU4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-DRnzgDU4.mjs","names":["fsConstants"],"sources":["../src/constants.ts","../src/credentials.ts","../src/trajectory.ts","../src/binding.ts"],"sourcesContent":["import { CONTAINER_HOME } from \"clanker-cleanroom\";\n\nexport const CLAUDE_IMAGE = \"clanker-cleanroom/claude\";\nexport const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${CONTAINER_HOME}/.claude/.credentials.json`;\n/** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */\nexport const CLAUDE_CONTAINER_SESSION_PATH = `${CONTAINER_HOME}/.claude/projects/-workspace`;\n\n/** Long-lived OAuth token from `claude setup-token` (Claude subscription). */\nexport const CLAUDE_OAUTH_TOKEN_ENV = \"CLAUDE_CODE_OAUTH_TOKEN\";\n/** Anthropic API key (pay-as-you-go). */\nexport const CLAUDE_API_KEY_ENV = \"ANTHROPIC_API_KEY\";\n\n/** Linux hosts persist OAuth credentials here; macOS uses the Keychain instead. */\nexport const defaultClaudeHostCredentialsFile = (home: string): string =>\n `${home}/.claude/.credentials.json`;\n","import { access } from \"node:fs/promises\";\nimport { constants as fsConstants } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n CLAUDE_API_KEY_ENV,\n CLAUDE_OAUTH_TOKEN_ENV,\n defaultClaudeHostCredentialsFile,\n} from \"./constants.js\";\n\n/**\n * How Claude Code authenticates inside the container. Env-backed kinds are forwarded\n * to `docker run` by name (never on argv); the file kind is bind-mounted read-only.\n * See `constants.ts` for which host setups produce each.\n */\nexport type ClaudeCredentials =\n | { kind: \"oauth-token\"; token: string }\n | { kind: \"api-key\"; apiKey: string }\n | { kind: \"credentials-file\"; file: string };\n\n/** Env OAuth token, then env API key, then a readable host credentials file. */\nexport async function resolveClaudeCredentials(\n options: { env?: NodeJS.ProcessEnv; home?: string } = {},\n): Promise<ClaudeCredentials> {\n const env = options.env ?? process.env;\n const home = options.home ?? homedir();\n\n const token = env[CLAUDE_OAUTH_TOKEN_ENV];\n if (token !== undefined && token !== \"\") {\n return { kind: \"oauth-token\", token };\n }\n\n const apiKey = env[CLAUDE_API_KEY_ENV];\n if (apiKey !== undefined && apiKey !== \"\") {\n return { kind: \"api-key\", apiKey };\n }\n\n const file = defaultClaudeHostCredentialsFile(home);\n try {\n await access(file, fsConstants.R_OK);\n } catch {\n throw new Error(\n `Claude Code credentials not found. Set ${CLAUDE_OAUTH_TOKEN_ENV} (run \\`claude setup-token\\` on the host) ` +\n `or ${CLAUDE_API_KEY_ENV}, or provide ${file} (Linux hosts; macOS keeps credentials in the Keychain).`,\n );\n }\n\n return { kind: \"credentials-file\", file };\n}\n\n/** Secret values for the docker CLI process, keyed by env names forwarded via envPassthrough. */\nexport function credentialsEnv(credentials: ClaudeCredentials): Record<string, string> {\n switch (credentials.kind) {\n case \"oauth-token\":\n return { [CLAUDE_OAUTH_TOKEN_ENV]: credentials.token };\n case \"api-key\":\n return { [CLAUDE_API_KEY_ENV]: credentials.apiKey };\n case \"credentials-file\":\n return {};\n }\n}\n","import {\n asRecord,\n extractTextContent,\n readString,\n readTokenCount,\n type TrajectoryEvent,\n} from \"clanker-cleanroom\";\n\nexport function adaptClaudeEvents(rawEvents: readonly unknown[]): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n\n for (const raw of rawEvents) {\n const record = asRecord(raw);\n if (record === undefined) {\n events.push({ kind: \"other\", type: \"unknown\", raw });\n continue;\n }\n\n const type = readString(record.type) ?? \"unknown\";\n\n switch (type) {\n case \"system\":\n events.push({\n kind: \"system\",\n sessionId: readString(record.session_id),\n model: readString(record.model),\n raw,\n });\n break;\n case \"user\":\n events.push(...adaptClaudeUser(record, raw));\n break;\n case \"assistant\":\n events.push(...adaptClaudeAssistant(record, raw));\n break;\n case \"result\":\n events.push({\n kind: \"result\",\n text: readString(record.result) ?? \"\",\n isError: record.is_error === true,\n durationMs: readTokenCount(record.duration_ms),\n costUsd: readTokenCount(record.total_cost_usd),\n raw,\n });\n break;\n default:\n events.push({ kind: \"other\", type, raw });\n }\n }\n\n return events;\n}\n\nfunction adaptClaudeAssistant(\n record: Record<string, unknown>,\n raw: unknown,\n): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n if (text !== \"\") {\n events.push({ kind: \"assistant\", text, raw });\n }\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_use\") {\n continue;\n }\n events.push({\n kind: \"tool\",\n phase: \"started\",\n callId: readString(block.id) ?? \"\",\n name: readString(block.name) ?? \"unknown\",\n args: block.input ?? null,\n result: null,\n raw,\n });\n }\n\n if (events.length === 0) {\n events.push({ kind: \"assistant\", text: \"\", raw });\n }\n\n return events;\n}\n\nfunction adaptClaudeUser(record: Record<string, unknown>, raw: unknown): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n const toolResults: TrajectoryEvent[] = [];\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_result\") {\n continue;\n }\n toolResults.push({\n kind: \"tool\",\n phase: \"completed\",\n callId: readString(block.tool_use_id) ?? \"\",\n name: \"tool_result\",\n args: null,\n result: block.content ?? null,\n raw,\n });\n }\n\n if (text !== \"\" || toolResults.length === 0) {\n events.push({ kind: \"user\", text, raw });\n }\n events.push(...toolResults);\n return events;\n}\n\nfunction contentBlocks(message: unknown): Record<string, unknown>[] {\n const content = asRecord(message)?.content;\n if (!Array.isArray(content)) {\n return [];\n }\n return content.map((block) => asRecord(block)).filter((block) => block !== undefined);\n}\n","import {\n asRecord,\n parseAgentJsonOutput,\n readTokenCount,\n type AgentBinding,\n type AgentRunResult,\n type DockerVolumeMount,\n} from \"clanker-cleanroom\";\n\nimport { CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_CONTAINER_SESSION_PATH, CLAUDE_IMAGE } from \"./constants.js\";\nimport { credentialsEnv, resolveClaudeCredentials } from \"./credentials.js\";\nimport { adaptClaudeEvents } from \"./trajectory.js\";\n\nexport const claudeBinding: AgentBinding = {\n image: CLAUDE_IMAGE,\n displayName: \"Claude\",\n trajectoryKind: \"claude\",\n adaptEvents: adaptClaudeEvents,\n sessionDataPath: CLAUDE_CONTAINER_SESSION_PATH,\n command: ({ prompt, model, sessionId }) => {\n const claudeArgs = [\n \"claude\",\n \"-p\",\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n \"--dangerously-skip-permissions\",\n ];\n if (sessionId !== undefined && sessionId !== \"\") {\n claudeArgs.push(\"--resume\", sessionId);\n }\n if (model !== undefined && model !== \"\") {\n claudeArgs.push(\"--model\", model);\n }\n claudeArgs.push(\"--\", prompt);\n return claudeArgs;\n },\n prepare: async () => {\n const credentials = await resolveClaudeCredentials();\n const volumes: DockerVolumeMount[] = [];\n if (credentials.kind === \"credentials-file\") {\n volumes.push({\n host: credentials.file,\n container: CLAUDE_CONTAINER_CREDENTIALS_PATH,\n mode: \"ro\",\n });\n }\n return {\n volumes,\n env: credentialsEnv(credentials),\n };\n },\n parseResult: parseClaudeResult,\n describeFailure: describeClaudeFailure,\n};\n\nfunction parseClaudeResult(trajectory: string): AgentRunResult {\n const parsed = parseAgentJsonOutput(trajectory);\n if (isErrorResult(parsed)) {\n throw new Error(`Claude agent reported an error: ${describeErrorResult(parsed)}`);\n }\n\n const record = asRecord(parsed);\n const usage = asRecord(record?.usage);\n\n return {\n durationMs: readTokenCount(record?.duration_ms),\n costUsd: readTokenCount(record?.total_cost_usd),\n usage: {\n inputTokens: readTokenCount(usage?.input_tokens ?? usage?.inputTokens),\n outputTokens: readTokenCount(usage?.output_tokens ?? usage?.outputTokens),\n cacheReadTokens: readTokenCount(usage?.cache_read_input_tokens ?? usage?.cacheReadTokens),\n cacheWriteTokens: readTokenCount(\n usage?.cache_creation_input_tokens ?? usage?.cacheWriteTokens,\n ),\n },\n };\n}\n\nfunction describeClaudeFailure(trajectory: string): string | undefined {\n try {\n const parsed = parseAgentJsonOutput(trajectory);\n return isErrorResult(parsed) ? describeErrorResult(parsed) : undefined;\n } catch {\n return undefined;\n }\n}\n\ntype ClaudeErrorResult = {\n is_error: true;\n result: string;\n subtype?: string;\n terminal_reason?: string;\n};\n\nfunction isErrorResult(value: unknown): value is ClaudeErrorResult {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { is_error?: unknown }).is_error === true\n );\n}\n\nfunction describeErrorResult(result: ClaudeErrorResult): string {\n const message = String(result.result);\n\n if (result.terminal_reason !== undefined && result.terminal_reason !== \"completed\") {\n return `${result.terminal_reason}: ${message}`;\n }\n\n if (result.subtype !== undefined && result.subtype !== \"success\") {\n return `${result.subtype}: ${message}`;\n }\n\n return message;\n}\n"],"mappings":"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"}
package/lib/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_binding = require("./binding-C2kSQy-B.cjs");
2
+ const require_binding = require("./binding-BZPFUSsx.cjs");
3
3
  let clanker_cleanroom = require("clanker-cleanroom");
4
4
  let node_path = require("node:path");
5
5
  let node_url = require("node:url");
@@ -14,6 +14,7 @@ const DOCKER_DIR = (0, node_path.join)(PACKAGE_ROOT, "docker");
14
14
  //#endregion
15
15
  exports.CLAUDE_API_KEY_ENV = require_binding.CLAUDE_API_KEY_ENV;
16
16
  exports.CLAUDE_CONTAINER_CREDENTIALS_PATH = require_binding.CLAUDE_CONTAINER_CREDENTIALS_PATH;
17
+ exports.CLAUDE_CONTAINER_SESSION_PATH = require_binding.CLAUDE_CONTAINER_SESSION_PATH;
17
18
  exports.CLAUDE_IMAGE = require_binding.CLAUDE_IMAGE;
18
19
  exports.CLAUDE_OAUTH_TOKEN_ENV = require_binding.CLAUDE_OAUTH_TOKEN_ENV;
19
20
  exports.DOCKER_DIR = DOCKER_DIR;
package/lib/index.d.cts CHANGED
@@ -2,6 +2,8 @@ import { Agent, AgentBinding, TrajectoryEvent } from "clanker-cleanroom";
2
2
  //#region src/constants.d.ts
3
3
  declare const CLAUDE_IMAGE = "clanker-cleanroom/claude";
4
4
  declare const CLAUDE_CONTAINER_CREDENTIALS_PATH = "/home/agent/.claude/.credentials.json";
5
+ /** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */
6
+ declare const CLAUDE_CONTAINER_SESSION_PATH = "/home/agent/.claude/projects/-workspace";
5
7
  /** Long-lived OAuth token from `claude setup-token` (Claude subscription). */
6
8
  declare const CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
7
9
  /** Anthropic API key (pay-as-you-go). */
@@ -48,5 +50,5 @@ declare const PACKAGE_ROOT: string;
48
50
  /** Folder of `*.Dockerfile` files for this agent. */
49
51
  declare const DOCKER_DIR: string;
50
52
  //#endregion
51
- export { CLAUDE_API_KEY_ENV, CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_IMAGE, CLAUDE_OAUTH_TOKEN_ENV, type ClaudeCredentials, DOCKER_DIR, PACKAGE_ROOT, adaptClaudeEvents, claudeAgent, claudeBinding, credentialsEnv, defaultClaudeHostCredentialsFile, resolveClaudeCredentials };
53
+ export { CLAUDE_API_KEY_ENV, CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_CONTAINER_SESSION_PATH, CLAUDE_IMAGE, CLAUDE_OAUTH_TOKEN_ENV, type ClaudeCredentials, DOCKER_DIR, PACKAGE_ROOT, adaptClaudeEvents, claudeAgent, claudeBinding, credentialsEnv, defaultClaudeHostCredentialsFile, resolveClaudeCredentials };
52
54
  //# sourceMappingURL=index.d.cts.map
package/lib/index.d.mts CHANGED
@@ -2,6 +2,8 @@ import { Agent, AgentBinding, TrajectoryEvent } from "clanker-cleanroom";
2
2
  //#region src/constants.d.ts
3
3
  declare const CLAUDE_IMAGE = "clanker-cleanroom/claude";
4
4
  declare const CLAUDE_CONTAINER_CREDENTIALS_PATH = "/home/agent/.claude/.credentials.json";
5
+ /** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */
6
+ declare const CLAUDE_CONTAINER_SESSION_PATH = "/home/agent/.claude/projects/-workspace";
5
7
  /** Long-lived OAuth token from `claude setup-token` (Claude subscription). */
6
8
  declare const CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
7
9
  /** Anthropic API key (pay-as-you-go). */
@@ -48,5 +50,5 @@ declare const PACKAGE_ROOT: string;
48
50
  /** Folder of `*.Dockerfile` files for this agent. */
49
51
  declare const DOCKER_DIR: string;
50
52
  //#endregion
51
- export { CLAUDE_API_KEY_ENV, CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_IMAGE, CLAUDE_OAUTH_TOKEN_ENV, type ClaudeCredentials, DOCKER_DIR, PACKAGE_ROOT, adaptClaudeEvents, claudeAgent, claudeBinding, credentialsEnv, defaultClaudeHostCredentialsFile, resolveClaudeCredentials };
53
+ export { CLAUDE_API_KEY_ENV, CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_CONTAINER_SESSION_PATH, CLAUDE_IMAGE, CLAUDE_OAUTH_TOKEN_ENV, type ClaudeCredentials, DOCKER_DIR, PACKAGE_ROOT, adaptClaudeEvents, claudeAgent, claudeBinding, credentialsEnv, defaultClaudeHostCredentialsFile, resolveClaudeCredentials };
52
54
  //# sourceMappingURL=index.d.mts.map
package/lib/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as CLAUDE_API_KEY_ENV, c as CLAUDE_OAUTH_TOKEN_ENV, i as resolveClaudeCredentials, l as defaultClaudeHostCredentialsFile, n as adaptClaudeEvents, o as CLAUDE_CONTAINER_CREDENTIALS_PATH, r as credentialsEnv, s as CLAUDE_IMAGE, t as claudeBinding } from "./binding-DAPBfelH.mjs";
1
+ import { a as CLAUDE_API_KEY_ENV, c as CLAUDE_IMAGE, i as resolveClaudeCredentials, l as CLAUDE_OAUTH_TOKEN_ENV, n as adaptClaudeEvents, o as CLAUDE_CONTAINER_CREDENTIALS_PATH, r as credentialsEnv, s as CLAUDE_CONTAINER_SESSION_PATH, t as claudeBinding, u as defaultClaudeHostCredentialsFile } from "./binding-DRnzgDU4.mjs";
2
2
  import { Agent } from "clanker-cleanroom";
3
3
  import { join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
@@ -11,6 +11,6 @@ const PACKAGE_ROOT = fileURLToPath(new URL("..", import.meta.url));
11
11
  /** Folder of `*.Dockerfile` files for this agent. */
12
12
  const DOCKER_DIR = join(PACKAGE_ROOT, "docker");
13
13
  //#endregion
14
- export { CLAUDE_API_KEY_ENV, CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_IMAGE, CLAUDE_OAUTH_TOKEN_ENV, DOCKER_DIR, PACKAGE_ROOT, adaptClaudeEvents, claudeAgent, claudeBinding, credentialsEnv, defaultClaudeHostCredentialsFile, resolveClaudeCredentials };
14
+ export { CLAUDE_API_KEY_ENV, CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_CONTAINER_SESSION_PATH, CLAUDE_IMAGE, CLAUDE_OAUTH_TOKEN_ENV, DOCKER_DIR, PACKAGE_ROOT, adaptClaudeEvents, claudeAgent, claudeBinding, credentialsEnv, defaultClaudeHostCredentialsFile, resolveClaudeCredentials };
15
15
 
16
16
  //# sourceMappingURL=index.mjs.map
package/lib/register.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_binding = require("./binding-C2kSQy-B.cjs");
1
+ const require_binding = require("./binding-BZPFUSsx.cjs");
2
2
  //#region src/register.ts
3
3
  (0, require("clanker-cleanroom").registerBinding)("claude", require_binding.claudeBinding);
4
4
  //#endregion
package/lib/register.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as claudeBinding } from "./binding-DAPBfelH.mjs";
1
+ import { t as claudeBinding } from "./binding-DRnzgDU4.mjs";
2
2
  import { registerBinding } from "clanker-cleanroom";
3
3
  //#region src/register.ts
4
4
  registerBinding("claude", claudeBinding);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clanker-cleanroom/claude",
3
- "version": "0.2.7-build.37",
3
+ "version": "0.2.7-build.38",
4
4
  "description": "Claude Code agent binding for clanker-cleanroom",
5
5
  "keywords": [
6
6
  "agent",
@@ -115,7 +115,7 @@
115
115
  }
116
116
  },
117
117
  "dependencies": {
118
- "clanker-cleanroom": "0.2.7-build.37"
118
+ "clanker-cleanroom": "0.2.7-build.38"
119
119
  },
120
120
  "devDependencies": {
121
121
  "@types/node": "^26.2.0",
package/src/binding.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  type DockerVolumeMount,
8
8
  } from "clanker-cleanroom";
9
9
 
10
- import { CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_IMAGE } from "./constants.js";
10
+ import { CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_CONTAINER_SESSION_PATH, CLAUDE_IMAGE } from "./constants.js";
11
11
  import { credentialsEnv, resolveClaudeCredentials } from "./credentials.js";
12
12
  import { adaptClaudeEvents } from "./trajectory.js";
13
13
 
@@ -16,7 +16,8 @@ export const claudeBinding: AgentBinding = {
16
16
  displayName: "Claude",
17
17
  trajectoryKind: "claude",
18
18
  adaptEvents: adaptClaudeEvents,
19
- command: ({ prompt, model }) => {
19
+ sessionDataPath: CLAUDE_CONTAINER_SESSION_PATH,
20
+ command: ({ prompt, model, sessionId }) => {
20
21
  const claudeArgs = [
21
22
  "claude",
22
23
  "-p",
@@ -25,6 +26,9 @@ export const claudeBinding: AgentBinding = {
25
26
  "--verbose",
26
27
  "--dangerously-skip-permissions",
27
28
  ];
29
+ if (sessionId !== undefined && sessionId !== "") {
30
+ claudeArgs.push("--resume", sessionId);
31
+ }
28
32
  if (model !== undefined && model !== "") {
29
33
  claudeArgs.push("--model", model);
30
34
  }
package/src/constants.ts CHANGED
@@ -2,6 +2,8 @@ import { CONTAINER_HOME } from "clanker-cleanroom";
2
2
 
3
3
  export const CLAUDE_IMAGE = "clanker-cleanroom/claude";
4
4
  export const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${CONTAINER_HOME}/.claude/.credentials.json`;
5
+ /** Session transcripts for cwd `/workspace` (Claude encodes `/` as `-`). */
6
+ export const CLAUDE_CONTAINER_SESSION_PATH = `${CONTAINER_HOME}/.claude/projects/-workspace`;
5
7
 
6
8
  /** Long-lived OAuth token from `claude setup-token` (Claude subscription). */
7
9
  export const CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export {
2
2
  CLAUDE_API_KEY_ENV,
3
3
  CLAUDE_CONTAINER_CREDENTIALS_PATH,
4
+ CLAUDE_CONTAINER_SESSION_PATH,
4
5
  CLAUDE_IMAGE,
5
6
  CLAUDE_OAUTH_TOKEN_ENV,
6
7
  defaultClaudeHostCredentialsFile,
@@ -1 +0,0 @@
1
- {"version":3,"file":"binding-C2kSQy-B.cjs","names":["CONTAINER_HOME","homedir","access","fsConstants","asRecord","readString","readTokenCount","extractTextContent","parseAgentJsonOutput","asRecord","readTokenCount"],"sources":["../src/constants.ts","../src/credentials.ts","../src/trajectory.ts","../src/binding.ts"],"sourcesContent":["import { CONTAINER_HOME } from \"clanker-cleanroom\";\n\nexport const CLAUDE_IMAGE = \"clanker-cleanroom/claude\";\nexport const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${CONTAINER_HOME}/.claude/.credentials.json`;\n\n/** Long-lived OAuth token from `claude setup-token` (Claude subscription). */\nexport const CLAUDE_OAUTH_TOKEN_ENV = \"CLAUDE_CODE_OAUTH_TOKEN\";\n/** Anthropic API key (pay-as-you-go). */\nexport const CLAUDE_API_KEY_ENV = \"ANTHROPIC_API_KEY\";\n\n/** Linux hosts persist OAuth credentials here; macOS uses the Keychain instead. */\nexport const defaultClaudeHostCredentialsFile = (home: string): string =>\n `${home}/.claude/.credentials.json`;\n","import { access } from \"node:fs/promises\";\nimport { constants as fsConstants } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n CLAUDE_API_KEY_ENV,\n CLAUDE_OAUTH_TOKEN_ENV,\n defaultClaudeHostCredentialsFile,\n} from \"./constants.js\";\n\n/**\n * How Claude Code authenticates inside the container. Env-backed kinds are forwarded\n * to `docker run` by name (never on argv); the file kind is bind-mounted read-only.\n * See `constants.ts` for which host setups produce each.\n */\nexport type ClaudeCredentials =\n | { kind: \"oauth-token\"; token: string }\n | { kind: \"api-key\"; apiKey: string }\n | { kind: \"credentials-file\"; file: string };\n\n/** Env OAuth token, then env API key, then a readable host credentials file. */\nexport async function resolveClaudeCredentials(\n options: { env?: NodeJS.ProcessEnv; home?: string } = {},\n): Promise<ClaudeCredentials> {\n const env = options.env ?? process.env;\n const home = options.home ?? homedir();\n\n const token = env[CLAUDE_OAUTH_TOKEN_ENV];\n if (token !== undefined && token !== \"\") {\n return { kind: \"oauth-token\", token };\n }\n\n const apiKey = env[CLAUDE_API_KEY_ENV];\n if (apiKey !== undefined && apiKey !== \"\") {\n return { kind: \"api-key\", apiKey };\n }\n\n const file = defaultClaudeHostCredentialsFile(home);\n try {\n await access(file, fsConstants.R_OK);\n } catch {\n throw new Error(\n `Claude Code credentials not found. Set ${CLAUDE_OAUTH_TOKEN_ENV} (run \\`claude setup-token\\` on the host) ` +\n `or ${CLAUDE_API_KEY_ENV}, or provide ${file} (Linux hosts; macOS keeps credentials in the Keychain).`,\n );\n }\n\n return { kind: \"credentials-file\", file };\n}\n\n/** Secret values for the docker CLI process, keyed by env names forwarded via envPassthrough. */\nexport function credentialsEnv(credentials: ClaudeCredentials): Record<string, string> {\n switch (credentials.kind) {\n case \"oauth-token\":\n return { [CLAUDE_OAUTH_TOKEN_ENV]: credentials.token };\n case \"api-key\":\n return { [CLAUDE_API_KEY_ENV]: credentials.apiKey };\n case \"credentials-file\":\n return {};\n }\n}\n","import {\n asRecord,\n extractTextContent,\n readString,\n readTokenCount,\n type TrajectoryEvent,\n} from \"clanker-cleanroom\";\n\nexport function adaptClaudeEvents(rawEvents: readonly unknown[]): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n\n for (const raw of rawEvents) {\n const record = asRecord(raw);\n if (record === undefined) {\n events.push({ kind: \"other\", type: \"unknown\", raw });\n continue;\n }\n\n const type = readString(record.type) ?? \"unknown\";\n\n switch (type) {\n case \"system\":\n events.push({\n kind: \"system\",\n sessionId: readString(record.session_id),\n model: readString(record.model),\n raw,\n });\n break;\n case \"user\":\n events.push(...adaptClaudeUser(record, raw));\n break;\n case \"assistant\":\n events.push(...adaptClaudeAssistant(record, raw));\n break;\n case \"result\":\n events.push({\n kind: \"result\",\n text: readString(record.result) ?? \"\",\n isError: record.is_error === true,\n durationMs: readTokenCount(record.duration_ms),\n costUsd: readTokenCount(record.total_cost_usd),\n raw,\n });\n break;\n default:\n events.push({ kind: \"other\", type, raw });\n }\n }\n\n return events;\n}\n\nfunction adaptClaudeAssistant(\n record: Record<string, unknown>,\n raw: unknown,\n): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n if (text !== \"\") {\n events.push({ kind: \"assistant\", text, raw });\n }\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_use\") {\n continue;\n }\n events.push({\n kind: \"tool\",\n phase: \"started\",\n callId: readString(block.id) ?? \"\",\n name: readString(block.name) ?? \"unknown\",\n args: block.input ?? null,\n result: null,\n raw,\n });\n }\n\n if (events.length === 0) {\n events.push({ kind: \"assistant\", text: \"\", raw });\n }\n\n return events;\n}\n\nfunction adaptClaudeUser(record: Record<string, unknown>, raw: unknown): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n const toolResults: TrajectoryEvent[] = [];\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_result\") {\n continue;\n }\n toolResults.push({\n kind: \"tool\",\n phase: \"completed\",\n callId: readString(block.tool_use_id) ?? \"\",\n name: \"tool_result\",\n args: null,\n result: block.content ?? null,\n raw,\n });\n }\n\n if (text !== \"\" || toolResults.length === 0) {\n events.push({ kind: \"user\", text, raw });\n }\n events.push(...toolResults);\n return events;\n}\n\nfunction contentBlocks(message: unknown): Record<string, unknown>[] {\n const content = asRecord(message)?.content;\n if (!Array.isArray(content)) {\n return [];\n }\n return content.map((block) => asRecord(block)).filter((block) => block !== undefined);\n}\n","import {\n asRecord,\n parseAgentJsonOutput,\n readTokenCount,\n type AgentBinding,\n type AgentRunResult,\n type DockerVolumeMount,\n} from \"clanker-cleanroom\";\n\nimport { CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_IMAGE } from \"./constants.js\";\nimport { credentialsEnv, resolveClaudeCredentials } from \"./credentials.js\";\nimport { adaptClaudeEvents } from \"./trajectory.js\";\n\nexport const claudeBinding: AgentBinding = {\n image: CLAUDE_IMAGE,\n displayName: \"Claude\",\n trajectoryKind: \"claude\",\n adaptEvents: adaptClaudeEvents,\n command: ({ prompt, model }) => {\n const claudeArgs = [\n \"claude\",\n \"-p\",\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n \"--dangerously-skip-permissions\",\n ];\n if (model !== undefined && model !== \"\") {\n claudeArgs.push(\"--model\", model);\n }\n claudeArgs.push(\"--\", prompt);\n return claudeArgs;\n },\n prepare: async () => {\n const credentials = await resolveClaudeCredentials();\n const volumes: DockerVolumeMount[] = [];\n if (credentials.kind === \"credentials-file\") {\n volumes.push({\n host: credentials.file,\n container: CLAUDE_CONTAINER_CREDENTIALS_PATH,\n mode: \"ro\",\n });\n }\n return {\n volumes,\n env: credentialsEnv(credentials),\n };\n },\n parseResult: parseClaudeResult,\n describeFailure: describeClaudeFailure,\n};\n\nfunction parseClaudeResult(trajectory: string): AgentRunResult {\n const parsed = parseAgentJsonOutput(trajectory);\n if (isErrorResult(parsed)) {\n throw new Error(`Claude agent reported an error: ${describeErrorResult(parsed)}`);\n }\n\n const record = asRecord(parsed);\n const usage = asRecord(record?.usage);\n\n return {\n durationMs: readTokenCount(record?.duration_ms),\n costUsd: readTokenCount(record?.total_cost_usd),\n usage: {\n inputTokens: readTokenCount(usage?.input_tokens ?? usage?.inputTokens),\n outputTokens: readTokenCount(usage?.output_tokens ?? usage?.outputTokens),\n cacheReadTokens: readTokenCount(usage?.cache_read_input_tokens ?? usage?.cacheReadTokens),\n cacheWriteTokens: readTokenCount(\n usage?.cache_creation_input_tokens ?? usage?.cacheWriteTokens,\n ),\n },\n };\n}\n\nfunction describeClaudeFailure(trajectory: string): string | undefined {\n try {\n const parsed = parseAgentJsonOutput(trajectory);\n return isErrorResult(parsed) ? describeErrorResult(parsed) : undefined;\n } catch {\n return undefined;\n }\n}\n\ntype ClaudeErrorResult = {\n is_error: true;\n result: string;\n subtype?: string;\n terminal_reason?: string;\n};\n\nfunction isErrorResult(value: unknown): value is ClaudeErrorResult {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { is_error?: unknown }).is_error === true\n );\n}\n\nfunction describeErrorResult(result: ClaudeErrorResult): string {\n const message = String(result.result);\n\n if (result.terminal_reason !== undefined && result.terminal_reason !== \"completed\") {\n return `${result.terminal_reason}: ${message}`;\n }\n\n if (result.subtype !== undefined && result.subtype !== \"success\") {\n return `${result.subtype}: ${message}`;\n }\n\n return message;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"binding-DAPBfelH.mjs","names":["fsConstants"],"sources":["../src/constants.ts","../src/credentials.ts","../src/trajectory.ts","../src/binding.ts"],"sourcesContent":["import { CONTAINER_HOME } from \"clanker-cleanroom\";\n\nexport const CLAUDE_IMAGE = \"clanker-cleanroom/claude\";\nexport const CLAUDE_CONTAINER_CREDENTIALS_PATH = `${CONTAINER_HOME}/.claude/.credentials.json`;\n\n/** Long-lived OAuth token from `claude setup-token` (Claude subscription). */\nexport const CLAUDE_OAUTH_TOKEN_ENV = \"CLAUDE_CODE_OAUTH_TOKEN\";\n/** Anthropic API key (pay-as-you-go). */\nexport const CLAUDE_API_KEY_ENV = \"ANTHROPIC_API_KEY\";\n\n/** Linux hosts persist OAuth credentials here; macOS uses the Keychain instead. */\nexport const defaultClaudeHostCredentialsFile = (home: string): string =>\n `${home}/.claude/.credentials.json`;\n","import { access } from \"node:fs/promises\";\nimport { constants as fsConstants } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n CLAUDE_API_KEY_ENV,\n CLAUDE_OAUTH_TOKEN_ENV,\n defaultClaudeHostCredentialsFile,\n} from \"./constants.js\";\n\n/**\n * How Claude Code authenticates inside the container. Env-backed kinds are forwarded\n * to `docker run` by name (never on argv); the file kind is bind-mounted read-only.\n * See `constants.ts` for which host setups produce each.\n */\nexport type ClaudeCredentials =\n | { kind: \"oauth-token\"; token: string }\n | { kind: \"api-key\"; apiKey: string }\n | { kind: \"credentials-file\"; file: string };\n\n/** Env OAuth token, then env API key, then a readable host credentials file. */\nexport async function resolveClaudeCredentials(\n options: { env?: NodeJS.ProcessEnv; home?: string } = {},\n): Promise<ClaudeCredentials> {\n const env = options.env ?? process.env;\n const home = options.home ?? homedir();\n\n const token = env[CLAUDE_OAUTH_TOKEN_ENV];\n if (token !== undefined && token !== \"\") {\n return { kind: \"oauth-token\", token };\n }\n\n const apiKey = env[CLAUDE_API_KEY_ENV];\n if (apiKey !== undefined && apiKey !== \"\") {\n return { kind: \"api-key\", apiKey };\n }\n\n const file = defaultClaudeHostCredentialsFile(home);\n try {\n await access(file, fsConstants.R_OK);\n } catch {\n throw new Error(\n `Claude Code credentials not found. Set ${CLAUDE_OAUTH_TOKEN_ENV} (run \\`claude setup-token\\` on the host) ` +\n `or ${CLAUDE_API_KEY_ENV}, or provide ${file} (Linux hosts; macOS keeps credentials in the Keychain).`,\n );\n }\n\n return { kind: \"credentials-file\", file };\n}\n\n/** Secret values for the docker CLI process, keyed by env names forwarded via envPassthrough. */\nexport function credentialsEnv(credentials: ClaudeCredentials): Record<string, string> {\n switch (credentials.kind) {\n case \"oauth-token\":\n return { [CLAUDE_OAUTH_TOKEN_ENV]: credentials.token };\n case \"api-key\":\n return { [CLAUDE_API_KEY_ENV]: credentials.apiKey };\n case \"credentials-file\":\n return {};\n }\n}\n","import {\n asRecord,\n extractTextContent,\n readString,\n readTokenCount,\n type TrajectoryEvent,\n} from \"clanker-cleanroom\";\n\nexport function adaptClaudeEvents(rawEvents: readonly unknown[]): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n\n for (const raw of rawEvents) {\n const record = asRecord(raw);\n if (record === undefined) {\n events.push({ kind: \"other\", type: \"unknown\", raw });\n continue;\n }\n\n const type = readString(record.type) ?? \"unknown\";\n\n switch (type) {\n case \"system\":\n events.push({\n kind: \"system\",\n sessionId: readString(record.session_id),\n model: readString(record.model),\n raw,\n });\n break;\n case \"user\":\n events.push(...adaptClaudeUser(record, raw));\n break;\n case \"assistant\":\n events.push(...adaptClaudeAssistant(record, raw));\n break;\n case \"result\":\n events.push({\n kind: \"result\",\n text: readString(record.result) ?? \"\",\n isError: record.is_error === true,\n durationMs: readTokenCount(record.duration_ms),\n costUsd: readTokenCount(record.total_cost_usd),\n raw,\n });\n break;\n default:\n events.push({ kind: \"other\", type, raw });\n }\n }\n\n return events;\n}\n\nfunction adaptClaudeAssistant(\n record: Record<string, unknown>,\n raw: unknown,\n): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n if (text !== \"\") {\n events.push({ kind: \"assistant\", text, raw });\n }\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_use\") {\n continue;\n }\n events.push({\n kind: \"tool\",\n phase: \"started\",\n callId: readString(block.id) ?? \"\",\n name: readString(block.name) ?? \"unknown\",\n args: block.input ?? null,\n result: null,\n raw,\n });\n }\n\n if (events.length === 0) {\n events.push({ kind: \"assistant\", text: \"\", raw });\n }\n\n return events;\n}\n\nfunction adaptClaudeUser(record: Record<string, unknown>, raw: unknown): TrajectoryEvent[] {\n const events: TrajectoryEvent[] = [];\n const text = extractTextContent(record.message);\n const toolResults: TrajectoryEvent[] = [];\n\n for (const block of contentBlocks(record.message)) {\n if (block.type !== \"tool_result\") {\n continue;\n }\n toolResults.push({\n kind: \"tool\",\n phase: \"completed\",\n callId: readString(block.tool_use_id) ?? \"\",\n name: \"tool_result\",\n args: null,\n result: block.content ?? null,\n raw,\n });\n }\n\n if (text !== \"\" || toolResults.length === 0) {\n events.push({ kind: \"user\", text, raw });\n }\n events.push(...toolResults);\n return events;\n}\n\nfunction contentBlocks(message: unknown): Record<string, unknown>[] {\n const content = asRecord(message)?.content;\n if (!Array.isArray(content)) {\n return [];\n }\n return content.map((block) => asRecord(block)).filter((block) => block !== undefined);\n}\n","import {\n asRecord,\n parseAgentJsonOutput,\n readTokenCount,\n type AgentBinding,\n type AgentRunResult,\n type DockerVolumeMount,\n} from \"clanker-cleanroom\";\n\nimport { CLAUDE_CONTAINER_CREDENTIALS_PATH, CLAUDE_IMAGE } from \"./constants.js\";\nimport { credentialsEnv, resolveClaudeCredentials } from \"./credentials.js\";\nimport { adaptClaudeEvents } from \"./trajectory.js\";\n\nexport const claudeBinding: AgentBinding = {\n image: CLAUDE_IMAGE,\n displayName: \"Claude\",\n trajectoryKind: \"claude\",\n adaptEvents: adaptClaudeEvents,\n command: ({ prompt, model }) => {\n const claudeArgs = [\n \"claude\",\n \"-p\",\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n \"--dangerously-skip-permissions\",\n ];\n if (model !== undefined && model !== \"\") {\n claudeArgs.push(\"--model\", model);\n }\n claudeArgs.push(\"--\", prompt);\n return claudeArgs;\n },\n prepare: async () => {\n const credentials = await resolveClaudeCredentials();\n const volumes: DockerVolumeMount[] = [];\n if (credentials.kind === \"credentials-file\") {\n volumes.push({\n host: credentials.file,\n container: CLAUDE_CONTAINER_CREDENTIALS_PATH,\n mode: \"ro\",\n });\n }\n return {\n volumes,\n env: credentialsEnv(credentials),\n };\n },\n parseResult: parseClaudeResult,\n describeFailure: describeClaudeFailure,\n};\n\nfunction parseClaudeResult(trajectory: string): AgentRunResult {\n const parsed = parseAgentJsonOutput(trajectory);\n if (isErrorResult(parsed)) {\n throw new Error(`Claude agent reported an error: ${describeErrorResult(parsed)}`);\n }\n\n const record = asRecord(parsed);\n const usage = asRecord(record?.usage);\n\n return {\n durationMs: readTokenCount(record?.duration_ms),\n costUsd: readTokenCount(record?.total_cost_usd),\n usage: {\n inputTokens: readTokenCount(usage?.input_tokens ?? usage?.inputTokens),\n outputTokens: readTokenCount(usage?.output_tokens ?? usage?.outputTokens),\n cacheReadTokens: readTokenCount(usage?.cache_read_input_tokens ?? usage?.cacheReadTokens),\n cacheWriteTokens: readTokenCount(\n usage?.cache_creation_input_tokens ?? usage?.cacheWriteTokens,\n ),\n },\n };\n}\n\nfunction describeClaudeFailure(trajectory: string): string | undefined {\n try {\n const parsed = parseAgentJsonOutput(trajectory);\n return isErrorResult(parsed) ? describeErrorResult(parsed) : undefined;\n } catch {\n return undefined;\n }\n}\n\ntype ClaudeErrorResult = {\n is_error: true;\n result: string;\n subtype?: string;\n terminal_reason?: string;\n};\n\nfunction isErrorResult(value: unknown): value is ClaudeErrorResult {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { is_error?: unknown }).is_error === true\n );\n}\n\nfunction describeErrorResult(result: ClaudeErrorResult): string {\n const message = String(result.result);\n\n if (result.terminal_reason !== undefined && result.terminal_reason !== \"completed\") {\n return `${result.terminal_reason}: ${message}`;\n }\n\n if (result.subtype !== undefined && result.subtype !== \"success\") {\n return `${result.subtype}: ${message}`;\n }\n\n return message;\n}\n"],"mappings":"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"}