@cabane/companion 0.6.142 → 0.6.143

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.
Files changed (3) hide show
  1. package/dist/cli.js +272 -160
  2. package/dist/runtime.js +226 -121
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -15,14 +15,14 @@ var init_errors = __esm({
15
15
  "src/errors.ts"() {
16
16
  "use strict";
17
17
  CompanionError = class extends Error {
18
- constructor(message) {
19
- super(message);
18
+ constructor(message2) {
19
+ super(message2);
20
20
  this.name = "CompanionError";
21
21
  }
22
22
  };
23
23
  ApiError = class extends CompanionError {
24
- constructor(status2, message, body) {
25
- super(message);
24
+ constructor(status2, message2, body) {
25
+ super(message2);
26
26
  this.status = status2;
27
27
  this.body = body;
28
28
  this.name = "ApiError";
@@ -31,14 +31,14 @@ var init_errors = __esm({
31
31
  body;
32
32
  };
33
33
  ConfigError = class extends CompanionError {
34
- constructor(message) {
35
- super(message);
34
+ constructor(message2) {
35
+ super(message2);
36
36
  this.name = "ConfigError";
37
37
  }
38
38
  };
39
39
  PrepareHookError = class extends CompanionError {
40
- constructor(message) {
41
- super(message);
40
+ constructor(message2) {
41
+ super(message2);
42
42
  this.name = "PrepareHookError";
43
43
  }
44
44
  };
@@ -1063,8 +1063,8 @@ function apiErrorCode(err) {
1063
1063
  function apiErrorMessage(err) {
1064
1064
  const body = err instanceof ApiError ? err.body : void 0;
1065
1065
  if (body !== null && typeof body === "object") {
1066
- const message = body.message;
1067
- if (typeof message === "string" && message.length > 0) return message;
1066
+ const message2 = body.message;
1067
+ if (typeof message2 === "string" && message2.length > 0) return message2;
1068
1068
  }
1069
1069
  return err instanceof Error ? err.message : String(err);
1070
1070
  }
@@ -1097,9 +1097,9 @@ function describeErrorBody(body) {
1097
1097
  return text.length > ERROR_BODY_LOG_CAP ? `${text.slice(0, ERROR_BODY_LOG_CAP)}\u2026` : text;
1098
1098
  }
1099
1099
  function describeError(err) {
1100
- const message = err instanceof Error ? err.message : String(err);
1100
+ const message2 = err instanceof Error ? err.message : String(err);
1101
1101
  const cause = describeCause(err);
1102
- return cause === void 0 ? message : `${message}: ${cause}`;
1102
+ return cause === void 0 ? message2 : `${message2}: ${cause}`;
1103
1103
  }
1104
1104
  function describeCause(err) {
1105
1105
  if (!(err instanceof Error)) return void 0;
@@ -2986,15 +2986,15 @@ async function* decodeSdkStream(iter, ctx) {
2986
2986
  }
2987
2987
  } catch (err) {
2988
2988
  if (ctx.signal.aborted) throw err;
2989
- const message = err instanceof Error ? err.message : String(err);
2990
- const failure = mcpInventory.initReceived ? classifyErrorText(message) : classifyFailure(signals, { text: message });
2989
+ const message2 = err instanceof Error ? err.message : String(err);
2990
+ const failure = mcpInventory.initReceived ? classifyErrorText(message2) : classifyFailure(signals, { text: message2 });
2991
2991
  if (!failure && (mcpInventory.initReceived || ctx.resumedSessionId === null)) throw err;
2992
2992
  ok = false;
2993
2993
  if (failure) {
2994
2994
  resultReason = encodeFailureReason(failure);
2995
2995
  } else {
2996
2996
  resultReason = "session_start_failed";
2997
- if (ctx.startEvidence) ctx.startEvidence.thrown = message.slice(0, 200);
2997
+ if (ctx.startEvidence) ctx.startEvidence.thrown = message2.slice(0, 200);
2998
2998
  }
2999
2999
  sawResult = true;
3000
3000
  }
@@ -3979,9 +3979,9 @@ function readSessionError(properties) {
3979
3979
  if (typeof err === "string") return err;
3980
3980
  const rec2 = asRecord(err);
3981
3981
  if (!rec2) return "unknown";
3982
- const { name, message } = deepestError(rec2);
3983
- if (message && name && !isGenericErrorName(name)) return `${name}: ${message}`;
3984
- if (message) return message;
3982
+ const { name, message: message2 } = deepestError(rec2);
3983
+ if (message2 && name && !isGenericErrorName(name)) return `${name}: ${message2}`;
3984
+ if (message2) return message2;
3985
3985
  if (name) return name;
3986
3986
  if (typeof rec2.name === "string") return rec2.name;
3987
3987
  return "unknown";
@@ -3993,10 +3993,10 @@ function deepestError(node) {
3993
3993
  const best = {};
3994
3994
  let current = node;
3995
3995
  for (let depth = 0; current && depth < 8; depth++) {
3996
- const message = typeof current.message === "string" ? current.message : void 0;
3996
+ const message2 = typeof current.message === "string" ? current.message : void 0;
3997
3997
  const name = typeof current.name === "string" && !isGenericErrorName(current.name) ? current.name : void 0;
3998
- if (message) {
3999
- best.message = message;
3998
+ if (message2) {
3999
+ best.message = message2;
4000
4000
  if (name) best.name = name;
4001
4001
  } else if (name && !best.name) {
4002
4002
  best.name = name;
@@ -4659,9 +4659,9 @@ var init_conformance3 = __esm({
4659
4659
  properties: { part: { type: "text", id: partId, messageID, text } }
4660
4660
  });
4661
4661
  idle = () => ({ type: "session.idle", properties: {} });
4662
- errored = (message) => ({
4662
+ errored = (message2) => ({
4663
4663
  type: "session.error",
4664
- properties: { error: { message } }
4664
+ properties: { error: { message: message2 } }
4665
4665
  });
4666
4666
  sessionEvent2 = (sessionId, directory = DIR, degraded = false) => ({
4667
4667
  type: "session",
@@ -5105,8 +5105,8 @@ function readItemText(item) {
5105
5105
  function readItemMessage(item) {
5106
5106
  return str(item.message) ?? "";
5107
5107
  }
5108
- function isModelMetadataError(message) {
5109
- return message.includes("Defaulting to fallback metadata");
5108
+ function isModelMetadataError(message2) {
5109
+ return message2.includes("Defaulting to fallback metadata");
5110
5110
  }
5111
5111
  function readToolItem(item, eventType) {
5112
5112
  const type = str(item.type);
@@ -5351,11 +5351,11 @@ async function* decodeCodexStream(events, ctx) {
5351
5351
  if (!item) continue;
5352
5352
  const itemType = readItemType(item);
5353
5353
  if (itemType === "error") {
5354
- const message = readItemMessage(item);
5355
- if (isModelMetadataError(message)) {
5354
+ const message2 = readItemMessage(item);
5355
+ if (isModelMetadataError(message2)) {
5356
5356
  yield* flushInterim();
5357
5357
  ok = false;
5358
- reason = `model_unavailable:${message.slice(0, 200)}`;
5358
+ reason = `model_unavailable:${message2.slice(0, 200)}`;
5359
5359
  settled = true;
5360
5360
  break;
5361
5361
  }
@@ -5522,7 +5522,7 @@ var init_model3 = __esm({
5522
5522
  });
5523
5523
 
5524
5524
  // packages/agent-runtime/src/codex/run-spec.ts
5525
- function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null) {
5525
+ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null, imagePaths = []) {
5526
5526
  const { policy, config } = req;
5527
5527
  const directory = req.local.cwd ?? "";
5528
5528
  const dialect = readCodexDialect(config.runtimeOptions);
@@ -5539,7 +5539,7 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
5539
5539
  skipGitRepoCheck: true,
5540
5540
  ...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
5541
5541
  baseInstructionsFile,
5542
- input: buildInput(req, promptRidesInput),
5542
+ input: withImages(buildInput(req, promptRidesInput), imagePaths),
5543
5543
  promptRidesInput,
5544
5544
  promptFingerprint,
5545
5545
  config: buildConfig(req, baseInstructionsFile),
@@ -5555,6 +5555,13 @@ function buildInput(req, promptRidesInput) {
5555
5555
 
5556
5556
  ${body}` : body;
5557
5557
  }
5558
+ function withImages(text, imagePaths) {
5559
+ if (imagePaths.length === 0) return text;
5560
+ return [
5561
+ { type: "text", text },
5562
+ ...imagePaths.map((path) => ({ type: "local_image", path }))
5563
+ ];
5564
+ }
5558
5565
  function buildConfig(req, baseInstructionsFile) {
5559
5566
  const mcp_servers = {};
5560
5567
  for (const [name, server] of Object.entries(req.local.mcpServers ?? {})) {
@@ -5826,12 +5833,35 @@ function createCodexAdapter(deps = {}) {
5826
5833
  }
5827
5834
  }
5828
5835
  }
5836
+ let images = null;
5829
5837
  try {
5838
+ const imageUrls = req.content.flatMap((b) => b.type === "image" ? [b.source.url] : []);
5839
+ if (imageUrls.length > 0) {
5840
+ if (!deps.prepareImages) {
5841
+ deps.onWarn?.(
5842
+ "codex adapter: no image preparer wired on this host \u2014 the turn runs without its images (CT1757)",
5843
+ { images: imageUrls.length }
5844
+ );
5845
+ } else {
5846
+ try {
5847
+ images = await deps.prepareImages(imageUrls, signal);
5848
+ } catch (err) {
5849
+ if (!signal.aborted) {
5850
+ deps.onWarn?.(
5851
+ "codex adapter: preparing the images failed \u2014 the turn runs without them (CT1757)",
5852
+ { err: err instanceof Error ? err.message : String(err) }
5853
+ );
5854
+ }
5855
+ }
5856
+ }
5857
+ if (signal.aborted) return;
5858
+ }
5830
5859
  const spec = buildRunSpec2(
5831
5860
  req,
5832
5861
  resumeThreadId,
5833
5862
  instructions?.path ?? null,
5834
- threadPromptFingerprint
5863
+ threadPromptFingerprint,
5864
+ images?.paths ?? []
5835
5865
  );
5836
5866
  if (spec.model === null) {
5837
5867
  deps.onWarn?.("Codex is using its default model because no model was resolved", {
@@ -5860,6 +5890,15 @@ function createCodexAdapter(deps = {}) {
5860
5890
  resolvedReasoningEffort: spec.modelReasoningEffort ?? null
5861
5891
  });
5862
5892
  } finally {
5893
+ if (images) {
5894
+ try {
5895
+ await images.cleanup();
5896
+ } catch (err) {
5897
+ deps.onWarn?.("codex adapter: image cleanup failed (CT1757)", {
5898
+ err: err instanceof Error ? err.message : String(err)
5899
+ });
5900
+ }
5901
+ }
5863
5902
  if (instructions) {
5864
5903
  try {
5865
5904
  await instructions.cleanup();
@@ -5952,14 +5991,14 @@ var init_conformance4 = __esm({
5952
5991
  type: "turn.completed",
5953
5992
  ...usage ? { usage } : {}
5954
5993
  });
5955
- turnFailed = (message) => ({
5994
+ turnFailed = (message2) => ({
5956
5995
  type: "turn.failed",
5957
- error: { message }
5996
+ error: { message: message2 }
5958
5997
  });
5959
- fatalError = (message) => ({ type: "error", message });
5960
- errorItem = (id, message) => ({
5998
+ fatalError = (message2) => ({ type: "error", message: message2 });
5999
+ errorItem = (id, message2) => ({
5961
6000
  type: "item.completed",
5962
- item: { id, type: "error", message }
6001
+ item: { id, type: "error", message: message2 }
5963
6002
  });
5964
6003
  OP_SUCCESS = { ok: true, result: { source: "native" } };
5965
6004
  OP_FAILURE = {
@@ -6897,8 +6936,8 @@ function unenforceable(policy) {
6897
6936
  deniedDomains: as === "deniedDomains" ? [host] : []
6898
6937
  });
6899
6938
  if (!check.success) {
6900
- const message = check.error.issues[0]?.message ?? "not a host pattern the sandbox accepts";
6901
- return `runners.policy.${field}.${i}: ${message}`;
6939
+ const message2 = check.error.issues[0]?.message ?? "not a host pattern the sandbox accepts";
6940
+ return `runners.policy.${field}.${i}: ${message2}`;
6902
6941
  }
6903
6942
  }
6904
6943
  }
@@ -6912,8 +6951,8 @@ function unenforceable(policy) {
6912
6951
  try {
6913
6952
  new RegExp(globToRegex(path));
6914
6953
  } catch (err) {
6915
- const message = err instanceof Error ? err.message : String(err);
6916
- return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${message})`;
6954
+ const message2 = err instanceof Error ? err.message : String(err);
6955
+ return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${message2})`;
6917
6956
  }
6918
6957
  }
6919
6958
  }
@@ -7118,8 +7157,8 @@ async function reapTurnScopes(turnId) {
7118
7157
  });
7119
7158
  return { failures: [] };
7120
7159
  } catch (err) {
7121
- const message = err instanceof Error ? err.message : String(err);
7122
- return classifyStopError(message) === "absent" ? { failures: [] } : { failures: [message.slice(0, 200)] };
7160
+ const message2 = err instanceof Error ? err.message : String(err);
7161
+ return classifyStopError(message2) === "absent" ? { failures: [] } : { failures: [message2.slice(0, 200)] };
7123
7162
  }
7124
7163
  }
7125
7164
  function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
@@ -7135,13 +7174,13 @@ function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
7135
7174
  return false;
7136
7175
  }
7137
7176
  }
7138
- function classifyStopError(message) {
7177
+ function classifyStopError(message2) {
7139
7178
  if (/failed to connect to bus|failed to get d-?bus connection|connection refused|refusing to operate|spawn systemctl|systemctl.*ENOENT|ENOENT.*systemctl/i.test(
7140
- message
7179
+ message2
7141
7180
  )) {
7142
7181
  return "failure";
7143
7182
  }
7144
- if (/not loaded|could not be found|no such unit|unit .* not found/i.test(message))
7183
+ if (/not loaded|could not be found|no such unit|unit .* not found/i.test(message2))
7145
7184
  return "absent";
7146
7185
  return "failure";
7147
7186
  }
@@ -7250,9 +7289,9 @@ function createTurnContainment(turnId, deps = {}) {
7250
7289
  timeout: (CONTAINMENT_STOP_GRACE_SEC + 10) * 1e3
7251
7290
  });
7252
7291
  } catch (err) {
7253
- const message = err instanceof Error ? err.message : String(err);
7254
- if (classifyStopError(message) === "failure") {
7255
- result.failures.push({ target: unit, error: message.slice(0, 200) });
7292
+ const message2 = err instanceof Error ? err.message : String(err);
7293
+ if (classifyStopError(message2) === "failure") {
7294
+ result.failures.push({ target: unit, error: message2.slice(0, 200) });
7256
7295
  continue;
7257
7296
  }
7258
7297
  result.reaped.push(unit);
@@ -7616,8 +7655,8 @@ function runnerStdin(input) {
7616
7655
  })}
7617
7656
  `;
7618
7657
  }
7619
- function encodeLauncherMessage(message) {
7620
- return `${JSON.stringify(message)}
7658
+ function encodeLauncherMessage(message2) {
7659
+ return `${JSON.stringify(message2)}
7621
7660
  `;
7622
7661
  }
7623
7662
  function decodeLauncherMessage(line) {
@@ -8479,6 +8518,74 @@ var init_codex_instructions = __esm({
8479
8518
  }
8480
8519
  });
8481
8520
 
8521
+ // src/codex-images.ts
8522
+ import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
8523
+ import { tmpdir as tmpdir2 } from "os";
8524
+ import { join as join12 } from "path";
8525
+ function createCodexImagePreparer(opts) {
8526
+ const doFetch = opts.fetch ?? fetch;
8527
+ const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
8528
+ return async (urls, signal) => {
8529
+ signal.throwIfAborted();
8530
+ const dir2 = await mkdtemp2(join12(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
8531
+ const cleanup = async () => {
8532
+ try {
8533
+ await rm2(dir2, { recursive: true, force: true });
8534
+ } catch (err) {
8535
+ opts.warn(
8536
+ { dir: dir2, err: message(err) },
8537
+ "codex images: could not remove the scratch directory"
8538
+ );
8539
+ }
8540
+ };
8541
+ const prepareOne = async (url, index) => {
8542
+ try {
8543
+ const res = await doFetch(url, {
8544
+ signal: AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)])
8545
+ });
8546
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
8547
+ const type = (res.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
8548
+ const ext = EXTENSIONS[type];
8549
+ if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
8550
+ const bytes = new Uint8Array(await res.arrayBuffer());
8551
+ const path = join12(dir2, `image-${index + 1}.${ext}`);
8552
+ await writeFile2(path, bytes, { mode: 384 });
8553
+ return path;
8554
+ } catch (err) {
8555
+ if (!signal.aborted) {
8556
+ opts.warn(
8557
+ { image: index + 1, of: urls.length, err: message(err) },
8558
+ "codex images: an image could not be prepared; the turn runs without it"
8559
+ );
8560
+ }
8561
+ return null;
8562
+ }
8563
+ };
8564
+ const paths = await Promise.all(urls.map(prepareOne));
8565
+ if (signal.aborted) {
8566
+ await cleanup();
8567
+ signal.throwIfAborted();
8568
+ }
8569
+ return { paths: paths.filter((p) => p !== null), cleanup };
8570
+ };
8571
+ }
8572
+ function message(err) {
8573
+ return err instanceof Error ? err.message : String(err);
8574
+ }
8575
+ var FETCH_TIMEOUT_MS, EXTENSIONS;
8576
+ var init_codex_images = __esm({
8577
+ "src/codex-images.ts"() {
8578
+ "use strict";
8579
+ FETCH_TIMEOUT_MS = 3e4;
8580
+ EXTENSIONS = {
8581
+ "image/png": "png",
8582
+ "image/jpeg": "jpg",
8583
+ "image/gif": "gif",
8584
+ "image/webp": "webp"
8585
+ };
8586
+ }
8587
+ });
8588
+
8482
8589
  // src/turn-seq-floor.ts
8483
8590
  function resolveSeqFloor(sources, ctx) {
8484
8591
  const { serverFloor, outboxFloor } = sources;
@@ -8522,15 +8629,15 @@ var init_turn_seq_floor = __esm({
8522
8629
  import { spawn as spawn5 } from "child_process";
8523
8630
  import { hostname as hostname2 } from "os";
8524
8631
  function prepareFailureCause(error) {
8525
- const message = error instanceof Error ? error.message : String(error);
8526
- if (/timed out/i.test(message)) return "prepare hook timed out";
8527
- if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(message))
8632
+ const message2 = error instanceof Error ? error.message : String(error);
8633
+ if (/timed out/i.test(message2)) return "prepare hook timed out";
8634
+ if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(message2))
8528
8635
  return "prepare hook module missing; check the configured command and its installation for this device";
8529
- if (/not valid JSON|JSON must carry|produced no output/.test(message))
8636
+ if (/not valid JSON|JSON must carry|produced no output/.test(message2))
8530
8637
  return "prepare hook returned malformed output; expected a cwd path or JSON with a non-empty cwd";
8531
- if (/ENOENT/.test(message)) return "prepare hook executable missing (ENOENT)";
8532
- if (/EACCES/.test(message)) return "prepare hook executable is not accessible (EACCES)";
8533
- const exit = message.match(/exited with code (\d+)/)?.[1];
8638
+ if (/ENOENT/.test(message2)) return "prepare hook executable missing (ENOENT)";
8639
+ if (/EACCES/.test(message2)) return "prepare hook executable is not accessible (EACCES)";
8640
+ const exit = message2.match(/exited with code (\d+)/)?.[1];
8534
8641
  return exit ? `prepare hook exited with code ${exit}` : "prepare hook failed; inspect the device-local hook log";
8535
8642
  }
8536
8643
  async function runPrepareWithReporting(runner, hook, input, context, reportError) {
@@ -8587,15 +8694,15 @@ var init_prepare_failure = __esm({
8587
8694
 
8588
8695
  // src/prepared.ts
8589
8696
  import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync8 } from "fs";
8590
- import { join as join12 } from "path";
8697
+ import { join as join13 } from "path";
8591
8698
  function dirFor(workspaceId) {
8592
- return join12(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
8699
+ return join13(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
8593
8700
  }
8594
8701
  function conversationDir(workspaceId, conversationId) {
8595
- return join12(dirFor(workspaceId), encodeURIComponent(conversationId));
8702
+ return join13(dirFor(workspaceId), encodeURIComponent(conversationId));
8596
8703
  }
8597
8704
  function pathFor2(workspaceId, conversationId, agentId) {
8598
- return join12(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
8705
+ return join13(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
8599
8706
  }
8600
8707
  function readPrepared(workspaceId, conversationId, agentId) {
8601
8708
  const path = pathFor2(workspaceId, conversationId, agentId);
@@ -8633,9 +8740,9 @@ var init_prepared = __esm({
8633
8740
 
8634
8741
  // src/transcript-writer.ts
8635
8742
  import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
8636
- import { basename, dirname as dirname6, join as join13 } from "path";
8743
+ import { basename, dirname as dirname6, join as join14 } from "path";
8637
8744
  function transcriptsDir() {
8638
- return join13(runnerStateDir(), "transcripts");
8745
+ return join14(runnerStateDir(), "transcripts");
8639
8746
  }
8640
8747
  function fileName(meta) {
8641
8748
  const d = new Date(meta.ts);
@@ -8661,7 +8768,7 @@ function pruneOld(dir2, retain) {
8661
8768
  const drop = files.sort().slice(0, files.length - retain);
8662
8769
  for (const f of drop) {
8663
8770
  try {
8664
- rmSync6(join13(dir2, f), { force: true });
8771
+ rmSync6(join14(dir2, f), { force: true });
8665
8772
  } catch {
8666
8773
  }
8667
8774
  }
@@ -8679,7 +8786,7 @@ var init_transcript_writer = __esm({
8679
8786
  onWarn;
8680
8787
  constructor(dir2, meta, onWarn) {
8681
8788
  this.onWarn = onWarn;
8682
- this.path = join13(dir2, fileName(meta));
8789
+ this.path = join14(dir2, fileName(meta));
8683
8790
  try {
8684
8791
  mkdirSync8(dir2, { recursive: true });
8685
8792
  try {
@@ -8708,10 +8815,10 @@ var init_transcript_writer = __esm({
8708
8815
  preserveAnomaly() {
8709
8816
  if (this.broken) return;
8710
8817
  try {
8711
- const dir2 = join13(dirname6(this.path), "anomalies");
8818
+ const dir2 = join14(dirname6(this.path), "anomalies");
8712
8819
  mkdirSync8(dir2, { recursive: true, mode: 448 });
8713
8820
  chmodSync4(dir2, 448);
8714
- const target = join13(dir2, basename(this.path));
8821
+ const target = join14(dir2, basename(this.path));
8715
8822
  copyFileSync(this.path, target);
8716
8823
  chmodSync4(target, 384);
8717
8824
  pruneOld(dir2, ANOMALY_RETAIN);
@@ -9006,6 +9113,7 @@ var init_turn_execution = __esm({
9006
9113
  init_build_options();
9007
9114
  init_bundled_binaries();
9008
9115
  init_codex_instructions();
9116
+ init_codex_images();
9009
9117
  init_config();
9010
9118
  init_api_error_shape();
9011
9119
  init_errors();
@@ -9354,16 +9462,16 @@ var init_turn_execution = __esm({
9354
9462
  }
9355
9463
  gateTrigger() {
9356
9464
  const { payload, turnLog } = this;
9357
- const message = this.turnContext.message;
9358
- const isDispatchableTrigger = message.role === "user" || message.role === "agent" || message.role === "system";
9465
+ const message2 = this.turnContext.message;
9466
+ const isDispatchableTrigger = message2.role === "user" || message2.role === "agent" || message2.role === "system";
9359
9467
  if (!isDispatchableTrigger) {
9360
- turnLog.debug({ role: message.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
9468
+ turnLog.debug({ role: message2.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
9361
9469
  throw this.concluded("unexpected_role", UNEXPECTED_ROLE_REASON);
9362
9470
  }
9363
9471
  this.supervisor.notifyStart({
9364
9472
  id: this.dispatchId,
9365
9473
  conversationId: payload.conversationId,
9366
- message: message.body
9474
+ message: message2.body
9367
9475
  });
9368
9476
  }
9369
9477
  async resolveSecrets() {
@@ -9714,6 +9822,10 @@ var init_turn_execution = __esm({
9714
9822
  enabled: true,
9715
9823
  onWarn,
9716
9824
  writeInstructionsFile: writeCodexInstructionsFile,
9825
+ prepareImages: createCodexImagePreparer({
9826
+ turnId: this.turnId,
9827
+ warn: (meta, msg) => turnLog.warn(meta, msg)
9828
+ }),
9717
9829
  ...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
9718
9830
  })
9719
9831
  );
@@ -10174,7 +10286,7 @@ var init_turn_execution = __esm({
10174
10286
  // src/dispatcher.ts
10175
10287
  import { randomUUID as randomUUID4 } from "crypto";
10176
10288
  import { existsSync as existsSync10, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
10177
- import { join as join14 } from "path";
10289
+ import { join as join15 } from "path";
10178
10290
  function checkoutState(cwd) {
10179
10291
  if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
10180
10292
  if (!existsSync10(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
@@ -10187,7 +10299,7 @@ function checkoutState(cwd) {
10187
10299
  if (entries.length === 0) {
10188
10300
  return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
10189
10301
  }
10190
- const gitPath = join14(cwd, ".git");
10302
+ const gitPath = join15(cwd, ".git");
10191
10303
  if (!existsSync10(gitPath)) return { ok: true, reason: "usable" };
10192
10304
  let stat;
10193
10305
  try {
@@ -10195,7 +10307,7 @@ function checkoutState(cwd) {
10195
10307
  } catch (error) {
10196
10308
  return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
10197
10309
  }
10198
- if (stat.isDirectory() && !existsSync10(join14(gitPath, "HEAD")))
10310
+ if (stat.isDirectory() && !existsSync10(join15(gitPath, "HEAD")))
10199
10311
  return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
10200
10312
  return { ok: true, reason: "usable" };
10201
10313
  }
@@ -10561,8 +10673,8 @@ import {
10561
10673
  } from "fs";
10562
10674
  import { createRequire as createRequire2 } from "module";
10563
10675
  import { isIP } from "net";
10564
- import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir3, userInfo as userInfo2 } from "os";
10565
- import { dirname as dirname8, join as join18, sep as sep2 } from "path";
10676
+ import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
10677
+ import { dirname as dirname8, join as join19, sep as sep2 } from "path";
10566
10678
  import { fileURLToPath as fileURLToPath4 } from "url";
10567
10679
  import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
10568
10680
  function accountHome() {
@@ -10596,7 +10708,7 @@ async function resolvedAddresses(baseUrl) {
10596
10708
  function companionPackageRoot(modulePath) {
10597
10709
  for (let dir2 = dirname8(modulePath); ; dir2 = dirname8(dir2)) {
10598
10710
  try {
10599
- const manifest = JSON.parse(readFileSync10(join18(dir2, "package.json"), "utf8"));
10711
+ const manifest = JSON.parse(readFileSync10(join19(dir2, "package.json"), "utf8"));
10600
10712
  if (manifest.name === "@cabane/companion") return dir2;
10601
10713
  } catch {
10602
10714
  }
@@ -10673,15 +10785,15 @@ function companionStoreDenies(input) {
10673
10785
  const always = [
10674
10786
  cabaneDir2,
10675
10787
  ...homes.flatMap((home) => [
10676
- join18(home, ".cabane"),
10677
- join18(home, "*", ".cabane"),
10678
- join18(home, "*", "*", ".cabane")
10788
+ join19(home, ".cabane"),
10789
+ join19(home, "*", ".cabane"),
10790
+ join19(home, "*", "*", ".cabane")
10679
10791
  ]),
10680
10792
  // A glob anchored at the root has no directory to start from.
10681
- ...dirname8(parent) === parent ? [] : [join18(parent, "*", ".cabane")]
10793
+ ...dirname8(parent) === parent ? [] : [join19(parent, "*", ".cabane")]
10682
10794
  ];
10683
10795
  const companionHome = dirname8(cabaneDir2);
10684
- const otherLogins = foundStores.map((store) => dirname8(store)).filter((home) => home !== companionHome).flatMap((home) => [join18(home, ".claude"), join18(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
10796
+ const otherLogins = foundStores.map((store) => dirname8(store)).filter((home) => home !== companionHome).flatMap((home) => [join19(home, ".claude"), join19(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
10685
10797
  return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
10686
10798
  }
10687
10799
  function companionStoreWatch(log) {
@@ -10698,7 +10810,7 @@ function isAtOrUnder(path, dir2) {
10698
10810
  }
10699
10811
  function expandHome(path, home) {
10700
10812
  if (path === "~") return home;
10701
- return path.startsWith("~/") ? join18(home, path.slice(2)) : path;
10813
+ return path.startsWith("~/") ? join19(home, path.slice(2)) : path;
10702
10814
  }
10703
10815
  function stripPort(pattern) {
10704
10816
  return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
@@ -10716,8 +10828,8 @@ function patternsOverlap(a, b) {
10716
10828
  }
10717
10829
  function harnessSandboxConfig(input) {
10718
10830
  const { policy } = input;
10719
- const claudeDir = input.claudeConfigDir ?? join18(input.home, ".claude");
10720
- const codexDir = input.codexHome ?? join18(input.home, ".codex");
10831
+ const claudeDir = input.claudeConfigDir ?? join19(input.home, ".claude");
10832
+ const codexDir = input.codexHome ?? join19(input.home, ".codex");
10721
10833
  const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
10722
10834
  const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
10723
10835
  const cabane = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
@@ -10734,7 +10846,7 @@ function harnessSandboxConfig(input) {
10734
10846
  foundStores: input.foundStores,
10735
10847
  ownState: [claudeDir, codexDir]
10736
10848
  }),
10737
- ...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join18(home, p))),
10849
+ ...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join19(home, p))),
10738
10850
  ...expand(policy.filesystem?.denyRead)
10739
10851
  ];
10740
10852
  const strict = policy.filesystem?.mode === "strict";
@@ -10742,7 +10854,7 @@ function harnessSandboxConfig(input) {
10742
10854
  const allowRead = strict ? [
10743
10855
  ...input.cwd ? [input.cwd] : [],
10744
10856
  claudeDir,
10745
- join18(input.home, ".claude.json"),
10857
+ join19(input.home, ".claude.json"),
10746
10858
  codexDir,
10747
10859
  ...input.runtimePaths ?? [],
10748
10860
  ...expand(policy.filesystem?.allowRead)
@@ -10782,7 +10894,7 @@ function harnessSandboxConfig(input) {
10782
10894
  ...input.cwd ? [input.cwd] : [],
10783
10895
  input.tmp,
10784
10896
  claudeDir,
10785
- join18(input.home, ".claude.json"),
10897
+ join19(input.home, ".claude.json"),
10786
10898
  codexDir,
10787
10899
  ...expand(policy.filesystem?.allowWrite)
10788
10900
  ],
@@ -10835,7 +10947,7 @@ async function prepareHarnessSandbox(input) {
10835
10947
  ownAddresses: ownAddresses(),
10836
10948
  home: homedir3(),
10837
10949
  accountHome: accountHome(),
10838
- tmp: tmpdir3(),
10950
+ tmp: tmpdir4(),
10839
10951
  cabaneDir: cabaneDir(),
10840
10952
  claudeConfigDir: process.env.CLAUDE_CONFIG_DIR,
10841
10953
  codexHome: process.env.CODEX_HOME,
@@ -10866,27 +10978,27 @@ async function prepareHarnessSandbox(input) {
10866
10978
  "export NO_PROXY= no_proxy=; " + gitCredentialExports(input.credentials) + 'eval "exec $CABANE_SANDBOXED_ARGV"'
10867
10979
  );
10868
10980
  } catch (err) {
10869
- const message = err instanceof Error ? err.message : String(err);
10981
+ const message2 = err instanceof Error ? err.message : String(err);
10870
10982
  await SandboxManager.reset().catch(() => {
10871
10983
  });
10872
10984
  if (held.length || Object.keys(input.policy).length) {
10873
10985
  input.log.warn(
10874
- { err: message, masked: held },
10986
+ { err: message2, masked: held },
10875
10987
  "runners: the OS sandbox could not start under this device's runners.policy; refusing the turn rather than running it unsandboxed"
10876
10988
  );
10877
- throw new HarnessSandboxRefused(`the OS sandbox could not start: ${message}`);
10989
+ throw new HarnessSandboxRefused(`the OS sandbox could not start: ${message2}`);
10878
10990
  }
10879
10991
  input.log.warn(
10880
- { err: message },
10992
+ { err: message2 },
10881
10993
  "runners: the OS sandbox could not start; running the turn unsandboxed"
10882
10994
  );
10883
10995
  return null;
10884
10996
  } finally {
10885
10997
  for (const name of held) delete process.env[name];
10886
10998
  }
10887
- mkdirSync11(join18(cabaneDir(), "sandbox"), { recursive: true });
10888
- const dir2 = mkdtempSync(join18(cabaneDir(), "sandbox", "turn-"));
10889
- const script = join18(dir2, "exec-sandboxed");
10999
+ mkdirSync11(join19(cabaneDir(), "sandbox"), { recursive: true });
11000
+ const dir2 = mkdtempSync(join19(cabaneDir(), "sandbox", "turn-"));
11001
+ const script = join19(dir2, "exec-sandboxed");
10890
11002
  writeFileSync7(
10891
11003
  script,
10892
11004
  [
@@ -10902,7 +11014,7 @@ async function prepareHarnessSandbox(input) {
10902
11014
  return {
10903
11015
  wrap: (command, args) => ({ command: script, args: [command, ...args] }),
10904
11016
  executableFor: (real) => {
10905
- const shim = join18(dir2, `exec-${++shims}`);
11017
+ const shim = join19(dir2, `exec-${++shims}`);
10906
11018
  writeFileSync7(shim, `#!/bin/bash
10907
11019
  exec ${shellQuote(script)} ${shellQuote(real)} "$@"
10908
11020
  `);
@@ -10995,7 +11107,7 @@ import {
10995
11107
  rmSync as rmSync11,
10996
11108
  writeFileSync as writeFileSync8
10997
11109
  } from "fs";
10998
- import { join as join19 } from "path";
11110
+ import { join as join20 } from "path";
10999
11111
  var MAX_ENTRIES, MAX_AGE_MS, Outbox;
11000
11112
  var init_outbox = __esm({
11001
11113
  "src/outbox.ts"() {
@@ -11013,10 +11125,10 @@ var init_outbox = __esm({
11013
11125
  // Resolved lazily (per call, like cursor.ts) so tests that swap HOME between
11014
11126
  // cases route writes at the right tmpdir.
11015
11127
  dir() {
11016
- return join19(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
11128
+ return join20(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
11017
11129
  }
11018
11130
  fileFor(turnId, seq) {
11019
- return join19(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
11131
+ return join20(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
11020
11132
  }
11021
11133
  // Persist a commit for later draining. Atomic (temp file + rename) so a
11022
11134
  // concurrent `list()` never reads a half-written entry, then enforces the
@@ -11058,7 +11170,7 @@ var init_outbox = __esm({
11058
11170
  const entries = [];
11059
11171
  for (const name of names) {
11060
11172
  if (!name.endsWith(".json")) continue;
11061
- const full = join19(dir2, name);
11173
+ const full = join20(dir2, name);
11062
11174
  try {
11063
11175
  const parsed = JSON.parse(readFileSync11(full, "utf8"));
11064
11176
  if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
@@ -11198,7 +11310,7 @@ var init_image_manifest = __esm({
11198
11310
 
11199
11311
  // src/runner/exec-shim.ts
11200
11312
  import { chmodSync as chmodSync7, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync2, rmSync as rmSync13, writeFileSync as writeFileSync10 } from "fs";
11201
- import { join as join22 } from "path";
11313
+ import { join as join23 } from "path";
11202
11314
  function shellQuote2(value) {
11203
11315
  return `'${value.replace(/'/g, `'\\''`)}'`;
11204
11316
  }
@@ -11208,10 +11320,10 @@ function createShimDir() {
11208
11320
  return {
11209
11321
  write(argv) {
11210
11322
  if (!dir2) {
11211
- mkdirSync15(join22(cabaneDir(), "runner"), { recursive: true });
11212
- dir2 = mkdtempSync2(join22(cabaneDir(), "runner", "turn-"));
11323
+ mkdirSync15(join23(cabaneDir(), "runner"), { recursive: true });
11324
+ dir2 = mkdtempSync2(join23(cabaneDir(), "runner", "turn-"));
11213
11325
  }
11214
- const path = join22(dir2, `exec-${++count}`);
11326
+ const path = join23(dir2, `exec-${++count}`);
11215
11327
  writeFileSync10(path, `#!/bin/sh
11216
11328
  exec ${argv.map(shellQuote2).join(" ")} "$@"
11217
11329
  `);
@@ -11247,9 +11359,9 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
11247
11359
  resolveFirst(line);
11248
11360
  return;
11249
11361
  }
11250
- const message = decodeLauncherMessage(line);
11251
- if (message?.cabaneLauncher === "granted") waiting.get(message.id)?.();
11252
- else if (message) replies.get(message.id)?.(message);
11362
+ const message2 = decodeLauncherMessage(line);
11363
+ if (message2?.cabaneLauncher === "granted") waiting.get(message2.id)?.();
11364
+ else if (message2) replies.get(message2.id)?.(message2);
11253
11365
  });
11254
11366
  let gone = false;
11255
11367
  lines2.once("close", () => {
@@ -11262,9 +11374,9 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
11262
11374
  if (gone) return Promise.resolve(null);
11263
11375
  const id = String(++next);
11264
11376
  return new Promise((resolve2) => {
11265
- replies.set(id, (message) => {
11377
+ replies.set(id, (message2) => {
11266
11378
  replies.delete(id);
11267
- resolve2(message);
11379
+ resolve2(message2);
11268
11380
  });
11269
11381
  emit(event(id));
11270
11382
  });
@@ -12474,8 +12586,8 @@ async function logout(opts = {}) {
12474
12586
  return;
12475
12587
  }
12476
12588
  if (!opts.yes) {
12477
- const message = opts.purge ? "Purge the entire local companion config (device + agent overrides + settings)?" : "Log out this device (removes the device token + cached agent credentials; keeps your config)?";
12478
- const ok = await confirm2({ message, default: false });
12589
+ const message2 = opts.purge ? "Purge the entire local companion config (device + agent overrides + settings)?" : "Log out this device (removes the device token + cached agent credentials; keeps your config)?";
12590
+ const ok = await confirm2({ message: message2, default: false });
12479
12591
  if (!ok) {
12480
12592
  process.stdout.write("cancelled\n");
12481
12593
  return;
@@ -12731,8 +12843,8 @@ function formatHumanLine(record) {
12731
12843
  let line = `${time} ${word ? `${word} ` : ""}${context ? `${context} ` : ""}${humanText(record.msg)}`;
12732
12844
  const err = record.err;
12733
12845
  const errorRecord = err !== null && typeof err === "object" ? err : null;
12734
- const message = humanText(errorRecord && "message" in errorRecord ? errorRecord.message : err);
12735
- const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) || message}` : message;
12846
+ const message2 = humanText(errorRecord && "message" in errorRecord ? errorRecord.message : err);
12847
+ const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) || message2}` : message2;
12736
12848
  if (detail && !line.includes(detail)) line += ` (${detail})`;
12737
12849
  if (level < 30) {
12738
12850
  const hidden = /* @__PURE__ */ new Set([
@@ -12966,9 +13078,9 @@ var CompanionStateHub = class {
12966
13078
  if (info.deviceLabel !== void 0) this.deviceLabel = info.deviceLabel;
12967
13079
  this.emitStatus();
12968
13080
  }
12969
- setDeviceError(message) {
12970
- if (this.deviceError === message) return;
12971
- this.deviceError = message;
13081
+ setDeviceError(message2) {
13082
+ if (this.deviceError === message2) return;
13083
+ this.deviceError = message2;
12972
13084
  this.emitStatus();
12973
13085
  }
12974
13086
  clearDeviceError() {
@@ -13420,7 +13532,7 @@ async function warnAboutHarnessReadiness(cfg, deps = {}) {
13420
13532
  const probeClaude = deps.probeClaude ?? claudeOnPath;
13421
13533
  const probeCodex = deps.probeCodex ?? codexOnPath;
13422
13534
  const resolveBundled = deps.resolveBundled ?? ((runtime) => resolveBundledBinary(runtime));
13423
- const warn = deps.warn ?? ((message) => process.stderr.write(`${message}
13535
+ const warn = deps.warn ?? ((message2) => process.stderr.write(`${message2}
13424
13536
  `));
13425
13537
  const connected = [
13426
13538
  ...isClaudeCodeConnected(cfg) ? ["Claude Code"] : [],
@@ -13912,7 +14024,7 @@ function createLocalProcessProvider(opts = {}) {
13912
14024
 
13913
14025
  // src/providers/docker.ts
13914
14026
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
13915
- import { constants as constants2, tmpdir as tmpdir2 } from "os";
14027
+ import { constants as constants2, tmpdir as tmpdir3 } from "os";
13916
14028
  import {
13917
14029
  createReadStream,
13918
14030
  createWriteStream as createWriteStream2,
@@ -13925,7 +14037,7 @@ import {
13925
14037
  statSync as statSync3,
13926
14038
  writeFileSync as writeFileSync6
13927
14039
  } from "fs";
13928
- import { join as join17 } from "path";
14040
+ import { join as join18 } from "path";
13929
14041
  import { Writable } from "stream";
13930
14042
 
13931
14043
  // src/providers/docker-cli.ts
@@ -13998,7 +14110,7 @@ import {
13998
14110
  renameSync as renameSync4,
13999
14111
  rmSync as rmSync8
14000
14112
  } from "fs";
14001
- import { join as join15 } from "path";
14113
+ import { join as join16 } from "path";
14002
14114
  function routes(t) {
14003
14115
  const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
14004
14116
  const agent = `${conv}/participants/agents/${t.agentId}`;
@@ -14044,7 +14156,7 @@ function publishStaged(from, to, move = atomicMove) {
14044
14156
  const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
14045
14157
  let entry = {};
14046
14158
  try {
14047
- entry = JSON.parse(readFileSync8(join15(from, name), "utf8"));
14159
+ entry = JSON.parse(readFileSync8(join16(from, name), "utf8"));
14048
14160
  } catch {
14049
14161
  }
14050
14162
  return { name, entry };
@@ -14057,7 +14169,7 @@ function publishStaged(from, to, move = atomicMove) {
14057
14169
  let published = 0;
14058
14170
  for (const { name } of staged) {
14059
14171
  try {
14060
- move(join15(from, name), join15(to, name));
14172
+ move(join16(from, name), join16(to, name));
14061
14173
  } catch {
14062
14174
  return { published, complete: false };
14063
14175
  }
@@ -14070,7 +14182,7 @@ function publishStaged(from, to, move = atomicMove) {
14070
14182
  // src/providers/runner-base.ts
14071
14183
  import { createHash as createHash3 } from "crypto";
14072
14184
  import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
14073
- import { dirname as dirname7, join as join16, sep } from "path";
14185
+ import { dirname as dirname7, join as join17, sep } from "path";
14074
14186
  import { fileURLToPath as fileURLToPath3 } from "url";
14075
14187
  var COMPANION_MOUNT = "/opt/cabane/companion";
14076
14188
  var BASE_DOCKERFILE = `FROM node:22-slim
@@ -14118,11 +14230,11 @@ async function ensureBaseImage(docker, image, log) {
14118
14230
  function ownInstall(moduleFile = fileURLToPath3(import.meta.url)) {
14119
14231
  let dir2 = dirname7(moduleFile);
14120
14232
  for (let i = 0; i < 4; i++) {
14121
- if (existsSync12(join16(dir2, "dist", "cli.js")) && existsSync12(join16(dir2, "package.json"))) break;
14233
+ if (existsSync12(join17(dir2, "dist", "cli.js")) && existsSync12(join17(dir2, "package.json"))) break;
14122
14234
  dir2 = dirname7(dir2);
14123
14235
  }
14124
- if (!existsSync12(join16(dir2, "dist", "cli.js"))) return null;
14125
- const sdk = join16(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
14236
+ if (!existsSync12(join17(dir2, "dist", "cli.js"))) return null;
14237
+ const sdk = join17(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
14126
14238
  if (!existsSync12(sdk)) return null;
14127
14239
  const root = realpathSync2(dir2);
14128
14240
  return realpathSync2(sdk).startsWith(root + sep) ? root : null;
@@ -14261,21 +14373,21 @@ function networkCreateError(stderr) {
14261
14373
  }
14262
14374
  function sessionArchivePath(deviceDir, attemptId) {
14263
14375
  if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
14264
- return join17(deviceDir, "attempts", attemptId, "session.tar");
14376
+ return join18(deviceDir, "attempts", attemptId, "session.tar");
14265
14377
  }
14266
14378
  function pruneOtherAttempts(deviceDir, pair2, keep) {
14267
- const root = join17(deviceDir, "attempts");
14379
+ const root = join18(deviceDir, "attempts");
14268
14380
  if (!existsSync13(root)) return;
14269
14381
  for (const id of readdirSync4(root)) {
14270
14382
  if (id === keep || !UUID_RE.test(id)) continue;
14271
14383
  let pairOf = "";
14272
14384
  try {
14273
- pairOf = readFileSync9(join17(root, id, "pair"), "utf8");
14385
+ pairOf = readFileSync9(join18(root, id, "pair"), "utf8");
14274
14386
  } catch {
14275
14387
  continue;
14276
14388
  }
14277
14389
  if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
14278
- rmSync9(join17(root, id), { recursive: true, force: true });
14390
+ rmSync9(join18(root, id), { recursive: true, force: true });
14279
14391
  }
14280
14392
  }
14281
14393
  }
@@ -14475,11 +14587,11 @@ function createDockerProvider(opts) {
14475
14587
  await must(["network", "disconnect", "-f", other, name], "moving the container");
14476
14588
  if (others.includes(sharedNetwork)) await retireSharedNetwork();
14477
14589
  };
14478
- const socketRoot = opts.socketRoot ?? join17(
14479
- process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir2(),
14590
+ const socketRoot = opts.socketRoot ?? join18(
14591
+ process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
14480
14592
  `cabane-runners-${opts.deviceKey}`
14481
14593
  );
14482
- const socketDir = (name) => join17(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
14594
+ const socketDir = (name) => join18(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
14483
14595
  const create = async (name, attempt) => {
14484
14596
  mkdirSync10(socketRoot, { recursive: true, mode: 448 });
14485
14597
  mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
@@ -14621,7 +14733,7 @@ function createDockerProvider(opts) {
14621
14733
  }
14622
14734
  };
14623
14735
  const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
14624
- const pendingPath = (name) => join17(opts.deviceDir, "runners-pending", `${name}.json`);
14736
+ const pendingPath = (name) => join18(opts.deviceDir, "runners-pending", `${name}.json`);
14625
14737
  const readPending = (name) => {
14626
14738
  try {
14627
14739
  const ids = JSON.parse(readFileSync9(pendingPath(name), "utf8"));
@@ -14635,12 +14747,12 @@ function createDockerProvider(opts) {
14635
14747
  rmSync9(pendingPath(name), { force: true });
14636
14748
  return;
14637
14749
  }
14638
- mkdirSync10(join17(opts.deviceDir, "runners-pending"), { recursive: true });
14750
+ mkdirSync10(join18(opts.deviceDir, "runners-pending"), { recursive: true });
14639
14751
  writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
14640
14752
  };
14641
14753
  const keepSession = async (name, attempt, attemptId) => {
14642
14754
  const archive = sessionArchivePath(opts.deviceDir, attemptId);
14643
- mkdirSync10(join17(archive, ".."), { recursive: true });
14755
+ mkdirSync10(join18(archive, ".."), { recursive: true });
14644
14756
  const partial = `${archive}.${process.pid}.partial`;
14645
14757
  const file = new CappedFile(partial, sessionCap);
14646
14758
  const r = await execIn(
@@ -14657,7 +14769,7 @@ function createDockerProvider(opts) {
14657
14769
  );
14658
14770
  if (r.code === 0 && !file.over) {
14659
14771
  renameSync5(partial, archive);
14660
- writeFileSync6(join17(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
14772
+ writeFileSync6(join18(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
14661
14773
  pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
14662
14774
  await writeMarker(name, attemptId);
14663
14775
  log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
@@ -14671,7 +14783,7 @@ function createDockerProvider(opts) {
14671
14783
  file.over ? "runners: the session files are over the size cap and weren't kept; the next new container starts the session over" : "runners: couldn't keep the session files; the next new container starts the session over"
14672
14784
  );
14673
14785
  };
14674
- const stagingDir = (name) => join17(opts.deviceDir, "runners-pending", `${name}.outbox`);
14786
+ const stagingDir = (name) => join18(opts.deviceDir, "runners-pending", `${name}.outbox`);
14675
14787
  const ensureRunning = async (name) => {
14676
14788
  const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
14677
14789
  if (seen.code !== 0) return false;
@@ -14679,7 +14791,7 @@ function createDockerProvider(opts) {
14679
14791
  return (await docker.run(["start", name])).code === 0;
14680
14792
  };
14681
14793
  const dropArchive = (attemptId) => {
14682
- rmSync9(join17(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
14794
+ rmSync9(join18(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
14683
14795
  recursive: true,
14684
14796
  force: true
14685
14797
  });
@@ -14748,7 +14860,7 @@ function createDockerProvider(opts) {
14748
14860
  if (seen.stdout.trim().split(" ")[0] !== "true")
14749
14861
  await must(["start", name], "starting the container");
14750
14862
  const git = await serveGitCredentials({
14751
- socketPath: join17(socketDir(name), "git.sock"),
14863
+ socketPath: join18(socketDir(name), "git.sock"),
14752
14864
  hosts: opts.gitHosts,
14753
14865
  fill: opts.gitFill,
14754
14866
  log
@@ -14897,15 +15009,15 @@ function createDockerProvider(opts) {
14897
15009
  const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
14898
15010
  if (transcripts) {
14899
15011
  const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
14900
- if (kept.length) mkdirSync10(join17(opts.hostStateDir, "transcripts"), { recursive: true });
14901
- for (const f of kept) writeFileSync6(join17(opts.hostStateDir, "transcripts", f.name), f.data);
15012
+ if (kept.length) mkdirSync10(join18(opts.hostStateDir, "transcripts"), { recursive: true });
15013
+ for (const f of kept) writeFileSync6(join18(opts.hostStateDir, "transcripts", f.name), f.data);
14902
15014
  await removeIn(
14903
15015
  name,
14904
15016
  `${CONTAINER_STATE}/transcripts`,
14905
15017
  kept.map((f) => f.name)
14906
15018
  );
14907
15019
  }
14908
- const outboxRel = join17("outbox", encodeURIComponent(attempt.agentId));
15020
+ const outboxRel = join18("outbox", encodeURIComponent(attempt.agentId));
14909
15021
  const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
14910
15022
  let unpublished = false;
14911
15023
  if (outbox) {
@@ -14931,7 +15043,7 @@ function createDockerProvider(opts) {
14931
15043
  const staged = stagingDir(name);
14932
15044
  mkdirSync10(staged, { recursive: true });
14933
15045
  writeFileSync6(
14934
- join17(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
15046
+ join18(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
14935
15047
  `${JSON.stringify(entry)}
14936
15048
  `
14937
15049
  );
@@ -14944,7 +15056,7 @@ function createDockerProvider(opts) {
14944
15056
  if (outbox.complete) {
14945
15057
  const published = publishStaged(
14946
15058
  stagingDir(name),
14947
- join17(opts.hostStateDir, outboxRel),
15059
+ join18(opts.hostStateDir, outboxRel),
14948
15060
  opts.moveFile
14949
15061
  );
14950
15062
  queued = published.published;
@@ -16774,8 +16886,8 @@ function errorCode(err) {
16774
16886
  function isRecoverableSocketError(err) {
16775
16887
  const code = errorCode(err);
16776
16888
  if (code && RECOVERABLE_CODES.has(code)) return true;
16777
- const message = err instanceof Error ? err.message : String(err);
16778
- return /\bEPIPE\b|\bECONNRESET\b/.test(message);
16889
+ const message2 = err instanceof Error ? err.message : String(err);
16890
+ return /\bEPIPE\b|\bECONNRESET\b/.test(message2);
16779
16891
  }
16780
16892
  function installProcessSafetyNet(log, opts = {}) {
16781
16893
  const proc = opts.proc ?? process;
@@ -16790,17 +16902,17 @@ function installProcessSafetyNet(log, opts = {}) {
16790
16902
  );
16791
16903
  }
16792
16904
  function handleUncaught(log, err, origin) {
16793
- const message = err instanceof Error ? err.message : String(err);
16905
+ const message2 = err instanceof Error ? err.message : String(err);
16794
16906
  const code = errorCode(err);
16795
16907
  if (isRecoverableSocketError(err)) {
16796
16908
  log.debug(
16797
- { origin, code, err: message },
16909
+ { origin, code, err: message2 },
16798
16910
  "companion: recovered from a socket error (kept running \u2014 a broken pipe never kills the companion)"
16799
16911
  );
16800
16912
  return;
16801
16913
  }
16802
16914
  log.error(
16803
- { origin, code, err: message, stack: err instanceof Error ? err.stack : void 0 },
16915
+ { origin, code, err: message2, stack: err instanceof Error ? err.stack : void 0 },
16804
16916
  "An unexpected error occurred; the companion is still running."
16805
16917
  );
16806
16918
  }
@@ -16808,9 +16920,9 @@ function handleUncaught(log, err, origin) {
16808
16920
  // src/crash-marker.ts
16809
16921
  init_config();
16810
16922
  import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync12, rmSync as rmSync12, writeFileSync as writeFileSync9 } from "fs";
16811
- import { join as join20 } from "path";
16923
+ import { join as join21 } from "path";
16812
16924
  function crashMarkerPath() {
16813
- return join20(cabaneDir(), "last-error.json");
16925
+ return join21(cabaneDir(), "last-error.json");
16814
16926
  }
16815
16927
  function recordCrash(rec2) {
16816
16928
  try {
@@ -17161,7 +17273,7 @@ async function startScript(opts, interactive) {
17161
17273
  const result = await createCompanionRuntime({
17162
17274
  // The script says what's connectable in its own words below; a stderr warning
17163
17275
  // in the middle of it would be the same news, worse.
17164
- onReadinessWarning: (message) => log.warn(message)
17276
+ onReadinessWarning: (message2) => log.warn(message2)
17165
17277
  });
17166
17278
  if (!result.ok) {
17167
17279
  reportAlreadyRunning(result.existing?.pid);
@@ -17566,7 +17678,7 @@ init_errors();
17566
17678
  init_src();
17567
17679
  init_transcript_writer();
17568
17680
  import { existsSync as existsSync17, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
17569
- import { isAbsolute, join as join21 } from "path";
17681
+ import { isAbsolute, join as join22 } from "path";
17570
17682
  async function transcript(opts = {}) {
17571
17683
  const dir2 = transcriptsDir();
17572
17684
  if (opts.follow) {
@@ -17583,7 +17695,7 @@ async function transcript(opts = {}) {
17583
17695
  process.stdout.write(emptyMessage(dir2));
17584
17696
  return;
17585
17697
  }
17586
- process.stdout.write(renderFile(join21(dir2, newest)) + "\n");
17698
+ process.stdout.write(renderFile(join22(dir2, newest)) + "\n");
17587
17699
  return;
17588
17700
  }
17589
17701
  printList(dir2);
@@ -17666,7 +17778,7 @@ function isComplete(content) {
17666
17778
  async function followTranscripts(dir2) {
17667
17779
  const follower = new TranscriptFollower({
17668
17780
  listFiles: () => listFiles(dir2),
17669
- read: (f) => readFileSync13(join21(dir2, f), "utf8"),
17781
+ read: (f) => readFileSync13(join22(dir2, f), "utf8"),
17670
17782
  write: (s) => process.stdout.write(s),
17671
17783
  // CSI: cursor up `n` lines, then erase from cursor to end of screen.
17672
17784
  clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
@@ -17706,7 +17818,7 @@ function printList(dir2) {
17706
17818
 
17707
17819
  `);
17708
17820
  for (const f of files.slice(0, 20)) {
17709
- const { meta, outcome } = peek(join21(dir2, f));
17821
+ const { meta, outcome } = peek(join22(dir2, f));
17710
17822
  const when = fmtTime(rec(meta)?.ts);
17711
17823
  const ws = str2(rec(meta)?.workspaceSlug);
17712
17824
  const o = rec(outcome);
@@ -17743,10 +17855,10 @@ function resolveTarget(dir2, target) {
17743
17855
  if (existsSync17(target)) return target;
17744
17856
  throw new CompanionError(`no transcript at ${target}.`);
17745
17857
  }
17746
- const exact = join21(dir2, target);
17858
+ const exact = join22(dir2, target);
17747
17859
  if (existsSync17(exact)) return exact;
17748
17860
  const matches = listFiles(dir2).filter((f) => f.includes(target));
17749
- if (matches.length === 1) return join21(dir2, matches[0]);
17861
+ if (matches.length === 1) return join22(dir2, matches[0]);
17750
17862
  if (matches.length === 0) {
17751
17863
  throw new CompanionError(
17752
17864
  `no transcript matching "${target}" in ${dir2}. Run \`cabane-companion transcript\` to list them.`