@cabane/companion 0.6.169 → 0.6.170

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 +353 -153
  2. package/dist/runtime.js +324 -139
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2084,9 +2084,15 @@ var init_turn_request = __esm({
2084
2084
  resolvedMcpServersSchema = z9.record(z9.string(), resolvedMcpServerSchema);
2085
2085
  hostInjectedServersSchema = z9.record(z9.string(), z9.unknown());
2086
2086
  turnRequestSchema = z9.object({
2087
- // Server-composed system prompt (core + capability prose + adapter addendum +
2088
- // charter). One string to the adapter.
2087
+ // Server-composed standing instructions: the core segment (body, tool naming, identity, agent
2088
+ // charter) and, after it, the conversation segment (workspace and room charters, skill indexes,
2089
+ // roster). One string to the adapter, current as of this dispatch.
2089
2090
  systemPrompt: z9.string(),
2091
+ // The same text as its two ordered segments, so an adapter that writes standing text into the
2092
+ // conversation (Codex's input preamble) can tell which half changed. `systemPrompt` is
2093
+ // `joinStanding(standing)`. Optional: absent from an API older than the split, where
2094
+ // `systemPrompt` is the core alone and the conversation material rides `prompt`.
2095
+ standing: z9.object({ core: z9.string(), conversation: z9.string() }).optional(),
2090
2096
  // Server-composed per-turn user text (anchor reminder + the triggering message).
2091
2097
  prompt: z9.string(),
2092
2098
  // The multi-block user-message body (text + vision).
@@ -2136,6 +2142,22 @@ var init_turn_request = __esm({
2136
2142
  }
2137
2143
  });
2138
2144
 
2145
+ // packages/agent-runtime/src/standing.ts
2146
+ function joinStanding(segments) {
2147
+ const conversation = segments.conversation.trim();
2148
+ return conversation.length > 0 ? `${segments.core}${STANDING_SEPARATOR}${conversation}` : segments.core;
2149
+ }
2150
+ function standingSegments(req) {
2151
+ return req.standing ?? { core: req.systemPrompt, conversation: "" };
2152
+ }
2153
+ var STANDING_SEPARATOR;
2154
+ var init_standing = __esm({
2155
+ "packages/agent-runtime/src/standing.ts"() {
2156
+ "use strict";
2157
+ STANDING_SEPARATOR = "\n\n---\n\n";
2158
+ }
2159
+ });
2160
+
2139
2161
  // packages/agent-runtime/src/conformance.ts
2140
2162
  import { z as z10 } from "zod";
2141
2163
  var conformanceFixtureSchema;
@@ -5374,7 +5396,14 @@ function fingerprintPrompt(prompt) {
5374
5396
  return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
5375
5397
  }
5376
5398
  function encodeSession3(state) {
5377
- return JSON.stringify(state);
5399
+ return JSON.stringify({
5400
+ threadId: state.threadId,
5401
+ cwd: state.cwd,
5402
+ promptFingerprint: state.promptFingerprint,
5403
+ conversationFingerprint: state.conversationFingerprint ?? null,
5404
+ compactions: state.compactions ?? null,
5405
+ usageBaseline: state.usageBaseline
5406
+ });
5378
5407
  }
5379
5408
  function decideResume3(stored, currentCwd) {
5380
5409
  if (!stored) return { fresh: true, reason: "no_session" };
@@ -5395,6 +5424,8 @@ function decideResume3(stored, currentCwd) {
5395
5424
  return {
5396
5425
  resume: state.threadId,
5397
5426
  promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
5427
+ conversationFingerprint: typeof state.conversationFingerprint === "string" ? state.conversationFingerprint : null,
5428
+ compactions: typeof state.compactions === "number" && Number.isInteger(state.compactions) && state.compactions >= 0 ? state.compactions : null,
5398
5429
  usageBaseline: readUsageBaseline(state.usageBaseline)
5399
5430
  };
5400
5431
  }
@@ -5432,6 +5463,7 @@ async function* decodeCodexStream(events, ctx2) {
5432
5463
  let usage;
5433
5464
  let cumulativeUsage;
5434
5465
  let threadIdForSession = ctx2.resumedThreadId;
5466
+ let lastState = ctx2.storedState ?? null;
5435
5467
  let settled = false;
5436
5468
  function* flushInterim() {
5437
5469
  const sealed = sealHeld2(held, false);
@@ -5447,14 +5479,14 @@ async function* decodeCodexStream(events, ctx2) {
5447
5479
  const state = encodeSession3({
5448
5480
  threadId,
5449
5481
  cwd: ctx2.cwd ?? "",
5450
- promptFingerprint: ctx2.promptFingerprint ?? null,
5451
- // The baseline this turn inherited, carried through unchanged. This early write records the
5452
- // thread id even if the turn dies mid-stream; it can't yet know what the turn will spend. The
5453
- // closing write records the new baseline.
5482
+ promptFingerprint: ctx2.heldStanding?.promptFingerprint ?? null,
5483
+ conversationFingerprint: ctx2.heldStanding?.conversationFingerprint ?? null,
5484
+ compactions: ctx2.heldStanding?.compactions ?? null,
5454
5485
  usageBaseline: ctx2.usageBaseline ?? null
5455
5486
  });
5456
5487
  if (threadId !== ctx2.resumedThreadId || state !== ctx2.storedState) {
5457
5488
  sessionEmitted = true;
5489
+ lastState = state;
5458
5490
  yield {
5459
5491
  type: "session",
5460
5492
  state,
@@ -5553,14 +5585,16 @@ async function* decodeCodexStream(events, ctx2) {
5553
5585
  if (sealed) yield sealed;
5554
5586
  reason = "no_terminal";
5555
5587
  }
5556
- if (ok && cumulativeUsage && threadIdForSession) {
5588
+ if (ok && threadIdForSession) {
5557
5589
  const closing = encodeSession3({
5558
5590
  threadId: threadIdForSession,
5559
5591
  cwd: ctx2.cwd ?? "",
5560
5592
  promptFingerprint: ctx2.promptFingerprint ?? null,
5561
- usageBaseline: cumulativeUsage
5593
+ conversationFingerprint: ctx2.conversationFingerprint ?? null,
5594
+ compactions: ctx2.compactions ?? null,
5595
+ usageBaseline: cumulativeUsage ?? ctx2.usageBaseline ?? null
5562
5596
  });
5563
- yield { type: "session", state: closing };
5597
+ if (closing !== lastState) yield { type: "session", state: closing };
5564
5598
  }
5565
5599
  const resolvedConfig = ctx2.resolvedReasoningEffort ? { reasoningEffort: ctx2.resolvedReasoningEffort } : void 0;
5566
5600
  yield {
@@ -5640,15 +5674,27 @@ var init_model3 = __esm({
5640
5674
  });
5641
5675
 
5642
5676
  // packages/agent-runtime/src/codex/run-spec.ts
5643
- function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null, imagePaths = []) {
5677
+ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadStanding = null, imagePaths = []) {
5644
5678
  const { policy, config } = req;
5645
5679
  const directory = req.local.cwd ?? "";
5646
5680
  const dialect = readCodexDialect(config.runtimeOptions);
5647
5681
  const baseInstructionsFile = !policy.hostFs && instructionsFile ? instructionsFile : null;
5648
5682
  const model = config.model ? parseCodexModel(config.model) : null;
5649
- const promptFingerprint = fingerprintPrompt(req.systemPrompt);
5650
- const threadHasThisPrompt = resumeThreadId !== null && threadPromptFingerprint !== null && threadPromptFingerprint === promptFingerprint;
5651
- const promptRidesInput = baseInstructionsFile === null && !threadHasThisPrompt;
5683
+ const segments = standingSegments(req);
5684
+ const conversationText = segments.conversation.trim();
5685
+ const fingerprints = {
5686
+ core: fingerprintPrompt(segments.core),
5687
+ conversation: conversationText.length > 0 ? fingerprintPrompt(conversationText) : null
5688
+ };
5689
+ const resumed = resumeThreadId !== null;
5690
+ const held = resumed ? threadStanding : null;
5691
+ const viaInput = baseInstructionsFile === null;
5692
+ const preamble = {
5693
+ core: viaInput && (held?.core ?? null) !== fingerprints.core,
5694
+ // Compared both ways: a held segment that is now empty differs from it, and nothing held
5695
+ // equals nothing to send.
5696
+ conversation: viaInput && (held?.conversation ?? null) !== fingerprints.conversation
5697
+ };
5652
5698
  return {
5653
5699
  resumeThreadId,
5654
5700
  directory,
@@ -5657,9 +5703,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
5657
5703
  skipGitRepoCheck: true,
5658
5704
  ...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
5659
5705
  baseInstructionsFile,
5660
- input: withImages(buildInput(req, promptRidesInput), imagePaths),
5661
- promptRidesInput,
5662
- promptFingerprint,
5706
+ input: withImages(buildInput(req, segments, preamble, resumed), imagePaths),
5707
+ preamble,
5708
+ fingerprints,
5663
5709
  config: buildConfig(req, baseInstructionsFile),
5664
5710
  secretEnv: codexSecretEnv(req)
5665
5711
  };
@@ -5675,12 +5721,24 @@ function codexSecretEnv(req) {
5675
5721
  }
5676
5722
  return Object.keys(env).length > 0 ? env : null;
5677
5723
  }
5678
- function buildInput(req, promptRidesInput) {
5724
+ function buildInput(req, segments, preamble, resumed) {
5679
5725
  const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
5680
5726
  const body = userText.trim().length > 0 ? userText : req.prompt;
5681
- if (!promptRidesInput) return body;
5682
- const system = req.systemPrompt.trim();
5683
- return system.length > 0 ? `${system}
5727
+ const standing = [];
5728
+ if (!resumed) {
5729
+ if (preamble.core) standing.push(joinStanding(segments).trim());
5730
+ } else {
5731
+ if (preamble.core) standing.push(`${CORE_REPLACES}
5732
+
5733
+ ${segments.core.trim()}`);
5734
+ if (preamble.conversation) {
5735
+ standing.push(`${CONVERSATION_REPLACES}
5736
+
5737
+ ${segments.conversation.trim()}`.trim());
5738
+ }
5739
+ }
5740
+ const lead = standing.filter((s) => s.length > 0);
5741
+ return lead.length > 0 ? `${lead.join("\n\n---\n\n")}
5684
5742
 
5685
5743
  ${body}` : body;
5686
5744
  }
@@ -5766,10 +5824,11 @@ function buildConfig(req, baseInstructionsFile) {
5766
5824
  function isStringRecord2(v) {
5767
5825
  return !!v && typeof v === "object" && Object.values(v).every((x) => typeof x === "string");
5768
5826
  }
5769
- var CABANE_MCP_SERVER3, TURN_CONTROL_MCP_SERVER2, ACTIVE_CONVERSATION_HEADER3, ENV_ENVELOPE_KEYS;
5827
+ var CABANE_MCP_SERVER3, TURN_CONTROL_MCP_SERVER2, ACTIVE_CONVERSATION_HEADER3, ENV_ENVELOPE_KEYS, CORE_REPLACES, CONVERSATION_REPLACES;
5770
5828
  var init_run_spec2 = __esm({
5771
5829
  "packages/agent-runtime/src/codex/run-spec.ts"() {
5772
5830
  "use strict";
5831
+ init_standing();
5773
5832
  init_policy3();
5774
5833
  init_model3();
5775
5834
  init_session3();
@@ -5784,6 +5843,8 @@ var init_run_spec2 = __esm({
5784
5843
  "CABANE_CONVERSATION_TITLE",
5785
5844
  "CABANE_HOST_ACCESS"
5786
5845
  ];
5846
+ CORE_REPLACES = "Your standing instructions changed since an earlier copy in this conversation, or that copy may have been compacted away. This copy replaces every earlier one: where they differ, this one holds, and anything an earlier copy said that this one does not no longer applies.";
5847
+ CONVERSATION_REPLACES = "This conversation's standing context \u2014 its charters, skill indexes and roster \u2014 changed since an earlier copy in this conversation, or that copy may have been compacted away. This copy replaces every earlier one, including charters and skills an earlier turn carried in its own text: where they differ, this one holds, and anything an earlier copy said that this one does not no longer applies.";
5787
5848
  }
5788
5849
  });
5789
5850
 
@@ -5922,7 +5983,6 @@ function createCodexAdapter(deps = {}) {
5922
5983
  const directory = req.local.cwd ?? "";
5923
5984
  const decision = decideResume3(req.session, directory);
5924
5985
  const resumeThreadId = "resume" in decision ? decision.resume : null;
5925
- const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
5926
5986
  const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
5927
5987
  if ("fresh" in decision && decision.reason === "cwd_mismatch") {
5928
5988
  deps.onWarn?.(
@@ -5939,6 +5999,32 @@ function createCodexAdapter(deps = {}) {
5939
5999
  degraded = false;
5940
6000
  }
5941
6001
  }
6002
+ let compactions = 0;
6003
+ let threadStanding = null;
6004
+ if ("resume" in decision) {
6005
+ compactions = deps.countCompactions ? await deps.countCompactions(decision.resume) : null;
6006
+ if (signal.aborted) return;
6007
+ if (compactions === null) {
6008
+ deps.onWarn?.(
6009
+ "codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions (ENG210)",
6010
+ { threadId: decision.resume }
6011
+ );
6012
+ } else if (decision.compactions === compactions) {
6013
+ threadStanding = {
6014
+ core: decision.promptFingerprint,
6015
+ conversation: decision.conversationFingerprint
6016
+ };
6017
+ } else if (decision.compactions !== null && compactions > decision.compactions) {
6018
+ deps.onWarn?.(
6019
+ "codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions (ENG210)",
6020
+ {
6021
+ threadId: decision.resume,
6022
+ compactions,
6023
+ compactionsSince: compactions - decision.compactions
6024
+ }
6025
+ );
6026
+ }
6027
+ }
5942
6028
  let instructions = null;
5943
6029
  if (!req.policy.hostFs) {
5944
6030
  if (!deps.writeInstructionsFile) {
@@ -5983,7 +6069,7 @@ function createCodexAdapter(deps = {}) {
5983
6069
  req,
5984
6070
  resumeThreadId,
5985
6071
  instructions?.path ?? null,
5986
- threadPromptFingerprint,
6072
+ threadStanding,
5987
6073
  images?.paths ?? []
5988
6074
  );
5989
6075
  if (spec.model === null) {
@@ -5998,10 +6084,18 @@ function createCodexAdapter(deps = {}) {
5998
6084
  cwd: req.local.cwd,
5999
6085
  resumedThreadId: resumeThreadId,
6000
6086
  degraded,
6001
- // Which prompt the thread carries once this turn lands. If we sent one, it's that one — the
6002
- // later copy is what the model reads. If we withheld it, the thread carries what it did. Read
6003
- // off the spec, so the record can't disagree with what was sent.
6004
- promptFingerprint: spec.promptRidesInput ? spec.promptFingerprint : threadPromptFingerprint,
6087
+ // Which segments the thread carries once this turn lands. A segment sent is that one —
6088
+ // the later copy is what the model reads. One withheld is what the thread had. Read off
6089
+ // the spec, so the record can't disagree with what was sent.
6090
+ promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
6091
+ conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
6092
+ compactions,
6093
+ // What the thread held before this turn, for the write at `thread.started`.
6094
+ heldStanding: {
6095
+ promptFingerprint: threadStanding?.core ?? null,
6096
+ conversationFingerprint: threadStanding?.conversation ?? null,
6097
+ compactions: threadStanding ? compactions : null
6098
+ },
6005
6099
  storedState: req.session,
6006
6100
  usageBaseline: threadUsageBaseline,
6007
6101
  // Codex reports no model in-stream, so record what the thread was started with (null on the
@@ -6072,7 +6166,7 @@ function makeRequest3(overrides2 = {}) {
6072
6166
  ...overrides2
6073
6167
  };
6074
6168
  }
6075
- var ABORT_SENTINEL3, NEW_THREAD_ID, BOUNDED_NOTICE2, COMPANION_POLICY2, DIR2, PROMPT, threadStarted, agentMessage, reasoning, toolFrame, turnCompleted, turnFailed, fatalError, errorItem, OP_SUCCESS, OP_FAILURE, sessionEvent3, CODEX_CONFORMANCE_FIXTURES;
6169
+ var ABORT_SENTINEL3, NEW_THREAD_ID, BOUNDED_NOTICE2, COMPANION_POLICY2, DIR2, PROMPT, threadStarted, agentMessage, reasoning, toolFrame, turnCompleted, turnFailed, fatalError, errorItem, OP_SUCCESS, OP_FAILURE, startedSession, closingSession, CODEX_CONFORMANCE_FIXTURES;
6076
6170
  var init_conformance4 = __esm({
6077
6171
  "packages/agent-runtime/src/codex/conformance.ts"() {
6078
6172
  "use strict";
@@ -6127,11 +6221,21 @@ var init_conformance4 = __esm({
6127
6221
  message: "files.read returned more than the result ceiling"
6128
6222
  }
6129
6223
  };
6130
- sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT), usageBaseline = null) => ({
6224
+ startedSession = (threadId, cwd = DIR2, degraded = false) => ({
6131
6225
  type: "session",
6132
- state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
6226
+ state: encodeSession3({ threadId, cwd, promptFingerprint: null, usageBaseline: null }),
6133
6227
  ...degraded ? { degraded: true } : {}
6134
6228
  });
6229
+ closingSession = (threadId, usageBaseline = null) => ({
6230
+ type: "session",
6231
+ state: encodeSession3({
6232
+ threadId,
6233
+ cwd: DIR2,
6234
+ promptFingerprint: fingerprintPrompt(PROMPT),
6235
+ usageBaseline,
6236
+ compactions: 0
6237
+ })
6238
+ });
6135
6239
  CODEX_CONFORMANCE_FIXTURES = [
6136
6240
  {
6137
6241
  // A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
@@ -6154,11 +6258,11 @@ var init_conformance4 = __esm({
6154
6258
  })
6155
6259
  ],
6156
6260
  expected: [
6157
- sessionEvent3(NEW_THREAD_ID),
6261
+ startedSession(NEW_THREAD_ID),
6158
6262
  { type: "text", body: "Hello there.", terminal: true },
6159
6263
  // The closing session write — the thread's ending totals, recorded as the baseline the next turn
6160
6264
  // subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
6161
- sessionEvent3(NEW_THREAD_ID, DIR2, false, fingerprintPrompt(PROMPT), {
6265
+ closingSession(NEW_THREAD_ID, {
6162
6266
  inputTokens: 100,
6163
6267
  outputTokens: 50,
6164
6268
  cacheReadTokens: 20,
@@ -6205,7 +6309,7 @@ var init_conformance4 = __esm({
6205
6309
  turnCompleted()
6206
6310
  ],
6207
6311
  expected: [
6208
- sessionEvent3(NEW_THREAD_ID),
6312
+ startedSession(NEW_THREAD_ID),
6209
6313
  { type: "text", body: "Reading your notes.", terminal: false },
6210
6314
  {
6211
6315
  type: "tool",
@@ -6225,6 +6329,7 @@ var init_conformance4 = __esm({
6225
6329
  result: [{ type: "text", text: "sleep early" }]
6226
6330
  },
6227
6331
  { type: "text", body: "Here's the summary.", terminal: true },
6332
+ closingSession(NEW_THREAD_ID),
6228
6333
  { type: "result", ok: true }
6229
6334
  ]
6230
6335
  },
@@ -6258,7 +6363,7 @@ var init_conformance4 = __esm({
6258
6363
  turnCompleted()
6259
6364
  ],
6260
6365
  expected: [
6261
- sessionEvent3(NEW_THREAD_ID),
6366
+ startedSession(NEW_THREAD_ID),
6262
6367
  {
6263
6368
  type: "tool",
6264
6369
  id: "query-success",
@@ -6276,6 +6381,7 @@ var init_conformance4 = __esm({
6276
6381
  input: { op: "files.read", args: { path: "/ABOUT.md" } },
6277
6382
  result: OP_SUCCESS
6278
6383
  },
6384
+ closingSession(NEW_THREAD_ID),
6279
6385
  { type: "result", ok: true }
6280
6386
  ]
6281
6387
  },
@@ -6311,7 +6417,7 @@ var init_conformance4 = __esm({
6311
6417
  turnCompleted()
6312
6418
  ],
6313
6419
  expected: [
6314
- sessionEvent3(NEW_THREAD_ID),
6420
+ startedSession(NEW_THREAD_ID),
6315
6421
  {
6316
6422
  type: "tool",
6317
6423
  id: "query-failed",
@@ -6329,6 +6435,7 @@ var init_conformance4 = __esm({
6329
6435
  input: { op: "files.read", args: { path: "/ABOUT.md" } },
6330
6436
  result: OP_FAILURE
6331
6437
  },
6438
+ closingSession(NEW_THREAD_ID),
6332
6439
  { type: "result", ok: true }
6333
6440
  ]
6334
6441
  },
@@ -6352,7 +6459,7 @@ var init_conformance4 = __esm({
6352
6459
  turnCompleted()
6353
6460
  ],
6354
6461
  expected: [
6355
- sessionEvent3(NEW_THREAD_ID),
6462
+ startedSession(NEW_THREAD_ID),
6356
6463
  {
6357
6464
  type: "tool",
6358
6465
  id: "mcp-failed",
@@ -6370,6 +6477,7 @@ var init_conformance4 = __esm({
6370
6477
  input: { q: "receipt" },
6371
6478
  result: "connection closed"
6372
6479
  },
6480
+ closingSession(NEW_THREAD_ID),
6373
6481
  { type: "result", ok: true }
6374
6482
  ]
6375
6483
  },
@@ -6395,7 +6503,7 @@ var init_conformance4 = __esm({
6395
6503
  turnCompleted()
6396
6504
  ],
6397
6505
  expected: [
6398
- sessionEvent3(NEW_THREAD_ID),
6506
+ startedSession(NEW_THREAD_ID),
6399
6507
  {
6400
6508
  type: "tool",
6401
6509
  id: "mcp-image",
@@ -6413,6 +6521,7 @@ var init_conformance4 = __esm({
6413
6521
  input: {},
6414
6522
  result: [{ type: "image", data: "base64-bytes", mimeType: "image/png" }]
6415
6523
  },
6524
+ closingSession(NEW_THREAD_ID),
6416
6525
  { type: "result", ok: true }
6417
6526
  ]
6418
6527
  },
@@ -6453,7 +6562,7 @@ var init_conformance4 = __esm({
6453
6562
  turnCompleted()
6454
6563
  ],
6455
6564
  expected: [
6456
- sessionEvent3(NEW_THREAD_ID),
6565
+ startedSession(NEW_THREAD_ID),
6457
6566
  { type: "tool", id: "t1", name: "list", phase: "start", summary: "", input: {} },
6458
6567
  {
6459
6568
  type: "tool",
@@ -6464,6 +6573,7 @@ var init_conformance4 = __esm({
6464
6573
  input: { path: "/product/tasks" },
6465
6574
  result: [{ type: "text", text: "CT481.md" }]
6466
6575
  },
6576
+ closingSession(NEW_THREAD_ID),
6467
6577
  { type: "result", ok: true }
6468
6578
  ]
6469
6579
  },
@@ -6481,7 +6591,7 @@ var init_conformance4 = __esm({
6481
6591
  turnCompleted()
6482
6592
  ],
6483
6593
  expected: [
6484
- sessionEvent3(NEW_THREAD_ID),
6594
+ startedSession(NEW_THREAD_ID),
6485
6595
  {
6486
6596
  type: "tool",
6487
6597
  id: "ws1",
@@ -6498,6 +6608,7 @@ var init_conformance4 = __esm({
6498
6608
  summary: "best pizza in nyc",
6499
6609
  input: { query: "best pizza in nyc" }
6500
6610
  },
6611
+ closingSession(NEW_THREAD_ID),
6501
6612
  { type: "result", ok: true }
6502
6613
  ]
6503
6614
  },
@@ -6513,7 +6624,7 @@ var init_conformance4 = __esm({
6513
6624
  turnCompleted()
6514
6625
  ],
6515
6626
  expected: [
6516
- sessionEvent3(NEW_THREAD_ID),
6627
+ startedSession(NEW_THREAD_ID),
6517
6628
  {
6518
6629
  type: "tool",
6519
6630
  id: "ws2",
@@ -6530,6 +6641,7 @@ var init_conformance4 = __esm({
6530
6641
  summary: "",
6531
6642
  input: { query: "" }
6532
6643
  },
6644
+ closingSession(NEW_THREAD_ID),
6533
6645
  { type: "result", ok: true }
6534
6646
  ]
6535
6647
  },
@@ -6560,7 +6672,7 @@ var init_conformance4 = __esm({
6560
6672
  turnCompleted()
6561
6673
  ],
6562
6674
  expected: [
6563
- sessionEvent3(NEW_THREAD_ID),
6675
+ startedSession(NEW_THREAD_ID),
6564
6676
  { type: "text", body: "Working on it.", terminal: false },
6565
6677
  {
6566
6678
  type: "tool",
@@ -6592,7 +6704,7 @@ var init_conformance4 = __esm({
6592
6704
  turnFailed("provider exploded")
6593
6705
  ],
6594
6706
  expected: [
6595
- sessionEvent3(NEW_THREAD_ID),
6707
+ startedSession(NEW_THREAD_ID),
6596
6708
  { type: "text", body: "Partial work.", terminal: false },
6597
6709
  { type: "runtime_notice", body: "provider exploded" },
6598
6710
  { type: "result", ok: false, reason: "error:provider exploded" }
@@ -6609,7 +6721,7 @@ var init_conformance4 = __esm({
6609
6721
  fatalError("stream died")
6610
6722
  ],
6611
6723
  expected: [
6612
- sessionEvent3(NEW_THREAD_ID),
6724
+ startedSession(NEW_THREAD_ID),
6613
6725
  { type: "text", body: "Starting.", terminal: false },
6614
6726
  { type: "runtime_notice", body: "stream died" },
6615
6727
  { type: "result", ok: false, reason: "error:stream died" }
@@ -6622,7 +6734,7 @@ var init_conformance4 = __esm({
6622
6734
  request: makeRequest3(),
6623
6735
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
6624
6736
  expected: [
6625
- sessionEvent3(NEW_THREAD_ID),
6737
+ startedSession(NEW_THREAD_ID),
6626
6738
  { type: "runtime_notice", body: "429 Too Many Requests" },
6627
6739
  { type: "result", ok: false, reason: "rate_limited" }
6628
6740
  ]
@@ -6632,7 +6744,7 @@ var init_conformance4 = __esm({
6632
6744
  request: makeRequest3(),
6633
6745
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("503 Service Unavailable")],
6634
6746
  expected: [
6635
- sessionEvent3(NEW_THREAD_ID),
6747
+ startedSession(NEW_THREAD_ID),
6636
6748
  { type: "runtime_notice", body: "503 Service Unavailable" },
6637
6749
  { type: "result", ok: false, reason: "server_error" }
6638
6750
  ]
@@ -6645,7 +6757,7 @@ var init_conformance4 = __esm({
6645
6757
  turnFailed("You've hit your session limit \xB7 resets 8:20pm (UTC)")
6646
6758
  ],
6647
6759
  expected: [
6648
- sessionEvent3(NEW_THREAD_ID),
6760
+ startedSession(NEW_THREAD_ID),
6649
6761
  {
6650
6762
  type: "runtime_notice",
6651
6763
  body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
@@ -6658,7 +6770,7 @@ var init_conformance4 = __esm({
6658
6770
  request: makeRequest3(),
6659
6771
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("401 Unauthorized: invalid api key")],
6660
6772
  expected: [
6661
- sessionEvent3(NEW_THREAD_ID),
6773
+ startedSession(NEW_THREAD_ID),
6662
6774
  { type: "runtime_notice", body: "401 Unauthorized: invalid api key" },
6663
6775
  { type: "result", ok: false, reason: "auth_expired" }
6664
6776
  ]
@@ -6670,7 +6782,7 @@ var init_conformance4 = __esm({
6670
6782
  request: makeRequest3(),
6671
6783
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
6672
6784
  expected: [
6673
- sessionEvent3(NEW_THREAD_ID),
6785
+ startedSession(NEW_THREAD_ID),
6674
6786
  { type: "runtime_notice", body: "something local broke" },
6675
6787
  { type: "result", ok: false, reason: "error:something local broke" }
6676
6788
  ]
@@ -6683,7 +6795,7 @@ var init_conformance4 = __esm({
6683
6795
  turnFailed(` ${BOUNDED_NOTICE2}${"y".repeat(20)} `)
6684
6796
  ],
6685
6797
  expected: [
6686
- sessionEvent3(NEW_THREAD_ID),
6798
+ startedSession(NEW_THREAD_ID),
6687
6799
  { type: "runtime_notice", body: BOUNDED_NOTICE2 },
6688
6800
  { type: "result", ok: false, reason: "usage_capped" }
6689
6801
  ]
@@ -6694,26 +6806,32 @@ var init_conformance4 = __esm({
6694
6806
  name: "empty failure text emits no runtime notice",
6695
6807
  request: makeRequest3(),
6696
6808
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
6697
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: false, reason: "error:unknown" }]
6809
+ expected: [
6810
+ startedSession(NEW_THREAD_ID),
6811
+ { type: "result", ok: false, reason: "error:unknown" }
6812
+ ]
6698
6813
  },
6699
6814
  {
6700
6815
  name: "whitespace-only failure text emits no runtime notice",
6701
6816
  request: makeRequest3(),
6702
6817
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed(" ")],
6703
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
6818
+ expected: [startedSession(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
6704
6819
  },
6705
6820
  {
6706
6821
  name: "bare status token emits no runtime notice",
6707
6822
  request: makeRequest3(),
6708
6823
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429")],
6709
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: false, reason: "rate_limited" }]
6824
+ expected: [
6825
+ startedSession(NEW_THREAD_ID),
6826
+ { type: "result", ok: false, reason: "rate_limited" }
6827
+ ]
6710
6828
  },
6711
6829
  {
6712
6830
  name: "single-word provider sentence emits a runtime notice",
6713
6831
  request: makeRequest3(),
6714
6832
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("Unavailable.")],
6715
6833
  expected: [
6716
- sessionEvent3(NEW_THREAD_ID),
6834
+ startedSession(NEW_THREAD_ID),
6717
6835
  { type: "runtime_notice", body: "Unavailable." },
6718
6836
  { type: "result", ok: false, reason: "error:Unavailable." }
6719
6837
  ]
@@ -6741,7 +6859,7 @@ var init_conformance4 = __esm({
6741
6859
  turnCompleted()
6742
6860
  ],
6743
6861
  expected: [
6744
- sessionEvent3(NEW_THREAD_ID),
6862
+ startedSession(NEW_THREAD_ID),
6745
6863
  {
6746
6864
  type: "result",
6747
6865
  ok: false,
@@ -6761,8 +6879,9 @@ var init_conformance4 = __esm({
6761
6879
  turnCompleted()
6762
6880
  ],
6763
6881
  expected: [
6764
- sessionEvent3(NEW_THREAD_ID),
6882
+ startedSession(NEW_THREAD_ID),
6765
6883
  { type: "text", body: "All good now.", terminal: true },
6884
+ closingSession(NEW_THREAD_ID),
6766
6885
  { type: "result", ok: true }
6767
6886
  ]
6768
6887
  },
@@ -6774,7 +6893,11 @@ var init_conformance4 = __esm({
6774
6893
  name: "zero-content success (empty runtime result)",
6775
6894
  request: makeRequest3(),
6776
6895
  nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
6777
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: true }]
6896
+ expected: [
6897
+ startedSession(NEW_THREAD_ID),
6898
+ closingSession(NEW_THREAD_ID),
6899
+ { type: "result", ok: true }
6900
+ ]
6778
6901
  },
6779
6902
  {
6780
6903
  // Empty-final: a clean turn that ended on a tool call with no closing message.
@@ -6799,7 +6922,7 @@ var init_conformance4 = __esm({
6799
6922
  turnCompleted()
6800
6923
  ],
6801
6924
  expected: [
6802
- sessionEvent3(NEW_THREAD_ID),
6925
+ startedSession(NEW_THREAD_ID),
6803
6926
  {
6804
6927
  type: "tool",
6805
6928
  id: "t1",
@@ -6817,6 +6940,7 @@ var init_conformance4 = __esm({
6817
6940
  input: { changes: [{ path: "a.md", kind: "add" }] },
6818
6941
  result: [{ path: "a.md", kind: "add" }]
6819
6942
  },
6943
+ closingSession(NEW_THREAD_ID),
6820
6944
  { type: "result", ok: true }
6821
6945
  ]
6822
6946
  },
@@ -6832,9 +6956,10 @@ var init_conformance4 = __esm({
6832
6956
  turnCompleted()
6833
6957
  ],
6834
6958
  expected: [
6835
- sessionEvent3(NEW_THREAD_ID),
6959
+ startedSession(NEW_THREAD_ID),
6836
6960
  { type: "thinking", text: "Let me check the notes." },
6837
6961
  { type: "text", body: "All set.", terminal: true },
6962
+ closingSession(NEW_THREAD_ID),
6838
6963
  { type: "result", ok: true }
6839
6964
  ]
6840
6965
  },
@@ -6852,8 +6977,9 @@ var init_conformance4 = __esm({
6852
6977
  }),
6853
6978
  nativeStream: [threadStarted(NEW_THREAD_ID), agentMessage("m1", "Done."), turnCompleted()],
6854
6979
  expected: [
6855
- sessionEvent3(NEW_THREAD_ID),
6980
+ startedSession(NEW_THREAD_ID),
6856
6981
  { type: "text", body: "Done.", terminal: true },
6982
+ closingSession(NEW_THREAD_ID),
6857
6983
  {
6858
6984
  type: "result",
6859
6985
  ok: true,
@@ -6875,7 +7001,8 @@ var init_conformance4 = __esm({
6875
7001
  threadId: "th1",
6876
7002
  cwd: DIR2,
6877
7003
  promptFingerprint: fingerprintPrompt(PROMPT),
6878
- usageBaseline: null
7004
+ usageBaseline: null,
7005
+ compactions: 0
6879
7006
  })
6880
7007
  }),
6881
7008
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
@@ -6897,8 +7024,9 @@ var init_conformance4 = __esm({
6897
7024
  }),
6898
7025
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
6899
7026
  expected: [
6900
- sessionEvent3("th1"),
7027
+ startedSession("th1"),
6901
7028
  { type: "text", body: "Back again.", terminal: true },
7029
+ closingSession("th1"),
6902
7030
  { type: "result", ok: true }
6903
7031
  ]
6904
7032
  },
@@ -6920,8 +7048,9 @@ var init_conformance4 = __esm({
6920
7048
  }),
6921
7049
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
6922
7050
  expected: [
6923
- sessionEvent3("th1"),
7051
+ startedSession("th1"),
6924
7052
  { type: "text", body: "Back again.", terminal: true },
7053
+ closingSession("th1"),
6925
7054
  { type: "result", ok: true }
6926
7055
  ]
6927
7056
  },
@@ -6947,8 +7076,9 @@ var init_conformance4 = __esm({
6947
7076
  turnCompleted()
6948
7077
  ],
6949
7078
  expected: [
6950
- sessionEvent3(NEW_THREAD_ID, DIR2, true),
7079
+ startedSession(NEW_THREAD_ID, DIR2, true),
6951
7080
  { type: "text", body: "Fresh start.", terminal: true },
7081
+ closingSession(NEW_THREAD_ID),
6952
7082
  { type: "result", ok: true }
6953
7083
  ]
6954
7084
  }
@@ -6976,6 +7106,7 @@ var init_src = __esm({
6976
7106
  init_empty_result();
6977
7107
  init_failure();
6978
7108
  init_turn_request();
7109
+ init_standing();
6979
7110
  init_conformance();
6980
7111
  init_transcript();
6981
7112
  init_transcript_sink();
@@ -8529,6 +8660,7 @@ function buildCompanionTurnRequest(params) {
8529
8660
  const { turnContext: t } = params;
8530
8661
  return {
8531
8662
  systemPrompt: t.systemPrompt,
8663
+ ...t.standing ? { standing: t.standing } : {},
8532
8664
  prompt: t.prompt,
8533
8665
  content: t.content,
8534
8666
  config: t.config,
@@ -8658,6 +8790,68 @@ var init_codex_images = __esm({
8658
8790
  }
8659
8791
  });
8660
8792
 
8793
+ // src/codex-compactions.ts
8794
+ import { createReadStream } from "fs";
8795
+ import { readdir } from "fs/promises";
8796
+ import { homedir as homedir8 } from "os";
8797
+ import { join as join23 } from "path";
8798
+ import { createInterface as createInterface2 } from "readline";
8799
+ function createCodexCompactionCounter(codexHome = process.env.CODEX_HOME || join23(homedir8(), ".codex")) {
8800
+ return async (threadId) => {
8801
+ try {
8802
+ const rollout = await findRollout(codexHome, threadId);
8803
+ return rollout ? await countCompactions(rollout) : null;
8804
+ } catch {
8805
+ return null;
8806
+ }
8807
+ };
8808
+ }
8809
+ async function findRollout(codexHome, threadId) {
8810
+ const id = UUID_V7.exec(threadId);
8811
+ if (!id) return null;
8812
+ const createdAt = parseInt(`${id[1]}${id[2]}`, 16);
8813
+ const suffix = `-${threadId}.jsonl`;
8814
+ for (const offset of [0, -DAY_MS, DAY_MS]) {
8815
+ const day = new Date(createdAt + offset);
8816
+ const dir2 = join23(
8817
+ codexHome,
8818
+ "sessions",
8819
+ String(day.getUTCFullYear()),
8820
+ String(day.getUTCMonth() + 1).padStart(2, "0"),
8821
+ String(day.getUTCDate()).padStart(2, "0")
8822
+ );
8823
+ let names;
8824
+ try {
8825
+ names = await readdir(dir2);
8826
+ } catch {
8827
+ continue;
8828
+ }
8829
+ const name = names.find((n) => n.startsWith("rollout-") && n.endsWith(suffix));
8830
+ if (name) return join23(dir2, name);
8831
+ }
8832
+ return null;
8833
+ }
8834
+ async function countCompactions(rollout) {
8835
+ const lines2 = createInterface2({
8836
+ input: createReadStream(rollout, { encoding: "utf8" }),
8837
+ crlfDelay: Infinity
8838
+ });
8839
+ let count = 0;
8840
+ for await (const line of lines2) {
8841
+ if (line.slice(0, RECORD_HEAD_CHARS).includes('"type":"compacted"')) count += 1;
8842
+ }
8843
+ return count;
8844
+ }
8845
+ var DAY_MS, UUID_V7, RECORD_HEAD_CHARS;
8846
+ var init_codex_compactions = __esm({
8847
+ "src/codex-compactions.ts"() {
8848
+ "use strict";
8849
+ DAY_MS = 864e5;
8850
+ UUID_V7 = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8851
+ RECORD_HEAD_CHARS = 200;
8852
+ }
8853
+ });
8854
+
8661
8855
  // src/turn-seq-floor.ts
8662
8856
  function resolveSeqFloor(sources, ctx2) {
8663
8857
  const { serverFloor, outboxFloor } = sources;
@@ -8766,15 +8960,15 @@ var init_prepare_failure = __esm({
8766
8960
 
8767
8961
  // src/prepared.ts
8768
8962
  import { mkdirSync as mkdirSync13, readFileSync as readFileSync13, rmSync as rmSync9, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
8769
- import { join as join23 } from "path";
8963
+ import { join as join24 } from "path";
8770
8964
  function dirFor(workspaceId) {
8771
- return join23(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
8965
+ return join24(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
8772
8966
  }
8773
8967
  function conversationDir(workspaceId, conversationId) {
8774
- return join23(dirFor(workspaceId), encodeURIComponent(conversationId));
8968
+ return join24(dirFor(workspaceId), encodeURIComponent(conversationId));
8775
8969
  }
8776
8970
  function pathFor2(workspaceId, conversationId, agentId) {
8777
- return join23(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
8971
+ return join24(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
8778
8972
  }
8779
8973
  function readPrepared(workspaceId, conversationId, agentId) {
8780
8974
  const path = pathFor2(workspaceId, conversationId, agentId);
@@ -8812,9 +9006,9 @@ var init_prepared = __esm({
8812
9006
 
8813
9007
  // src/transcript-writer.ts
8814
9008
  import { appendFileSync, chmodSync as chmodSync5, copyFileSync as copyFileSync2, mkdirSync as mkdirSync14, readdirSync as readdirSync2, rmSync as rmSync10 } from "fs";
8815
- import { basename as basename3, dirname as dirname12, join as join24 } from "path";
9009
+ import { basename as basename3, dirname as dirname12, join as join25 } from "path";
8816
9010
  function transcriptsDir() {
8817
- return join24(runnerStateDir(), "transcripts");
9011
+ return join25(runnerStateDir(), "transcripts");
8818
9012
  }
8819
9013
  function fileName(meta) {
8820
9014
  const d = new Date(meta.ts);
@@ -8840,7 +9034,7 @@ function pruneOld(dir2, retain) {
8840
9034
  const drop = files.sort().slice(0, files.length - retain);
8841
9035
  for (const f of drop) {
8842
9036
  try {
8843
- rmSync10(join24(dir2, f), { force: true });
9037
+ rmSync10(join25(dir2, f), { force: true });
8844
9038
  } catch {
8845
9039
  }
8846
9040
  }
@@ -8858,7 +9052,7 @@ var init_transcript_writer = __esm({
8858
9052
  onWarn;
8859
9053
  constructor(dir2, meta, onWarn) {
8860
9054
  this.onWarn = onWarn;
8861
- this.path = join24(dir2, fileName(meta));
9055
+ this.path = join25(dir2, fileName(meta));
8862
9056
  try {
8863
9057
  mkdirSync14(dir2, { recursive: true });
8864
9058
  try {
@@ -8887,10 +9081,10 @@ var init_transcript_writer = __esm({
8887
9081
  preserveAnomaly() {
8888
9082
  if (this.broken) return;
8889
9083
  try {
8890
- const dir2 = join24(dirname12(this.path), "anomalies");
9084
+ const dir2 = join25(dirname12(this.path), "anomalies");
8891
9085
  mkdirSync14(dir2, { recursive: true, mode: 448 });
8892
9086
  chmodSync5(dir2, 448);
8893
- const target = join24(dir2, basename3(this.path));
9087
+ const target = join25(dir2, basename3(this.path));
8894
9088
  copyFileSync2(this.path, target);
8895
9089
  chmodSync5(target, 384);
8896
9090
  pruneOld(dir2, ANOMALY_RETAIN);
@@ -9190,6 +9384,7 @@ var init_turn_execution = __esm({
9190
9384
  init_bundled_binaries();
9191
9385
  init_codex_instructions();
9192
9386
  init_codex_images();
9387
+ init_codex_compactions();
9193
9388
  init_config();
9194
9389
  init_api_error_shape();
9195
9390
  init_errors();
@@ -9875,6 +10070,8 @@ var init_turn_execution = __esm({
9875
10070
  turnId: this.turnId,
9876
10071
  warn: (meta, msg) => turnLog.warn(meta, msg)
9877
10072
  }),
10073
+ // Whether a resumed thread compacted since its standing preamble was sent.
10074
+ countCompactions: createCodexCompactionCounter(),
9878
10075
  ...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
9879
10076
  })
9880
10077
  );
@@ -9935,6 +10132,9 @@ var init_turn_execution = __esm({
9935
10132
  );
9936
10133
  return {
9937
10134
  systemPrompt: context.systemPrompt,
10135
+ // Replaced even when absent, so the segments can't outlive the
10136
+ // `systemPrompt` they were split from.
10137
+ standing: context.standing,
9938
10138
  prompt: context.prompt,
9939
10139
  content: context.content
9940
10140
  };
@@ -10327,7 +10527,7 @@ var init_turn_execution = __esm({
10327
10527
  // src/dispatcher.ts
10328
10528
  import { randomUUID as randomUUID4 } from "crypto";
10329
10529
  import { existsSync as existsSync15, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
10330
- import { join as join25 } from "path";
10530
+ import { join as join26 } from "path";
10331
10531
  function checkoutState(cwd) {
10332
10532
  if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
10333
10533
  if (!existsSync15(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
@@ -10340,7 +10540,7 @@ function checkoutState(cwd) {
10340
10540
  if (entries.length === 0) {
10341
10541
  return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
10342
10542
  }
10343
- const gitPath = join25(cwd, ".git");
10543
+ const gitPath = join26(cwd, ".git");
10344
10544
  if (!existsSync15(gitPath)) return { ok: true, reason: "usable" };
10345
10545
  let stat;
10346
10546
  try {
@@ -10348,7 +10548,7 @@ function checkoutState(cwd) {
10348
10548
  } catch (error) {
10349
10549
  return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
10350
10550
  }
10351
- if (stat.isDirectory() && !existsSync15(join25(gitPath, "HEAD")))
10551
+ if (stat.isDirectory() && !existsSync15(join26(gitPath, "HEAD")))
10352
10552
  return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
10353
10553
  return { ok: true, reason: "usable" };
10354
10554
  }
@@ -10726,8 +10926,8 @@ import {
10726
10926
  } from "fs";
10727
10927
  import { createRequire as createRequire3 } from "module";
10728
10928
  import { isIP } from "net";
10729
- import { homedir as homedir8, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo3 } from "os";
10730
- import { dirname as dirname14, join as join29, sep as sep5 } from "path";
10929
+ import { homedir as homedir9, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo3 } from "os";
10930
+ import { dirname as dirname14, join as join30, sep as sep5 } from "path";
10731
10931
  import { fileURLToPath as fileURLToPath7 } from "url";
10732
10932
  import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
10733
10933
  function accountHome() {
@@ -10761,7 +10961,7 @@ async function resolvedAddresses(baseUrl) {
10761
10961
  function companionPackageRoot(modulePath) {
10762
10962
  for (let dir2 = dirname14(modulePath); ; dir2 = dirname14(dir2)) {
10763
10963
  try {
10764
- const manifest = JSON.parse(readFileSync16(join29(dir2, "package.json"), "utf8"));
10964
+ const manifest = JSON.parse(readFileSync16(join30(dir2, "package.json"), "utf8"));
10765
10965
  if (manifest.name === "@cabane/companion") return dir2;
10766
10966
  } catch {
10767
10967
  }
@@ -10838,22 +11038,22 @@ function companionStoreDenies(input) {
10838
11038
  const always = [
10839
11039
  cabaneDir2,
10840
11040
  ...homes.flatMap((home) => [
10841
- join29(home, ".cabane"),
10842
- join29(home, "*", ".cabane"),
10843
- join29(home, "*", "*", ".cabane")
11041
+ join30(home, ".cabane"),
11042
+ join30(home, "*", ".cabane"),
11043
+ join30(home, "*", "*", ".cabane")
10844
11044
  ]),
10845
11045
  // A glob anchored at the root has no directory to start from.
10846
- ...dirname14(parent) === parent ? [] : [join29(parent, "*", ".cabane")]
11046
+ ...dirname14(parent) === parent ? [] : [join30(parent, "*", ".cabane")]
10847
11047
  ];
10848
11048
  const companionHome = dirname14(cabaneDir2);
10849
- const otherLogins = foundStores.map((store) => dirname14(store)).filter((home) => home !== companionHome).flatMap((home) => [join29(home, ".claude"), join29(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
11049
+ const otherLogins = foundStores.map((store) => dirname14(store)).filter((home) => home !== companionHome).flatMap((home) => [join30(home, ".claude"), join30(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
10850
11050
  return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
10851
11051
  }
10852
11052
  function companionStoreWatch(log2) {
10853
11053
  return new CompanionStoreWatch({
10854
11054
  log: log2,
10855
11055
  roots: () => {
10856
- const home = homedir8();
11056
+ const home = homedir9();
10857
11057
  return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
10858
11058
  }
10859
11059
  });
@@ -10863,7 +11063,7 @@ function isAtOrUnder(path, dir2) {
10863
11063
  }
10864
11064
  function expandHome(path, home) {
10865
11065
  if (path === "~") return home;
10866
- return path.startsWith("~/") ? join29(home, path.slice(2)) : path;
11066
+ return path.startsWith("~/") ? join30(home, path.slice(2)) : path;
10867
11067
  }
10868
11068
  function stripPort(pattern) {
10869
11069
  return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
@@ -10881,8 +11081,8 @@ function patternsOverlap(a, b) {
10881
11081
  }
10882
11082
  function harnessSandboxConfig(input) {
10883
11083
  const { policy } = input;
10884
- const claudeDir = input.claudeConfigDir ?? join29(input.home, ".claude");
10885
- const codexDir = input.codexHome ?? join29(input.home, ".codex");
11084
+ const claudeDir = input.claudeConfigDir ?? join30(input.home, ".claude");
11085
+ const codexDir = input.codexHome ?? join30(input.home, ".codex");
10886
11086
  const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
10887
11087
  const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
10888
11088
  const cabane2 = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
@@ -10899,7 +11099,7 @@ function harnessSandboxConfig(input) {
10899
11099
  foundStores: input.foundStores,
10900
11100
  ownState: [claudeDir, codexDir]
10901
11101
  }),
10902
- ...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join29(home, p))),
11102
+ ...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join30(home, p))),
10903
11103
  ...expand(policy.filesystem?.denyRead)
10904
11104
  ];
10905
11105
  const strict = policy.filesystem?.mode === "strict";
@@ -10907,7 +11107,7 @@ function harnessSandboxConfig(input) {
10907
11107
  const allowRead = strict ? [
10908
11108
  ...input.cwd ? [input.cwd] : [],
10909
11109
  claudeDir,
10910
- join29(input.home, ".claude.json"),
11110
+ join30(input.home, ".claude.json"),
10911
11111
  codexDir,
10912
11112
  ...input.runtimePaths ?? [],
10913
11113
  ...expand(policy.filesystem?.allowRead)
@@ -10947,7 +11147,7 @@ function harnessSandboxConfig(input) {
10947
11147
  ...input.cwd ? [input.cwd] : [],
10948
11148
  input.tmp,
10949
11149
  claudeDir,
10950
- join29(input.home, ".claude.json"),
11150
+ join30(input.home, ".claude.json"),
10951
11151
  codexDir,
10952
11152
  ...expand(policy.filesystem?.allowWrite)
10953
11153
  ],
@@ -10998,7 +11198,7 @@ async function prepareHarnessSandbox(input) {
10998
11198
  baseUrl: input.baseUrl,
10999
11199
  resolved: await resolvedAddresses(input.baseUrl),
11000
11200
  ownAddresses: ownAddresses(),
11001
- home: homedir8(),
11201
+ home: homedir9(),
11002
11202
  accountHome: accountHome(),
11003
11203
  tmp: tmpdir4(),
11004
11204
  cabaneDir: cabaneDir(),
@@ -11049,9 +11249,9 @@ async function prepareHarnessSandbox(input) {
11049
11249
  } finally {
11050
11250
  for (const name of held) delete process.env[name];
11051
11251
  }
11052
- mkdirSync17(join29(cabaneDir(), "sandbox"), { recursive: true });
11053
- const dir2 = mkdtempSync(join29(cabaneDir(), "sandbox", "turn-"));
11054
- const script = join29(dir2, "exec-sandboxed");
11252
+ mkdirSync17(join30(cabaneDir(), "sandbox"), { recursive: true });
11253
+ const dir2 = mkdtempSync(join30(cabaneDir(), "sandbox", "turn-"));
11254
+ const script = join30(dir2, "exec-sandboxed");
11055
11255
  writeFileSync10(
11056
11256
  script,
11057
11257
  [
@@ -11067,7 +11267,7 @@ async function prepareHarnessSandbox(input) {
11067
11267
  return {
11068
11268
  wrap: (command, args) => ({ command: script, args: [command, ...args] }),
11069
11269
  executableFor: (real) => {
11070
- const shim = join29(dir2, `exec-${++shims}`);
11270
+ const shim = join30(dir2, `exec-${++shims}`);
11071
11271
  writeFileSync10(shim, `#!/bin/bash
11072
11272
  exec ${shellQuote(script)} ${shellQuote(real)} "$@"
11073
11273
  `);
@@ -11160,7 +11360,7 @@ import {
11160
11360
  rmSync as rmSync15,
11161
11361
  writeFileSync as writeFileSync11
11162
11362
  } from "fs";
11163
- import { join as join30 } from "path";
11363
+ import { join as join31 } from "path";
11164
11364
  var MAX_ENTRIES, MAX_AGE_MS, Outbox;
11165
11365
  var init_outbox = __esm({
11166
11366
  "src/outbox.ts"() {
@@ -11178,10 +11378,10 @@ var init_outbox = __esm({
11178
11378
  // Resolved lazily (per call) so tests that swap HOME between
11179
11379
  // cases route writes at the right tmpdir.
11180
11380
  dir() {
11181
- return join30(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
11381
+ return join31(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
11182
11382
  }
11183
11383
  fileFor(turnId, seq) {
11184
- return join30(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
11384
+ return join31(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
11185
11385
  }
11186
11386
  // Persist a commit for later draining. Atomic (temp file + rename) so a
11187
11387
  // concurrent `list()` never reads a half-written entry, then enforces the
@@ -11223,7 +11423,7 @@ var init_outbox = __esm({
11223
11423
  const entries = [];
11224
11424
  for (const name of names) {
11225
11425
  if (!name.endsWith(".json")) continue;
11226
- const full = join30(dir2, name);
11426
+ const full = join31(dir2, name);
11227
11427
  try {
11228
11428
  const parsed = JSON.parse(readFileSync17(full, "utf8"));
11229
11429
  if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
@@ -11352,7 +11552,7 @@ var init_image_manifest = __esm({
11352
11552
 
11353
11553
  // src/runner/exec-shim.ts
11354
11554
  import { chmodSync as chmodSync8, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "fs";
11355
- import { join as join34 } from "path";
11555
+ import { join as join35 } from "path";
11356
11556
  function shellQuote2(value) {
11357
11557
  return `'${value.replace(/'/g, `'\\''`)}'`;
11358
11558
  }
@@ -11362,10 +11562,10 @@ function createShimDir() {
11362
11562
  return {
11363
11563
  write(argv) {
11364
11564
  if (!dir2) {
11365
- mkdirSync20(join34(cabaneDir(), "runner"), { recursive: true });
11366
- dir2 = mkdtempSync2(join34(cabaneDir(), "runner", "turn-"));
11565
+ mkdirSync20(join35(cabaneDir(), "runner"), { recursive: true });
11566
+ dir2 = mkdtempSync2(join35(cabaneDir(), "runner", "turn-"));
11367
11567
  }
11368
- const path = join34(dir2, `exec-${++count}`);
11568
+ const path = join35(dir2, `exec-${++count}`);
11369
11569
  writeFileSync13(path, `#!/bin/sh
11370
11570
  exec ${argv.map(shellQuote2).join(" ")} "$@"
11371
11571
  `);
@@ -11386,7 +11586,7 @@ var init_exec_shim = __esm({
11386
11586
  });
11387
11587
 
11388
11588
  // src/runner/launcher-channel.ts
11389
- import { createInterface as createInterface2 } from "readline";
11589
+ import { createInterface as createInterface3 } from "readline";
11390
11590
  function openLauncherChannel(stdin, emit, onLauncherGone) {
11391
11591
  const waiting = /* @__PURE__ */ new Map();
11392
11592
  const replies = /* @__PURE__ */ new Map();
@@ -11394,7 +11594,7 @@ function openLauncherChannel(stdin, emit, onLauncherGone) {
11394
11594
  let resolveFirst;
11395
11595
  let gotFirst = false;
11396
11596
  const first = new Promise((resolve2) => resolveFirst = resolve2);
11397
- const lines2 = createInterface2({ input: stdin, crlfDelay: Infinity });
11597
+ const lines2 = createInterface3({ input: stdin, crlfDelay: Infinity });
11398
11598
  lines2.on("line", (line) => {
11399
11599
  if (!gotFirst) {
11400
11600
  gotFirst = true;
@@ -15754,7 +15954,7 @@ function createLocalProcessProvider(opts = {}) {
15754
15954
  import { createHash as createHash5, randomUUID as randomUUID5 } from "crypto";
15755
15955
  import { constants as constants3, tmpdir as tmpdir3 } from "os";
15756
15956
  import {
15757
- createReadStream,
15957
+ createReadStream as createReadStream2,
15758
15958
  createWriteStream as createWriteStream2,
15759
15959
  existsSync as existsSync18,
15760
15960
  mkdirSync as mkdirSync16,
@@ -15765,7 +15965,7 @@ import {
15765
15965
  statSync as statSync4,
15766
15966
  writeFileSync as writeFileSync9
15767
15967
  } from "fs";
15768
- import { join as join28 } from "path";
15968
+ import { join as join29 } from "path";
15769
15969
  import { Writable } from "stream";
15770
15970
 
15771
15971
  // src/providers/docker-cli.ts
@@ -15838,7 +16038,7 @@ import {
15838
16038
  renameSync as renameSync10,
15839
16039
  rmSync as rmSync12
15840
16040
  } from "fs";
15841
- import { join as join26 } from "path";
16041
+ import { join as join27 } from "path";
15842
16042
  function routes(t) {
15843
16043
  const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
15844
16044
  const agent = `${conv}/participants/agents/${t.agentId}`;
@@ -15884,7 +16084,7 @@ function publishStaged(from, to, move = atomicMove) {
15884
16084
  const staged = readdirSync4(from).filter((n) => n.endsWith(".json")).map((name) => {
15885
16085
  let entry = {};
15886
16086
  try {
15887
- entry = JSON.parse(readFileSync14(join26(from, name), "utf8"));
16087
+ entry = JSON.parse(readFileSync14(join27(from, name), "utf8"));
15888
16088
  } catch {
15889
16089
  }
15890
16090
  return { name, entry };
@@ -15897,7 +16097,7 @@ function publishStaged(from, to, move = atomicMove) {
15897
16097
  let published = 0;
15898
16098
  for (const { name } of staged) {
15899
16099
  try {
15900
- move(join26(from, name), join26(to, name));
16100
+ move(join27(from, name), join27(to, name));
15901
16101
  } catch {
15902
16102
  return { published, complete: false };
15903
16103
  }
@@ -15910,7 +16110,7 @@ function publishStaged(from, to, move = atomicMove) {
15910
16110
  // src/providers/runner-base.ts
15911
16111
  import { createHash as createHash4 } from "crypto";
15912
16112
  import { existsSync as existsSync17, realpathSync as realpathSync6 } from "fs";
15913
- import { dirname as dirname13, join as join27, sep as sep4 } from "path";
16113
+ import { dirname as dirname13, join as join28, sep as sep4 } from "path";
15914
16114
  import { fileURLToPath as fileURLToPath6 } from "url";
15915
16115
  var COMPANION_MOUNT = "/opt/cabane/companion";
15916
16116
  var BASE_DOCKERFILE = `FROM node:22-slim
@@ -15958,11 +16158,11 @@ async function ensureBaseImage(docker, image, log2) {
15958
16158
  function ownInstall(moduleFile = fileURLToPath6(import.meta.url)) {
15959
16159
  let dir2 = dirname13(moduleFile);
15960
16160
  for (let i = 0; i < 4; i++) {
15961
- if (existsSync17(join27(dir2, "dist", "cli.js")) && existsSync17(join27(dir2, "package.json"))) break;
16161
+ if (existsSync17(join28(dir2, "dist", "cli.js")) && existsSync17(join28(dir2, "package.json"))) break;
15962
16162
  dir2 = dirname13(dir2);
15963
16163
  }
15964
- if (!existsSync17(join27(dir2, "dist", "cli.js"))) return null;
15965
- const sdk = join27(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
16164
+ if (!existsSync17(join28(dir2, "dist", "cli.js"))) return null;
16165
+ const sdk = join28(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
15966
16166
  if (!existsSync17(sdk)) return null;
15967
16167
  const root = realpathSync6(dir2);
15968
16168
  return realpathSync6(sdk).startsWith(root + sep4) ? root : null;
@@ -16101,39 +16301,39 @@ function networkCreateError(stderr) {
16101
16301
  }
16102
16302
  function sessionArchivePath(deviceDir, attemptId) {
16103
16303
  if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
16104
- return join28(deviceDir, "attempts", attemptId, "session.tar");
16304
+ return join29(deviceDir, "attempts", attemptId, "session.tar");
16105
16305
  }
16106
16306
  function pruneOtherAttempts(deviceDir, pair2, keep) {
16107
- const root = join28(deviceDir, "attempts");
16307
+ const root = join29(deviceDir, "attempts");
16108
16308
  if (!existsSync18(root)) return;
16109
16309
  for (const id of readdirSync5(root)) {
16110
16310
  if (id === keep || !UUID_RE.test(id)) continue;
16111
16311
  let pairOf = "";
16112
16312
  try {
16113
- pairOf = readFileSync15(join28(root, id, "pair"), "utf8");
16313
+ pairOf = readFileSync15(join29(root, id, "pair"), "utf8");
16114
16314
  } catch {
16115
16315
  continue;
16116
16316
  }
16117
16317
  if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
16118
- rmSync13(join28(root, id), { recursive: true, force: true });
16318
+ rmSync13(join29(root, id), { recursive: true, force: true });
16119
16319
  }
16120
16320
  }
16121
16321
  }
16122
16322
  var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
16123
16323
  function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
16124
- const root = join28(deviceDir, "attempts");
16324
+ const root = join29(deviceDir, "attempts");
16125
16325
  if (!existsSync18(root)) return [];
16126
16326
  const removed = [];
16127
16327
  for (const id of readdirSync5(root)) {
16128
16328
  if (!UUID_RE.test(id)) continue;
16129
16329
  let kept;
16130
16330
  try {
16131
- kept = statSync4(join28(root, id)).mtimeMs;
16331
+ kept = statSync4(join29(root, id)).mtimeMs;
16132
16332
  } catch {
16133
16333
  continue;
16134
16334
  }
16135
16335
  if (now - kept < maxAgeMs) continue;
16136
- rmSync13(join28(root, id), { recursive: true, force: true });
16336
+ rmSync13(join29(root, id), { recursive: true, force: true });
16137
16337
  removed.push(id);
16138
16338
  }
16139
16339
  return removed;
@@ -16334,11 +16534,11 @@ function createDockerProvider(opts) {
16334
16534
  await must(["network", "disconnect", "-f", other, name], "moving the container");
16335
16535
  if (others.includes(sharedNetwork)) await retireSharedNetwork();
16336
16536
  };
16337
- const socketRoot = opts.socketRoot ?? join28(
16537
+ const socketRoot = opts.socketRoot ?? join29(
16338
16538
  process.env.XDG_RUNTIME_DIR && existsSync18(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
16339
16539
  `cabane-runners-${opts.deviceKey}`
16340
16540
  );
16341
- const socketDir = (name) => join28(socketRoot, createHash5("sha256").update(name).digest("hex").slice(0, 12));
16541
+ const socketDir = (name) => join29(socketRoot, createHash5("sha256").update(name).digest("hex").slice(0, 12));
16342
16542
  const create = async (name, attempt) => {
16343
16543
  mkdirSync16(socketRoot, { recursive: true, mode: 448 });
16344
16544
  mkdirSync16(socketDir(name), { recursive: true, mode: 457 });
@@ -16480,7 +16680,7 @@ function createDockerProvider(opts) {
16480
16680
  }
16481
16681
  };
16482
16682
  const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
16483
- const pendingPath = (name) => join28(opts.deviceDir, "runners-pending", `${name}.json`);
16683
+ const pendingPath = (name) => join29(opts.deviceDir, "runners-pending", `${name}.json`);
16484
16684
  const readPending = (name) => {
16485
16685
  try {
16486
16686
  const ids = JSON.parse(readFileSync15(pendingPath(name), "utf8"));
@@ -16494,12 +16694,12 @@ function createDockerProvider(opts) {
16494
16694
  rmSync13(pendingPath(name), { force: true });
16495
16695
  return;
16496
16696
  }
16497
- mkdirSync16(join28(opts.deviceDir, "runners-pending"), { recursive: true });
16697
+ mkdirSync16(join29(opts.deviceDir, "runners-pending"), { recursive: true });
16498
16698
  writeFileSync9(pendingPath(name), JSON.stringify([...new Set(ids)]));
16499
16699
  };
16500
16700
  const keepSession = async (name, attempt, attemptId) => {
16501
16701
  const archive = sessionArchivePath(opts.deviceDir, attemptId);
16502
- mkdirSync16(join28(archive, ".."), { recursive: true });
16702
+ mkdirSync16(join29(archive, ".."), { recursive: true });
16503
16703
  const partial = `${archive}.${process.pid}.partial`;
16504
16704
  const file = new CappedFile(partial, sessionCap);
16505
16705
  const r = await execIn(
@@ -16516,7 +16716,7 @@ function createDockerProvider(opts) {
16516
16716
  );
16517
16717
  if (r.code === 0 && !file.over) {
16518
16718
  renameSync11(partial, archive);
16519
- writeFileSync9(join28(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
16719
+ writeFileSync9(join29(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
16520
16720
  pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
16521
16721
  await writeMarker(name, attemptId);
16522
16722
  log2.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
@@ -16530,7 +16730,7 @@ function createDockerProvider(opts) {
16530
16730
  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"
16531
16731
  );
16532
16732
  };
16533
- const stagingDir = (name) => join28(opts.deviceDir, "runners-pending", `${name}.outbox`);
16733
+ const stagingDir = (name) => join29(opts.deviceDir, "runners-pending", `${name}.outbox`);
16534
16734
  const ensureRunning = async (name) => {
16535
16735
  const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
16536
16736
  if (seen.code !== 0) return false;
@@ -16538,7 +16738,7 @@ function createDockerProvider(opts) {
16538
16738
  return (await docker.run(["start", name])).code === 0;
16539
16739
  };
16540
16740
  const dropArchive = (attemptId) => {
16541
- rmSync13(join28(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
16741
+ rmSync13(join29(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
16542
16742
  recursive: true,
16543
16743
  force: true
16544
16744
  });
@@ -16607,7 +16807,7 @@ function createDockerProvider(opts) {
16607
16807
  if (seen.stdout.trim().split(" ")[0] !== "true")
16608
16808
  await must(["start", name], "starting the container");
16609
16809
  const git = await serveGitCredentials({
16610
- socketPath: join28(socketDir(name), "git.sock"),
16810
+ socketPath: join29(socketDir(name), "git.sock"),
16611
16811
  hosts: opts.gitHosts,
16612
16812
  fill: opts.gitFill,
16613
16813
  log: log2
@@ -16643,7 +16843,7 @@ function createDockerProvider(opts) {
16643
16843
  const archive = sessionArchivePath(opts.deviceDir, attemptId);
16644
16844
  if (existsSync18(archive)) {
16645
16845
  const r = await execIn(name, ["tar", "-x", "-C", CONTAINER_HOME, "-f", "-"], {
16646
- input: createReadStream(archive)
16846
+ input: createReadStream2(archive)
16647
16847
  });
16648
16848
  if (r.code !== 0) {
16649
16849
  log2.warn(
@@ -16756,15 +16956,15 @@ function createDockerProvider(opts) {
16756
16956
  const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
16757
16957
  if (transcripts) {
16758
16958
  const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
16759
- if (kept.length) mkdirSync16(join28(opts.hostStateDir, "transcripts"), { recursive: true });
16760
- for (const f of kept) writeFileSync9(join28(opts.hostStateDir, "transcripts", f.name), f.data);
16959
+ if (kept.length) mkdirSync16(join29(opts.hostStateDir, "transcripts"), { recursive: true });
16960
+ for (const f of kept) writeFileSync9(join29(opts.hostStateDir, "transcripts", f.name), f.data);
16761
16961
  await removeIn(
16762
16962
  name,
16763
16963
  `${CONTAINER_STATE}/transcripts`,
16764
16964
  kept.map((f) => f.name)
16765
16965
  );
16766
16966
  }
16767
- const outboxRel = join28("outbox", encodeURIComponent(attempt.agentId));
16967
+ const outboxRel = join29("outbox", encodeURIComponent(attempt.agentId));
16768
16968
  const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
16769
16969
  let unpublished = false;
16770
16970
  if (outbox) {
@@ -16790,7 +16990,7 @@ function createDockerProvider(opts) {
16790
16990
  const staged = stagingDir(name);
16791
16991
  mkdirSync16(staged, { recursive: true });
16792
16992
  writeFileSync9(
16793
- join28(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
16993
+ join29(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
16794
16994
  `${JSON.stringify(entry)}
16795
16995
  `
16796
16996
  );
@@ -16803,7 +17003,7 @@ function createDockerProvider(opts) {
16803
17003
  if (outbox.complete) {
16804
17004
  const published = publishStaged(
16805
17005
  stagingDir(name),
16806
- join28(opts.hostStateDir, outboxRel),
17006
+ join29(opts.hostStateDir, outboxRel),
16807
17007
  opts.moveFile
16808
17008
  );
16809
17009
  queued = published.published;
@@ -18778,9 +18978,9 @@ function handleUncaught(log2, err, origin) {
18778
18978
  // src/crash-marker.ts
18779
18979
  init_config();
18780
18980
  import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as readFileSync18, rmSync as rmSync16, writeFileSync as writeFileSync12 } from "fs";
18781
- import { join as join31 } from "path";
18981
+ import { join as join32 } from "path";
18782
18982
  function crashMarkerPath() {
18783
- return join31(cabaneDir(), "last-error.json");
18983
+ return join32(cabaneDir(), "last-error.json");
18784
18984
  }
18785
18985
  function recordCrash(rec2) {
18786
18986
  try {
@@ -19093,8 +19293,8 @@ async function runAttached(runtime) {
19093
19293
  init_install_kind();
19094
19294
  init_cabane_command();
19095
19295
  import { readdirSync as readdirSync7, realpathSync as realpathSync8, existsSync as existsSync21 } from "fs";
19096
- import { homedir as homedir9 } from "os";
19097
- import { dirname as dirname15, join as join32 } from "path";
19296
+ import { homedir as homedir10 } from "os";
19297
+ import { dirname as dirname15, join as join33 } from "path";
19098
19298
  init_config();
19099
19299
  init_sandbox_policy();
19100
19300
  init_secrets();
@@ -19163,7 +19363,7 @@ function versionLine() {
19163
19363
  const install = companionInstall();
19164
19364
  const own = installedCommandPath({ install });
19165
19365
  if (install === "script") {
19166
- return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join32(homedir9(), ".cabane"))})`;
19366
+ return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join33(homedir10(), ".cabane"))})`;
19167
19367
  }
19168
19368
  if (own)
19169
19369
  return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(dirname15(dirname15(own)))})`;
@@ -19189,8 +19389,8 @@ function otherInstall(install, own) {
19189
19389
  }
19190
19390
  };
19191
19391
  if (install === "npm") {
19192
- const script = join32(
19193
- homedir9(),
19392
+ const script = join33(
19393
+ homedir10(),
19194
19394
  ".cabane",
19195
19395
  "node",
19196
19396
  "lib",
@@ -19198,11 +19398,11 @@ function otherInstall(install, own) {
19198
19398
  "@cabane",
19199
19399
  "companion"
19200
19400
  );
19201
- return existsSync21(script) ? join32(homedir9(), ".cabane") : null;
19401
+ return existsSync21(script) ? join33(homedir10(), ".cabane") : null;
19202
19402
  }
19203
19403
  const found = firstOnPath("cabane-companion", process.env.PATH);
19204
19404
  if (!found) return null;
19205
- const ownAlias = own ? real(join32(dirname15(own), "cabane-companion")) : null;
19405
+ const ownAlias = own ? real(join33(dirname15(own), "cabane-companion")) : null;
19206
19406
  return real(found) === ownAlias ? null : dirname15(found);
19207
19407
  }
19208
19408
  async function liveState() {
@@ -19429,7 +19629,7 @@ init_errors();
19429
19629
  init_src();
19430
19630
  init_transcript_writer();
19431
19631
  import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync8 } from "fs";
19432
- import { isAbsolute, join as join33 } from "path";
19632
+ import { isAbsolute, join as join34 } from "path";
19433
19633
  async function transcript(opts = {}) {
19434
19634
  const dir2 = transcriptsDir();
19435
19635
  if (opts.follow) {
@@ -19446,7 +19646,7 @@ async function transcript(opts = {}) {
19446
19646
  process.stdout.write(emptyMessage(dir2));
19447
19647
  return;
19448
19648
  }
19449
- process.stdout.write(renderFile(join33(dir2, newest)) + "\n");
19649
+ process.stdout.write(renderFile(join34(dir2, newest)) + "\n");
19450
19650
  return;
19451
19651
  }
19452
19652
  printList(dir2);
@@ -19529,7 +19729,7 @@ function isComplete(content) {
19529
19729
  async function followTranscripts(dir2) {
19530
19730
  const follower = new TranscriptFollower({
19531
19731
  listFiles: () => listFiles(dir2),
19532
- read: (f) => readFileSync19(join33(dir2, f), "utf8"),
19732
+ read: (f) => readFileSync19(join34(dir2, f), "utf8"),
19533
19733
  write: (s) => process.stdout.write(s),
19534
19734
  // CSI: cursor up `n` lines, then erase from cursor to end of screen.
19535
19735
  clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
@@ -19569,7 +19769,7 @@ function printList(dir2) {
19569
19769
 
19570
19770
  `);
19571
19771
  for (const f of files.slice(0, 20)) {
19572
- const { meta, outcome } = peek(join33(dir2, f));
19772
+ const { meta, outcome } = peek(join34(dir2, f));
19573
19773
  const when = fmtTime(rec(meta)?.ts);
19574
19774
  const ws = str2(rec(meta)?.workspaceSlug);
19575
19775
  const o = rec(outcome);
@@ -19607,10 +19807,10 @@ function resolveTarget(dir2, target) {
19607
19807
  if (existsSync22(target)) return target;
19608
19808
  throw new CompanionError(`no transcript at ${target}.`);
19609
19809
  }
19610
- const exact = join33(dir2, target);
19810
+ const exact = join34(dir2, target);
19611
19811
  if (existsSync22(exact)) return exact;
19612
19812
  const matches = listFiles(dir2).filter((f) => f.includes(target));
19613
- if (matches.length === 1) return join33(dir2, matches[0]);
19813
+ if (matches.length === 1) return join34(dir2, matches[0]);
19614
19814
  if (matches.length === 0) {
19615
19815
  throw new CompanionError(
19616
19816
  `no transcript matching "${target}" in ${dir2}. Run \`${cabane("transcript")}\` to list them.`