@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/runtime.js CHANGED
@@ -3056,9 +3056,15 @@ var resolvedMcpServerSchema = z8.union([
3056
3056
  var resolvedMcpServersSchema = z8.record(z8.string(), resolvedMcpServerSchema);
3057
3057
  var hostInjectedServersSchema = z8.record(z8.string(), z8.unknown());
3058
3058
  var turnRequestSchema = z8.object({
3059
- // Server-composed system prompt (core + capability prose + adapter addendum +
3060
- // charter). One string to the adapter.
3059
+ // Server-composed standing instructions: the core segment (body, tool naming, identity, agent
3060
+ // charter) and, after it, the conversation segment (workspace and room charters, skill indexes,
3061
+ // roster). One string to the adapter, current as of this dispatch.
3061
3062
  systemPrompt: z8.string(),
3063
+ // The same text as its two ordered segments, so an adapter that writes standing text into the
3064
+ // conversation (Codex's input preamble) can tell which half changed. `systemPrompt` is
3065
+ // `joinStanding(standing)`. Optional: absent from an API older than the split, where
3066
+ // `systemPrompt` is the core alone and the conversation material rides `prompt`.
3067
+ standing: z8.object({ core: z8.string(), conversation: z8.string() }).optional(),
3062
3068
  // Server-composed per-turn user text (anchor reminder + the triggering message).
3063
3069
  prompt: z8.string(),
3064
3070
  // The multi-block user-message body (text + vision).
@@ -3106,6 +3112,16 @@ var turnRequestSchema = z8.object({
3106
3112
  })
3107
3113
  });
3108
3114
 
3115
+ // packages/agent-runtime/src/standing.ts
3116
+ var STANDING_SEPARATOR = "\n\n---\n\n";
3117
+ function joinStanding(segments) {
3118
+ const conversation = segments.conversation.trim();
3119
+ return conversation.length > 0 ? `${segments.core}${STANDING_SEPARATOR}${conversation}` : segments.core;
3120
+ }
3121
+ function standingSegments(req) {
3122
+ return req.standing ?? { core: req.systemPrompt, conversation: "" };
3123
+ }
3124
+
3109
3125
  // packages/agent-runtime/src/conformance.ts
3110
3126
  import { z as z9 } from "zod";
3111
3127
  var conformanceFixtureSchema = z9.object({
@@ -6114,7 +6130,14 @@ function fingerprintPrompt(prompt) {
6114
6130
  return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
6115
6131
  }
6116
6132
  function encodeSession3(state) {
6117
- return JSON.stringify(state);
6133
+ return JSON.stringify({
6134
+ threadId: state.threadId,
6135
+ cwd: state.cwd,
6136
+ promptFingerprint: state.promptFingerprint,
6137
+ conversationFingerprint: state.conversationFingerprint ?? null,
6138
+ compactions: state.compactions ?? null,
6139
+ usageBaseline: state.usageBaseline
6140
+ });
6118
6141
  }
6119
6142
  function decideResume3(stored, currentCwd) {
6120
6143
  if (!stored) return { fresh: true, reason: "no_session" };
@@ -6135,6 +6158,8 @@ function decideResume3(stored, currentCwd) {
6135
6158
  return {
6136
6159
  resume: state.threadId,
6137
6160
  promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null,
6161
+ conversationFingerprint: typeof state.conversationFingerprint === "string" ? state.conversationFingerprint : null,
6162
+ compactions: typeof state.compactions === "number" && Number.isInteger(state.compactions) && state.compactions >= 0 ? state.compactions : null,
6138
6163
  usageBaseline: readUsageBaseline(state.usageBaseline)
6139
6164
  };
6140
6165
  }
@@ -6167,6 +6192,7 @@ async function* decodeCodexStream(events, ctx) {
6167
6192
  let usage;
6168
6193
  let cumulativeUsage;
6169
6194
  let threadIdForSession = ctx.resumedThreadId;
6195
+ let lastState = ctx.storedState ?? null;
6170
6196
  let settled = false;
6171
6197
  function* flushInterim() {
6172
6198
  const sealed = sealHeld2(held, false);
@@ -6182,14 +6208,14 @@ async function* decodeCodexStream(events, ctx) {
6182
6208
  const state = encodeSession3({
6183
6209
  threadId,
6184
6210
  cwd: ctx.cwd ?? "",
6185
- promptFingerprint: ctx.promptFingerprint ?? null,
6186
- // The baseline this turn inherited, carried through unchanged. This early write records the
6187
- // thread id even if the turn dies mid-stream; it can't yet know what the turn will spend. The
6188
- // closing write records the new baseline.
6211
+ promptFingerprint: ctx.heldStanding?.promptFingerprint ?? null,
6212
+ conversationFingerprint: ctx.heldStanding?.conversationFingerprint ?? null,
6213
+ compactions: ctx.heldStanding?.compactions ?? null,
6189
6214
  usageBaseline: ctx.usageBaseline ?? null
6190
6215
  });
6191
6216
  if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
6192
6217
  sessionEmitted = true;
6218
+ lastState = state;
6193
6219
  yield {
6194
6220
  type: "session",
6195
6221
  state,
@@ -6288,14 +6314,16 @@ async function* decodeCodexStream(events, ctx) {
6288
6314
  if (sealed) yield sealed;
6289
6315
  reason = "no_terminal";
6290
6316
  }
6291
- if (ok && cumulativeUsage && threadIdForSession) {
6317
+ if (ok && threadIdForSession) {
6292
6318
  const closing = encodeSession3({
6293
6319
  threadId: threadIdForSession,
6294
6320
  cwd: ctx.cwd ?? "",
6295
6321
  promptFingerprint: ctx.promptFingerprint ?? null,
6296
- usageBaseline: cumulativeUsage
6322
+ conversationFingerprint: ctx.conversationFingerprint ?? null,
6323
+ compactions: ctx.compactions ?? null,
6324
+ usageBaseline: cumulativeUsage ?? ctx.usageBaseline ?? null
6297
6325
  });
6298
- yield { type: "session", state: closing };
6326
+ if (closing !== lastState) yield { type: "session", state: closing };
6299
6327
  }
6300
6328
  const resolvedConfig = ctx.resolvedReasoningEffort ? { reasoningEffort: ctx.resolvedReasoningEffort } : void 0;
6301
6329
  yield {
@@ -6365,15 +6393,27 @@ var ENV_ENVELOPE_KEYS = [
6365
6393
  "CABANE_CONVERSATION_TITLE",
6366
6394
  "CABANE_HOST_ACCESS"
6367
6395
  ];
6368
- function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null, imagePaths = []) {
6396
+ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadStanding = null, imagePaths = []) {
6369
6397
  const { policy, config } = req;
6370
6398
  const directory = req.local.cwd ?? "";
6371
6399
  const dialect = readCodexDialect(config.runtimeOptions);
6372
6400
  const baseInstructionsFile = !policy.hostFs && instructionsFile ? instructionsFile : null;
6373
6401
  const model = config.model ? parseCodexModel(config.model) : null;
6374
- const promptFingerprint = fingerprintPrompt(req.systemPrompt);
6375
- const threadHasThisPrompt = resumeThreadId !== null && threadPromptFingerprint !== null && threadPromptFingerprint === promptFingerprint;
6376
- const promptRidesInput = baseInstructionsFile === null && !threadHasThisPrompt;
6402
+ const segments = standingSegments(req);
6403
+ const conversationText = segments.conversation.trim();
6404
+ const fingerprints = {
6405
+ core: fingerprintPrompt(segments.core),
6406
+ conversation: conversationText.length > 0 ? fingerprintPrompt(conversationText) : null
6407
+ };
6408
+ const resumed = resumeThreadId !== null;
6409
+ const held = resumed ? threadStanding : null;
6410
+ const viaInput = baseInstructionsFile === null;
6411
+ const preamble = {
6412
+ core: viaInput && (held?.core ?? null) !== fingerprints.core,
6413
+ // Compared both ways: a held segment that is now empty differs from it, and nothing held
6414
+ // equals nothing to send.
6415
+ conversation: viaInput && (held?.conversation ?? null) !== fingerprints.conversation
6416
+ };
6377
6417
  return {
6378
6418
  resumeThreadId,
6379
6419
  directory,
@@ -6382,9 +6422,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
6382
6422
  skipGitRepoCheck: true,
6383
6423
  ...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
6384
6424
  baseInstructionsFile,
6385
- input: withImages(buildInput(req, promptRidesInput), imagePaths),
6386
- promptRidesInput,
6387
- promptFingerprint,
6425
+ input: withImages(buildInput(req, segments, preamble, resumed), imagePaths),
6426
+ preamble,
6427
+ fingerprints,
6388
6428
  config: buildConfig(req, baseInstructionsFile),
6389
6429
  secretEnv: codexSecretEnv(req)
6390
6430
  };
@@ -6400,12 +6440,26 @@ function codexSecretEnv(req) {
6400
6440
  }
6401
6441
  return Object.keys(env).length > 0 ? env : null;
6402
6442
  }
6403
- function buildInput(req, promptRidesInput) {
6443
+ var 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.";
6444
+ var 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.";
6445
+ function buildInput(req, segments, preamble, resumed) {
6404
6446
  const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
6405
6447
  const body = userText.trim().length > 0 ? userText : req.prompt;
6406
- if (!promptRidesInput) return body;
6407
- const system = req.systemPrompt.trim();
6408
- return system.length > 0 ? `${system}
6448
+ const standing = [];
6449
+ if (!resumed) {
6450
+ if (preamble.core) standing.push(joinStanding(segments).trim());
6451
+ } else {
6452
+ if (preamble.core) standing.push(`${CORE_REPLACES}
6453
+
6454
+ ${segments.core.trim()}`);
6455
+ if (preamble.conversation) {
6456
+ standing.push(`${CONVERSATION_REPLACES}
6457
+
6458
+ ${segments.conversation.trim()}`.trim());
6459
+ }
6460
+ }
6461
+ const lead = standing.filter((s) => s.length > 0);
6462
+ return lead.length > 0 ? `${lead.join("\n\n---\n\n")}
6409
6463
 
6410
6464
  ${body}` : body;
6411
6465
  }
@@ -6617,7 +6671,6 @@ function createCodexAdapter(deps = {}) {
6617
6671
  const directory = req.local.cwd ?? "";
6618
6672
  const decision = decideResume3(req.session, directory);
6619
6673
  const resumeThreadId = "resume" in decision ? decision.resume : null;
6620
- const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
6621
6674
  const threadUsageBaseline = "resume" in decision ? decision.usageBaseline : null;
6622
6675
  if ("fresh" in decision && decision.reason === "cwd_mismatch") {
6623
6676
  deps.onWarn?.(
@@ -6634,6 +6687,32 @@ function createCodexAdapter(deps = {}) {
6634
6687
  degraded = false;
6635
6688
  }
6636
6689
  }
6690
+ let compactions = 0;
6691
+ let threadStanding = null;
6692
+ if ("resume" in decision) {
6693
+ compactions = deps.countCompactions ? await deps.countCompactions(decision.resume) : null;
6694
+ if (signal.aborted) return;
6695
+ if (compactions === null) {
6696
+ deps.onWarn?.(
6697
+ "codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions (ENG210)",
6698
+ { threadId: decision.resume }
6699
+ );
6700
+ } else if (decision.compactions === compactions) {
6701
+ threadStanding = {
6702
+ core: decision.promptFingerprint,
6703
+ conversation: decision.conversationFingerprint
6704
+ };
6705
+ } else if (decision.compactions !== null && compactions > decision.compactions) {
6706
+ deps.onWarn?.(
6707
+ "codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions (ENG210)",
6708
+ {
6709
+ threadId: decision.resume,
6710
+ compactions,
6711
+ compactionsSince: compactions - decision.compactions
6712
+ }
6713
+ );
6714
+ }
6715
+ }
6637
6716
  let instructions = null;
6638
6717
  if (!req.policy.hostFs) {
6639
6718
  if (!deps.writeInstructionsFile) {
@@ -6678,7 +6757,7 @@ function createCodexAdapter(deps = {}) {
6678
6757
  req,
6679
6758
  resumeThreadId,
6680
6759
  instructions?.path ?? null,
6681
- threadPromptFingerprint,
6760
+ threadStanding,
6682
6761
  images?.paths ?? []
6683
6762
  );
6684
6763
  if (spec.model === null) {
@@ -6693,10 +6772,18 @@ function createCodexAdapter(deps = {}) {
6693
6772
  cwd: req.local.cwd,
6694
6773
  resumedThreadId: resumeThreadId,
6695
6774
  degraded,
6696
- // Which prompt the thread carries once this turn lands. If we sent one, it's that one — the
6697
- // later copy is what the model reads. If we withheld it, the thread carries what it did. Read
6698
- // off the spec, so the record can't disagree with what was sent.
6699
- promptFingerprint: spec.promptRidesInput ? spec.promptFingerprint : threadPromptFingerprint,
6775
+ // Which segments the thread carries once this turn lands. A segment sent is that one —
6776
+ // the later copy is what the model reads. One withheld is what the thread had. Read off
6777
+ // the spec, so the record can't disagree with what was sent.
6778
+ promptFingerprint: spec.preamble.core ? spec.fingerprints.core : threadStanding?.core ?? null,
6779
+ conversationFingerprint: spec.preamble.conversation ? spec.fingerprints.conversation : threadStanding?.conversation ?? null,
6780
+ compactions,
6781
+ // What the thread held before this turn, for the write at `thread.started`.
6782
+ heldStanding: {
6783
+ promptFingerprint: threadStanding?.core ?? null,
6784
+ conversationFingerprint: threadStanding?.conversation ?? null,
6785
+ compactions: threadStanding ? compactions : null
6786
+ },
6700
6787
  storedState: req.session,
6701
6788
  usageBaseline: threadUsageBaseline,
6702
6789
  // Codex reports no model in-stream, so record what the thread was started with (null on the
@@ -6796,11 +6883,21 @@ var OP_FAILURE = {
6796
6883
  message: "files.read returned more than the result ceiling"
6797
6884
  }
6798
6885
  };
6799
- var sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT), usageBaseline = null) => ({
6886
+ var startedSession = (threadId, cwd = DIR2, degraded = false) => ({
6800
6887
  type: "session",
6801
- state: encodeSession3({ threadId, cwd, promptFingerprint, usageBaseline }),
6888
+ state: encodeSession3({ threadId, cwd, promptFingerprint: null, usageBaseline: null }),
6802
6889
  ...degraded ? { degraded: true } : {}
6803
6890
  });
6891
+ var closingSession = (threadId, usageBaseline = null) => ({
6892
+ type: "session",
6893
+ state: encodeSession3({
6894
+ threadId,
6895
+ cwd: DIR2,
6896
+ promptFingerprint: fingerprintPrompt(PROMPT),
6897
+ usageBaseline,
6898
+ compactions: 0
6899
+ })
6900
+ });
6804
6901
  var CODEX_CONFORMANCE_FIXTURES = [
6805
6902
  {
6806
6903
  // A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
@@ -6823,11 +6920,11 @@ var CODEX_CONFORMANCE_FIXTURES = [
6823
6920
  })
6824
6921
  ],
6825
6922
  expected: [
6826
- sessionEvent3(NEW_THREAD_ID),
6923
+ startedSession(NEW_THREAD_ID),
6827
6924
  { type: "text", body: "Hello there.", terminal: true },
6828
6925
  // The closing session write — the thread's ending totals, recorded as the baseline the next turn
6829
6926
  // subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
6830
- sessionEvent3(NEW_THREAD_ID, DIR2, false, fingerprintPrompt(PROMPT), {
6927
+ closingSession(NEW_THREAD_ID, {
6831
6928
  inputTokens: 100,
6832
6929
  outputTokens: 50,
6833
6930
  cacheReadTokens: 20,
@@ -6874,7 +6971,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6874
6971
  turnCompleted()
6875
6972
  ],
6876
6973
  expected: [
6877
- sessionEvent3(NEW_THREAD_ID),
6974
+ startedSession(NEW_THREAD_ID),
6878
6975
  { type: "text", body: "Reading your notes.", terminal: false },
6879
6976
  {
6880
6977
  type: "tool",
@@ -6894,6 +6991,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6894
6991
  result: [{ type: "text", text: "sleep early" }]
6895
6992
  },
6896
6993
  { type: "text", body: "Here's the summary.", terminal: true },
6994
+ closingSession(NEW_THREAD_ID),
6897
6995
  { type: "result", ok: true }
6898
6996
  ]
6899
6997
  },
@@ -6927,7 +7025,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6927
7025
  turnCompleted()
6928
7026
  ],
6929
7027
  expected: [
6930
- sessionEvent3(NEW_THREAD_ID),
7028
+ startedSession(NEW_THREAD_ID),
6931
7029
  {
6932
7030
  type: "tool",
6933
7031
  id: "query-success",
@@ -6945,6 +7043,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6945
7043
  input: { op: "files.read", args: { path: "/ABOUT.md" } },
6946
7044
  result: OP_SUCCESS
6947
7045
  },
7046
+ closingSession(NEW_THREAD_ID),
6948
7047
  { type: "result", ok: true }
6949
7048
  ]
6950
7049
  },
@@ -6980,7 +7079,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6980
7079
  turnCompleted()
6981
7080
  ],
6982
7081
  expected: [
6983
- sessionEvent3(NEW_THREAD_ID),
7082
+ startedSession(NEW_THREAD_ID),
6984
7083
  {
6985
7084
  type: "tool",
6986
7085
  id: "query-failed",
@@ -6998,6 +7097,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
6998
7097
  input: { op: "files.read", args: { path: "/ABOUT.md" } },
6999
7098
  result: OP_FAILURE
7000
7099
  },
7100
+ closingSession(NEW_THREAD_ID),
7001
7101
  { type: "result", ok: true }
7002
7102
  ]
7003
7103
  },
@@ -7021,7 +7121,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7021
7121
  turnCompleted()
7022
7122
  ],
7023
7123
  expected: [
7024
- sessionEvent3(NEW_THREAD_ID),
7124
+ startedSession(NEW_THREAD_ID),
7025
7125
  {
7026
7126
  type: "tool",
7027
7127
  id: "mcp-failed",
@@ -7039,6 +7139,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7039
7139
  input: { q: "receipt" },
7040
7140
  result: "connection closed"
7041
7141
  },
7142
+ closingSession(NEW_THREAD_ID),
7042
7143
  { type: "result", ok: true }
7043
7144
  ]
7044
7145
  },
@@ -7064,7 +7165,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7064
7165
  turnCompleted()
7065
7166
  ],
7066
7167
  expected: [
7067
- sessionEvent3(NEW_THREAD_ID),
7168
+ startedSession(NEW_THREAD_ID),
7068
7169
  {
7069
7170
  type: "tool",
7070
7171
  id: "mcp-image",
@@ -7082,6 +7183,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7082
7183
  input: {},
7083
7184
  result: [{ type: "image", data: "base64-bytes", mimeType: "image/png" }]
7084
7185
  },
7186
+ closingSession(NEW_THREAD_ID),
7085
7187
  { type: "result", ok: true }
7086
7188
  ]
7087
7189
  },
@@ -7122,7 +7224,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7122
7224
  turnCompleted()
7123
7225
  ],
7124
7226
  expected: [
7125
- sessionEvent3(NEW_THREAD_ID),
7227
+ startedSession(NEW_THREAD_ID),
7126
7228
  { type: "tool", id: "t1", name: "list", phase: "start", summary: "", input: {} },
7127
7229
  {
7128
7230
  type: "tool",
@@ -7133,6 +7235,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7133
7235
  input: { path: "/product/tasks" },
7134
7236
  result: [{ type: "text", text: "CT481.md" }]
7135
7237
  },
7238
+ closingSession(NEW_THREAD_ID),
7136
7239
  { type: "result", ok: true }
7137
7240
  ]
7138
7241
  },
@@ -7150,7 +7253,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7150
7253
  turnCompleted()
7151
7254
  ],
7152
7255
  expected: [
7153
- sessionEvent3(NEW_THREAD_ID),
7256
+ startedSession(NEW_THREAD_ID),
7154
7257
  {
7155
7258
  type: "tool",
7156
7259
  id: "ws1",
@@ -7167,6 +7270,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7167
7270
  summary: "best pizza in nyc",
7168
7271
  input: { query: "best pizza in nyc" }
7169
7272
  },
7273
+ closingSession(NEW_THREAD_ID),
7170
7274
  { type: "result", ok: true }
7171
7275
  ]
7172
7276
  },
@@ -7182,7 +7286,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7182
7286
  turnCompleted()
7183
7287
  ],
7184
7288
  expected: [
7185
- sessionEvent3(NEW_THREAD_ID),
7289
+ startedSession(NEW_THREAD_ID),
7186
7290
  {
7187
7291
  type: "tool",
7188
7292
  id: "ws2",
@@ -7199,6 +7303,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7199
7303
  summary: "",
7200
7304
  input: { query: "" }
7201
7305
  },
7306
+ closingSession(NEW_THREAD_ID),
7202
7307
  { type: "result", ok: true }
7203
7308
  ]
7204
7309
  },
@@ -7229,7 +7334,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7229
7334
  turnCompleted()
7230
7335
  ],
7231
7336
  expected: [
7232
- sessionEvent3(NEW_THREAD_ID),
7337
+ startedSession(NEW_THREAD_ID),
7233
7338
  { type: "text", body: "Working on it.", terminal: false },
7234
7339
  {
7235
7340
  type: "tool",
@@ -7261,7 +7366,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7261
7366
  turnFailed("provider exploded")
7262
7367
  ],
7263
7368
  expected: [
7264
- sessionEvent3(NEW_THREAD_ID),
7369
+ startedSession(NEW_THREAD_ID),
7265
7370
  { type: "text", body: "Partial work.", terminal: false },
7266
7371
  { type: "runtime_notice", body: "provider exploded" },
7267
7372
  { type: "result", ok: false, reason: "error:provider exploded" }
@@ -7278,7 +7383,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7278
7383
  fatalError("stream died")
7279
7384
  ],
7280
7385
  expected: [
7281
- sessionEvent3(NEW_THREAD_ID),
7386
+ startedSession(NEW_THREAD_ID),
7282
7387
  { type: "text", body: "Starting.", terminal: false },
7283
7388
  { type: "runtime_notice", body: "stream died" },
7284
7389
  { type: "result", ok: false, reason: "error:stream died" }
@@ -7291,7 +7396,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7291
7396
  request: makeRequest3(),
7292
7397
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
7293
7398
  expected: [
7294
- sessionEvent3(NEW_THREAD_ID),
7399
+ startedSession(NEW_THREAD_ID),
7295
7400
  { type: "runtime_notice", body: "429 Too Many Requests" },
7296
7401
  { type: "result", ok: false, reason: "rate_limited" }
7297
7402
  ]
@@ -7301,7 +7406,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7301
7406
  request: makeRequest3(),
7302
7407
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("503 Service Unavailable")],
7303
7408
  expected: [
7304
- sessionEvent3(NEW_THREAD_ID),
7409
+ startedSession(NEW_THREAD_ID),
7305
7410
  { type: "runtime_notice", body: "503 Service Unavailable" },
7306
7411
  { type: "result", ok: false, reason: "server_error" }
7307
7412
  ]
@@ -7314,7 +7419,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7314
7419
  turnFailed("You've hit your session limit \xB7 resets 8:20pm (UTC)")
7315
7420
  ],
7316
7421
  expected: [
7317
- sessionEvent3(NEW_THREAD_ID),
7422
+ startedSession(NEW_THREAD_ID),
7318
7423
  {
7319
7424
  type: "runtime_notice",
7320
7425
  body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
@@ -7327,7 +7432,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7327
7432
  request: makeRequest3(),
7328
7433
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("401 Unauthorized: invalid api key")],
7329
7434
  expected: [
7330
- sessionEvent3(NEW_THREAD_ID),
7435
+ startedSession(NEW_THREAD_ID),
7331
7436
  { type: "runtime_notice", body: "401 Unauthorized: invalid api key" },
7332
7437
  { type: "result", ok: false, reason: "auth_expired" }
7333
7438
  ]
@@ -7339,7 +7444,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7339
7444
  request: makeRequest3(),
7340
7445
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
7341
7446
  expected: [
7342
- sessionEvent3(NEW_THREAD_ID),
7447
+ startedSession(NEW_THREAD_ID),
7343
7448
  { type: "runtime_notice", body: "something local broke" },
7344
7449
  { type: "result", ok: false, reason: "error:something local broke" }
7345
7450
  ]
@@ -7352,7 +7457,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7352
7457
  turnFailed(` ${BOUNDED_NOTICE2}${"y".repeat(20)} `)
7353
7458
  ],
7354
7459
  expected: [
7355
- sessionEvent3(NEW_THREAD_ID),
7460
+ startedSession(NEW_THREAD_ID),
7356
7461
  { type: "runtime_notice", body: BOUNDED_NOTICE2 },
7357
7462
  { type: "result", ok: false, reason: "usage_capped" }
7358
7463
  ]
@@ -7363,26 +7468,32 @@ var CODEX_CONFORMANCE_FIXTURES = [
7363
7468
  name: "empty failure text emits no runtime notice",
7364
7469
  request: makeRequest3(),
7365
7470
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
7366
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: false, reason: "error:unknown" }]
7471
+ expected: [
7472
+ startedSession(NEW_THREAD_ID),
7473
+ { type: "result", ok: false, reason: "error:unknown" }
7474
+ ]
7367
7475
  },
7368
7476
  {
7369
7477
  name: "whitespace-only failure text emits no runtime notice",
7370
7478
  request: makeRequest3(),
7371
7479
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed(" ")],
7372
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
7480
+ expected: [startedSession(NEW_THREAD_ID), { type: "result", ok: false, reason: "error: " }]
7373
7481
  },
7374
7482
  {
7375
7483
  name: "bare status token emits no runtime notice",
7376
7484
  request: makeRequest3(),
7377
7485
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429")],
7378
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: false, reason: "rate_limited" }]
7486
+ expected: [
7487
+ startedSession(NEW_THREAD_ID),
7488
+ { type: "result", ok: false, reason: "rate_limited" }
7489
+ ]
7379
7490
  },
7380
7491
  {
7381
7492
  name: "single-word provider sentence emits a runtime notice",
7382
7493
  request: makeRequest3(),
7383
7494
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("Unavailable.")],
7384
7495
  expected: [
7385
- sessionEvent3(NEW_THREAD_ID),
7496
+ startedSession(NEW_THREAD_ID),
7386
7497
  { type: "runtime_notice", body: "Unavailable." },
7387
7498
  { type: "result", ok: false, reason: "error:Unavailable." }
7388
7499
  ]
@@ -7410,7 +7521,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7410
7521
  turnCompleted()
7411
7522
  ],
7412
7523
  expected: [
7413
- sessionEvent3(NEW_THREAD_ID),
7524
+ startedSession(NEW_THREAD_ID),
7414
7525
  {
7415
7526
  type: "result",
7416
7527
  ok: false,
@@ -7430,8 +7541,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
7430
7541
  turnCompleted()
7431
7542
  ],
7432
7543
  expected: [
7433
- sessionEvent3(NEW_THREAD_ID),
7544
+ startedSession(NEW_THREAD_ID),
7434
7545
  { type: "text", body: "All good now.", terminal: true },
7546
+ closingSession(NEW_THREAD_ID),
7435
7547
  { type: "result", ok: true }
7436
7548
  ]
7437
7549
  },
@@ -7443,7 +7555,11 @@ var CODEX_CONFORMANCE_FIXTURES = [
7443
7555
  name: "zero-content success (empty runtime result)",
7444
7556
  request: makeRequest3(),
7445
7557
  nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
7446
- expected: [sessionEvent3(NEW_THREAD_ID), { type: "result", ok: true }]
7558
+ expected: [
7559
+ startedSession(NEW_THREAD_ID),
7560
+ closingSession(NEW_THREAD_ID),
7561
+ { type: "result", ok: true }
7562
+ ]
7447
7563
  },
7448
7564
  {
7449
7565
  // Empty-final: a clean turn that ended on a tool call with no closing message.
@@ -7468,7 +7584,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7468
7584
  turnCompleted()
7469
7585
  ],
7470
7586
  expected: [
7471
- sessionEvent3(NEW_THREAD_ID),
7587
+ startedSession(NEW_THREAD_ID),
7472
7588
  {
7473
7589
  type: "tool",
7474
7590
  id: "t1",
@@ -7486,6 +7602,7 @@ var CODEX_CONFORMANCE_FIXTURES = [
7486
7602
  input: { changes: [{ path: "a.md", kind: "add" }] },
7487
7603
  result: [{ path: "a.md", kind: "add" }]
7488
7604
  },
7605
+ closingSession(NEW_THREAD_ID),
7489
7606
  { type: "result", ok: true }
7490
7607
  ]
7491
7608
  },
@@ -7501,9 +7618,10 @@ var CODEX_CONFORMANCE_FIXTURES = [
7501
7618
  turnCompleted()
7502
7619
  ],
7503
7620
  expected: [
7504
- sessionEvent3(NEW_THREAD_ID),
7621
+ startedSession(NEW_THREAD_ID),
7505
7622
  { type: "thinking", text: "Let me check the notes." },
7506
7623
  { type: "text", body: "All set.", terminal: true },
7624
+ closingSession(NEW_THREAD_ID),
7507
7625
  { type: "result", ok: true }
7508
7626
  ]
7509
7627
  },
@@ -7521,8 +7639,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
7521
7639
  }),
7522
7640
  nativeStream: [threadStarted(NEW_THREAD_ID), agentMessage("m1", "Done."), turnCompleted()],
7523
7641
  expected: [
7524
- sessionEvent3(NEW_THREAD_ID),
7642
+ startedSession(NEW_THREAD_ID),
7525
7643
  { type: "text", body: "Done.", terminal: true },
7644
+ closingSession(NEW_THREAD_ID),
7526
7645
  {
7527
7646
  type: "result",
7528
7647
  ok: true,
@@ -7544,7 +7663,8 @@ var CODEX_CONFORMANCE_FIXTURES = [
7544
7663
  threadId: "th1",
7545
7664
  cwd: DIR2,
7546
7665
  promptFingerprint: fingerprintPrompt(PROMPT),
7547
- usageBaseline: null
7666
+ usageBaseline: null,
7667
+ compactions: 0
7548
7668
  })
7549
7669
  }),
7550
7670
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
@@ -7566,8 +7686,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
7566
7686
  }),
7567
7687
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
7568
7688
  expected: [
7569
- sessionEvent3("th1"),
7689
+ startedSession("th1"),
7570
7690
  { type: "text", body: "Back again.", terminal: true },
7691
+ closingSession("th1"),
7571
7692
  { type: "result", ok: true }
7572
7693
  ]
7573
7694
  },
@@ -7589,8 +7710,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
7589
7710
  }),
7590
7711
  nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
7591
7712
  expected: [
7592
- sessionEvent3("th1"),
7713
+ startedSession("th1"),
7593
7714
  { type: "text", body: "Back again.", terminal: true },
7715
+ closingSession("th1"),
7594
7716
  { type: "result", ok: true }
7595
7717
  ]
7596
7718
  },
@@ -7616,8 +7738,9 @@ var CODEX_CONFORMANCE_FIXTURES = [
7616
7738
  turnCompleted()
7617
7739
  ],
7618
7740
  expected: [
7619
- sessionEvent3(NEW_THREAD_ID, DIR2, true),
7741
+ startedSession(NEW_THREAD_ID, DIR2, true),
7620
7742
  { type: "text", body: "Fresh start.", terminal: true },
7743
+ closingSession(NEW_THREAD_ID),
7621
7744
  { type: "result", ok: true }
7622
7745
  ]
7623
7746
  }
@@ -7699,7 +7822,7 @@ var ConnectorHealthStore = class {
7699
7822
  // src/dispatcher.ts
7700
7823
  import { randomUUID as randomUUID4 } from "crypto";
7701
7824
  import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
7702
- import { join as join18 } from "path";
7825
+ import { join as join19 } from "path";
7703
7826
 
7704
7827
  // src/runner/launch.ts
7705
7828
  import { createInterface } from "readline";
@@ -9097,6 +9220,7 @@ function buildCompanionTurnRequest(params) {
9097
9220
  const { turnContext: t } = params;
9098
9221
  return {
9099
9222
  systemPrompt: t.systemPrompt,
9223
+ ...t.standing ? { standing: t.standing } : {},
9100
9224
  prompt: t.prompt,
9101
9225
  content: t.content,
9102
9226
  config: t.config,
@@ -9208,6 +9332,62 @@ function message(err) {
9208
9332
  return err instanceof Error ? err.message : String(err);
9209
9333
  }
9210
9334
 
9335
+ // src/codex-compactions.ts
9336
+ import { createReadStream } from "fs";
9337
+ import { readdir } from "fs/promises";
9338
+ import { homedir as homedir6 } from "os";
9339
+ import { join as join16 } from "path";
9340
+ import { createInterface as createInterface2 } from "readline";
9341
+ var DAY_MS = 864e5;
9342
+ var UUID_V7 = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9343
+ var RECORD_HEAD_CHARS = 200;
9344
+ function createCodexCompactionCounter(codexHome = process.env.CODEX_HOME || join16(homedir6(), ".codex")) {
9345
+ return async (threadId) => {
9346
+ try {
9347
+ const rollout = await findRollout(codexHome, threadId);
9348
+ return rollout ? await countCompactions(rollout) : null;
9349
+ } catch {
9350
+ return null;
9351
+ }
9352
+ };
9353
+ }
9354
+ async function findRollout(codexHome, threadId) {
9355
+ const id = UUID_V7.exec(threadId);
9356
+ if (!id) return null;
9357
+ const createdAt = parseInt(`${id[1]}${id[2]}`, 16);
9358
+ const suffix = `-${threadId}.jsonl`;
9359
+ for (const offset of [0, -DAY_MS, DAY_MS]) {
9360
+ const day = new Date(createdAt + offset);
9361
+ const dir2 = join16(
9362
+ codexHome,
9363
+ "sessions",
9364
+ String(day.getUTCFullYear()),
9365
+ String(day.getUTCMonth() + 1).padStart(2, "0"),
9366
+ String(day.getUTCDate()).padStart(2, "0")
9367
+ );
9368
+ let names;
9369
+ try {
9370
+ names = await readdir(dir2);
9371
+ } catch {
9372
+ continue;
9373
+ }
9374
+ const name = names.find((n) => n.startsWith("rollout-") && n.endsWith(suffix));
9375
+ if (name) return join16(dir2, name);
9376
+ }
9377
+ return null;
9378
+ }
9379
+ async function countCompactions(rollout) {
9380
+ const lines2 = createInterface2({
9381
+ input: createReadStream(rollout, { encoding: "utf8" }),
9382
+ crlfDelay: Infinity
9383
+ });
9384
+ let count = 0;
9385
+ for await (const line of lines2) {
9386
+ if (line.slice(0, RECORD_HEAD_CHARS).includes('"type":"compacted"')) count += 1;
9387
+ }
9388
+ return count;
9389
+ }
9390
+
9211
9391
  // src/turn-seq-floor.ts
9212
9392
  var SeqFloorUnavailable = class extends Error {
9213
9393
  constructor(detail) {
@@ -9305,15 +9485,15 @@ function reportFailure(config, body) {
9305
9485
 
9306
9486
  // src/prepared.ts
9307
9487
  import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "fs";
9308
- import { join as join16 } from "path";
9488
+ import { join as join17 } from "path";
9309
9489
  function dirFor(workspaceId) {
9310
- return join16(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
9490
+ return join17(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
9311
9491
  }
9312
9492
  function conversationDir(workspaceId, conversationId) {
9313
- return join16(dirFor(workspaceId), encodeURIComponent(conversationId));
9493
+ return join17(dirFor(workspaceId), encodeURIComponent(conversationId));
9314
9494
  }
9315
9495
  function pathFor2(workspaceId, conversationId, agentId) {
9316
- return join16(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
9496
+ return join17(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
9317
9497
  }
9318
9498
  function readPrepared(workspaceId, conversationId, agentId) {
9319
9499
  const path = pathFor2(workspaceId, conversationId, agentId);
@@ -9345,9 +9525,9 @@ function clearPrepared(workspaceId, conversationId, agentId) {
9345
9525
 
9346
9526
  // src/transcript-writer.ts
9347
9527
  import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
9348
- import { basename as basename3, dirname as dirname7, join as join17 } from "path";
9528
+ import { basename as basename3, dirname as dirname7, join as join18 } from "path";
9349
9529
  function transcriptsDir() {
9350
- return join17(runnerStateDir(), "transcripts");
9530
+ return join18(runnerStateDir(), "transcripts");
9351
9531
  }
9352
9532
  var RETAIN = 200;
9353
9533
  var ANOMALY_RETAIN = 50;
@@ -9357,7 +9537,7 @@ var TranscriptWriter = class {
9357
9537
  onWarn;
9358
9538
  constructor(dir2, meta, onWarn) {
9359
9539
  this.onWarn = onWarn;
9360
- this.path = join17(dir2, fileName(meta));
9540
+ this.path = join18(dir2, fileName(meta));
9361
9541
  try {
9362
9542
  mkdirSync8(dir2, { recursive: true });
9363
9543
  try {
@@ -9386,10 +9566,10 @@ var TranscriptWriter = class {
9386
9566
  preserveAnomaly() {
9387
9567
  if (this.broken) return;
9388
9568
  try {
9389
- const dir2 = join17(dirname7(this.path), "anomalies");
9569
+ const dir2 = join18(dirname7(this.path), "anomalies");
9390
9570
  mkdirSync8(dir2, { recursive: true, mode: 448 });
9391
9571
  chmodSync4(dir2, 448);
9392
- const target = join17(dir2, basename3(this.path));
9572
+ const target = join18(dir2, basename3(this.path));
9393
9573
  copyFileSync(this.path, target);
9394
9574
  chmodSync4(target, 384);
9395
9575
  pruneOld(dir2, ANOMALY_RETAIN);
@@ -9435,7 +9615,7 @@ function pruneOld(dir2, retain) {
9435
9615
  const drop = files.sort().slice(0, files.length - retain);
9436
9616
  for (const f of drop) {
9437
9617
  try {
9438
- rmSync6(join17(dir2, f), { force: true });
9618
+ rmSync6(join18(dir2, f), { force: true });
9439
9619
  } catch {
9440
9620
  }
9441
9621
  }
@@ -10344,6 +10524,8 @@ var TurnExecution = class {
10344
10524
  turnId: this.turnId,
10345
10525
  warn: (meta, msg) => turnLog.warn(meta, msg)
10346
10526
  }),
10527
+ // Whether a resumed thread compacted since its standing preamble was sent.
10528
+ countCompactions: createCodexCompactionCounter(),
10347
10529
  ...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
10348
10530
  })
10349
10531
  );
@@ -10404,6 +10586,9 @@ var TurnExecution = class {
10404
10586
  );
10405
10587
  return {
10406
10588
  systemPrompt: context.systemPrompt,
10589
+ // Replaced even when absent, so the segments can't outlive the
10590
+ // `systemPrompt` they were split from.
10591
+ standing: context.standing,
10407
10592
  prompt: context.prompt,
10408
10593
  content: context.content
10409
10594
  };
@@ -10815,7 +11000,7 @@ function checkoutState(cwd) {
10815
11000
  if (entries.length === 0) {
10816
11001
  return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
10817
11002
  }
10818
- const gitPath = join18(cwd, ".git");
11003
+ const gitPath = join19(cwd, ".git");
10819
11004
  if (!existsSync12(gitPath)) return { ok: true, reason: "usable" };
10820
11005
  let stat;
10821
11006
  try {
@@ -10823,7 +11008,7 @@ function checkoutState(cwd) {
10823
11008
  } catch (error) {
10824
11009
  return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
10825
11010
  }
10826
- if (stat.isDirectory() && !existsSync12(join18(gitPath, "HEAD")))
11011
+ if (stat.isDirectory() && !existsSync12(join19(gitPath, "HEAD")))
10827
11012
  return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
10828
11013
  return { ok: true, reason: "usable" };
10829
11014
  }
@@ -11030,7 +11215,7 @@ function createLocalProcessProvider(opts = {}) {
11030
11215
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
11031
11216
  import { constants as constants3, tmpdir as tmpdir3 } from "os";
11032
11217
  import {
11033
- createReadStream,
11218
+ createReadStream as createReadStream2,
11034
11219
  createWriteStream as createWriteStream2,
11035
11220
  existsSync as existsSync15,
11036
11221
  mkdirSync as mkdirSync10,
@@ -11041,7 +11226,7 @@ import {
11041
11226
  statSync as statSync4,
11042
11227
  writeFileSync as writeFileSync6
11043
11228
  } from "fs";
11044
- import { join as join21 } from "path";
11229
+ import { join as join22 } from "path";
11045
11230
  import { Writable } from "stream";
11046
11231
 
11047
11232
  // src/providers/docker-cli.ts
@@ -11235,7 +11420,7 @@ import {
11235
11420
  renameSync as renameSync5,
11236
11421
  rmSync as rmSync8
11237
11422
  } from "fs";
11238
- import { join as join19 } from "path";
11423
+ import { join as join20 } from "path";
11239
11424
  function routes(t) {
11240
11425
  const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
11241
11426
  const agent = `${conv}/participants/agents/${t.agentId}`;
@@ -11281,7 +11466,7 @@ function publishStaged(from, to, move = atomicMove) {
11281
11466
  const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
11282
11467
  let entry = {};
11283
11468
  try {
11284
- entry = JSON.parse(readFileSync9(join19(from, name), "utf8"));
11469
+ entry = JSON.parse(readFileSync9(join20(from, name), "utf8"));
11285
11470
  } catch {
11286
11471
  }
11287
11472
  return { name, entry };
@@ -11294,7 +11479,7 @@ function publishStaged(from, to, move = atomicMove) {
11294
11479
  let published = 0;
11295
11480
  for (const { name } of staged) {
11296
11481
  try {
11297
- move(join19(from, name), join19(to, name));
11482
+ move(join20(from, name), join20(to, name));
11298
11483
  } catch {
11299
11484
  return { published, complete: false };
11300
11485
  }
@@ -11307,7 +11492,7 @@ function publishStaged(from, to, move = atomicMove) {
11307
11492
  // src/providers/runner-base.ts
11308
11493
  import { createHash as createHash3 } from "crypto";
11309
11494
  import { existsSync as existsSync14, realpathSync as realpathSync4 } from "fs";
11310
- import { dirname as dirname8, join as join20, sep as sep3 } from "path";
11495
+ import { dirname as dirname8, join as join21, sep as sep3 } from "path";
11311
11496
  import { fileURLToPath as fileURLToPath5 } from "url";
11312
11497
  var COMPANION_MOUNT = "/opt/cabane/companion";
11313
11498
  var BASE_DOCKERFILE = `FROM node:22-slim
@@ -11355,11 +11540,11 @@ async function ensureBaseImage(docker, image, log) {
11355
11540
  function ownInstall(moduleFile = fileURLToPath5(import.meta.url)) {
11356
11541
  let dir2 = dirname8(moduleFile);
11357
11542
  for (let i = 0; i < 4; i++) {
11358
- if (existsSync14(join20(dir2, "dist", "cli.js")) && existsSync14(join20(dir2, "package.json"))) break;
11543
+ if (existsSync14(join21(dir2, "dist", "cli.js")) && existsSync14(join21(dir2, "package.json"))) break;
11359
11544
  dir2 = dirname8(dir2);
11360
11545
  }
11361
- if (!existsSync14(join20(dir2, "dist", "cli.js"))) return null;
11362
- const sdk = join20(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
11546
+ if (!existsSync14(join21(dir2, "dist", "cli.js"))) return null;
11547
+ const sdk = join21(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
11363
11548
  if (!existsSync14(sdk)) return null;
11364
11549
  const root = realpathSync4(dir2);
11365
11550
  return realpathSync4(sdk).startsWith(root + sep3) ? root : null;
@@ -11498,39 +11683,39 @@ function networkCreateError(stderr) {
11498
11683
  }
11499
11684
  function sessionArchivePath(deviceDir, attemptId) {
11500
11685
  if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
11501
- return join21(deviceDir, "attempts", attemptId, "session.tar");
11686
+ return join22(deviceDir, "attempts", attemptId, "session.tar");
11502
11687
  }
11503
11688
  function pruneOtherAttempts(deviceDir, pair, keep) {
11504
- const root = join21(deviceDir, "attempts");
11689
+ const root = join22(deviceDir, "attempts");
11505
11690
  if (!existsSync15(root)) return;
11506
11691
  for (const id of readdirSync4(root)) {
11507
11692
  if (id === keep || !UUID_RE.test(id)) continue;
11508
11693
  let pairOf = "";
11509
11694
  try {
11510
- pairOf = readFileSync10(join21(root, id, "pair"), "utf8");
11695
+ pairOf = readFileSync10(join22(root, id, "pair"), "utf8");
11511
11696
  } catch {
11512
11697
  continue;
11513
11698
  }
11514
11699
  if (pairOf === `${pair.conversationId}/${pair.agentId}`) {
11515
- rmSync9(join21(root, id), { recursive: true, force: true });
11700
+ rmSync9(join22(root, id), { recursive: true, force: true });
11516
11701
  }
11517
11702
  }
11518
11703
  }
11519
11704
  var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
11520
11705
  function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
11521
- const root = join21(deviceDir, "attempts");
11706
+ const root = join22(deviceDir, "attempts");
11522
11707
  if (!existsSync15(root)) return [];
11523
11708
  const removed = [];
11524
11709
  for (const id of readdirSync4(root)) {
11525
11710
  if (!UUID_RE.test(id)) continue;
11526
11711
  let kept;
11527
11712
  try {
11528
- kept = statSync4(join21(root, id)).mtimeMs;
11713
+ kept = statSync4(join22(root, id)).mtimeMs;
11529
11714
  } catch {
11530
11715
  continue;
11531
11716
  }
11532
11717
  if (now - kept < maxAgeMs) continue;
11533
- rmSync9(join21(root, id), { recursive: true, force: true });
11718
+ rmSync9(join22(root, id), { recursive: true, force: true });
11534
11719
  removed.push(id);
11535
11720
  }
11536
11721
  return removed;
@@ -11731,11 +11916,11 @@ function createDockerProvider(opts) {
11731
11916
  await must(["network", "disconnect", "-f", other, name], "moving the container");
11732
11917
  if (others.includes(sharedNetwork)) await retireSharedNetwork();
11733
11918
  };
11734
- const socketRoot = opts.socketRoot ?? join21(
11919
+ const socketRoot = opts.socketRoot ?? join22(
11735
11920
  process.env.XDG_RUNTIME_DIR && existsSync15(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
11736
11921
  `cabane-runners-${opts.deviceKey}`
11737
11922
  );
11738
- const socketDir = (name) => join21(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
11923
+ const socketDir = (name) => join22(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
11739
11924
  const create = async (name, attempt) => {
11740
11925
  mkdirSync10(socketRoot, { recursive: true, mode: 448 });
11741
11926
  mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
@@ -11877,7 +12062,7 @@ function createDockerProvider(opts) {
11877
12062
  }
11878
12063
  };
11879
12064
  const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
11880
- const pendingPath = (name) => join21(opts.deviceDir, "runners-pending", `${name}.json`);
12065
+ const pendingPath = (name) => join22(opts.deviceDir, "runners-pending", `${name}.json`);
11881
12066
  const readPending = (name) => {
11882
12067
  try {
11883
12068
  const ids = JSON.parse(readFileSync10(pendingPath(name), "utf8"));
@@ -11891,12 +12076,12 @@ function createDockerProvider(opts) {
11891
12076
  rmSync9(pendingPath(name), { force: true });
11892
12077
  return;
11893
12078
  }
11894
- mkdirSync10(join21(opts.deviceDir, "runners-pending"), { recursive: true });
12079
+ mkdirSync10(join22(opts.deviceDir, "runners-pending"), { recursive: true });
11895
12080
  writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
11896
12081
  };
11897
12082
  const keepSession = async (name, attempt, attemptId) => {
11898
12083
  const archive = sessionArchivePath(opts.deviceDir, attemptId);
11899
- mkdirSync10(join21(archive, ".."), { recursive: true });
12084
+ mkdirSync10(join22(archive, ".."), { recursive: true });
11900
12085
  const partial = `${archive}.${process.pid}.partial`;
11901
12086
  const file = new CappedFile(partial, sessionCap);
11902
12087
  const r = await execIn(
@@ -11913,7 +12098,7 @@ function createDockerProvider(opts) {
11913
12098
  );
11914
12099
  if (r.code === 0 && !file.over) {
11915
12100
  renameSync6(partial, archive);
11916
- writeFileSync6(join21(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
12101
+ writeFileSync6(join22(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
11917
12102
  pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
11918
12103
  await writeMarker(name, attemptId);
11919
12104
  log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
@@ -11927,7 +12112,7 @@ function createDockerProvider(opts) {
11927
12112
  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"
11928
12113
  );
11929
12114
  };
11930
- const stagingDir = (name) => join21(opts.deviceDir, "runners-pending", `${name}.outbox`);
12115
+ const stagingDir = (name) => join22(opts.deviceDir, "runners-pending", `${name}.outbox`);
11931
12116
  const ensureRunning = async (name) => {
11932
12117
  const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
11933
12118
  if (seen.code !== 0) return false;
@@ -11935,7 +12120,7 @@ function createDockerProvider(opts) {
11935
12120
  return (await docker.run(["start", name])).code === 0;
11936
12121
  };
11937
12122
  const dropArchive = (attemptId) => {
11938
- rmSync9(join21(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
12123
+ rmSync9(join22(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
11939
12124
  recursive: true,
11940
12125
  force: true
11941
12126
  });
@@ -12004,7 +12189,7 @@ function createDockerProvider(opts) {
12004
12189
  if (seen.stdout.trim().split(" ")[0] !== "true")
12005
12190
  await must(["start", name], "starting the container");
12006
12191
  const git = await serveGitCredentials({
12007
- socketPath: join21(socketDir(name), "git.sock"),
12192
+ socketPath: join22(socketDir(name), "git.sock"),
12008
12193
  hosts: opts.gitHosts,
12009
12194
  fill: opts.gitFill,
12010
12195
  log
@@ -12040,7 +12225,7 @@ function createDockerProvider(opts) {
12040
12225
  const archive = sessionArchivePath(opts.deviceDir, attemptId);
12041
12226
  if (existsSync15(archive)) {
12042
12227
  const r = await execIn(name, ["tar", "-x", "-C", CONTAINER_HOME, "-f", "-"], {
12043
- input: createReadStream(archive)
12228
+ input: createReadStream2(archive)
12044
12229
  });
12045
12230
  if (r.code !== 0) {
12046
12231
  log.warn(
@@ -12153,15 +12338,15 @@ function createDockerProvider(opts) {
12153
12338
  const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
12154
12339
  if (transcripts) {
12155
12340
  const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
12156
- if (kept.length) mkdirSync10(join21(opts.hostStateDir, "transcripts"), { recursive: true });
12157
- for (const f of kept) writeFileSync6(join21(opts.hostStateDir, "transcripts", f.name), f.data);
12341
+ if (kept.length) mkdirSync10(join22(opts.hostStateDir, "transcripts"), { recursive: true });
12342
+ for (const f of kept) writeFileSync6(join22(opts.hostStateDir, "transcripts", f.name), f.data);
12158
12343
  await removeIn(
12159
12344
  name,
12160
12345
  `${CONTAINER_STATE}/transcripts`,
12161
12346
  kept.map((f) => f.name)
12162
12347
  );
12163
12348
  }
12164
- const outboxRel = join21("outbox", encodeURIComponent(attempt.agentId));
12349
+ const outboxRel = join22("outbox", encodeURIComponent(attempt.agentId));
12165
12350
  const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
12166
12351
  let unpublished = false;
12167
12352
  if (outbox) {
@@ -12187,7 +12372,7 @@ function createDockerProvider(opts) {
12187
12372
  const staged = stagingDir(name);
12188
12373
  mkdirSync10(staged, { recursive: true });
12189
12374
  writeFileSync6(
12190
- join21(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
12375
+ join22(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
12191
12376
  `${JSON.stringify(entry)}
12192
12377
  `
12193
12378
  );
@@ -12200,7 +12385,7 @@ function createDockerProvider(opts) {
12200
12385
  if (outbox.complete) {
12201
12386
  const published = publishStaged(
12202
12387
  stagingDir(name),
12203
- join21(opts.hostStateDir, outboxRel),
12388
+ join22(opts.hostStateDir, outboxRel),
12204
12389
  opts.moveFile
12205
12390
  );
12206
12391
  queued = published.published;
@@ -12415,8 +12600,8 @@ import {
12415
12600
  } from "fs";
12416
12601
  import { createRequire as createRequire2 } from "module";
12417
12602
  import { isIP } from "net";
12418
- import { homedir as homedir6, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
12419
- import { dirname as dirname9, join as join22, sep as sep4 } from "path";
12603
+ import { homedir as homedir7, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
12604
+ import { dirname as dirname9, join as join23, sep as sep4 } from "path";
12420
12605
  import { fileURLToPath as fileURLToPath6 } from "url";
12421
12606
  import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
12422
12607
  function accountHome() {
@@ -12513,7 +12698,7 @@ function companionStoreWatch(log) {
12513
12698
  return new CompanionStoreWatch({
12514
12699
  log,
12515
12700
  roots: () => {
12516
- const home = homedir6();
12701
+ const home = homedir7();
12517
12702
  return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
12518
12703
  }
12519
12704
  });
@@ -12612,8 +12797,8 @@ function emptyHarnessVersions() {
12612
12797
  }
12613
12798
 
12614
12799
  // src/harness-copy.ts
12615
- import { homedir as homedir7 } from "os";
12616
- import { join as join23 } from "path";
12800
+ import { homedir as homedir8 } from "os";
12801
+ import { join as join24 } from "path";
12617
12802
  var HARNESS_LABELS = {
12618
12803
  "claude-code": "Claude Code",
12619
12804
  codex: "Codex",
@@ -12627,7 +12812,7 @@ function failureText(runtime, reason) {
12627
12812
  case "spawn":
12628
12813
  return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
12629
12814
  case "config":
12630
- return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(join23(homedir7(), ".codex", "config.toml"))})` : ""}. Fix it, then try again.`;
12815
+ return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(join24(homedir8(), ".codex", "config.toml"))})` : ""}. Fix it, then try again.`;
12631
12816
  case "unreadable":
12632
12817
  return `Couldn't check whether ${label} is signed in: it answered in a way Cabane couldn't read. Try again, and if it keeps happening, update Cabane.`;
12633
12818
  }
@@ -12967,7 +13152,7 @@ import {
12967
13152
  rmSync as rmSync11,
12968
13153
  writeFileSync as writeFileSync8
12969
13154
  } from "fs";
12970
- import { join as join24 } from "path";
13155
+ import { join as join25 } from "path";
12971
13156
  var MAX_ENTRIES = 2e3;
12972
13157
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
12973
13158
  var Outbox = class {
@@ -12980,10 +13165,10 @@ var Outbox = class {
12980
13165
  // Resolved lazily (per call) so tests that swap HOME between
12981
13166
  // cases route writes at the right tmpdir.
12982
13167
  dir() {
12983
- return join24(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
13168
+ return join25(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
12984
13169
  }
12985
13170
  fileFor(turnId, seq) {
12986
- return join24(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
13171
+ return join25(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
12987
13172
  }
12988
13173
  // Persist a commit for later draining. Atomic (temp file + rename) so a
12989
13174
  // concurrent `list()` never reads a half-written entry, then enforces the
@@ -13025,7 +13210,7 @@ var Outbox = class {
13025
13210
  const entries = [];
13026
13211
  for (const name of names) {
13027
13212
  if (!name.endsWith(".json")) continue;
13028
- const full = join24(dir2, name);
13213
+ const full = join25(dir2, name);
13029
13214
  try {
13030
13215
  const parsed = JSON.parse(readFileSync12(full, "utf8"));
13031
13216
  if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
@@ -13249,7 +13434,7 @@ var SseSubscriber = class {
13249
13434
  // src/updater.ts
13250
13435
  import { spawn as spawn8 } from "child_process";
13251
13436
  import { copyFileSync as copyFileSync3, existsSync as existsSync18, mkdirSync as mkdirSync16, renameSync as renameSync11, rmSync as rmSync14 } from "fs";
13252
- import { dirname as dirname13, join as join28 } from "path";
13437
+ import { dirname as dirname13, join as join29 } from "path";
13253
13438
 
13254
13439
  // src/boot-rule.ts
13255
13440
  import {
@@ -13261,7 +13446,7 @@ import {
13261
13446
  symlinkSync,
13262
13447
  writeFileSync as writeFileSync9
13263
13448
  } from "fs";
13264
- import { dirname as dirname10, join as join25 } from "path";
13449
+ import { dirname as dirname10, join as join26 } from "path";
13265
13450
  var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
13266
13451
  var MAX_STAGE_ATTEMPTS = 3;
13267
13452
  function superseded(pending, ...running) {
@@ -13294,26 +13479,26 @@ import {
13294
13479
  rmSync as rmSync13,
13295
13480
  symlinkSync as symlinkSync2
13296
13481
  } from "fs";
13297
- import { dirname as dirname11, join as join26 } from "path";
13482
+ import { dirname as dirname11, join as join27 } from "path";
13298
13483
  import { fileURLToPath as fileURLToPath7 } from "url";
13299
13484
  function versionsDir() {
13300
- return join26(cabaneDir(), "versions");
13485
+ return join27(cabaneDir(), "versions");
13301
13486
  }
13302
13487
  function currentLink() {
13303
- return join26(cabaneDir(), "current");
13488
+ return join27(cabaneDir(), "current");
13304
13489
  }
13305
13490
  function bootGuardPath() {
13306
- return join26(cabaneDir(), "boot-guard.mjs");
13491
+ return join27(cabaneDir(), "boot-guard.mjs");
13307
13492
  }
13308
13493
  function versionPackageRoot(version) {
13309
- return join26(versionsDir(), version, "node_modules", "@cabane", "companion");
13494
+ return join27(versionsDir(), version, "node_modules", "@cabane", "companion");
13310
13495
  }
13311
13496
  function ownPackageRoot(moduleUrl = import.meta.url) {
13312
13497
  return dirname11(dirname11(fileURLToPath7(moduleUrl)));
13313
13498
  }
13314
13499
  function packageVersion(root) {
13315
13500
  try {
13316
- const pkg2 = JSON.parse(readFileSync14(join26(root, "package.json"), "utf8"));
13501
+ const pkg2 = JSON.parse(readFileSync14(join27(root, "package.json"), "utf8"));
13317
13502
  return typeof pkg2.version === "string" ? pkg2.version : null;
13318
13503
  } catch {
13319
13504
  return null;
@@ -13322,7 +13507,7 @@ function packageVersion(root) {
13322
13507
  function currentRoot() {
13323
13508
  try {
13324
13509
  const root = realpathSync7(currentLink());
13325
- return existsSync17(join26(root, "dist", "cli.js")) ? root : null;
13510
+ return existsSync17(join27(root, "dist", "cli.js")) ? root : null;
13326
13511
  } catch {
13327
13512
  return null;
13328
13513
  }
@@ -13345,7 +13530,7 @@ function pruneVersions(keep) {
13345
13530
  const removed = [];
13346
13531
  for (const name of readdirSync6(dir2)) {
13347
13532
  if (keep.includes(name)) continue;
13348
- rmSync13(join26(dir2, name), { recursive: true, force: true });
13533
+ rmSync13(join27(dir2, name), { recursive: true, force: true });
13349
13534
  removed.push(name);
13350
13535
  }
13351
13536
  return removed;
@@ -13364,7 +13549,7 @@ function compareVersions(a, b) {
13364
13549
 
13365
13550
  // src/update-state.ts
13366
13551
  import { mkdirSync as mkdirSync15, readFileSync as readFileSync15, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "fs";
13367
- import { dirname as dirname12, join as join27 } from "path";
13552
+ import { dirname as dirname12, join as join28 } from "path";
13368
13553
  import { z as z18 } from "zod";
13369
13554
  var resultSchema = z18.object({
13370
13555
  kind: z18.enum(["updated", "rolled_back", "failed"]),
@@ -13388,7 +13573,7 @@ var stateSchema = z18.object({
13388
13573
  failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
13389
13574
  });
13390
13575
  function updateStatePath(dir2 = cabaneDir()) {
13391
- return join27(dir2, "update.json");
13576
+ return join28(dir2, "update.json");
13392
13577
  }
13393
13578
  function readUpdateState(path = updateStatePath()) {
13394
13579
  try {
@@ -13437,7 +13622,7 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
13437
13622
  return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
13438
13623
  }
13439
13624
  function writeBootGuard(root = ownPackageRoot()) {
13440
- const built = join28(root, "dist", "boot-guard.js");
13625
+ const built = join29(root, "dist", "boot-guard.js");
13441
13626
  if (!existsSync18(built)) return false;
13442
13627
  const target = bootGuardPath();
13443
13628
  mkdirSync16(dirname13(target), { recursive: true });
@@ -13477,17 +13662,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
13477
13662
  async function stageVersion(version, harnesses, deps = {}) {
13478
13663
  const run = deps.run ?? runProcess;
13479
13664
  const node = deps.execPath ?? process.execPath;
13480
- const target = join28(versionsDir(), version);
13665
+ const target = join29(versionsDir(), version);
13481
13666
  const placed = versionPackageRoot(version);
13482
13667
  if (existsSync18(placed)) {
13483
13668
  if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
13484
13669
  return { ok: true, root: placed };
13485
13670
  }
13486
- const npm = join28(dirname13(node), "npm");
13671
+ const npm = join29(dirname13(node), "npm");
13487
13672
  if (!existsSync18(npm))
13488
13673
  return { ok: false, reason: `there is no npm beside Node at ${dirname13(node)}` };
13489
13674
  mkdirSync16(versionsDir(), { recursive: true });
13490
- const staging = join28(versionsDir(), `.${version}.staging-${process.pid}`);
13675
+ const staging = join29(versionsDir(), `.${version}.staging-${process.pid}`);
13491
13676
  rmSync14(staging, { recursive: true, force: true });
13492
13677
  const installed = await run(
13493
13678
  npm,
@@ -13516,7 +13701,7 @@ async function stageVersion(version, harnesses, deps = {}) {
13516
13701
  return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
13517
13702
  return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
13518
13703
  }
13519
- const root = join28(staging, "node_modules", "@cabane", "companion");
13704
+ const root = join29(staging, "node_modules", "@cabane", "companion");
13520
13705
  const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
13521
13706
  if (verdict) {
13522
13707
  rmSync14(staging, { recursive: true, force: true });
@@ -13528,7 +13713,7 @@ async function stageVersion(version, harnesses, deps = {}) {
13528
13713
  }
13529
13714
  async function verifyInstall(root, version, harnesses, deps) {
13530
13715
  if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
13531
- const answered = await deps.run(deps.execPath, [join28(root, "dist", "bin.js"), "--version"], {
13716
+ const answered = await deps.run(deps.execPath, [join29(root, "dist", "bin.js"), "--version"], {
13532
13717
  timeoutMs: 6e4,
13533
13718
  env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
13534
13719
  });
@@ -13536,7 +13721,7 @@ async function verifyInstall(root, version, harnesses, deps) {
13536
13721
  return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
13537
13722
  }
13538
13723
  if (harnesses.length === 0) return null;
13539
- const probed = await deps.run(deps.execPath, [join28(root, "dist", "postinstall.js"), "--json"], {
13724
+ const probed = await deps.run(deps.execPath, [join29(root, "dist", "postinstall.js"), "--json"], {
13540
13725
  timeoutMs: 6e4
13541
13726
  });
13542
13727
  let resolutions = {};
@@ -15337,9 +15522,9 @@ function handleUncaught(log, err, origin) {
15337
15522
 
15338
15523
  // src/crash-marker.ts
15339
15524
  import { existsSync as existsSync19, mkdirSync as mkdirSync17, readFileSync as readFileSync16, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "fs";
15340
- import { join as join29 } from "path";
15525
+ import { join as join30 } from "path";
15341
15526
  function crashMarkerPath() {
15342
- return join29(cabaneDir(), "last-error.json");
15527
+ return join30(cabaneDir(), "last-error.json");
15343
15528
  }
15344
15529
  function recordCrash(rec) {
15345
15530
  try {
@@ -15359,7 +15544,7 @@ function clearCrash() {
15359
15544
  // src/stray-companion.ts
15360
15545
  import { spawnSync } from "child_process";
15361
15546
  import { readFileSync as readFileSync17 } from "fs";
15362
- import { join as join30 } from "path";
15547
+ import { join as join31 } from "path";
15363
15548
  var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
15364
15549
  var START = /\sstart(\s|$)/;
15365
15550
  function companionStarts(psOutput) {
@@ -15376,7 +15561,7 @@ function companionStarts(psOutput) {
15376
15561
  }
15377
15562
  function stateDirOf(env) {
15378
15563
  const home = env.CABANE_COMPANION_HOME || env.HOME;
15379
- return home ? join30(home, ".cabane") : null;
15564
+ return home ? join31(home, ".cabane") : null;
15380
15565
  }
15381
15566
  function envFromPsE(output, command) {
15382
15567
  const line = output.replace(/\n$/, "");