@cabane/companion 0.6.142 → 0.6.144

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 +383 -181
  2. package/dist/runtime.js +336 -141
  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;
@@ -1816,14 +1816,11 @@ var init_turn_event = __esm({
1816
1816
  // claude-code/codex it's the runtime's CUMULATIVE total summed across every model
1817
1817
  // request in the agentic turn, so it grows with the tool-call count and is NOT
1818
1818
  // "how full is the window." `contextTokens` is the distinct CONTEXT-OCCUPANCY
1819
- // read: the input the model saw on its FINAL request of the turn (uncached +
1820
- // cache, since cached tokens still occupy the window) — the number the composer
1821
- // gauge wants. `contextWindow` is the model's true window in tokens when the
1822
- // runtime reports it (claude-code's SDK does, per model) — a real denominator so
1823
- // the gauge can show a fraction. Both optional: a runtime that can't source a
1824
- // clean final-request figure (codex's cumulative-only usage) omits `contextTokens`
1825
- // and the gauge falls back to the raw count; `contextWindow` falls back to the
1826
- // model catalog.
1819
+ // read and `contextWindow` the window it is measured against, both as the
1820
+ // harness reported them (claude-code's `getContextUsage`, or its final
1821
+ // request's input and `modelUsage` window when that call gives nothing). A
1822
+ // runtime that can't source them (codex's cumulative-only usage) omits both,
1823
+ // and no surface shows a reading for that turn.
1827
1824
  //
1828
1825
  // CT601: two more optional carry-homes on the terminal result, alongside
1829
1826
  // `usage`. `resolvedModel` is the CONCRETE model the runtime actually ran —
@@ -1846,7 +1843,25 @@ var init_turn_event = __esm({
1846
1843
  cacheReadTokens: z6.number().optional(),
1847
1844
  cacheCreationTokens: z6.number().optional(),
1848
1845
  contextTokens: z6.number().optional(),
1849
- contextWindow: z6.number().optional()
1846
+ contextWindow: z6.number().optional(),
1847
+ // CT1751: the rest of the harness's own context reading, when it gave
1848
+ // one: where it will compact, its `/context` breakdown, and every
1849
+ // compaction it reported during the turn. Claude Code only.
1850
+ contextCompactAt: z6.number().optional(),
1851
+ contextCategories: z6.array(
1852
+ z6.object({
1853
+ name: z6.string(),
1854
+ tokens: z6.number(),
1855
+ kind: z6.enum(["used", "free", "buffer", "deferred"])
1856
+ })
1857
+ ).optional(),
1858
+ contextCompactions: z6.array(
1859
+ z6.object({
1860
+ trigger: z6.enum(["manual", "auto"]),
1861
+ preTokens: z6.number(),
1862
+ postTokens: z6.number().optional()
1863
+ })
1864
+ ).optional()
1850
1865
  }).optional(),
1851
1866
  resolvedModel: z6.string().optional(),
1852
1867
  resolvedConfig: z6.object({
@@ -2902,6 +2917,7 @@ async function* decodeSdkStream(iter, ctx) {
2902
2917
  let resultReason;
2903
2918
  let sawResult = false;
2904
2919
  let usage;
2920
+ const compactions = [];
2905
2921
  let lastRequestContextTokens;
2906
2922
  let resolvedModel;
2907
2923
  const signals = { sawRejectedLimit: false };
@@ -2927,6 +2943,9 @@ async function* decodeSdkStream(iter, ctx) {
2927
2943
  ...ctx.degraded ? { degraded: true } : {}
2928
2944
  };
2929
2945
  }
2946
+ } else if (msg.type === "system" && msg.subtype === "compact_boundary") {
2947
+ const compaction = readCompaction(msg);
2948
+ if (compaction) compactions.push(compaction);
2930
2949
  } else if (!workspaceProven && (msg.type === "assistant" || msg.type === "user" || msg.type === "result")) {
2931
2950
  if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
2932
2951
  const failure = classifyPreInitFrame(msg, signals);
@@ -2971,6 +2990,9 @@ async function* decodeSdkStream(iter, ctx) {
2971
2990
  usage.contextTokens = lastRequestContextTokens;
2972
2991
  const window = readContextWindow(msg, resolvedModel);
2973
2992
  if (window !== void 0) usage.contextWindow = window;
2993
+ if (compactions.length > 0) usage.contextCompactions = compactions;
2994
+ const reading = await readHarnessContext(ctx.readContextUsage);
2995
+ if (reading) Object.assign(usage, reading);
2974
2996
  }
2975
2997
  const isError = msg.is_error === true;
2976
2998
  if (msg.subtype === "success" && !isError) {
@@ -2986,15 +3008,15 @@ async function* decodeSdkStream(iter, ctx) {
2986
3008
  }
2987
3009
  } catch (err) {
2988
3010
  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 });
3011
+ const message2 = err instanceof Error ? err.message : String(err);
3012
+ const failure = mcpInventory.initReceived ? classifyErrorText(message2) : classifyFailure(signals, { text: message2 });
2991
3013
  if (!failure && (mcpInventory.initReceived || ctx.resumedSessionId === null)) throw err;
2992
3014
  ok = false;
2993
3015
  if (failure) {
2994
3016
  resultReason = encodeFailureReason(failure);
2995
3017
  } else {
2996
3018
  resultReason = "session_start_failed";
2997
- if (ctx.startEvidence) ctx.startEvidence.thrown = message.slice(0, 200);
3019
+ if (ctx.startEvidence) ctx.startEvidence.thrown = message2.slice(0, 200);
2998
3020
  }
2999
3021
  sawResult = true;
3000
3022
  }
@@ -3121,6 +3143,56 @@ function readRequestContextTokens(msg) {
3121
3143
  const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
3122
3144
  return num(usage.input_tokens) + num(usage.cache_read_input_tokens) + num(usage.cache_creation_input_tokens);
3123
3145
  }
3146
+ async function readHarnessContext(read) {
3147
+ if (!read) return void 0;
3148
+ let timer;
3149
+ const timeout = new Promise((resolve2) => {
3150
+ timer = setTimeout(() => resolve2(void 0), CONTEXT_READING_TIMEOUT_MS);
3151
+ });
3152
+ try {
3153
+ return parseContextUsage(await Promise.race([read(), timeout]));
3154
+ } catch {
3155
+ return void 0;
3156
+ } finally {
3157
+ clearTimeout(timer);
3158
+ }
3159
+ }
3160
+ function parseContextUsage(response) {
3161
+ const r = response;
3162
+ if (!r || typeof r !== "object") return void 0;
3163
+ const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
3164
+ const total = count(r.totalTokens);
3165
+ const window = count(r.maxTokens);
3166
+ if (total === void 0 || window === void 0 || window === 0) return void 0;
3167
+ const categories = [];
3168
+ for (const c of Array.isArray(r.categories) ? r.categories : []) {
3169
+ const row = c;
3170
+ const tokens = count(row?.tokens);
3171
+ if (typeof row?.name === "string" && tokens !== void 0 && typeof row.kind === "string" && CATEGORY_KINDS.has(row.kind)) {
3172
+ categories.push({ name: row.name, tokens, kind: row.kind });
3173
+ }
3174
+ }
3175
+ const buffer = categories.filter((c) => c.kind === "buffer").reduce((sum, c) => sum + c.tokens, 0);
3176
+ return {
3177
+ contextTokens: total,
3178
+ contextWindow: window,
3179
+ ...buffer > 0 && buffer < window ? { contextCompactAt: window - buffer } : {},
3180
+ ...categories.length > 0 ? { contextCategories: categories } : {}
3181
+ };
3182
+ }
3183
+ function readCompaction(msg) {
3184
+ const meta = msg.compact_metadata;
3185
+ const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
3186
+ const preTokens = count(meta?.pre_tokens);
3187
+ if (!meta || preTokens === void 0) return void 0;
3188
+ if (meta.trigger !== "manual" && meta.trigger !== "auto") return void 0;
3189
+ const postTokens = count(meta.post_tokens);
3190
+ return {
3191
+ trigger: meta.trigger,
3192
+ preTokens,
3193
+ ...postTokens !== void 0 ? { postTokens } : {}
3194
+ };
3195
+ }
3124
3196
  function readContextWindow(msg, resolvedModel) {
3125
3197
  const modelUsage = msg.modelUsage;
3126
3198
  if (!modelUsage || typeof modelUsage !== "object") return void 0;
@@ -3136,7 +3208,7 @@ function readContextWindow(msg, resolvedModel) {
3136
3208
  }
3137
3209
  return max;
3138
3210
  }
3139
- var HARNESS_PLACEHOLDERS;
3211
+ var HARNESS_PLACEHOLDERS, CONTEXT_READING_TIMEOUT_MS, CATEGORY_KINDS;
3140
3212
  var init_loop = __esm({
3141
3213
  "packages/agent-runtime/src/claude-code/loop.ts"() {
3142
3214
  "use strict";
@@ -3144,11 +3216,13 @@ var init_loop = __esm({
3144
3216
  init_failure();
3145
3217
  init_session();
3146
3218
  HARNESS_PLACEHOLDERS = /* @__PURE__ */ new Set(["No response requested.", "(no content)"]);
3219
+ CONTEXT_READING_TIMEOUT_MS = 2e3;
3220
+ CATEGORY_KINDS = /* @__PURE__ */ new Set(["used", "free", "buffer", "deferred"]);
3147
3221
  }
3148
3222
  });
3149
3223
 
3150
3224
  // packages/agent-runtime/src/claude-code/prompt-input.ts
3151
- function buildQueryPrompt(req) {
3225
+ function buildQueryPrompt(req, openUntil) {
3152
3226
  const content = req.content;
3153
3227
  return (async function* () {
3154
3228
  yield {
@@ -3157,6 +3231,7 @@ function buildQueryPrompt(req) {
3157
3231
  message: { role: "user", content },
3158
3232
  parent_tool_use_id: null
3159
3233
  };
3234
+ if (openUntil) await openUntil;
3160
3235
  })();
3161
3236
  }
3162
3237
  var init_prompt_input = __esm({
@@ -3176,19 +3251,34 @@ function attachAbortController(options, signal) {
3176
3251
  else signal.addEventListener("abort", () => sdkAbort.abort(), { once: true });
3177
3252
  options.abortController = sdkAbort;
3178
3253
  }
3179
- function sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
3254
+ async function* sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
3180
3255
  attachAbortController(built.options, signal);
3181
- const prompt = buildQueryPrompt(req);
3182
- const iter = {
3183
- [Symbol.asyncIterator]: () => queryFn({ prompt, options: built.options })[Symbol.asyncIterator]()
3256
+ let release = () => {
3184
3257
  };
3185
- return decodeSdkStream(iter, {
3186
- signal,
3187
- cwd: req.local.cwd,
3188
- resumedSessionId: built.resume,
3189
- degraded,
3190
- ...startEvidence ? { startEvidence } : {}
3258
+ const held = new Promise((resolve2) => {
3259
+ release = resolve2;
3191
3260
  });
3261
+ const prompt = buildQueryPrompt(req, held);
3262
+ let active;
3263
+ const iter = {
3264
+ [Symbol.asyncIterator]: () => {
3265
+ const q = queryFn({ prompt, options: built.options });
3266
+ active = q;
3267
+ return q[Symbol.asyncIterator]();
3268
+ }
3269
+ };
3270
+ try {
3271
+ yield* decodeSdkStream(iter, {
3272
+ signal,
3273
+ readContextUsage: async () => typeof active?.getContextUsage === "function" ? active.getContextUsage({ detail: "summary" }) : void 0,
3274
+ cwd: req.local.cwd,
3275
+ resumedSessionId: built.resume,
3276
+ degraded,
3277
+ ...startEvidence ? { startEvidence } : {}
3278
+ });
3279
+ } finally {
3280
+ release();
3281
+ }
3192
3282
  }
3193
3283
  async function freshRequest(req, reason, hooks) {
3194
3284
  const recomposed = hooks?.fresh ? await hooks.fresh(reason) : null;
@@ -3979,9 +4069,9 @@ function readSessionError(properties) {
3979
4069
  if (typeof err === "string") return err;
3980
4070
  const rec2 = asRecord(err);
3981
4071
  if (!rec2) return "unknown";
3982
- const { name, message } = deepestError(rec2);
3983
- if (message && name && !isGenericErrorName(name)) return `${name}: ${message}`;
3984
- if (message) return message;
4072
+ const { name, message: message2 } = deepestError(rec2);
4073
+ if (message2 && name && !isGenericErrorName(name)) return `${name}: ${message2}`;
4074
+ if (message2) return message2;
3985
4075
  if (name) return name;
3986
4076
  if (typeof rec2.name === "string") return rec2.name;
3987
4077
  return "unknown";
@@ -3993,10 +4083,10 @@ function deepestError(node) {
3993
4083
  const best = {};
3994
4084
  let current = node;
3995
4085
  for (let depth = 0; current && depth < 8; depth++) {
3996
- const message = typeof current.message === "string" ? current.message : void 0;
4086
+ const message2 = typeof current.message === "string" ? current.message : void 0;
3997
4087
  const name = typeof current.name === "string" && !isGenericErrorName(current.name) ? current.name : void 0;
3998
- if (message) {
3999
- best.message = message;
4088
+ if (message2) {
4089
+ best.message = message2;
4000
4090
  if (name) best.name = name;
4001
4091
  } else if (name && !best.name) {
4002
4092
  best.name = name;
@@ -4659,9 +4749,9 @@ var init_conformance3 = __esm({
4659
4749
  properties: { part: { type: "text", id: partId, messageID, text } }
4660
4750
  });
4661
4751
  idle = () => ({ type: "session.idle", properties: {} });
4662
- errored = (message) => ({
4752
+ errored = (message2) => ({
4663
4753
  type: "session.error",
4664
- properties: { error: { message } }
4754
+ properties: { error: { message: message2 } }
4665
4755
  });
4666
4756
  sessionEvent2 = (sessionId, directory = DIR, degraded = false) => ({
4667
4757
  type: "session",
@@ -5105,8 +5195,8 @@ function readItemText(item) {
5105
5195
  function readItemMessage(item) {
5106
5196
  return str(item.message) ?? "";
5107
5197
  }
5108
- function isModelMetadataError(message) {
5109
- return message.includes("Defaulting to fallback metadata");
5198
+ function isModelMetadataError(message2) {
5199
+ return message2.includes("Defaulting to fallback metadata");
5110
5200
  }
5111
5201
  function readToolItem(item, eventType) {
5112
5202
  const type = str(item.type);
@@ -5351,11 +5441,11 @@ async function* decodeCodexStream(events, ctx) {
5351
5441
  if (!item) continue;
5352
5442
  const itemType = readItemType(item);
5353
5443
  if (itemType === "error") {
5354
- const message = readItemMessage(item);
5355
- if (isModelMetadataError(message)) {
5444
+ const message2 = readItemMessage(item);
5445
+ if (isModelMetadataError(message2)) {
5356
5446
  yield* flushInterim();
5357
5447
  ok = false;
5358
- reason = `model_unavailable:${message.slice(0, 200)}`;
5448
+ reason = `model_unavailable:${message2.slice(0, 200)}`;
5359
5449
  settled = true;
5360
5450
  break;
5361
5451
  }
@@ -5522,7 +5612,7 @@ var init_model3 = __esm({
5522
5612
  });
5523
5613
 
5524
5614
  // packages/agent-runtime/src/codex/run-spec.ts
5525
- function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null) {
5615
+ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null, imagePaths = []) {
5526
5616
  const { policy, config } = req;
5527
5617
  const directory = req.local.cwd ?? "";
5528
5618
  const dialect = readCodexDialect(config.runtimeOptions);
@@ -5539,7 +5629,7 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
5539
5629
  skipGitRepoCheck: true,
5540
5630
  ...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
5541
5631
  baseInstructionsFile,
5542
- input: buildInput(req, promptRidesInput),
5632
+ input: withImages(buildInput(req, promptRidesInput), imagePaths),
5543
5633
  promptRidesInput,
5544
5634
  promptFingerprint,
5545
5635
  config: buildConfig(req, baseInstructionsFile),
@@ -5555,6 +5645,13 @@ function buildInput(req, promptRidesInput) {
5555
5645
 
5556
5646
  ${body}` : body;
5557
5647
  }
5648
+ function withImages(text, imagePaths) {
5649
+ if (imagePaths.length === 0) return text;
5650
+ return [
5651
+ { type: "text", text },
5652
+ ...imagePaths.map((path) => ({ type: "local_image", path }))
5653
+ ];
5654
+ }
5558
5655
  function buildConfig(req, baseInstructionsFile) {
5559
5656
  const mcp_servers = {};
5560
5657
  for (const [name, server] of Object.entries(req.local.mcpServers ?? {})) {
@@ -5826,12 +5923,35 @@ function createCodexAdapter(deps = {}) {
5826
5923
  }
5827
5924
  }
5828
5925
  }
5926
+ let images = null;
5829
5927
  try {
5928
+ const imageUrls = req.content.flatMap((b) => b.type === "image" ? [b.source.url] : []);
5929
+ if (imageUrls.length > 0) {
5930
+ if (!deps.prepareImages) {
5931
+ deps.onWarn?.(
5932
+ "codex adapter: no image preparer wired on this host \u2014 the turn runs without its images (CT1757)",
5933
+ { images: imageUrls.length }
5934
+ );
5935
+ } else {
5936
+ try {
5937
+ images = await deps.prepareImages(imageUrls, signal);
5938
+ } catch (err) {
5939
+ if (!signal.aborted) {
5940
+ deps.onWarn?.(
5941
+ "codex adapter: preparing the images failed \u2014 the turn runs without them (CT1757)",
5942
+ { err: err instanceof Error ? err.message : String(err) }
5943
+ );
5944
+ }
5945
+ }
5946
+ }
5947
+ if (signal.aborted) return;
5948
+ }
5830
5949
  const spec = buildRunSpec2(
5831
5950
  req,
5832
5951
  resumeThreadId,
5833
5952
  instructions?.path ?? null,
5834
- threadPromptFingerprint
5953
+ threadPromptFingerprint,
5954
+ images?.paths ?? []
5835
5955
  );
5836
5956
  if (spec.model === null) {
5837
5957
  deps.onWarn?.("Codex is using its default model because no model was resolved", {
@@ -5860,6 +5980,15 @@ function createCodexAdapter(deps = {}) {
5860
5980
  resolvedReasoningEffort: spec.modelReasoningEffort ?? null
5861
5981
  });
5862
5982
  } finally {
5983
+ if (images) {
5984
+ try {
5985
+ await images.cleanup();
5986
+ } catch (err) {
5987
+ deps.onWarn?.("codex adapter: image cleanup failed (CT1757)", {
5988
+ err: err instanceof Error ? err.message : String(err)
5989
+ });
5990
+ }
5991
+ }
5863
5992
  if (instructions) {
5864
5993
  try {
5865
5994
  await instructions.cleanup();
@@ -5952,14 +6081,14 @@ var init_conformance4 = __esm({
5952
6081
  type: "turn.completed",
5953
6082
  ...usage ? { usage } : {}
5954
6083
  });
5955
- turnFailed = (message) => ({
6084
+ turnFailed = (message2) => ({
5956
6085
  type: "turn.failed",
5957
- error: { message }
6086
+ error: { message: message2 }
5958
6087
  });
5959
- fatalError = (message) => ({ type: "error", message });
5960
- errorItem = (id, message) => ({
6088
+ fatalError = (message2) => ({ type: "error", message: message2 });
6089
+ errorItem = (id, message2) => ({
5961
6090
  type: "item.completed",
5962
- item: { id, type: "error", message }
6091
+ item: { id, type: "error", message: message2 }
5963
6092
  });
5964
6093
  OP_SUCCESS = { ok: true, result: { source: "native" } };
5965
6094
  OP_FAILURE = {
@@ -6897,8 +7026,8 @@ function unenforceable(policy) {
6897
7026
  deniedDomains: as === "deniedDomains" ? [host] : []
6898
7027
  });
6899
7028
  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}`;
7029
+ const message2 = check.error.issues[0]?.message ?? "not a host pattern the sandbox accepts";
7030
+ return `runners.policy.${field}.${i}: ${message2}`;
6902
7031
  }
6903
7032
  }
6904
7033
  }
@@ -6912,8 +7041,8 @@ function unenforceable(policy) {
6912
7041
  try {
6913
7042
  new RegExp(globToRegex(path));
6914
7043
  } 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})`;
7044
+ const message2 = err instanceof Error ? err.message : String(err);
7045
+ return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${message2})`;
6917
7046
  }
6918
7047
  }
6919
7048
  }
@@ -7118,8 +7247,8 @@ async function reapTurnScopes(turnId) {
7118
7247
  });
7119
7248
  return { failures: [] };
7120
7249
  } catch (err) {
7121
- const message = err instanceof Error ? err.message : String(err);
7122
- return classifyStopError(message) === "absent" ? { failures: [] } : { failures: [message.slice(0, 200)] };
7250
+ const message2 = err instanceof Error ? err.message : String(err);
7251
+ return classifyStopError(message2) === "absent" ? { failures: [] } : { failures: [message2.slice(0, 200)] };
7123
7252
  }
7124
7253
  }
7125
7254
  function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
@@ -7135,13 +7264,13 @@ function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
7135
7264
  return false;
7136
7265
  }
7137
7266
  }
7138
- function classifyStopError(message) {
7267
+ function classifyStopError(message2) {
7139
7268
  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
7269
+ message2
7141
7270
  )) {
7142
7271
  return "failure";
7143
7272
  }
7144
- if (/not loaded|could not be found|no such unit|unit .* not found/i.test(message))
7273
+ if (/not loaded|could not be found|no such unit|unit .* not found/i.test(message2))
7145
7274
  return "absent";
7146
7275
  return "failure";
7147
7276
  }
@@ -7250,9 +7379,9 @@ function createTurnContainment(turnId, deps = {}) {
7250
7379
  timeout: (CONTAINMENT_STOP_GRACE_SEC + 10) * 1e3
7251
7380
  });
7252
7381
  } 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) });
7382
+ const message2 = err instanceof Error ? err.message : String(err);
7383
+ if (classifyStopError(message2) === "failure") {
7384
+ result.failures.push({ target: unit, error: message2.slice(0, 200) });
7256
7385
  continue;
7257
7386
  }
7258
7387
  result.reaped.push(unit);
@@ -7616,8 +7745,8 @@ function runnerStdin(input) {
7616
7745
  })}
7617
7746
  `;
7618
7747
  }
7619
- function encodeLauncherMessage(message) {
7620
- return `${JSON.stringify(message)}
7748
+ function encodeLauncherMessage(message2) {
7749
+ return `${JSON.stringify(message2)}
7621
7750
  `;
7622
7751
  }
7623
7752
  function decodeLauncherMessage(line) {
@@ -8479,6 +8608,74 @@ var init_codex_instructions = __esm({
8479
8608
  }
8480
8609
  });
8481
8610
 
8611
+ // src/codex-images.ts
8612
+ import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
8613
+ import { tmpdir as tmpdir2 } from "os";
8614
+ import { join as join12 } from "path";
8615
+ function createCodexImagePreparer(opts) {
8616
+ const doFetch = opts.fetch ?? fetch;
8617
+ const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
8618
+ return async (urls, signal) => {
8619
+ signal.throwIfAborted();
8620
+ const dir2 = await mkdtemp2(join12(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
8621
+ const cleanup = async () => {
8622
+ try {
8623
+ await rm2(dir2, { recursive: true, force: true });
8624
+ } catch (err) {
8625
+ opts.warn(
8626
+ { dir: dir2, err: message(err) },
8627
+ "codex images: could not remove the scratch directory"
8628
+ );
8629
+ }
8630
+ };
8631
+ const prepareOne = async (url, index) => {
8632
+ try {
8633
+ const res = await doFetch(url, {
8634
+ signal: AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)])
8635
+ });
8636
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
8637
+ const type = (res.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
8638
+ const ext = EXTENSIONS[type];
8639
+ if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
8640
+ const bytes = new Uint8Array(await res.arrayBuffer());
8641
+ const path = join12(dir2, `image-${index + 1}.${ext}`);
8642
+ await writeFile2(path, bytes, { mode: 384 });
8643
+ return path;
8644
+ } catch (err) {
8645
+ if (!signal.aborted) {
8646
+ opts.warn(
8647
+ { image: index + 1, of: urls.length, err: message(err) },
8648
+ "codex images: an image could not be prepared; the turn runs without it"
8649
+ );
8650
+ }
8651
+ return null;
8652
+ }
8653
+ };
8654
+ const paths = await Promise.all(urls.map(prepareOne));
8655
+ if (signal.aborted) {
8656
+ await cleanup();
8657
+ signal.throwIfAborted();
8658
+ }
8659
+ return { paths: paths.filter((p) => p !== null), cleanup };
8660
+ };
8661
+ }
8662
+ function message(err) {
8663
+ return err instanceof Error ? err.message : String(err);
8664
+ }
8665
+ var FETCH_TIMEOUT_MS, EXTENSIONS;
8666
+ var init_codex_images = __esm({
8667
+ "src/codex-images.ts"() {
8668
+ "use strict";
8669
+ FETCH_TIMEOUT_MS = 3e4;
8670
+ EXTENSIONS = {
8671
+ "image/png": "png",
8672
+ "image/jpeg": "jpg",
8673
+ "image/gif": "gif",
8674
+ "image/webp": "webp"
8675
+ };
8676
+ }
8677
+ });
8678
+
8482
8679
  // src/turn-seq-floor.ts
8483
8680
  function resolveSeqFloor(sources, ctx) {
8484
8681
  const { serverFloor, outboxFloor } = sources;
@@ -8522,15 +8719,15 @@ var init_turn_seq_floor = __esm({
8522
8719
  import { spawn as spawn5 } from "child_process";
8523
8720
  import { hostname as hostname2 } from "os";
8524
8721
  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))
8722
+ const message2 = error instanceof Error ? error.message : String(error);
8723
+ if (/timed out/i.test(message2)) return "prepare hook timed out";
8724
+ if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(message2))
8528
8725
  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))
8726
+ if (/not valid JSON|JSON must carry|produced no output/.test(message2))
8530
8727
  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];
8728
+ if (/ENOENT/.test(message2)) return "prepare hook executable missing (ENOENT)";
8729
+ if (/EACCES/.test(message2)) return "prepare hook executable is not accessible (EACCES)";
8730
+ const exit = message2.match(/exited with code (\d+)/)?.[1];
8534
8731
  return exit ? `prepare hook exited with code ${exit}` : "prepare hook failed; inspect the device-local hook log";
8535
8732
  }
8536
8733
  async function runPrepareWithReporting(runner, hook, input, context, reportError) {
@@ -8587,15 +8784,15 @@ var init_prepare_failure = __esm({
8587
8784
 
8588
8785
  // src/prepared.ts
8589
8786
  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";
8787
+ import { join as join13 } from "path";
8591
8788
  function dirFor(workspaceId) {
8592
- return join12(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
8789
+ return join13(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
8593
8790
  }
8594
8791
  function conversationDir(workspaceId, conversationId) {
8595
- return join12(dirFor(workspaceId), encodeURIComponent(conversationId));
8792
+ return join13(dirFor(workspaceId), encodeURIComponent(conversationId));
8596
8793
  }
8597
8794
  function pathFor2(workspaceId, conversationId, agentId) {
8598
- return join12(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
8795
+ return join13(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
8599
8796
  }
8600
8797
  function readPrepared(workspaceId, conversationId, agentId) {
8601
8798
  const path = pathFor2(workspaceId, conversationId, agentId);
@@ -8633,9 +8830,9 @@ var init_prepared = __esm({
8633
8830
 
8634
8831
  // src/transcript-writer.ts
8635
8832
  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";
8833
+ import { basename, dirname as dirname6, join as join14 } from "path";
8637
8834
  function transcriptsDir() {
8638
- return join13(runnerStateDir(), "transcripts");
8835
+ return join14(runnerStateDir(), "transcripts");
8639
8836
  }
8640
8837
  function fileName(meta) {
8641
8838
  const d = new Date(meta.ts);
@@ -8661,7 +8858,7 @@ function pruneOld(dir2, retain) {
8661
8858
  const drop = files.sort().slice(0, files.length - retain);
8662
8859
  for (const f of drop) {
8663
8860
  try {
8664
- rmSync6(join13(dir2, f), { force: true });
8861
+ rmSync6(join14(dir2, f), { force: true });
8665
8862
  } catch {
8666
8863
  }
8667
8864
  }
@@ -8679,7 +8876,7 @@ var init_transcript_writer = __esm({
8679
8876
  onWarn;
8680
8877
  constructor(dir2, meta, onWarn) {
8681
8878
  this.onWarn = onWarn;
8682
- this.path = join13(dir2, fileName(meta));
8879
+ this.path = join14(dir2, fileName(meta));
8683
8880
  try {
8684
8881
  mkdirSync8(dir2, { recursive: true });
8685
8882
  try {
@@ -8708,10 +8905,10 @@ var init_transcript_writer = __esm({
8708
8905
  preserveAnomaly() {
8709
8906
  if (this.broken) return;
8710
8907
  try {
8711
- const dir2 = join13(dirname6(this.path), "anomalies");
8908
+ const dir2 = join14(dirname6(this.path), "anomalies");
8712
8909
  mkdirSync8(dir2, { recursive: true, mode: 448 });
8713
8910
  chmodSync4(dir2, 448);
8714
- const target = join13(dir2, basename(this.path));
8911
+ const target = join14(dir2, basename(this.path));
8715
8912
  copyFileSync(this.path, target);
8716
8913
  chmodSync4(target, 384);
8717
8914
  pruneOld(dir2, ANOMALY_RETAIN);
@@ -9006,6 +9203,7 @@ var init_turn_execution = __esm({
9006
9203
  init_build_options();
9007
9204
  init_bundled_binaries();
9008
9205
  init_codex_instructions();
9206
+ init_codex_images();
9009
9207
  init_config();
9010
9208
  init_api_error_shape();
9011
9209
  init_errors();
@@ -9354,16 +9552,16 @@ var init_turn_execution = __esm({
9354
9552
  }
9355
9553
  gateTrigger() {
9356
9554
  const { payload, turnLog } = this;
9357
- const message = this.turnContext.message;
9358
- const isDispatchableTrigger = message.role === "user" || message.role === "agent" || message.role === "system";
9555
+ const message2 = this.turnContext.message;
9556
+ const isDispatchableTrigger = message2.role === "user" || message2.role === "agent" || message2.role === "system";
9359
9557
  if (!isDispatchableTrigger) {
9360
- turnLog.debug({ role: message.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
9558
+ turnLog.debug({ role: message2.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
9361
9559
  throw this.concluded("unexpected_role", UNEXPECTED_ROLE_REASON);
9362
9560
  }
9363
9561
  this.supervisor.notifyStart({
9364
9562
  id: this.dispatchId,
9365
9563
  conversationId: payload.conversationId,
9366
- message: message.body
9564
+ message: message2.body
9367
9565
  });
9368
9566
  }
9369
9567
  async resolveSecrets() {
@@ -9714,6 +9912,10 @@ var init_turn_execution = __esm({
9714
9912
  enabled: true,
9715
9913
  onWarn,
9716
9914
  writeInstructionsFile: writeCodexInstructionsFile,
9915
+ prepareImages: createCodexImagePreparer({
9916
+ turnId: this.turnId,
9917
+ warn: (meta, msg) => turnLog.warn(meta, msg)
9918
+ }),
9717
9919
  ...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
9718
9920
  })
9719
9921
  );
@@ -10174,7 +10376,7 @@ var init_turn_execution = __esm({
10174
10376
  // src/dispatcher.ts
10175
10377
  import { randomUUID as randomUUID4 } from "crypto";
10176
10378
  import { existsSync as existsSync10, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
10177
- import { join as join14 } from "path";
10379
+ import { join as join15 } from "path";
10178
10380
  function checkoutState(cwd) {
10179
10381
  if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
10180
10382
  if (!existsSync10(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
@@ -10187,7 +10389,7 @@ function checkoutState(cwd) {
10187
10389
  if (entries.length === 0) {
10188
10390
  return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
10189
10391
  }
10190
- const gitPath = join14(cwd, ".git");
10392
+ const gitPath = join15(cwd, ".git");
10191
10393
  if (!existsSync10(gitPath)) return { ok: true, reason: "usable" };
10192
10394
  let stat;
10193
10395
  try {
@@ -10195,7 +10397,7 @@ function checkoutState(cwd) {
10195
10397
  } catch (error) {
10196
10398
  return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
10197
10399
  }
10198
- if (stat.isDirectory() && !existsSync10(join14(gitPath, "HEAD")))
10400
+ if (stat.isDirectory() && !existsSync10(join15(gitPath, "HEAD")))
10199
10401
  return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
10200
10402
  return { ok: true, reason: "usable" };
10201
10403
  }
@@ -10561,8 +10763,8 @@ import {
10561
10763
  } from "fs";
10562
10764
  import { createRequire as createRequire2 } from "module";
10563
10765
  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";
10766
+ import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
10767
+ import { dirname as dirname8, join as join19, sep as sep2 } from "path";
10566
10768
  import { fileURLToPath as fileURLToPath4 } from "url";
10567
10769
  import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
10568
10770
  function accountHome() {
@@ -10596,7 +10798,7 @@ async function resolvedAddresses(baseUrl) {
10596
10798
  function companionPackageRoot(modulePath) {
10597
10799
  for (let dir2 = dirname8(modulePath); ; dir2 = dirname8(dir2)) {
10598
10800
  try {
10599
- const manifest = JSON.parse(readFileSync10(join18(dir2, "package.json"), "utf8"));
10801
+ const manifest = JSON.parse(readFileSync10(join19(dir2, "package.json"), "utf8"));
10600
10802
  if (manifest.name === "@cabane/companion") return dir2;
10601
10803
  } catch {
10602
10804
  }
@@ -10673,15 +10875,15 @@ function companionStoreDenies(input) {
10673
10875
  const always = [
10674
10876
  cabaneDir2,
10675
10877
  ...homes.flatMap((home) => [
10676
- join18(home, ".cabane"),
10677
- join18(home, "*", ".cabane"),
10678
- join18(home, "*", "*", ".cabane")
10878
+ join19(home, ".cabane"),
10879
+ join19(home, "*", ".cabane"),
10880
+ join19(home, "*", "*", ".cabane")
10679
10881
  ]),
10680
10882
  // A glob anchored at the root has no directory to start from.
10681
- ...dirname8(parent) === parent ? [] : [join18(parent, "*", ".cabane")]
10883
+ ...dirname8(parent) === parent ? [] : [join19(parent, "*", ".cabane")]
10682
10884
  ];
10683
10885
  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)));
10886
+ 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
10887
  return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
10686
10888
  }
10687
10889
  function companionStoreWatch(log) {
@@ -10698,7 +10900,7 @@ function isAtOrUnder(path, dir2) {
10698
10900
  }
10699
10901
  function expandHome(path, home) {
10700
10902
  if (path === "~") return home;
10701
- return path.startsWith("~/") ? join18(home, path.slice(2)) : path;
10903
+ return path.startsWith("~/") ? join19(home, path.slice(2)) : path;
10702
10904
  }
10703
10905
  function stripPort(pattern) {
10704
10906
  return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
@@ -10716,8 +10918,8 @@ function patternsOverlap(a, b) {
10716
10918
  }
10717
10919
  function harnessSandboxConfig(input) {
10718
10920
  const { policy } = input;
10719
- const claudeDir = input.claudeConfigDir ?? join18(input.home, ".claude");
10720
- const codexDir = input.codexHome ?? join18(input.home, ".codex");
10921
+ const claudeDir = input.claudeConfigDir ?? join19(input.home, ".claude");
10922
+ const codexDir = input.codexHome ?? join19(input.home, ".codex");
10721
10923
  const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
10722
10924
  const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
10723
10925
  const cabane = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
@@ -10734,7 +10936,7 @@ function harnessSandboxConfig(input) {
10734
10936
  foundStores: input.foundStores,
10735
10937
  ownState: [claudeDir, codexDir]
10736
10938
  }),
10737
- ...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join18(home, p))),
10939
+ ...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join19(home, p))),
10738
10940
  ...expand(policy.filesystem?.denyRead)
10739
10941
  ];
10740
10942
  const strict = policy.filesystem?.mode === "strict";
@@ -10742,7 +10944,7 @@ function harnessSandboxConfig(input) {
10742
10944
  const allowRead = strict ? [
10743
10945
  ...input.cwd ? [input.cwd] : [],
10744
10946
  claudeDir,
10745
- join18(input.home, ".claude.json"),
10947
+ join19(input.home, ".claude.json"),
10746
10948
  codexDir,
10747
10949
  ...input.runtimePaths ?? [],
10748
10950
  ...expand(policy.filesystem?.allowRead)
@@ -10782,7 +10984,7 @@ function harnessSandboxConfig(input) {
10782
10984
  ...input.cwd ? [input.cwd] : [],
10783
10985
  input.tmp,
10784
10986
  claudeDir,
10785
- join18(input.home, ".claude.json"),
10987
+ join19(input.home, ".claude.json"),
10786
10988
  codexDir,
10787
10989
  ...expand(policy.filesystem?.allowWrite)
10788
10990
  ],
@@ -10835,7 +11037,7 @@ async function prepareHarnessSandbox(input) {
10835
11037
  ownAddresses: ownAddresses(),
10836
11038
  home: homedir3(),
10837
11039
  accountHome: accountHome(),
10838
- tmp: tmpdir3(),
11040
+ tmp: tmpdir4(),
10839
11041
  cabaneDir: cabaneDir(),
10840
11042
  claudeConfigDir: process.env.CLAUDE_CONFIG_DIR,
10841
11043
  codexHome: process.env.CODEX_HOME,
@@ -10866,27 +11068,27 @@ async function prepareHarnessSandbox(input) {
10866
11068
  "export NO_PROXY= no_proxy=; " + gitCredentialExports(input.credentials) + 'eval "exec $CABANE_SANDBOXED_ARGV"'
10867
11069
  );
10868
11070
  } catch (err) {
10869
- const message = err instanceof Error ? err.message : String(err);
11071
+ const message2 = err instanceof Error ? err.message : String(err);
10870
11072
  await SandboxManager.reset().catch(() => {
10871
11073
  });
10872
11074
  if (held.length || Object.keys(input.policy).length) {
10873
11075
  input.log.warn(
10874
- { err: message, masked: held },
11076
+ { err: message2, masked: held },
10875
11077
  "runners: the OS sandbox could not start under this device's runners.policy; refusing the turn rather than running it unsandboxed"
10876
11078
  );
10877
- throw new HarnessSandboxRefused(`the OS sandbox could not start: ${message}`);
11079
+ throw new HarnessSandboxRefused(`the OS sandbox could not start: ${message2}`);
10878
11080
  }
10879
11081
  input.log.warn(
10880
- { err: message },
11082
+ { err: message2 },
10881
11083
  "runners: the OS sandbox could not start; running the turn unsandboxed"
10882
11084
  );
10883
11085
  return null;
10884
11086
  } finally {
10885
11087
  for (const name of held) delete process.env[name];
10886
11088
  }
10887
- mkdirSync11(join18(cabaneDir(), "sandbox"), { recursive: true });
10888
- const dir2 = mkdtempSync(join18(cabaneDir(), "sandbox", "turn-"));
10889
- const script = join18(dir2, "exec-sandboxed");
11089
+ mkdirSync11(join19(cabaneDir(), "sandbox"), { recursive: true });
11090
+ const dir2 = mkdtempSync(join19(cabaneDir(), "sandbox", "turn-"));
11091
+ const script = join19(dir2, "exec-sandboxed");
10890
11092
  writeFileSync7(
10891
11093
  script,
10892
11094
  [
@@ -10902,7 +11104,7 @@ async function prepareHarnessSandbox(input) {
10902
11104
  return {
10903
11105
  wrap: (command, args) => ({ command: script, args: [command, ...args] }),
10904
11106
  executableFor: (real) => {
10905
- const shim = join18(dir2, `exec-${++shims}`);
11107
+ const shim = join19(dir2, `exec-${++shims}`);
10906
11108
  writeFileSync7(shim, `#!/bin/bash
10907
11109
  exec ${shellQuote(script)} ${shellQuote(real)} "$@"
10908
11110
  `);
@@ -10995,7 +11197,7 @@ import {
10995
11197
  rmSync as rmSync11,
10996
11198
  writeFileSync as writeFileSync8
10997
11199
  } from "fs";
10998
- import { join as join19 } from "path";
11200
+ import { join as join20 } from "path";
10999
11201
  var MAX_ENTRIES, MAX_AGE_MS, Outbox;
11000
11202
  var init_outbox = __esm({
11001
11203
  "src/outbox.ts"() {
@@ -11013,10 +11215,10 @@ var init_outbox = __esm({
11013
11215
  // Resolved lazily (per call, like cursor.ts) so tests that swap HOME between
11014
11216
  // cases route writes at the right tmpdir.
11015
11217
  dir() {
11016
- return join19(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
11218
+ return join20(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
11017
11219
  }
11018
11220
  fileFor(turnId, seq) {
11019
- return join19(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
11221
+ return join20(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
11020
11222
  }
11021
11223
  // Persist a commit for later draining. Atomic (temp file + rename) so a
11022
11224
  // concurrent `list()` never reads a half-written entry, then enforces the
@@ -11058,7 +11260,7 @@ var init_outbox = __esm({
11058
11260
  const entries = [];
11059
11261
  for (const name of names) {
11060
11262
  if (!name.endsWith(".json")) continue;
11061
- const full = join19(dir2, name);
11263
+ const full = join20(dir2, name);
11062
11264
  try {
11063
11265
  const parsed = JSON.parse(readFileSync11(full, "utf8"));
11064
11266
  if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
@@ -11198,7 +11400,7 @@ var init_image_manifest = __esm({
11198
11400
 
11199
11401
  // src/runner/exec-shim.ts
11200
11402
  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";
11403
+ import { join as join23 } from "path";
11202
11404
  function shellQuote2(value) {
11203
11405
  return `'${value.replace(/'/g, `'\\''`)}'`;
11204
11406
  }
@@ -11208,10 +11410,10 @@ function createShimDir() {
11208
11410
  return {
11209
11411
  write(argv) {
11210
11412
  if (!dir2) {
11211
- mkdirSync15(join22(cabaneDir(), "runner"), { recursive: true });
11212
- dir2 = mkdtempSync2(join22(cabaneDir(), "runner", "turn-"));
11413
+ mkdirSync15(join23(cabaneDir(), "runner"), { recursive: true });
11414
+ dir2 = mkdtempSync2(join23(cabaneDir(), "runner", "turn-"));
11213
11415
  }
11214
- const path = join22(dir2, `exec-${++count}`);
11416
+ const path = join23(dir2, `exec-${++count}`);
11215
11417
  writeFileSync10(path, `#!/bin/sh
11216
11418
  exec ${argv.map(shellQuote2).join(" ")} "$@"
11217
11419
  `);
@@ -11247,9 +11449,9 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
11247
11449
  resolveFirst(line);
11248
11450
  return;
11249
11451
  }
11250
- const message = decodeLauncherMessage(line);
11251
- if (message?.cabaneLauncher === "granted") waiting.get(message.id)?.();
11252
- else if (message) replies.get(message.id)?.(message);
11452
+ const message2 = decodeLauncherMessage(line);
11453
+ if (message2?.cabaneLauncher === "granted") waiting.get(message2.id)?.();
11454
+ else if (message2) replies.get(message2.id)?.(message2);
11253
11455
  });
11254
11456
  let gone = false;
11255
11457
  lines2.once("close", () => {
@@ -11262,9 +11464,9 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
11262
11464
  if (gone) return Promise.resolve(null);
11263
11465
  const id = String(++next);
11264
11466
  return new Promise((resolve2) => {
11265
- replies.set(id, (message) => {
11467
+ replies.set(id, (message2) => {
11266
11468
  replies.delete(id);
11267
- resolve2(message);
11469
+ resolve2(message2);
11268
11470
  });
11269
11471
  emit(event(id));
11270
11472
  });
@@ -12474,8 +12676,8 @@ async function logout(opts = {}) {
12474
12676
  return;
12475
12677
  }
12476
12678
  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 });
12679
+ 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)?";
12680
+ const ok = await confirm2({ message: message2, default: false });
12479
12681
  if (!ok) {
12480
12682
  process.stdout.write("cancelled\n");
12481
12683
  return;
@@ -12731,8 +12933,8 @@ function formatHumanLine(record) {
12731
12933
  let line = `${time} ${word ? `${word} ` : ""}${context ? `${context} ` : ""}${humanText(record.msg)}`;
12732
12934
  const err = record.err;
12733
12935
  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;
12936
+ const message2 = humanText(errorRecord && "message" in errorRecord ? errorRecord.message : err);
12937
+ const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) || message2}` : message2;
12736
12938
  if (detail && !line.includes(detail)) line += ` (${detail})`;
12737
12939
  if (level < 30) {
12738
12940
  const hidden = /* @__PURE__ */ new Set([
@@ -12966,9 +13168,9 @@ var CompanionStateHub = class {
12966
13168
  if (info.deviceLabel !== void 0) this.deviceLabel = info.deviceLabel;
12967
13169
  this.emitStatus();
12968
13170
  }
12969
- setDeviceError(message) {
12970
- if (this.deviceError === message) return;
12971
- this.deviceError = message;
13171
+ setDeviceError(message2) {
13172
+ if (this.deviceError === message2) return;
13173
+ this.deviceError = message2;
12972
13174
  this.emitStatus();
12973
13175
  }
12974
13176
  clearDeviceError() {
@@ -13420,7 +13622,7 @@ async function warnAboutHarnessReadiness(cfg, deps = {}) {
13420
13622
  const probeClaude = deps.probeClaude ?? claudeOnPath;
13421
13623
  const probeCodex = deps.probeCodex ?? codexOnPath;
13422
13624
  const resolveBundled = deps.resolveBundled ?? ((runtime) => resolveBundledBinary(runtime));
13423
- const warn = deps.warn ?? ((message) => process.stderr.write(`${message}
13625
+ const warn = deps.warn ?? ((message2) => process.stderr.write(`${message2}
13424
13626
  `));
13425
13627
  const connected = [
13426
13628
  ...isClaudeCodeConnected(cfg) ? ["Claude Code"] : [],
@@ -13912,7 +14114,7 @@ function createLocalProcessProvider(opts = {}) {
13912
14114
 
13913
14115
  // src/providers/docker.ts
13914
14116
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
13915
- import { constants as constants2, tmpdir as tmpdir2 } from "os";
14117
+ import { constants as constants2, tmpdir as tmpdir3 } from "os";
13916
14118
  import {
13917
14119
  createReadStream,
13918
14120
  createWriteStream as createWriteStream2,
@@ -13925,7 +14127,7 @@ import {
13925
14127
  statSync as statSync3,
13926
14128
  writeFileSync as writeFileSync6
13927
14129
  } from "fs";
13928
- import { join as join17 } from "path";
14130
+ import { join as join18 } from "path";
13929
14131
  import { Writable } from "stream";
13930
14132
 
13931
14133
  // src/providers/docker-cli.ts
@@ -13998,7 +14200,7 @@ import {
13998
14200
  renameSync as renameSync4,
13999
14201
  rmSync as rmSync8
14000
14202
  } from "fs";
14001
- import { join as join15 } from "path";
14203
+ import { join as join16 } from "path";
14002
14204
  function routes(t) {
14003
14205
  const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
14004
14206
  const agent = `${conv}/participants/agents/${t.agentId}`;
@@ -14044,7 +14246,7 @@ function publishStaged(from, to, move = atomicMove) {
14044
14246
  const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
14045
14247
  let entry = {};
14046
14248
  try {
14047
- entry = JSON.parse(readFileSync8(join15(from, name), "utf8"));
14249
+ entry = JSON.parse(readFileSync8(join16(from, name), "utf8"));
14048
14250
  } catch {
14049
14251
  }
14050
14252
  return { name, entry };
@@ -14057,7 +14259,7 @@ function publishStaged(from, to, move = atomicMove) {
14057
14259
  let published = 0;
14058
14260
  for (const { name } of staged) {
14059
14261
  try {
14060
- move(join15(from, name), join15(to, name));
14262
+ move(join16(from, name), join16(to, name));
14061
14263
  } catch {
14062
14264
  return { published, complete: false };
14063
14265
  }
@@ -14070,7 +14272,7 @@ function publishStaged(from, to, move = atomicMove) {
14070
14272
  // src/providers/runner-base.ts
14071
14273
  import { createHash as createHash3 } from "crypto";
14072
14274
  import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
14073
- import { dirname as dirname7, join as join16, sep } from "path";
14275
+ import { dirname as dirname7, join as join17, sep } from "path";
14074
14276
  import { fileURLToPath as fileURLToPath3 } from "url";
14075
14277
  var COMPANION_MOUNT = "/opt/cabane/companion";
14076
14278
  var BASE_DOCKERFILE = `FROM node:22-slim
@@ -14118,11 +14320,11 @@ async function ensureBaseImage(docker, image, log) {
14118
14320
  function ownInstall(moduleFile = fileURLToPath3(import.meta.url)) {
14119
14321
  let dir2 = dirname7(moduleFile);
14120
14322
  for (let i = 0; i < 4; i++) {
14121
- if (existsSync12(join16(dir2, "dist", "cli.js")) && existsSync12(join16(dir2, "package.json"))) break;
14323
+ if (existsSync12(join17(dir2, "dist", "cli.js")) && existsSync12(join17(dir2, "package.json"))) break;
14122
14324
  dir2 = dirname7(dir2);
14123
14325
  }
14124
- if (!existsSync12(join16(dir2, "dist", "cli.js"))) return null;
14125
- const sdk = join16(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
14326
+ if (!existsSync12(join17(dir2, "dist", "cli.js"))) return null;
14327
+ const sdk = join17(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
14126
14328
  if (!existsSync12(sdk)) return null;
14127
14329
  const root = realpathSync2(dir2);
14128
14330
  return realpathSync2(sdk).startsWith(root + sep) ? root : null;
@@ -14261,21 +14463,21 @@ function networkCreateError(stderr) {
14261
14463
  }
14262
14464
  function sessionArchivePath(deviceDir, attemptId) {
14263
14465
  if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
14264
- return join17(deviceDir, "attempts", attemptId, "session.tar");
14466
+ return join18(deviceDir, "attempts", attemptId, "session.tar");
14265
14467
  }
14266
14468
  function pruneOtherAttempts(deviceDir, pair2, keep) {
14267
- const root = join17(deviceDir, "attempts");
14469
+ const root = join18(deviceDir, "attempts");
14268
14470
  if (!existsSync13(root)) return;
14269
14471
  for (const id of readdirSync4(root)) {
14270
14472
  if (id === keep || !UUID_RE.test(id)) continue;
14271
14473
  let pairOf = "";
14272
14474
  try {
14273
- pairOf = readFileSync9(join17(root, id, "pair"), "utf8");
14475
+ pairOf = readFileSync9(join18(root, id, "pair"), "utf8");
14274
14476
  } catch {
14275
14477
  continue;
14276
14478
  }
14277
14479
  if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
14278
- rmSync9(join17(root, id), { recursive: true, force: true });
14480
+ rmSync9(join18(root, id), { recursive: true, force: true });
14279
14481
  }
14280
14482
  }
14281
14483
  }
@@ -14475,11 +14677,11 @@ function createDockerProvider(opts) {
14475
14677
  await must(["network", "disconnect", "-f", other, name], "moving the container");
14476
14678
  if (others.includes(sharedNetwork)) await retireSharedNetwork();
14477
14679
  };
14478
- const socketRoot = opts.socketRoot ?? join17(
14479
- process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir2(),
14680
+ const socketRoot = opts.socketRoot ?? join18(
14681
+ process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
14480
14682
  `cabane-runners-${opts.deviceKey}`
14481
14683
  );
14482
- const socketDir = (name) => join17(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
14684
+ const socketDir = (name) => join18(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
14483
14685
  const create = async (name, attempt) => {
14484
14686
  mkdirSync10(socketRoot, { recursive: true, mode: 448 });
14485
14687
  mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
@@ -14621,7 +14823,7 @@ function createDockerProvider(opts) {
14621
14823
  }
14622
14824
  };
14623
14825
  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`);
14826
+ const pendingPath = (name) => join18(opts.deviceDir, "runners-pending", `${name}.json`);
14625
14827
  const readPending = (name) => {
14626
14828
  try {
14627
14829
  const ids = JSON.parse(readFileSync9(pendingPath(name), "utf8"));
@@ -14635,12 +14837,12 @@ function createDockerProvider(opts) {
14635
14837
  rmSync9(pendingPath(name), { force: true });
14636
14838
  return;
14637
14839
  }
14638
- mkdirSync10(join17(opts.deviceDir, "runners-pending"), { recursive: true });
14840
+ mkdirSync10(join18(opts.deviceDir, "runners-pending"), { recursive: true });
14639
14841
  writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
14640
14842
  };
14641
14843
  const keepSession = async (name, attempt, attemptId) => {
14642
14844
  const archive = sessionArchivePath(opts.deviceDir, attemptId);
14643
- mkdirSync10(join17(archive, ".."), { recursive: true });
14845
+ mkdirSync10(join18(archive, ".."), { recursive: true });
14644
14846
  const partial = `${archive}.${process.pid}.partial`;
14645
14847
  const file = new CappedFile(partial, sessionCap);
14646
14848
  const r = await execIn(
@@ -14657,7 +14859,7 @@ function createDockerProvider(opts) {
14657
14859
  );
14658
14860
  if (r.code === 0 && !file.over) {
14659
14861
  renameSync5(partial, archive);
14660
- writeFileSync6(join17(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
14862
+ writeFileSync6(join18(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
14661
14863
  pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
14662
14864
  await writeMarker(name, attemptId);
14663
14865
  log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
@@ -14671,7 +14873,7 @@ function createDockerProvider(opts) {
14671
14873
  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
14874
  );
14673
14875
  };
14674
- const stagingDir = (name) => join17(opts.deviceDir, "runners-pending", `${name}.outbox`);
14876
+ const stagingDir = (name) => join18(opts.deviceDir, "runners-pending", `${name}.outbox`);
14675
14877
  const ensureRunning = async (name) => {
14676
14878
  const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
14677
14879
  if (seen.code !== 0) return false;
@@ -14679,7 +14881,7 @@ function createDockerProvider(opts) {
14679
14881
  return (await docker.run(["start", name])).code === 0;
14680
14882
  };
14681
14883
  const dropArchive = (attemptId) => {
14682
- rmSync9(join17(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
14884
+ rmSync9(join18(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
14683
14885
  recursive: true,
14684
14886
  force: true
14685
14887
  });
@@ -14748,7 +14950,7 @@ function createDockerProvider(opts) {
14748
14950
  if (seen.stdout.trim().split(" ")[0] !== "true")
14749
14951
  await must(["start", name], "starting the container");
14750
14952
  const git = await serveGitCredentials({
14751
- socketPath: join17(socketDir(name), "git.sock"),
14953
+ socketPath: join18(socketDir(name), "git.sock"),
14752
14954
  hosts: opts.gitHosts,
14753
14955
  fill: opts.gitFill,
14754
14956
  log
@@ -14897,15 +15099,15 @@ function createDockerProvider(opts) {
14897
15099
  const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
14898
15100
  if (transcripts) {
14899
15101
  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);
15102
+ if (kept.length) mkdirSync10(join18(opts.hostStateDir, "transcripts"), { recursive: true });
15103
+ for (const f of kept) writeFileSync6(join18(opts.hostStateDir, "transcripts", f.name), f.data);
14902
15104
  await removeIn(
14903
15105
  name,
14904
15106
  `${CONTAINER_STATE}/transcripts`,
14905
15107
  kept.map((f) => f.name)
14906
15108
  );
14907
15109
  }
14908
- const outboxRel = join17("outbox", encodeURIComponent(attempt.agentId));
15110
+ const outboxRel = join18("outbox", encodeURIComponent(attempt.agentId));
14909
15111
  const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
14910
15112
  let unpublished = false;
14911
15113
  if (outbox) {
@@ -14931,7 +15133,7 @@ function createDockerProvider(opts) {
14931
15133
  const staged = stagingDir(name);
14932
15134
  mkdirSync10(staged, { recursive: true });
14933
15135
  writeFileSync6(
14934
- join17(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
15136
+ join18(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
14935
15137
  `${JSON.stringify(entry)}
14936
15138
  `
14937
15139
  );
@@ -14944,7 +15146,7 @@ function createDockerProvider(opts) {
14944
15146
  if (outbox.complete) {
14945
15147
  const published = publishStaged(
14946
15148
  stagingDir(name),
14947
- join17(opts.hostStateDir, outboxRel),
15149
+ join18(opts.hostStateDir, outboxRel),
14948
15150
  opts.moveFile
14949
15151
  );
14950
15152
  queued = published.published;
@@ -16774,8 +16976,8 @@ function errorCode(err) {
16774
16976
  function isRecoverableSocketError(err) {
16775
16977
  const code = errorCode(err);
16776
16978
  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);
16979
+ const message2 = err instanceof Error ? err.message : String(err);
16980
+ return /\bEPIPE\b|\bECONNRESET\b/.test(message2);
16779
16981
  }
16780
16982
  function installProcessSafetyNet(log, opts = {}) {
16781
16983
  const proc = opts.proc ?? process;
@@ -16790,17 +16992,17 @@ function installProcessSafetyNet(log, opts = {}) {
16790
16992
  );
16791
16993
  }
16792
16994
  function handleUncaught(log, err, origin) {
16793
- const message = err instanceof Error ? err.message : String(err);
16995
+ const message2 = err instanceof Error ? err.message : String(err);
16794
16996
  const code = errorCode(err);
16795
16997
  if (isRecoverableSocketError(err)) {
16796
16998
  log.debug(
16797
- { origin, code, err: message },
16999
+ { origin, code, err: message2 },
16798
17000
  "companion: recovered from a socket error (kept running \u2014 a broken pipe never kills the companion)"
16799
17001
  );
16800
17002
  return;
16801
17003
  }
16802
17004
  log.error(
16803
- { origin, code, err: message, stack: err instanceof Error ? err.stack : void 0 },
17005
+ { origin, code, err: message2, stack: err instanceof Error ? err.stack : void 0 },
16804
17006
  "An unexpected error occurred; the companion is still running."
16805
17007
  );
16806
17008
  }
@@ -16808,9 +17010,9 @@ function handleUncaught(log, err, origin) {
16808
17010
  // src/crash-marker.ts
16809
17011
  init_config();
16810
17012
  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";
17013
+ import { join as join21 } from "path";
16812
17014
  function crashMarkerPath() {
16813
- return join20(cabaneDir(), "last-error.json");
17015
+ return join21(cabaneDir(), "last-error.json");
16814
17016
  }
16815
17017
  function recordCrash(rec2) {
16816
17018
  try {
@@ -17161,7 +17363,7 @@ async function startScript(opts, interactive) {
17161
17363
  const result = await createCompanionRuntime({
17162
17364
  // The script says what's connectable in its own words below; a stderr warning
17163
17365
  // in the middle of it would be the same news, worse.
17164
- onReadinessWarning: (message) => log.warn(message)
17366
+ onReadinessWarning: (message2) => log.warn(message2)
17165
17367
  });
17166
17368
  if (!result.ok) {
17167
17369
  reportAlreadyRunning(result.existing?.pid);
@@ -17566,7 +17768,7 @@ init_errors();
17566
17768
  init_src();
17567
17769
  init_transcript_writer();
17568
17770
  import { existsSync as existsSync17, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
17569
- import { isAbsolute, join as join21 } from "path";
17771
+ import { isAbsolute, join as join22 } from "path";
17570
17772
  async function transcript(opts = {}) {
17571
17773
  const dir2 = transcriptsDir();
17572
17774
  if (opts.follow) {
@@ -17583,7 +17785,7 @@ async function transcript(opts = {}) {
17583
17785
  process.stdout.write(emptyMessage(dir2));
17584
17786
  return;
17585
17787
  }
17586
- process.stdout.write(renderFile(join21(dir2, newest)) + "\n");
17788
+ process.stdout.write(renderFile(join22(dir2, newest)) + "\n");
17587
17789
  return;
17588
17790
  }
17589
17791
  printList(dir2);
@@ -17666,7 +17868,7 @@ function isComplete(content) {
17666
17868
  async function followTranscripts(dir2) {
17667
17869
  const follower = new TranscriptFollower({
17668
17870
  listFiles: () => listFiles(dir2),
17669
- read: (f) => readFileSync13(join21(dir2, f), "utf8"),
17871
+ read: (f) => readFileSync13(join22(dir2, f), "utf8"),
17670
17872
  write: (s) => process.stdout.write(s),
17671
17873
  // CSI: cursor up `n` lines, then erase from cursor to end of screen.
17672
17874
  clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
@@ -17706,7 +17908,7 @@ function printList(dir2) {
17706
17908
 
17707
17909
  `);
17708
17910
  for (const f of files.slice(0, 20)) {
17709
- const { meta, outcome } = peek(join21(dir2, f));
17911
+ const { meta, outcome } = peek(join22(dir2, f));
17710
17912
  const when = fmtTime(rec(meta)?.ts);
17711
17913
  const ws = str2(rec(meta)?.workspaceSlug);
17712
17914
  const o = rec(outcome);
@@ -17743,10 +17945,10 @@ function resolveTarget(dir2, target) {
17743
17945
  if (existsSync17(target)) return target;
17744
17946
  throw new CompanionError(`no transcript at ${target}.`);
17745
17947
  }
17746
- const exact = join21(dir2, target);
17948
+ const exact = join22(dir2, target);
17747
17949
  if (existsSync17(exact)) return exact;
17748
17950
  const matches = listFiles(dir2).filter((f) => f.includes(target));
17749
- if (matches.length === 1) return join21(dir2, matches[0]);
17951
+ if (matches.length === 1) return join22(dir2, matches[0]);
17750
17952
  if (matches.length === 0) {
17751
17953
  throw new CompanionError(
17752
17954
  `no transcript matching "${target}" in ${dir2}. Run \`cabane-companion transcript\` to list them.`