@cabane/companion 0.6.145 → 0.6.146

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.
package/dist/cli.js CHANGED
@@ -110,18 +110,15 @@ var init_prepare_hook = __esm({
110
110
  // default is generous; a hook that hangs past this is killed and the turn
111
111
  // fails with a clear timeout message rather than pinning the companion.
112
112
  timeoutMs: z.number().int().positive().optional(),
113
- // CT943: re-invoke the hook on turns where a prepared result already exists,
114
- // in VALIDATE mode — the cached result is handed back as `prepared` and the
115
- // hook's stdout is ignored, so the cwd provably cannot move. Its only power
116
- // is to exit non-zero and fail the turn.
113
+ // Re-invoke the hook when a prepared result already exists, in VALIDATE
114
+ // mode: the cached result is handed back as `prepared` and stdout is
115
+ // ignored, so the cwd cannot move. Its only power is to exit non-zero and
116
+ // fail the turn. The hook resolves once per (conversation, agent), so a
117
+ // later message asking for a different environment is otherwise silently
118
+ // ignored; this lets a hook refuse it out loud.
117
119
  //
118
- // Why it exists: the hook runs exactly once per (conversation, agent), so a
119
- // later message asking for a different environment can't be honoured. Without
120
- // this, it is silently ignored — the host knows and says nothing, which is
121
- // the worst available outcome. An opt-in hook can now refuse it out loud.
122
- //
123
- // Default OFF, and deliberately: an operator hook that provisions
124
- // unconditionally must not start running on every turn because it upgraded.
120
+ // Default OFF: a hook that provisions unconditionally must not start
121
+ // running on every turn because the companion upgraded.
125
122
  validateCached: z.boolean().optional(),
126
123
  // Independent operator-owned reporter. Receives sanitized failure metadata
127
124
  // on stdin; never re-enters this hook and cannot change the turn outcome.
@@ -160,16 +157,15 @@ var init_prepare_hook = __esm({
160
157
  CABANE_AGENT_USERNAME: input.agentUsername,
161
158
  CABANE_RUNTIME: input.runtime ?? "",
162
159
  CABANE_HOST_ACCESS: input.hostAccess ? "1" : "0",
163
- // CT319: the conversation anchor is gone. These are kept as DEPRECATED
164
- // back-compat constants so an old user prepare script that still reads
165
- // them doesn't crash; a hook should key off CABANE_TRIGGER_ENTRY_PATHS.
160
+ // DEPRECATED constants, kept so an old prepare script that reads them
161
+ // doesn't crash; a hook should key off CABANE_TRIGGER_ENTRY_PATHS.
166
162
  CABANE_ANCHOR_KIND: "workspace",
167
163
  CABANE_ANCHOR_PATH: "",
168
- // CT317: newline-joined — workspace paths never contain a newline, so a
164
+ // Newline-joined — workspace paths never contain a newline, so a
169
165
  // hook splits on `\n` unambiguously. Empty string when none.
170
166
  CABANE_TRIGGER_ENTRY_PATHS: input.triggerEntryPaths.join("\n"),
171
167
  CABANE_CONVERSATION_TITLE: input.title ?? "",
172
- // CT943: the dispatching message's text, and — in validate mode only —
168
+ // The dispatching message's text, and — in validate mode only —
173
169
  // the prepared result the hook is being asked to check this dispatch
174
170
  // against. Both empty/absent on an ordinary first run.
175
171
  CABANE_TRIGGER_MESSAGE: input.messageBody ?? "",
@@ -458,18 +454,17 @@ var init_config = __esm({
458
454
  localAgentConfigSchema = z2.object({
459
455
  cwd: z2.string().optional(),
460
456
  prepareHook: prepareHookSchema.optional(),
461
- // CT289: the Claude Code auto-memory escape hatch. Auto-memory is forced OFF
462
- // by default on every companion (memory belongs in the Cabane workspace, and a
463
- // shared companion would otherwise pool one cwd-keyed memory dir across users).
464
- // On a companion you run yourself, set `claudeCode: { autoMemory: true }` to hand
465
- // auto-memory back to your own `~/.claude/settings.json` — Cabane then stops
457
+ // The Claude Code auto-memory escape hatch. Auto-memory is forced OFF by
458
+ // default (memory belongs in the Cabane workspace, and a shared companion
459
+ // would pool one cwd-keyed memory dir across users). On a companion you run
460
+ // yourself, `claudeCode: { autoMemory: true }` hands auto-memory back to your
461
+ // own `~/.claude/settings.json` — Cabane then stops
466
462
  // injecting the off switch and your normal Claude Code memory workflow applies
467
463
  // (in coding mode, where the checkout's project settings are read).
468
464
  claudeCode: z2.object({ autoMemory: z2.boolean().optional() }).strict().optional(),
469
- // CT1693: this agent's exception to `runners.sandbox` — an ops agent that
470
- // runs with the machine on a sandboxed device, or one sandboxed agent on a
471
- // device that isn't. Absent is "inherit"; see `runnerSandboxFor`. Only on a
472
- // row keyed by the agent's id (`agentsConfigSchema`).
465
+ // This agent's exception to `runners.sandbox`: an ops agent that runs with
466
+ // the machine on a sandboxed device, or one sandboxed agent on a device that
467
+ // isn't. Absent is "inherit". Only on a row keyed by the agent's id.
473
468
  sandbox: z2.enum(["os", "none"]).optional()
474
469
  }).strict();
475
470
  AGENT_ID_KEY = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -485,9 +480,8 @@ var init_config = __esm({
485
480
  }
486
481
  });
487
482
  companionConfigSchema = z2.object({
488
- // The cabane instance this device is paired with. SJ515: https-enforced
489
- // (loopback exempt) so a hand-edited config can't smuggle a plaintext-http
490
- // base URL onto the MITM-able channel the device token + prompt ride.
483
+ // https-enforced (loopback exempt), so a hand-edited config can't put the
484
+ // device token and prompt on a plaintext channel.
491
485
  baseUrl: z2.string().url().refine(isAllowedBaseUrl, {
492
486
  message: "baseUrl must be https (loopback http is allowed for local dev only)"
493
487
  }),
@@ -505,91 +499,59 @@ var init_config = __esm({
505
499
  // agentId / username / `slug/username`. Hand-added by the operator; the companion
506
500
  // never writes this (it only persists credentials + the device, elsewhere).
507
501
  agents: agentsConfigSchema.optional(),
508
- // CT1095: the DEVICE-LEVEL prepare hook — one entry that applies to every agent
509
- // assigned to this device, unless that agent's own `agents` row names a hook of
510
- // its own.
511
- //
512
- // Until now `prepareHook` existed only under `agents[<id>]`, which made the
513
- // config a second silent roster: a device whose provisioning is genuinely
514
- // per-device (ours resolves a directory from the conversation and agent ids the
515
- // companion already hands the hook) still had to list every agent by id, and an
516
- // agent nobody remembered to add got no hook at all — no error, no log, just a
517
- // turn that started in the operator's `$HOME`. That is how @dale woke in
518
- // `/home/joshed` on 2026-08-17 with a perfectly correct dispatch.
519
- //
520
- // Absent → nothing changes for anyone, which is why this can land inert: a
521
- // device with no device-level entry behaves exactly as it did before. A hook
522
- // that fails still fails the dispatch loudly, as now.
502
+ // The DEVICE-LEVEL prepare hook, for every agent assigned here unless its own
503
+ // `agents` row names one. Per-agent hooks alone make the config a second
504
+ // silent roster: an agent nobody listed gets no hook and no error, and starts
505
+ // in the operator's `$HOME` (an agent woke in `/home/joshed` this way on
506
+ // 2026-08-17).
523
507
  prepareHook: prepareHookSchema.optional(),
524
- // CT1085 §9: `dashboardPort` and `autoOpen` are READ AND IGNORED by the CLI —
525
- // it binds no port and opens no browser. They stay in the schema (rather than
526
- // becoming a strict-mode parse error on an existing config file) and still mean
527
- // what they say to the Electron shell, the one front-end that renders a
528
- // dashboard. `logLevel` is unaffected: pino level, live-editable.
508
+ // `dashboardPort` and `autoOpen` are IGNORED by the CLI (no port, no browser)
509
+ // but stay in the schema so an existing file still parses; the Electron shell
510
+ // reads them. `logLevel` is pino's level, live-editable.
529
511
  dashboardPort: z2.number().int().min(1).max(65535).optional(),
530
512
  autoOpen: z2.boolean().optional(),
531
513
  logLevel: z2.enum(["warn", "info", "debug"]).optional(),
532
514
  logFormat: z2.enum(["human", "json"]).optional(),
533
- // CT270: the opencode runtime, when the operator runs one on this machine. The
534
- // operator installs opencode, starts `opencode serve` (auth via opencode's own
535
- // `/connect` — Cabane never sees provider keys), and points the companion at it
536
- // here. Setting this makes the device advertise the `opencode` runtime on its
537
- // heartbeat manifest (so the server offers DeepSeek/opencode models here and
538
- // routes those turns to this device) AND registers the opencode adapter in the
539
- // dispatcher. Absent → the device is claude-code-only, exactly as before.
515
+ // An `opencode serve` the operator runs here (auth via opencode's own
516
+ // `/connect`; Cabane never sees provider keys). Set, the device advertises the
517
+ // `opencode` runtime and registers its adapter.
540
518
  opencode: z2.object({
541
519
  serverUrl: z2.string().url()
542
520
  }).strict().optional(),
543
- // CT1082: the Claude Code runtime, when the user connected it on this machine.
544
- // The change this task exists for: Claude Code used to be the one harness a
545
- // device exposed with no configuration — a `claude --version` exit-0 was taken as
546
- // consent — and it is now the third config-driven harness, opted into exactly
547
- // like codex. CT1379: the binary that must be there is the Agent SDK's own
548
- // bundled executable — the file a turn actually launches — not the user's
549
- // `claude` on PATH, which the SDK never runs. The manifest advertises
550
- // claude-code only when this block says so AND that binary resolves.
521
+ // Whether the user connected Claude Code on this machine: consent, opted into
522
+ // like codex. The manifest advertises claude-code only when this says so AND
523
+ // the Agent SDK's bundled executable resolves (not `claude` on PATH, which the
524
+ // SDK never runs).
551
525
  //
552
- // The block's PRESENCE is also the migration marker (see `migrateConnectedHarnesses`).
553
- // Absent means the config predates this task — a device whose user was never
554
- // asked — and the migration grandfathers it on first start. So every config
555
- // written from here on carries the block explicitly, including a freshly-paired
556
- // one, which starts at `enabled: false`: a new device is connected to nothing
557
- // until its user says otherwise.
526
+ // The block's PRESENCE is also the migration marker
527
+ // (`migrateConnectedHarnesses`): absent means a config whose user was never
528
+ // asked. So every config written carries it, a fresh pair at `enabled: false`.
558
529
  //
559
- // Not to be confused with the per-agent `agents.<key>.claudeCode.autoMemory`
560
- // above — that's one agent's memory switch, this is the device's connected set.
530
+ // Not the per-agent `agents.<key>.claudeCode.autoMemory`, one agent's memory
531
+ // switch.
561
532
  claudeCode: z2.object({
562
533
  enabled: z2.boolean().optional()
563
534
  }).strict().optional(),
564
- // CT481: the codex runtime, when the operator runs Codex on this machine. Unlike
565
- // opencode (a long-lived `opencode serve` addressed by URL), Codex is a local CLI
566
- // the `@openai/codex-sdk` spawns per turn — so the config is just an opt-in flag,
567
- // not a server address. The operator installs Codex + logs in (`codex login`, or
568
- // `CODEX_API_KEY` — Cabane never sees the key) and sets `{ "codex": { "enabled":
569
- // true } }`. Presence of the block enables it (an explicit `enabled: false`
570
- // keeps the block but turns it off). Absent → the device doesn't offer codex.
571
- // CT1379: enabling is now half the answer, not all of it — the manifest and the
572
- // adapter registry additionally require the SDK's vendored `codex` binary to
573
- // resolve, because the enable flag alone advertised a runtime a device with a
574
- // half-installed platform package could not start.
535
+ // Codex is a local CLI the SDK spawns per turn, so this is an opt-in flag, not
536
+ // an address. The operator logs in (`codex login` or `CODEX_API_KEY`; Cabane
537
+ // never sees the key). The block's presence enables it; `enabled: false` turns
538
+ // it off. Half the answer: advertising also requires the SDK's vendored
539
+ // `codex` binary to resolve.
575
540
  codex: z2.object({
576
541
  enabled: z2.boolean().optional()
577
542
  }).strict().optional(),
578
- // CT1679: run each turn in a child runner process instead of in this one.
579
- // Absent, or `enabled: false`, is the in-process path, untouched. `sandbox`
580
- // wraps the runner in an OS sandbox where the host supports one; `'none'`
581
- // (the default) runs it as a plain child beside this process, a supported
582
- // configuration in its own right rather than a degraded one.
583
- // CT1684: `policy` is what the OS sandbox lets a turn reach, read and hold
584
- // (`runner/sandbox-policy.ts`). It is validated there rather than here, so a
585
- // bad policy refuses the turns it governs, with the field named, instead of
586
- // keeping the companion from starting at all.
543
+ // Run each turn in a child runner process. Absent or `enabled: false` is the
544
+ // in-process path. `sandbox: 'os'` wraps the runner in an OS sandbox where the
545
+ // host supports one; `'none'` (the default) is a plain child, supported in its
546
+ // own right. `policy` is what the sandbox lets a turn reach, read and hold,
547
+ // validated in `runner/sandbox-policy.ts` so a bad policy refuses the turns it
548
+ // governs, field named, instead of stopping the companion starting.
587
549
  runners: z2.object({
588
550
  enabled: z2.boolean().optional(),
589
551
  provider: z2.enum(["local-process", "docker"]).optional(),
590
552
  sandbox: z2.enum(["os", "none"]).optional(),
591
- // CT1681: the `docker` provider — a container per conversation and agent,
592
- // from one image, on a network whose only way out is an allow-listed proxy.
553
+ // A container per conversation and agent, from one image, on a network
554
+ // whose only way out is an allow-listed proxy.
593
555
  docker: z2.object({
594
556
  // The base image a container runs. Default: the one this companion
595
557
  // builds from its own Dockerfile on first use. One named here must
@@ -1133,7 +1095,7 @@ var init_api_error_shape = __esm({
1133
1095
  "dispatch_not_admitted",
1134
1096
  "turn_already_ended",
1135
1097
  "turn_belongs_elsewhere",
1136
- // Rooms: the context this start was composed from named an attempt the
1098
+ // The context this start was composed from named an attempt the
1137
1099
  // lease no longer picks (midnight rolled it, a degrade retired it). Nothing
1138
1100
  // was granted; the turn recomposes its context and starts again, once.
1139
1101
  "dispatch_context_stale"
@@ -1207,10 +1169,8 @@ var init_api = __esm({
1207
1169
  this.opts = opts;
1208
1170
  }
1209
1171
  opts;
1210
- // CT25: re-entrancy guard for `drainOutbox`. The supervisor kicks a drain
1211
- // from several places (startup, SSE reconnect, the backoff timer); without
1212
- // this guard two overlapping drains could each pick up the same queued entry
1213
- // and double-send it (the server dedupes, but the wasted POSTs aren't free).
1172
+ // Re-entrancy guard for `drainOutbox`: drains are kicked from several places,
1173
+ // and two overlapping ones would double-send the same entry.
1214
1174
  draining = false;
1215
1175
  lastDeliveryWarning = 0;
1216
1176
  get base() {
@@ -1272,19 +1232,13 @@ var init_api = __esm({
1272
1232
  }
1273
1233
  }
1274
1234
  }
1275
- // CT25: send-or-enqueue for a turn-row commit. Tries the immediate
1276
- // (retry-budgeted) send first so the happy path keeps its current latency,
1277
- // then on a *retryable* failure persists the row to the on-disk outbox
1278
- // instead of dropping it. Classification is deliberate:
1279
- // - success → done.
1280
- // - no outbox configured (one-shot CLI / tests) → rethrow, exactly the
1281
- // pre-CT25 behaviour.
1282
- // - cancel (the turn was stopped; `signal` aborted or an AbortError) →
1283
- // rethrow without queuing: the user asked us to stop, and the held text
1284
- // was already dropped — we must not resurrect it from the queue.
1285
- // - terminal 4xx (a deterministic rejection a replay can't fix) → rethrow,
1286
- // so the caller's error path runs as it does today (e.g. the committer
1287
- // re-arms its empty-final fallback).
1235
+ // Send-or-enqueue for a turn-row commit: the immediate (retry-budgeted) send
1236
+ // first, then on a *retryable* failure persist to the outbox.
1237
+ // - no outbox configured (one-shot CLI / tests) → rethrow.
1238
+ // - cancel (`signal` aborted or an AbortError) → rethrow without queuing:
1239
+ // the user asked us to stop, and the text must not resurrect from the queue.
1240
+ // - terminal 4xx (a replay can't fix it) → rethrow, so the caller's error
1241
+ // path runs.
1288
1242
  // - retryable (transport / 5xx — commit outcome may be unknown) → queue
1289
1243
  // and return. This acknowledges local delivery responsibility, not server
1290
1244
  // persistence. Replays retain the endpoint's identity: messages/thinking/
@@ -1309,13 +1263,10 @@ var init_api = __esm({
1309
1263
  );
1310
1264
  }
1311
1265
  }
1312
- // CT25: flush the outbox FIFO. Removes an entry once the server acknowledges
1313
- // it (2xx) or rejects it terminally (any 4xx — a deleted conversation 404, a
1314
- // forbidden, etc. — which a replay can never satisfy, so we discard with a
1315
- // log rather than spin forever). A retryable failure (transport / 5xx) means
1316
- // the API is still down: stop the cycle and leave that entry and the rest
1317
- // queued for the next drain. Returns a summary the supervisor's backoff
1318
- // scheduler reads. Re-entrancy-guarded so overlapping kicks don't double-send.
1266
+ // Flush the outbox FIFO. An entry goes once the server acknowledges it (2xx)
1267
+ // or rejects it terminally (any 4xx, which a replay can never satisfy: discard
1268
+ // with a log rather than spin). A retryable failure means the API is still
1269
+ // down: stop and leave that entry and the rest for the next drain.
1319
1270
  async drainOutbox() {
1320
1271
  const outbox = this.opts.outbox;
1321
1272
  if (!outbox) return { remaining: 0, progressed: false };
@@ -1363,40 +1314,24 @@ var init_api = __esm({
1363
1314
  }
1364
1315
  return { remaining: outbox.size(), progressed };
1365
1316
  }
1366
- // CT25: how many commits are currently queued for this workspace — the
1367
- // supervisor's drain scheduler reads it to decide whether to keep polling.
1317
+ // How many commits are queued; the drain scheduler reads it to decide whether
1318
+ // to keep polling.
1368
1319
  outboxSize() {
1369
1320
  return this.opts.outbox?.size() ?? 0;
1370
1321
  }
1371
- // CT262: the ONE turn-context fetch. Collapses the companion's old four-fetch
1372
- // choreography (getConversation + getMessage + getAgentSelf + getParticipantAgent)
1373
- // into a single call: the server composes the whole server portion of the
1374
- // `TurnRequest` — systemPrompt, per-turn prompt + vision content, effective
1375
- // run-config, `HostPolicy`, prior session, the user MCP definitions to resolve
1376
- // locally, plus the anchor/title + trigger-message summary the host needs.
1377
- // Agent-PAT authed; the workspace is implied by the PAT.
1378
- // CT714: `turnId` is the host-minted id for THIS turn, passed so the server can
1379
- // bind the minted turn token to it — the turn-control surface then rejects a
1380
- // token whose turn has ended. The dispatcher mints it before this call and
1381
- // reuses the same value on its active-run PATCH, so the token's turn id and the
1382
- // pair's `active_turn_id` agree.
1383
- // CT1380: `resumed` says this dispatch is a REPLAY of an interrupted turn, so
1384
- // the server should hand back `turnSeqFloor` — the turn's committed seq
1385
- // high-water mark. Only the supervisor knows (it minted the id or recovered
1386
- // it), so it is passed down rather than inferred here. It gates the COST of
1387
- // the aggregate, never the tenancy scoping, which the route applies
1388
- // unconditionally.
1389
- // `fresh` asks for the context a session that holds NOTHING needs: the read
1390
- // from the start of the conversation, the agent's own words included, and
1391
- // the first-turn material — what the adapter runs on when it refused to
1392
- // resume the stored session and is starting a fresh one.
1393
- // CT1543: `episode` is the dispatch event's own, echoed back so the server can
1394
- // tell this frame from a later wake on the same message. Omitted when the
1395
- // event didn't carry one, which is the only thing an older event can mean.
1396
- // CT1657: `signal` is the turn's own abort. It matters here more than on any
1397
- // other read, because this call now waits up to ~8s across five attempts: a
1398
- // Stop landing in that window used to leave the whole schedule running, and
1399
- // the person watched a turn they had already stopped keep trying.
1322
+ // The ONE turn-context fetch: the server composes the whole server portion of
1323
+ // the `TurnRequest`. Agent-PAT authed; the workspace is implied by the PAT.
1324
+ // - `turnId`: the host-minted id the server binds the turn token to, so the
1325
+ // turn-control surface rejects a token whose turn has ended.
1326
+ // - `resumed`: a REPLAY of an interrupted turn, so the server returns
1327
+ // `turnSeqFloor`. Only the supervisor knows. It gates the cost of the
1328
+ // aggregate, never the tenancy scoping.
1329
+ // - `fresh`: the context a session holding NOTHING needs (the whole read,
1330
+ // own words included, plus first-turn material), for an adapter that
1331
+ // refused to resume the stored session.
1332
+ // - `episode`: the dispatch event's own, so the server can tell this frame
1333
+ // from a later wake on the same message. Omitted by an older event.
1334
+ // - `signal`: the turn's abort, so a Stop cuts the ~8s retry schedule short.
1400
1335
  getTurnContext(conversationId, messageId2, turnId, resumed, fresh, episode, signal) {
1401
1336
  const q = `conversationId=${encodeURIComponent(conversationId)}&messageId=${encodeURIComponent(messageId2)}` + (turnId ? `&turnId=${encodeURIComponent(turnId)}` : "") + (resumed ? "&resumed=1" : "") + (fresh ? "&fresh=1" : "") + (episode !== void 0 ? `&episode=${encodeURIComponent(String(episode))}` : "");
1402
1337
  return this.request("GET", `/api/agent/turn-context?${q}`, void 0, {
@@ -1404,9 +1339,8 @@ var init_api = __esm({
1404
1339
  ...signal ? { signal } : {}
1405
1340
  });
1406
1341
  }
1407
- // CT1380: the local half of a resumed turn's seq floor (see
1408
- // `Outbox.maxSeqForTurn`). 0 when no outbox is configured — a one-shot CLI or
1409
- // test has no durable queue, so nothing can be hiding in it.
1342
+ // The local half of a resumed turn's seq floor (`Outbox.maxSeqForTurn`). 0
1343
+ // with no outbox: nothing can be hiding in a queue that doesn't exist.
1410
1344
  outboxMaxSeqForTurn(turnId) {
1411
1345
  return this.opts.outbox?.maxSeqForTurn(turnId) ?? 0;
1412
1346
  }
@@ -1422,13 +1356,11 @@ var init_api = __esm({
1422
1356
  (e) => e.turnId === turnId && e.kind === "message" && e.body?.kind !== "runtime_notice"
1423
1357
  );
1424
1358
  }
1425
- // CT1677: ADOPT the issuance a turn context carried as the execution source of
1426
- // the work about to run on it. Made once per context, before preparation emits
1427
- // or queues anything, so every row that work commits can name a source the
1428
- // server has durably recorded and will never reap. Retried like any write: the
1429
- // server's adoption is idempotent. A 409 `execution_source_unavailable` is a
1430
- // refusal — nothing to adopt, the reaper got there first — and the caller
1431
- // fetches a fresh context rather than working on this one.
1359
+ // ADOPT a turn context's issuance as the execution source of its work, once,
1360
+ // before preparation emits or queues anything, so every row can name a source
1361
+ // the server durably recorded and will never reap. Adoption is idempotent, so
1362
+ // retried. A 409 `execution_source_unavailable` means the reaper got there
1363
+ // first; the caller fetches a fresh context.
1432
1364
  adoptExecutionSource(issuanceId, body, signal) {
1433
1365
  return this.request(
1434
1366
  "POST",
@@ -1437,10 +1369,10 @@ var init_api = __esm({
1437
1369
  signal ? { retry: true, signal } : { retry: true }
1438
1370
  );
1439
1371
  }
1440
- // CT1680: the launcher mints the runner credential for one dispatch, on its
1441
- // PAT and from its device, and hands it to the runner in place of both. A
1442
- // retry is safe: the server returns the same runner id and retires the
1443
- // credential the lost response carried.
1372
+ // The launcher mints the runner credential for one dispatch, on its PAT and
1373
+ // from its device, and hands it to the runner in place of both. A retry is
1374
+ // safe: the server returns the same runner id and retires the credential the
1375
+ // lost response carried.
1444
1376
  mintRunnerCredential(body) {
1445
1377
  return this.request("POST", "/api/agent/runner-credentials", body, { retry: true });
1446
1378
  }
@@ -1460,7 +1392,7 @@ var init_api = __esm({
1460
1392
  // invocation. The server records the effect and receipt in one transaction;
1461
1393
  // an ok:false response is a recorded refusal for the tool to report.
1462
1394
  // Retries retain the caller's callId and recover the same receipt, even
1463
- // when the turn settled between attempts (CT1447): the route consults the
1395
+ // when the turn settled between attempts: the route consults the
1464
1396
  // receipt before it refuses an ended turn, so a 4xx proves no attempt wrote.
1465
1397
  // The caller's signal bounds how long the model waits for the tool result.
1466
1398
  turnAct(workspaceId, turnId, act, signal) {
@@ -1471,36 +1403,23 @@ var init_api = __esm({
1471
1403
  signal ? { retry: true, signal } : { retry: true }
1472
1404
  );
1473
1405
  }
1474
- // CT1292: RENEW THE TURN'S LEASE — ask the server whether this turn is still
1475
- // running. The dispatcher calls this on a cadence for the life of the SDK loop,
1476
- // and out of cadence the moment a commit is refused, so a loop whose turn was
1477
- // terminalized underneath it stops instead of running to the six-hour cap.
1406
+ // RENEW THE TURN'S LEASE: is this turn still running? Called on a cadence for
1407
+ // the life of the SDK loop and the moment a commit is refused, so a loop whose
1408
+ // turn was terminalized underneath it stops.
1478
1409
  //
1479
- // Single-shot and NEVER retried, on purpose. A renewal is a question about
1480
- // right now, not a durable write: a failed one has no answer worth replaying,
1481
- // and the next tick asks again in a few seconds anyway. Retrying would only
1482
- // stretch one tick over the next one's slot.
1410
+ // Single-shot, NEVER retried: a failed renewal has no answer worth replaying
1411
+ // and the next tick asks again; retrying stretches one tick over the next.
1483
1412
  //
1484
- // ALL FAILURE IS `unknown`, and this is the safety property the whole feature
1485
- // rests on. The only thing that may stop a running loop is the server SAYING
1486
- // the turn is over; a 500, a dropped connection, an expired token or a body we
1487
- // cannot parse says nothing about the turn, and reading any of them as death
1488
- // would kill healthy turns every time the API hiccups — strictly worse than the
1489
- // gap this closes. So the failure direction is fixed here, at the boundary,
1490
- // rather than left to each caller to remember.
1413
+ // ALL FAILURE IS `unknown`. Only the server SAYING the turn is over may stop a
1414
+ // running loop; a 500, a dropped connection, an expired token or an
1415
+ // unparseable body says nothing about the turn, and reading it as death would
1416
+ // kill healthy turns on every API hiccup. Fixed here, at the boundary, so no
1417
+ // caller has to remember.
1491
1418
  //
1492
- // AND IT IS DEADLINED (Codo's blocking finding #1). `fetch` has no default
1493
- // timeout, so a request that neither resolves nor rejects — a black-holed
1494
- // connection, a half-open socket surviving a network change — would hang this
1495
- // promise forever. That is not merely a slow probe: the caller single-flights
1496
- // on it, so one stalled request latches the check permanently, every later
1497
- // cadence tick returns immediately, and the loop this exists to stop runs to
1498
- // the six-hour cap even after connectivity comes back — the exact defect the
1499
- // task is about, reintroduced through the mechanism meant to close it.
1500
- //
1501
- // The deadline turns that into an ordinary `unknown`: the request rejects, the
1502
- // in-flight latch clears, and the NEXT tick asks again. Well under the renewal
1503
- // cadence so a stalled probe is finished with before its successor is due.
1419
+ // DEADLINED, because `fetch` has no default timeout: a black-holed request
1420
+ // would hang forever, and the caller single-flights on it, so one stall would
1421
+ // latch the check and let the loop run to the cap. The deadline makes it an
1422
+ // ordinary `unknown` and the next tick asks again.
1504
1423
  async checkTurnLease(workspaceId, conversationId, agentId, turnId) {
1505
1424
  const q = `turnId=${encodeURIComponent(turnId)}`;
1506
1425
  try {
@@ -1509,11 +1428,7 @@ var init_api = __esm({
1509
1428
  `/api/workspaces/${workspaceId}/conversations/${conversationId}/participants/agents/${agentId}/turn-lease?${q}`,
1510
1429
  void 0,
1511
1430
  {
1512
- // CT1657: the one GET that opts OUT of the default retry. The reasoning
1513
- // is right above — a failed probe has no answer worth replaying and the
1514
- // next tick asks again in seconds, so retrying inside this one would
1515
- // stretch a tick over its successor's slot, which is what the deadline
1516
- // exists to prevent.
1431
+ // The one GET that opts OUT of the default retry; see above.
1517
1432
  retry: false,
1518
1433
  signal: AbortSignal.timeout(this.opts.leaseCheckTimeoutMs ?? LEASE_CHECK_TIMEOUT_MS)
1519
1434
  }
@@ -1547,9 +1462,8 @@ var init_api = __esm({
1547
1462
  }
1548
1463
  return this.request("PATCH", path, body);
1549
1464
  }
1550
- // CT93: send-or-enqueue for an active-run flag write, with last-writer-wins
1551
- // across the (conversation, agent) pair. Mirrors `durableCommit`, with two
1552
- // deliberate differences:
1465
+ // Send-or-enqueue for an active-run flag write, last-writer-wins per
1466
+ // (conversation, agent) pair. Like `durableCommit`, except:
1553
1467
  // - No abort signal is threaded. The clear is the turn's teardown and must
1554
1468
  // land even when the turn was *cancelled* (the abort signal is set) — a
1555
1469
  // cancelled turn still has to drop its "working" indicator.
@@ -1603,9 +1517,6 @@ var init_api = __esm({
1603
1517
  );
1604
1518
  }
1605
1519
  }
1606
- // CT29: per-device liveness moved off the per-workspace agent PAT and onto the
1607
- // device token — see `DeviceApi.heartbeat`. There is no agent-PAT heartbeat
1608
- // anymore.
1609
1520
  // Commit the agent's first/last words (message) or a runtime_notice through
1610
1521
  // the public messages endpoint. Intermediate working text uses reportText.
1611
1522
  // Attribution comes from the agent credential; a new speech row can offer
@@ -1689,36 +1600,26 @@ var init_host_policy = __esm({
1689
1600
  "packages/agent-runtime/src/host-policy.ts"() {
1690
1601
  "use strict";
1691
1602
  hostPolicySchema = z5.object({
1692
- // Host filesystem + shell: `Bash`/`Read`/`Write`/`Edit`/`Glob`/`Grep`, the
1693
- // notebook read/write pair, git-worktree tools, and the `REPL` host code-exec
1694
- // tool. Off under the locked assistant surface (today's `DISALLOWED_TOOLS`);
1695
- // on under `coding` mode.
1603
+ // Host filesystem and shell: `Bash`/`Read`/`Write`/`Edit`/`Glob`/`Grep`, the notebook pair,
1604
+ // git-worktree tools and `REPL`. Off on the locked assistant surface; on in the coding posture.
1696
1605
  hostFs: z5.boolean(),
1697
- // Public web (today's `WebSearch` / `WebFetch`, the `DEFAULT_WEB_TOOLS`). Public
1698
- // web, not host reach — granted by default today, but expressible as a grant.
1606
+ // Public web (`WebSearch` / `WebFetch`). Not host reach; a grant like the rest.
1699
1607
  web: z5.boolean(),
1700
- // Browser automation (the Playwright MCP surface). Varies by host: a companion has
1701
- // it, the house executor does not (CT230).
1608
+ // Browser automation (the Playwright MCP surface). Varies by host.
1702
1609
  browser: z5.boolean(),
1703
- // User-configured MCP servers permitted. False for the house executor
1704
- // (CT227: Cabane agents run no user MCP servers), true for a personal companion.
1610
+ // User-configured MCP servers permitted.
1705
1611
  userMcp: z5.boolean(),
1706
- // Subagent / sub-dispatch (`Task` / `Agent`). A per-host GRANT (see the CT261
1707
- // amendment above): `false` on the locked assistant/house surface (banned via
1708
- // `DISALLOWED_TOOLS`), `true` under coding / custom where the fan-out is left to
1709
- // the coding allow-hook (which reaches sub-agent tool calls, CT680) or the user
1710
- // allowlist. The subagent completes within the turn, so
1711
- // it's not the turn-model invariant `scheduling` is.
1612
+ // Subagent dispatch (`Task` / `Agent`) — a grant: `false` on the locked surface, `true` in the
1613
+ // coding posture, where the coding allow-hook reaches sub-agent tool calls. A subagent completes
1614
+ // within the turn, so it's not an invariant like `scheduling`.
1712
1615
  subagents: z5.boolean(),
1713
1616
  // ── Hard platform invariants — always denied, never granted ────────────────
1714
- // Deferred re-invocation / scheduling (`ScheduleWakeup`, the `Cron*` / `Task*`
1715
- // families, `Monitor`, `Workflow`, …). A turn is one query that resolves when
1716
- // `result` fires; a scheduled callback fires after the reply window has closed
1717
- // and strands the agent (the CT155/CT156 rule).
1617
+ // Deferred re-invocation / scheduling (`ScheduleWakeup`, the `Cron*` / `Task*` families,
1618
+ // `Monitor`, `Workflow`, …). A turn is one query that resolves on `result`; a scheduled callback
1619
+ // fires after the reply window has closed and strands the agent.
1718
1620
  scheduling: z5.literal("never"),
1719
- // Human-facing UI prompts (`AskUserQuestion`). A Cabane conversation has no
1720
- // handler to answer a structured prompt, so the call hangs the turn
1721
- // (`UNSUPPORTED_TOOLS`). The agent asks in its reply instead.
1621
+ // Human-facing UI prompts (`AskUserQuestion`). Nothing in a Cabane conversation can answer a
1622
+ // structured prompt, so the call hangs the turn. The agent asks in its reply instead.
1722
1623
  uiPrompts: z5.literal("never")
1723
1624
  });
1724
1625
  }
@@ -1731,59 +1632,40 @@ var init_turn_event = __esm({
1731
1632
  "packages/agent-runtime/src/turn-event.ts"() {
1732
1633
  "use strict";
1733
1634
  turnEventSchema = z6.discriminatedUnion("type", [
1734
- // The runtime's opaque session state, emitted when the adapter learns it (e.g.
1735
- // the SDK `system/init` frame). The platform stores `state` verbatim per
1736
- // (conversation, agent) and hands it back on the next turn; only the adapter
1737
- // knows what it means. Today's companion captures the raw SDK session id here; a
1738
- // future adapter may encode more (e.g. `{sdkSessionId, cwd}`) — still one
1739
- // opaque string to the platform.
1635
+ // The runtime's opaque session state, emitted when the adapter learns it (e.g. the SDK
1636
+ // `system/init` frame). The platform stores `state` verbatim per (conversation, agent) and
1637
+ // hands it back next turn; only the adapter knows what it means.
1740
1638
  //
1741
- // CT315: `degraded: true` means the adapter started this FRESH session despite
1742
- // a stored one it was handed — a cwd/directory mismatch (the SJ527 guard) or
1743
- // legacy/foreign state it refused to resume. It is NOT set for a genuine
1744
- // first turn (`no_session`). The signal matters because the server built this
1745
- // turn's catch-up manifest assuming the prior session carried everything up to
1746
- // the high-water mark (CT298's two-channel invariant); a degrade breaks that,
1747
- // so the mark now over-reaches an empty session. The host relays `degraded`
1748
- // on settle and the server rewinds the mark, so the NEXT turn rebuilds a full
1749
- // catch-up (this failing turn is unavoidably lossy — the degrade is only known
1750
- // on the companion, after the server committed the manifest). Runtime-neutral: a
1751
- // plain boolean, not a runtime-specific reason string (that stays in the
1752
- // adapter's `onWarn` log). Additive + optional — an old receiver ignores it.
1639
+ // `degraded: true`: the adapter started this fresh session despite a stored one (a cwd
1640
+ // mismatch, or legacy/foreign state it refused to resume) — never for a genuine first turn.
1641
+ // The server built this turn's catch-up manifest assuming the prior session held everything up
1642
+ // to the high-water mark; a degrade breaks that. The host relays `degraded` on settle and the
1643
+ // server rewinds the mark, so the next turn rebuilds a full catch-up (this turn is unavoidably
1644
+ // lossy — the degrade is known only after the manifest was committed). A plain boolean; the
1645
+ // runtime-specific reason stays in the adapter's `onWarn` log. Optional.
1753
1646
  z6.object({
1754
1647
  type: z6.literal("session"),
1755
1648
  state: z6.string(),
1756
1649
  degraded: z6.boolean().optional()
1757
1650
  }),
1758
- // One readable thinking summary. Maps `onThinking({ text })`. Transient —
1759
- // surfaced live, never persisted as durable content.
1651
+ // One readable thinking summary. Transient — surfaced live, never persisted as content.
1760
1652
  z6.object({ type: z6.literal("thinking"), text: z6.string() }),
1761
- // Assistant text. Maps `onAssistantText({ text, final })` — `text`→`body`,
1762
- // `final`→`terminal`. `terminal: false` is interim narration (commits as a
1763
- // `progress` row); `terminal: true` is the turn's closing reply (commits as
1764
- // the `final` row).
1653
+ // Assistant text. `terminal: false` is interim narration (commits as `progress`);
1654
+ // `terminal: true` is the turn's closing reply (commits as `final`).
1765
1655
  z6.object({ type: z6.literal("text"), body: z6.string(), terminal: z6.boolean() }),
1766
- // Runtime/provider-authored prose about the turn — a failure's explanation, or
1767
- // a line the harness wrote on a turn it recovered (CT1750). Unlike
1768
- // `text`, this is not the agent's narration or reply: the pump persists it as
1769
- // `runtime_notice`, and the transcript renders it in the shared SystemNote
1770
- // voice. Adapters emit it only from positive runtime evidence; the web never
1771
- // classifies English strings.
1656
+ // Runtime/provider-authored prose about the turn — a failure's explanation, or a line the
1657
+ // harness wrote on a turn it recovered. Not the agent's words: the pump persists it as
1658
+ // `runtime_notice`, drawn in the SystemNote voice. Adapters emit it only from positive runtime
1659
+ // evidence; the web never classifies English strings.
1772
1660
  z6.object({ type: z6.literal("runtime_notice"), body: z6.string() }),
1773
- // A tool-activity transition. Maps `onToolActivity` — `toolUseId`→`id`,
1774
- // `toolName`→`name` (already prefix-stripped: `cabane_read`, not
1775
- // `mcp__cabane__cabane_read`), `summary` is the short card label. `phase`
1776
- // pairs `start` with a later `done`/`error` on the same `id`.
1661
+ // A tool-activity transition. `name` is prefix-stripped (`cabane_read`, not
1662
+ // `mcp__cabane__cabane_read`); `summary` is the short card label; `phase` pairs `start` with a
1663
+ // later `done`/`error` on the same `id`.
1777
1664
  //
1778
- // `input`/`result` are typed `unknown` (the raw tool payloads), widening
1779
- // today's already-truncated `input: string` / `result: string` — see
1780
- // docs/turn-protocol.md: rendering/truncation for the tool log is a
1781
- // pump/consumer concern applied to the value, not baked into the event type.
1782
- // CT496: `mcpServer` is the MCP server the call came from (`cabane`,
1783
- // `playwright`, …), derived off the raw `mcp__<server>__` name before `name`
1784
- // dropped the prefix; null for a host / built-in tool. The client tags Cabane
1785
- // MCP calls (`=== 'cabane'`) with a subtle glyph. Nullable + optional so a
1786
- // pre-CT496 producer that never sets it is unaffected (treated as null).
1665
+ // `input`/`result` are `unknown` (the raw tool payloads): rendering and truncation for the tool
1666
+ // log are a consumer concern (docs/turn-protocol.md). `mcpServer` is the MCP server the call
1667
+ // came from (`cabane`, `playwright`, …), derived before `name` dropped the prefix; null for a
1668
+ // host tool. Optional, read as null when absent.
1787
1669
  z6.object({
1788
1670
  type: z6.literal("tool"),
1789
1671
  id: z6.string(),
@@ -1794,45 +1676,25 @@ var init_turn_event = __esm({
1794
1676
  result: z6.unknown().optional(),
1795
1677
  mcpServer: z6.string().nullable().optional()
1796
1678
  }),
1797
- // The turn's terminal outcome. Maps the `{ ok, reason }` both loops return
1798
- // inline. `ok:false` carries a machine reason (`no_session`, an error code);
1799
- // the host's clear-PATCH surfaces it as `errorReason`.
1800
- //
1801
- // CT277: an optional `usage` — the SDK result's token counts, which were
1802
- // dropped on the floor before. `inputTokens` is the full context the model saw
1803
- // (uncached + cache-read + cache-creation input), so it doubles as the
1804
- // context-window cost; `outputTokens` the generated tokens. Optional ⇒
1805
- // backward-compatible: an old companion / adapter that never sets it, and a
1806
- // receiver that never reads it, are unaffected (the turn's token columns stay
1807
- // null → the UI shows `—`).
1679
+ // The turn's terminal outcome. `ok:false` carries a machine reason (`no_session`, an error
1680
+ // code), surfaced by the host's clear-PATCH as `errorReason`.
1808
1681
  //
1809
- // CT597: two optional cache-token counts — the slice of `inputTokens` that was
1810
- // a cache read vs. a cache creation, when the runtime breaks it out (claude-code
1811
- // does). Additive + optional: a runtime that doesn't report them omits them, and
1812
- // the server just leaves the cache columns null. Captured now because honest
1813
- // costing later prices a cache-read token far below a fresh input token.
1682
+ // `usage`: the SDK result's token counts. `inputTokens` is the full context the model saw
1683
+ // (uncached + cache-read + cache-creation). The cache counts, when the runtime breaks them out,
1684
+ // are a subset of it, kept because a cache-read token costs far less. All optional; absent
1685
+ // leaves the columns null and the UI shows `—`.
1814
1686
  //
1815
- // CT699: `inputTokens` (and the cache slice) is a BILLING quantity — for
1816
- // claude-code/codex it's the runtime's CUMULATIVE total summed across every model
1817
- // request in the agentic turn, so it grows with the tool-call count and is NOT
1818
- // "how full is the window." `contextTokens` is the distinct CONTEXT-OCCUPANCY
1819
- // read and `contextWindow` the window it is measured against, both as the
1820
- // harness reported them (claude-code's `getContextUsage`, or its final
1821
- // request's input and `modelUsage` window when that call gives nothing). A
1822
- // runtime that can't source them (codex's cumulative-only usage) omits both,
1823
- // and no surface shows a reading for that turn.
1687
+ // `inputTokens` is a billing quantity — for claude-code/codex the cumulative total across every
1688
+ // model request in the turn, growing with tool calls, not "how full is the window".
1689
+ // `contextTokens` is the occupancy read and `contextWindow` its window, as the harness reported
1690
+ // them (claude-code's `getContextUsage`, else its final request's input and `modelUsage`
1691
+ // window). A runtime that can't source them (codex) omits both.
1824
1692
  //
1825
- // CT601: two more optional carry-homes on the terminal result, alongside
1826
- // `usage`. `resolvedModel` is the CONCRETE model the runtime actually ran —
1827
- // claude-code learns it from the `system/init` frame mid-run (even for a
1828
- // subscription-default token, where the configured model is null), codex records
1829
- // the model the thread was started with. `resolvedConfig` is the resolved
1830
- // per-runtime knobs the harness reported (`{ reasoningEffort }` for codex; none
1831
- // for claude-code today). Both only known after the run streams — so they ride
1832
- // the terminal event home, the host relays them on settle, and the server writes
1833
- // `message_turn_metadata.resolved_model` / `resolved_config`. Optional ⇒
1834
- // backward-compatible: an old adapter/companion omits them, a cancel has no result
1835
- // event at all, and the columns stay null → the UI shows `—`.
1693
+ // `resolvedModel` is the concrete model the runtime ran (claude-code learns it from
1694
+ // `system/init` mid-run, even for a subscription default; codex records the model the thread
1695
+ // started with); `resolvedConfig` the knobs the harness reported (`{ reasoningEffort }` for
1696
+ // codex). Known only after the run streams, so they ride this event home and the host relays
1697
+ // them on settle. A cancel has no result event at all.
1836
1698
  z6.object({
1837
1699
  type: z6.literal("result"),
1838
1700
  ok: z6.boolean(),
@@ -1844,9 +1706,8 @@ var init_turn_event = __esm({
1844
1706
  cacheCreationTokens: z6.number().optional(),
1845
1707
  contextTokens: z6.number().optional(),
1846
1708
  contextWindow: z6.number().optional(),
1847
- // CT1751: the rest of the harness's own context reading, when it gave
1848
- // one: where it will compact, its `/context` breakdown, and every
1849
- // compaction it reported during the turn. Claude Code only.
1709
+ // The rest of the harness's context reading, when it gave one: where it will compact, its
1710
+ // `/context` breakdown, and every compaction it reported. Claude Code only.
1850
1711
  contextCompactAt: z6.number().optional(),
1851
1712
  contextCategories: z6.array(
1852
1713
  z6.object({
@@ -1869,10 +1730,9 @@ var init_turn_event = __esm({
1869
1730
  thinking: z6.string().optional(),
1870
1731
  reasoningEffort: z6.string().optional()
1871
1732
  }).optional(),
1872
- // CT1275: the harness-reported MCP inventory from this turn's init frame.
1873
- // This is deliberately diagnostic-only: names, statuses and a count, never
1874
- // server definitions, credentials or session ids. `initReceived:false`
1875
- // distinguishes a missing init frame from a real empty inventory.
1733
+ // The harness-reported MCP inventory from this turn's init frame. Diagnostic only: names,
1734
+ // statuses and a count — never server definitions, credentials or session ids.
1735
+ // `initReceived:false` tells a missing init frame from a real empty inventory.
1876
1736
  mcpInventory: z6.object({
1877
1737
  initReceived: z6.boolean(),
1878
1738
  servers: z6.array(z6.object({ name: z6.string(), status: z6.string() })),
@@ -1989,9 +1849,8 @@ function isContentBearingEvent(event) {
1989
1849
  return normalizeCommitText(event.text) !== "";
1990
1850
  case "tool":
1991
1851
  return true;
1992
- // CT1750: a runtime notice is shown, but it is the harness speaking about
1993
- // the turn, not the turn's output — a success whose only output is a CLI
1994
- // line still produced nothing.
1852
+ // A runtime notice is shown, but it is the harness speaking about the turn, not its output — a
1853
+ // success whose only output is a CLI line still produced nothing.
1995
1854
  case "runtime_notice":
1996
1855
  case "session":
1997
1856
  case "result":
@@ -2073,11 +1932,10 @@ var init_turn_request = __esm({
2073
1932
  mcpUrl: z8.string(),
2074
1933
  bearer: z8.string(),
2075
1934
  activeConversationId: z8.string(),
2076
- // Scoped turn-control MCP URL. Codex/opencode mount it as cabane_companion
2077
- // with the bearer and active-conversation header. It supplies ask,
2078
- // withdraw_ask, wake_me, cancel_wake and skip_turn. Claude Code mounts the
2079
- // companion's in-process instance instead. Optional for compatibility;
2080
- // the companion populates it in build-options.ts.
1935
+ // Scoped turn-control MCP URL. Codex and opencode mount it as `cabane_companion` with the bearer
1936
+ // and active-conversation header, for ask, withdraw_ask, wake_me, cancel_wake and skip_turn.
1937
+ // Claude Code mounts the companion's in-process instance instead. Optional; the companion
1938
+ // populates it.
2081
1939
  turnControlUrl: z8.string().optional(),
2082
1940
  // Workspace coordinate retained for envelope compatibility. Current
2083
1941
  // adapters use MCP, whose workspace operations take workspaceId explicitly.
@@ -2090,19 +1948,17 @@ var init_turn_request = __esm({
2090
1948
  cwd: z8.string().optional(),
2091
1949
  env: z8.record(z8.string(), z8.string()).optional(),
2092
1950
  mcpServers: resolvedMcpServersSchema.optional(),
2093
- // CT1698: the values `headersFromEnv` names, per turn (`CABANE_MCP_<n>` →
2094
- // complete header value). The adapter merges them into the harness
2095
- // process's environment and nowhere else; the host never merges them into
2096
- // its own. Absent when no server references one.
1951
+ // The values `headersFromEnv` names, per turn (`CABANE_MCP_<n>` → complete header value). The
1952
+ // adapter merges them into the harness process's environment and nowhere else; the host never
1953
+ // merges them into its own. Absent when no server references one.
2097
1954
  secretEnv: z8.record(z8.string(), z8.string()).optional(),
2098
- // CT289: machine-local claude-code adapter knobs the operator sets on a
2099
- // companion they run themselves — the auto-memory escape hatch. `autoMemory:
2100
- // true` opts back into Claude Code's auto-memory (governed by the operator's
2101
- // own `.claude/settings.json`); absent/false leaves the adapter's force-off
2102
- // default in place (see `buildClaudeCodeOptions`).
1955
+ // Machine-local claude-code knobs for an operator running their own companion — the auto-memory
1956
+ // escape hatch. `autoMemory: true` opts back into Claude Code's auto-memory (governed by the
1957
+ // operator's `.claude/settings.json`); absent/false keeps the adapter's force-off default
1958
+ // (`buildClaudeCodeOptions`).
2103
1959
  claudeCode: z8.object({ autoMemory: z8.boolean().optional() }).optional()
2104
1960
  }),
2105
- // Host-owned injected servers (host-filled) — e.g. the send server.
1961
+ // Host-owned injected servers (host-filled).
2106
1962
  extra: z8.object({
2107
1963
  mcpServers: hostInjectedServersSchema
2108
1964
  })
@@ -2190,8 +2046,8 @@ async function emitToolResults(msg, emit, pending, onError) {
2190
2046
  // result payload off the block.
2191
2047
  input: started?.input,
2192
2048
  result: block.content,
2193
- // CT496: carry the server captured at `start` (the pretty name here has
2194
- // already lost its prefix). Null when the start wasn't seen.
2049
+ // The server captured at `start` (the pretty name here has lost its prefix). Null when the
2050
+ // start wasn't seen.
2195
2051
  mcpServer: started?.mcpServer ?? null
2196
2052
  },
2197
2053
  onError
@@ -2322,12 +2178,11 @@ function sinkEmitter(sink, _onError) {
2322
2178
  summary: event.summary,
2323
2179
  input: truncate(input, TOOL_IO_MAX_CHARS),
2324
2180
  result: result === null ? "" : truncate(result, TOOL_IO_MAX_CHARS),
2325
- // CT1752: only when the event carried an input — a completion whose
2326
- // start this process never saw has none, and a zero would overwrite
2327
- // the size its start recorded.
2181
+ // Only when the event carried an input: a completion whose start this process never saw has
2182
+ // none, and a zero would overwrite the size its start recorded.
2328
2183
  ...event.input === void 0 ? {} : { inputChars: input.length },
2329
2184
  ...result === null ? {} : { resultChars: result.length },
2330
- // CT496: carry the originating MCP server (null when absent / a host tool).
2185
+ // The originating MCP server (null when absent / a host tool).
2331
2186
  mcpServer: event.mcpServer ?? null,
2332
2187
  phase: event.phase
2333
2188
  });
@@ -2433,22 +2288,18 @@ var init_pump = __esm({
2433
2288
  // The turn's final reply text, captured for the dashboard feed / logging.
2434
2289
  finalReplyBody = null;
2435
2290
  emittedFinalSource = "none";
2436
- // CT1383: provider tool id → the persisted identity of its CURRENT call. The
2437
- // provider's id is only a lookup key: it is unique within one adapter
2438
- // invocation at best (Codex restarts at `item_0` on every invocation, and a
2439
- // resumed turn keeps its turn id), so the row and the outbox entry are keyed
2440
- // on what the pump mints — a host id for the host's `(turn_id, tool_use_id)`
2441
- // upsert, and a seq for the outbox's `(turnId, seq)` file. Both are minted
2442
- // together; minting only the id would let two calls overwrite each other's
2443
- // queued body.
2291
+ // Provider tool id → the persisted identity of its current call. The provider's id is only a
2292
+ // lookup key, unique within one adapter invocation at best (Codex restarts at `item_0` every
2293
+ // invocation, and a resumed turn keeps its turn id), so the row and the outbox entry are keyed on
2294
+ // what the pump mints: a host id for the host's `(turn_id, tool_use_id)` upsert, and a seq for the
2295
+ // outbox's `(turnId, seq)` file. Minted together; minting only the id would let two calls
2296
+ // overwrite each other's queued body.
2444
2297
  //
2445
- // In memory on purpose: it dies with the process, and so does the runtime.
2446
- // After a crash no in-flight `done` awaits a pre-crash `start` — the resumed
2447
- // invocation starts its calls afresh — so there is nothing to recover.
2298
+ // In memory on purpose: it dies with the process, and so does the runtime. After a crash no
2299
+ // in-flight `done` awaits a pre-crash `start` — the resumed invocation starts its calls afresh.
2448
2300
  toolCallByProviderId = /* @__PURE__ */ new Map();
2449
- // The callback consumer the classification (via `sinkEmitter`) — and the
2450
- // frozen committer test — drive. Each callback applies the choreography and
2451
- // commits through the injected sink.
2301
+ // The callback consumer the classification (via `sinkEmitter`) and the committer test drive.
2302
+ // Each callback applies the choreography and commits through the injected sink.
2452
2303
  sink;
2453
2304
  // Whether a message has landed under this span — first words, at least. A
2454
2305
  // span that spoke settles; one that never did is a pass.
@@ -2541,7 +2392,7 @@ var init_descriptions = __esm({
2541
2392
  wake_me: {
2542
2393
  afterSeconds: "Relative delay in seconds from NOW \u2014 this call (e.g. 300 = five minutes). Provide EITHER this or `at`, not both. Floor 60s, horizon 14 days \u2014 enforced server-side.",
2543
2394
  at: "Absolute ISO-8601 timestamp WITH a zone (`Z` or `\xB1HH:MM`), e.g. `2026-07-16T09:00:00-07:00`. YOU compute it from a natural-language phrase using the current datetime in your turn context. Provide EITHER this or `afterSeconds`, not both.",
2544
- note: `A note to your future self \u2014 becomes the body of the wake message that re-dispatches you, up to ${MAX_WAKE_NOTE_BYTES} characters. Write the condition to re-check ("check whether the PR merged").`
2395
+ note: `A note to your future self \u2014 becomes the body of the wake message that re-dispatches you, up to ${MAX_WAKE_NOTE_BYTES} characters. Write the condition to re-check in a sentence or two ("check whether the PR merged"), not a plan. You never need to name yourself: the wake reaches you on its own.`
2545
2396
  },
2546
2397
  withdraw_ask: {
2547
2398
  askId: "The open ask to withdraw. Ask ids ride the conversation's message rows (`ask: { id, status, items }`), and your turn context lists the ones you have open.",
@@ -2565,7 +2416,7 @@ var init_prompt_guidance = __esm({
2565
2416
  ask: "put a structured question to a person",
2566
2417
  cancel_wake: "cancel your own armed wake in this conversation",
2567
2418
  skip_turn: "end without posting a reply",
2568
- wake_me: "schedule your own return",
2419
+ wake_me: "schedule your own return; when it fires, say first what you came back to check",
2569
2420
  withdraw_ask: "withdraw an open ask in this conversation with a reason"
2570
2421
  };
2571
2422
  }
@@ -2811,52 +2662,41 @@ function buildClaudeCodeOptions(req, augment) {
2811
2662
  // pinned `model` arrives as a bare SDK id — the host already resolved the
2812
2663
  // namespaced catalog id to it.
2813
2664
  ...model ? { model } : {},
2814
- // SJ530: surface thinking summaries — default adaptive+summarized, and fill in
2815
- // `display:'summarized'` on a configured adaptive/enabled block that omits one.
2816
- // Read from the adapter's own dialect slice (`runtimeOptions['claude-code']`).
2665
+ // Surface thinking summaries: default adaptive+summarized, and fill in `display:'summarized'`
2666
+ // on a configured adaptive/enabled block that omits one. Read from the adapter's dialect slice
2667
+ // (`runtimeOptions['claude-code']`).
2817
2668
  thinking: withThinkingSummaries(readThinking(config.runtimeOptions)),
2818
2669
  ...config.effort ? { effort: config.effort } : {},
2819
2670
  // Never merge arbitrary user `~/.claude` config; without `strictMcpConfig` the
2820
2671
  // user's existing `cabane` MCP registration could shadow ours.
2821
2672
  settingSources: [],
2822
2673
  strictMcpConfig: true,
2823
- // CT289: force auto-memory off unless the operator opted back in (see above).
2674
+ // Auto-memory off unless the operator opted back in (see above).
2824
2675
  ...devControlsAutoMemory ? {} : { settings: { autoMemoryEnabled: false } },
2825
2676
  mcpServers,
2826
2677
  ...cwd ? { cwd } : {},
2827
- // Extra env (a companion prepare hook's tokens/ports; the in-app's debug flags)
2828
- // merged OVER the inherited environment.
2678
+ // Extra env (a companion prepare hook's tokens/ports) merged over the inherited environment.
2829
2679
  //
2830
- // CT1503: and the background-tasks kill switch LAST, so nothing in
2831
- // `process.env` or `req.local.env` can turn it back on. Claude Code converts
2832
- // a foreground command that hits its Bash ceiling (600 000 ms) into a
2833
- // background task instead of erroring; the agent then reads a phantom task
2834
- // id inside a live turn. A later turn that resumed such a session was then
2835
- // seen getting every tool call aborted with kind `background` — which the CLI
2836
- // renders as "The user doesn't want to take this action right now", a
2837
- // cancellation wearing a refusal's words. The automatic conversion is
2838
- // REPRODUCED and is what this flag fixes; that the stopped task is what
2839
- // poisoned the resumed session is an INFERENCE from one incident, consistent
2840
- // with the transcript but never reproduced, and this change does not rest on
2841
- // it. `Dl()` reads this variable on the Bash tool's own call
2842
- // path and sets `canAutoBackground: false` and `turnAbortBackgrounds: false`
2843
- // together, so a timed-out command comes back as a readable error instead.
2680
+ // The background-tasks kill switch goes last, so nothing in `process.env` or `req.local.env`
2681
+ // turns it back on. Claude Code converts a foreground command that hits its Bash ceiling
2682
+ // (600 000 ms) into a background task instead of erroring, and the agent then holds a phantom
2683
+ // task id inside a live turn; a resumed session was seen with every tool call aborted as
2684
+ // `background` ("The user doesn't want to take this action right now"). The conversion is
2685
+ // reproduced and fixed by this flag; that it poisoned the resumed session is an inference
2686
+ // from one incident, and this change doesn't rest on it. `Dl()` reads the variable on the
2687
+ // Bash tool's call path and sets `canAutoBackground: false` and `turnAbortBackgrounds: false`,
2688
+ // so a timed-out command returns a readable error.
2844
2689
  //
2845
- // The cost is acknowledged and deliberate: it ALSO removes deliberate
2846
- // `run_in_background` (the parameter leaves the tool schema). That is the
2847
- // only switch this harness offers, and there is no operator opt-out here on
2848
- // purpose — an escape hatch would contradict the guarantee. It is a targeted
2849
- // mitigation for the automatic conversion, NOT a cleanup mechanism: step 0
2850
- // measured that this flag changes no process-survival outcome on any turn-end
2851
- // path. Cleanup is its own change.
2690
+ // The cost is deliberate: it also removes `run_in_background` (the parameter leaves the tool
2691
+ // schema). It is the only switch, and there is no opt-out — an escape hatch would contradict
2692
+ // the guarantee. It changes no process-survival outcome on any turn-end path (measured);
2693
+ // cleanup is a separate concern.
2852
2694
  //
2853
- // Note `env` REPLACES the subprocess environment rather than merging (the
2854
- // SDK's own doc on `Options.env`), which is why `process.env` is spread here
2855
- // and why this key is now always present — previously it appeared only when
2856
- // `req.local.env` was set, and an absent `env` inherits `process.env`.
2695
+ // `env` replaces the subprocess environment rather than merging (`Options.env`), which is why
2696
+ // `process.env` is spread here and the key is always present.
2857
2697
  //
2858
- // CT1698: `secretEnv` joins here, and only here — the values the MCP
2859
- // headers above reference. It is this child's environment, not the host's.
2698
+ // `secretEnv` — the values the MCP headers above reference — joins here and only here: it is
2699
+ // this child's environment, not the host's.
2860
2700
  env: {
2861
2701
  ...process.env,
2862
2702
  ...req.local.env,
@@ -3378,8 +3218,7 @@ function makeRequest(overrides2 = {}) {
3378
3218
  systemPrompt: "system",
3379
3219
  prompt: "hi there",
3380
3220
  content: [{ type: "text", text: "hi there" }],
3381
- // CT302: model is required — the adapter rejects a null model, so the
3382
- // conformance fixture carries a real bare SDK id.
3221
+ // The adapter rejects a null model, so the fixture carries a real bare SDK id.
3383
3222
  config: { model: "claude-opus-4-8" },
3384
3223
  policy: CABANE_POLICY,
3385
3224
  session: null,
@@ -3514,7 +3353,7 @@ var init_conformance2 = __esm({
3514
3353
  phase: "start",
3515
3354
  summary: "sleep.md",
3516
3355
  input: { path: "sleep.md" },
3517
- // CT496: derived off the raw `mcp__cabane__` name.
3356
+ // Derived from the raw `mcp__cabane__` name.
3518
3357
  mcpServer: "cabane"
3519
3358
  },
3520
3359
  {
@@ -3532,9 +3371,9 @@ var init_conformance2 = __esm({
3532
3371
  ]
3533
3372
  },
3534
3373
  {
3535
- // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result;
3536
- // the adapter stops before the closing reply — no final text, no `result`
3537
- // event. CT1295: the server writes the `kind:'stopped'` marker.
3374
+ // Highest risk: cancel mid-stream. The abort lands after the tool result; the adapter stops
3375
+ // before the closing reply — no final text, no `result` event (the server writes the stopped
3376
+ // marker).
3538
3377
  name: "cancel mid-stream",
3539
3378
  request: makeRequest(),
3540
3379
  nativeStream: [
@@ -3586,12 +3425,9 @@ var init_conformance2 = __esm({
3586
3425
  ]
3587
3426
  },
3588
3427
  {
3589
- // CT1144: the empty-runtime anomaly's exact captured trace — `system/init`
3590
- // then a successful `result`, with nothing in between. Distinct from
3591
- // `empty-final` below: THAT turn did work (a tool ran) and just didn't speak;
3592
- // this one produced nothing at all. The adapter's job is unchanged either way
3593
- // — report faithfully what the runtime said — so it must still emit
3594
- // `result{ok:true}` here; the host is what decides that a success with no
3428
+ // The empty-runtime anomaly's captured trace: `system/init` then a successful `result`, nothing
3429
+ // in between. Distinct from `empty-final` (a tool ran, no speech); this produced nothing. The
3430
+ // adapter reports faithfully — `result{ok:true}` — and the host decides that a success with no
3595
3431
  // content-bearing event is a failed turn.
3596
3432
  name: "zero-content success (empty runtime result)",
3597
3433
  request: makeRequest(),
@@ -3599,9 +3435,9 @@ var init_conformance2 = __esm({
3599
3435
  expected: [sessionEvent("s1"), { type: "result", ok: true }]
3600
3436
  },
3601
3437
  {
3602
- // CT1220: on resume, a completed background task can emit a notification and
3603
- // its own zero-token result before the prompt's frames. That result is not the
3604
- // turn boundary; decoding continues through the prompt's reply and real usage.
3438
+ // On resume, a completed background task can emit a notification and its own zero-token result
3439
+ // before the prompt's frames. That result is not the turn boundary; decoding continues through
3440
+ // the reply and real usage.
3605
3441
  name: "task-notification result does not end a resumed turn",
3606
3442
  request: makeRequest({ session: encodeSession({ sdkSessionId: "s1", cwd: CWD }) }),
3607
3443
  nativeStream: [
@@ -3677,8 +3513,8 @@ var init_conformance2 = __esm({
3677
3513
  ]
3678
3514
  },
3679
3515
  {
3680
- // CT1220: the filter is deliberately narrow. An origin-less zero-token result
3681
- // keeps today's break-on-first-result behavior, even if more frames follow.
3516
+ // The filter is deliberately narrow: an origin-less zero-token result still ends the turn, even
3517
+ // if more frames follow.
3682
3518
  name: "origin-less result still ends a resumed turn",
3683
3519
  request: makeRequest({ session: encodeSession({ sdkSessionId: "s1", cwd: CWD }) }),
3684
3520
  nativeStream: [
@@ -3749,11 +3585,10 @@ var init_conformance2 = __esm({
3749
3585
  ]
3750
3586
  },
3751
3587
  {
3752
- // CT558/CT592/CT731: a subscription cap. The SDK emits a rejected
3753
- // `rate_limit_event` with a named subscription window; the terminal error result
3754
- // then classifies as the structured `usage_capped` reason. The held provider
3755
- // line independently classifies to the same kind, so it lands as a runtime
3756
- // notice rather than agent-authored progress.
3588
+ // A subscription cap. The SDK emits a rejected `rate_limit_event` with a named subscription
3589
+ // window; the terminal error result classifies as `usage_capped`. The held provider line
3590
+ // independently classifies to the same kind, so it lands as a runtime notice, not agent
3591
+ // progress.
3757
3592
  name: "subscription cap \u2192 usage_capped",
3758
3593
  request: makeRequest(),
3759
3594
  nativeStream: [
@@ -3772,9 +3607,8 @@ var init_conformance2 = __esm({
3772
3607
  ]
3773
3608
  },
3774
3609
  {
3775
- // CT1336: a failed turn can still contain the agent's own partial narration.
3776
- // No text classification means no authorship rewrite, even though the
3777
- // structured result is the same usage cap.
3610
+ // A failed turn can still contain the agent's own partial narration. No text classification
3611
+ // means no authorship rewrite, even though the structured result is the same usage cap.
3778
3612
  name: "partial narration before subscription cap stays progress",
3779
3613
  request: makeRequest(),
3780
3614
  nativeStream: [
@@ -3790,8 +3624,8 @@ var init_conformance2 = __esm({
3790
3624
  ]
3791
3625
  },
3792
3626
  {
3793
- // CT1336: even recognized runtime prose stays authored when its kind
3794
- // disagrees with the structured terminal failure.
3627
+ // Recognized runtime prose stays authored when its kind disagrees with the structured terminal
3628
+ // failure.
3795
3629
  name: "cap notice before server failure stays progress",
3796
3630
  request: makeRequest(),
3797
3631
  nativeStream: [
@@ -3813,10 +3647,9 @@ var init_conformance2 = __esm({
3813
3647
  ]
3814
3648
  },
3815
3649
  {
3816
- // CT1336 review: `classifyErrorText` intentionally searches arbitrary error
3817
- // blobs for status tokens. Assistant narration needs the stricter whole-
3818
- // notice classifier, or an incidental count is de-authored when the turn
3819
- // later fails with the matching provider kind.
3650
+ // `classifyErrorText` deliberately searches arbitrary error blobs for status tokens. Assistant
3651
+ // narration needs the stricter whole-notice classifier, or an incidental count would be
3652
+ // de-authored when the turn later fails with the matching provider kind.
3820
3653
  name: "incidental 5xx-shaped narration before server failure stays progress",
3821
3654
  request: makeRequest(),
3822
3655
  nativeStream: [
@@ -3838,9 +3671,8 @@ var init_conformance2 = __esm({
3838
3671
  ]
3839
3672
  },
3840
3673
  {
3841
- // CT558/CT592: a cap signalled only by the result's
3842
- // `terminal_reason:'blocking_limit'` (no discrete rate-limit event) still
3843
- // classifies as the cap (`usage_capped`).
3674
+ // A cap signalled only by the result's `terminal_reason:'blocking_limit'` (no rate-limit event)
3675
+ // still classifies as `usage_capped`.
3844
3676
  name: "blocking_limit terminal reason \u2192 usage_capped",
3845
3677
  request: makeRequest(),
3846
3678
  nativeStream: [
@@ -3854,8 +3686,8 @@ var init_conformance2 = __esm({
3854
3686
  expected: [sessionEvent("s1"), { type: "result", ok: false, reason: "usage_capped" }]
3855
3687
  },
3856
3688
  {
3857
- // CT558: an expired login surfaces as an `auth_status` error frame; the failing
3858
- // result classifies as `auth_expired`.
3689
+ // An expired login surfaces as an `auth_status` error frame; the failing result classifies as
3690
+ // `auth_expired`.
3859
3691
  name: "expired login \u2192 auth_expired",
3860
3692
  request: makeRequest(),
3861
3693
  nativeStream: [
@@ -3869,10 +3701,9 @@ var init_conformance2 = __esm({
3869
3701
  expected: [sessionEvent("s1"), { type: "result", ok: false, reason: "auth_expired" }]
3870
3702
  },
3871
3703
  {
3872
- // CT592: a transient provider outage surfaced in the terminal result text (no
3873
- // structured event/enum) — a 529 overload — classifies as `server_error` via
3874
- // the text fallback. It must NOT read as the cap and must not stamp the
3875
- // connector `limited` (the whole point of the honest taxonomy).
3704
+ // A transient provider outage in the terminal result text (no structured event or enum) — a 529
3705
+ // overload — classifies as `server_error` via the text fallback. It must not read as the cap or
3706
+ // stamp the connector `limited`.
3876
3707
  name: "provider overload text \u2192 server_error",
3877
3708
  request: makeRequest(),
3878
3709
  nativeStream: [
@@ -3885,11 +3716,9 @@ var init_conformance2 = __esm({
3885
3716
  expected: [sessionEvent("s1"), { type: "result", ok: false, reason: "server_error" }]
3886
3717
  },
3887
3718
  {
3888
- // CT601: the resolved model is captured off `system/init.model` and attached
3889
- // to the terminal `result` — even on a plain resume (this fixture resumes, so
3890
- // no `session` event fires, proving the model doesn't ride the session event).
3891
- // The configured request model is irrelevant here; what's asserted is that the
3892
- // concrete id the HARNESS reported comes home.
3719
+ // The resolved model is captured off `system/init.model` and attached to the terminal `result`
3720
+ // even on a plain resume (this fixture resumes, so no `session` event fires). What's asserted is
3721
+ // that the concrete id the harness reported comes home.
3893
3722
  name: "resolved model captured from system/init",
3894
3723
  request: makeRequest({ session: encodeSession({ sdkSessionId: "s1", cwd: CWD }) }),
3895
3724
  nativeStream: [init("s1", "claude-opus-4-8"), assistantText("Done."), resultSuccess("s1")],
@@ -3899,9 +3728,8 @@ var init_conformance2 = __esm({
3899
3728
  ]
3900
3729
  },
3901
3730
  {
3902
- // Session resume echo: the stored state matches the current cwd, so we resume
3903
- // its SDK id; `system/init` echoes that same id, so NO session event fires
3904
- // (the id didn't change).
3731
+ // Session resume echo: the stored state matches the cwd, so its SDK id is resumed and
3732
+ // `system/init` echoes the same id — no session event fires.
3905
3733
  name: "session resume (no re-emit on echo)",
3906
3734
  request: makeRequest({ session: encodeSession({ sdkSessionId: "s1", cwd: CWD }) }),
3907
3735
  nativeStream: [init("s1"), assistantText("Back again."), resultSuccess("s1")],
@@ -3911,16 +3739,14 @@ var init_conformance2 = __esm({
3911
3739
  ]
3912
3740
  },
3913
3741
  {
3914
- // SJ527 cwd-mismatch degrade: the stored state was created under a DIFFERENT
3915
- // cwd, so the adapter refuses to resume and starts fresh — `system/init`
3916
- // reports a new id, and the emitted session event records the CURRENT cwd.
3742
+ // Cwd-mismatch degrade: the stored state was created under another cwd, so the adapter starts
3743
+ // fresh — `system/init` reports a new id, and the session event records the current cwd.
3917
3744
  name: "session cwd-mismatch degrades to fresh",
3918
3745
  request: makeRequest({ session: encodeSession({ sdkSessionId: "old", cwd: "/env/gone" }) }),
3919
3746
  nativeStream: [init("fresh1"), assistantText("Fresh start."), resultSuccess("fresh1")],
3920
3747
  expected: [
3921
- // CT315: the degrade is flagged on the session event so the host relays it
3922
- // on settle and the server rewinds the catch-up mark (recovering the
3923
- // pre-mark context the discarded session no longer carries).
3748
+ // The degrade is flagged on the session event so the host relays it on settle and the server
3749
+ // rewinds the catch-up mark (recovering context the discarded session no longer carries).
3924
3750
  sessionEvent("fresh1", CWD, true),
3925
3751
  { type: "text", body: "Fresh start.", terminal: true },
3926
3752
  { type: "result", ok: true }
@@ -4623,8 +4449,6 @@ function createOpencodeAdapter(deps = {}) {
4623
4449
  const transport = deps.transport ?? (deps.serverUrl ? createHttpOpencodeTransport({ baseUrl: deps.serverUrl }) : null);
4624
4450
  return {
4625
4451
  name: "opencode",
4626
- // CT829: one surface, so one addendum and no parameter — CT614's `codeMode`
4627
- // argument went with the named tool surface it selected against.
4628
4452
  promptAddendum: () => OPENCODE_ADDENDUM,
4629
4453
  dialectSchema: opencodeDialectSchema,
4630
4454
  async *runTurn(request, signal, hooks) {
@@ -4813,15 +4637,12 @@ var init_conformance3 = __esm({
4813
4637
  ]
4814
4638
  },
4815
4639
  {
4816
- // CT301: a STREAMING provider's tool input arrives late. opencode re-emits the
4817
- // tool part as a full snapshot as the args parse: DeepSeek sends `input: {}` on
4818
- // the early `pending`/`running` frame and the full args only on a later snapshot
4819
- // (live-captured against opencode 1.17: `pending {}` → `running {full}` →
4820
- // `completed {full}`). The `start` event fires immediately off the first (empty)
4821
- // frame — that's the live card — but the `done` event (and the DB row it
4822
- // upserts) must carry the LATEST non-empty input + its summary, not the
4823
- // first-seen `{}`. This is the case the pre-CT301 suite structurally couldn't
4824
- // catch: every other tool fixture bakes the full input into the first snapshot.
4640
+ // A streaming provider's tool input arrives late. opencode re-emits the tool part as a full
4641
+ // snapshot as the args parse: DeepSeek sends `input: {}` on the early `pending`/`running` frame
4642
+ // and the full args on a later one (captured on opencode 1.17: `pending {}` → `running {full}` →
4643
+ // `completed {full}`). `start` fires off the first (empty) frame — the live card — but `done`
4644
+ // (and the row it upserts) must carry the latest non-empty input and its summary. Every other
4645
+ // tool fixture bakes the full input into the first snapshot, so only this one catches it.
4825
4646
  name: "streaming tool input \u2014 late args refresh the done event (CT301)",
4826
4647
  request: makeRequest2(),
4827
4648
  nativeStream: [
@@ -4859,9 +4680,8 @@ var init_conformance3 = __esm({
4859
4680
  ]
4860
4681
  },
4861
4682
  {
4862
- // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result; the
4863
- // adapter stops before the closing reply — no final text, no `result` event.
4864
- // CT1295: the server writes the `kind:'stopped'` marker.
4683
+ // Highest risk: cancel mid-stream. The abort lands after the tool result; the adapter stops before
4684
+ // the closing reply — no final text, no `result` event (the server writes the stopped marker).
4865
4685
  name: "cancel mid-stream",
4866
4686
  request: makeRequest2(),
4867
4687
  nativeStream: [
@@ -4901,10 +4721,9 @@ var init_conformance3 = __esm({
4901
4721
  ]
4902
4722
  },
4903
4723
  {
4904
- // CT558/CT592: a provider 429 throttle. The wrapped `RateLimitError` is
4905
- // unwrapped by `readSessionError` then text-classified — a bare "rate limit
4906
- // (429)" with no cap noun is the throttle (`rate_limited`), distinct from the
4907
- // account cap. opencode is text-only so it inherits the CT592 split for free.
4724
+ // A provider 429 throttle. The wrapped `RateLimitError` is unwrapped by `readSessionError` and
4725
+ // text-classified: a bare "rate limit (429)" with no cap noun is the throttle (`rate_limited`),
4726
+ // not the account cap.
4908
4727
  name: "provider 429 \u2192 rate_limited (throttle, no reset)",
4909
4728
  request: makeRequest2(),
4910
4729
  nativeStream: [
@@ -4920,9 +4739,8 @@ var init_conformance3 = __esm({
4920
4739
  ]
4921
4740
  },
4922
4741
  {
4923
- // CT592: a subscription cap surfaced as text ("usage limit reached") → the cap
4924
- // (`usage_capped`). opencode carries no reset hint, so it's reset-less (the
4925
- // server renders "try again later").
4742
+ // A subscription cap surfaced as text ("usage limit reached") → `usage_capped`. opencode carries
4743
+ // no reset hint, so it's reset-less (the server says "try again later").
4926
4744
  name: "usage cap text \u2192 usage_capped (no reset)",
4927
4745
  request: makeRequest2(),
4928
4746
  nativeStream: [
@@ -4938,9 +4756,7 @@ var init_conformance3 = __esm({
4938
4756
  ]
4939
4757
  },
4940
4758
  {
4941
- // CT592: a transient provider outage (5xx / overload) surfaced as text → the
4942
- // honest `server_error`, never the cap. This is the outage class that used to
4943
- // fall through to an opaque `unknown`.
4759
+ // A transient provider outage (5xx / overload) surfaced as text → `server_error`, never the cap.
4944
4760
  name: "provider overload text \u2192 server_error",
4945
4761
  request: makeRequest2(),
4946
4762
  nativeStream: [
@@ -4958,7 +4774,7 @@ var init_conformance3 = __esm({
4958
4774
  ]
4959
4775
  },
4960
4776
  {
4961
- // CT558: an expired/invalid credential classifies as `auth_expired`.
4777
+ // An expired or invalid credential classifies as `auth_expired`.
4962
4778
  name: "auth failure \u2192 auth_expired",
4963
4779
  request: makeRequest2(),
4964
4780
  nativeStream: [
@@ -4974,8 +4790,8 @@ var init_conformance3 = __esm({
4974
4790
  ]
4975
4791
  },
4976
4792
  {
4977
- // CT1351: an unclassified failure remains as informative as a classified one:
4978
- // its provider text is visible while the raw machine reason stays unchanged.
4793
+ // An unclassified failure stays informative: its provider text is visible, and the raw machine
4794
+ // reason is unchanged.
4979
4795
  name: "unclassified failure keeps a runtime notice and raw reason",
4980
4796
  request: makeRequest2(),
4981
4797
  nativeStream: [errored("something local broke")],
@@ -4996,8 +4812,8 @@ var init_conformance3 = __esm({
4996
4812
  ]
4997
4813
  },
4998
4814
  {
4999
- // CT1351: empty, whitespace-only, and bare status-token failures still set
5000
- // the machine reason but do not add an empty/unhelpful transcript notice.
4815
+ // Empty, whitespace-only and bare status-token failures still set the machine reason but add no
4816
+ // empty or unhelpful transcript notice.
5001
4817
  name: "empty failure text emits no runtime notice",
5002
4818
  request: makeRequest2(),
5003
4819
  nativeStream: [errored("")],
@@ -5029,12 +4845,10 @@ var init_conformance3 = __esm({
5029
4845
  ]
5030
4846
  },
5031
4847
  {
5032
- // CT1144: the same zero-content success the claude-code suite pins, in
5033
- // opencode's vocabulary — the session goes idle having emitted no part at
5034
- // all. No opencode sample of the anomaly has been seen in the wild; the
5035
- // fixture exists because the host's classification is runtime-neutral, so a
5036
- // runtime that CAN produce this envelope must be shown producing it in the
5037
- // shape the classifier reads.
4848
+ // The zero-content success the claude-code suite pins, in opencode's vocabulary — the session
4849
+ // goes idle having emitted no part. No opencode sample has been seen; the fixture exists because
4850
+ // the host's classification is runtime-neutral, so a runtime that can produce this envelope must
4851
+ // be shown producing it in the shape the classifier reads.
5038
4852
  name: "zero-content success (empty runtime result)",
5039
4853
  request: makeRequest2(),
5040
4854
  nativeStream: [idle()],
@@ -5091,10 +4905,9 @@ var init_conformance3 = __esm({
5091
4905
  ]
5092
4906
  },
5093
4907
  {
5094
- // CT298: the stored state matches the current directory, so the adapter resumes
5095
- // its id and the transport reports `created:false` on the same id — NO session
5096
- // event fires (the id didn't change). This is the resume-echo case CT294
5097
- // removed; restoring it is the crux of restoring cross-turn memory.
4908
+ // The stored state matches the directory, so the adapter resumes its id and the transport reports
4909
+ // `created:false` on the same id — no session event fires. Resume is what carries cross-turn
4910
+ // memory.
5098
4911
  name: "session resume (no re-emit on echo)",
5099
4912
  request: makeRequest2({ session: encodeSession2({ sessionId: "s1", directory: DIR }) }),
5100
4913
  nativeStream: [textPart("p1", "Back again."), idle()],
@@ -5104,12 +4917,10 @@ var init_conformance3 = __esm({
5104
4917
  ]
5105
4918
  },
5106
4919
  {
5107
- // CT298: on a resumed turn the transport mints NO client message id (supplying
5108
- // one no-ops the turn), so the decoder can't be handed a `skipMessageId` up
5109
- // front. It learns the user-message id from the stream — the `message.updated`
5110
- // user frame precedes that message's echoed part — and drops the echo, so the
5111
- // injected per-turn context still can't leak into Steps. Only the assistant
5112
- // reply surfaces. No session event (resume echo on the same id).
4920
+ // On a resumed turn the transport mints no client message id (doing so no-ops the turn), so the
4921
+ // decoder gets no `skipMessageId`. It learns the user-message id from the stream — the
4922
+ // `message.updated` user frame precedes the echoed part — and drops the echo, so the injected
4923
+ // per-turn context can't leak into Steps. Only the reply surfaces; no session event.
5113
4924
  name: "session resume \u2014 prompt echo filtered via stream-derived user id",
5114
4925
  request: makeRequest2({ session: encodeSession2({ sessionId: "s1", directory: DIR }) }),
5115
4926
  nativeStream: [
@@ -5125,17 +4936,15 @@ var init_conformance3 = __esm({
5125
4936
  ]
5126
4937
  },
5127
4938
  {
5128
- // Directory-mismatch degrade (the SJ527 analogue): the stored state was
5129
- // created under a DIFFERENT directory, so the adapter refuses to resume and
5130
- // starts fresh — the transport creates a new session, and the emitted session
5131
- // event records the CURRENT directory.
4939
+ // Directory-mismatch degrade: the stored state was created under another directory, so the
4940
+ // adapter starts fresh — the transport creates a new session, and the session event records the
4941
+ // current directory.
5132
4942
  name: "session directory-mismatch degrades to fresh",
5133
4943
  request: makeRequest2({ session: encodeSession2({ sessionId: "old", directory: "/env/gone" }) }),
5134
4944
  nativeStream: [textPart("p1", "Fresh start."), idle()],
5135
4945
  expected: [
5136
- // CT315: the degrade is flagged on the session event (parity with
5137
- // claude-code) so the host relays it on settle and the server rewinds the
5138
- // catch-up mark, recovering the context the discarded session dropped.
4946
+ // The degrade is flagged on the session event so the host relays it on settle and the server
4947
+ // rewinds the catch-up mark, recovering context the discarded session dropped.
5139
4948
  sessionEvent2(NEW_SESSION_ID, DIR, true),
5140
4949
  { type: "text", body: "Fresh start.", terminal: true },
5141
4950
  { type: "result", ok: true }
@@ -5160,11 +4969,9 @@ var init_addendum3 = __esm({
5160
4969
  "absent without attempting discovery and invocation, and if a call fails, report the recorded",
5161
4970
  "error.",
5162
4971
  turnControlPromptGuidance("mcp__cabane_companion__"),
5163
- // CT1552: every ancillary tool is written the way Codex's own tool list shows
5164
- // it — qualified. The bare `read_binary` / `upload` / … this list used to mix
5165
- // in sat two lines under the instruction to invoke the exact qualified names,
5166
- // so the sentence contradicted itself. The bare `query` / `mutate` below stay:
5167
- // that sentence is about operation names, after the qualified forms are given.
4972
+ // Every ancillary tool is written as Codex's own tool list shows it — qualified — so the sentence
4973
+ // instructing exact qualified names doesn't contradict itself. The bare `query` / `mutate` below
4974
+ // stay: that sentence is about operation names, after the qualified forms are given.
5168
4975
  "The ancillary tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`,",
5169
4976
  "`mcp__cabane__begin_upload`, `mcp__cabane__finalize_upload` and",
5170
4977
  "`mcp__cabane__mint_render_token` \u2014 all possibly deferred too.",
@@ -5420,10 +5227,9 @@ async function* decodeCodexStream(events, ctx) {
5420
5227
  threadId,
5421
5228
  cwd: ctx.cwd ?? "",
5422
5229
  promptFingerprint: ctx.promptFingerprint ?? null,
5423
- // CT1645: the baseline this turn INHERITED, carried through unchanged.
5424
- // This early write exists so a turn that dies mid-stream still leaves
5425
- // the thread id recorded; it cannot yet know what this turn will spend.
5426
- // The closing write below records the new baseline.
5230
+ // The baseline this turn inherited, carried through unchanged. This early write records the
5231
+ // thread id even if the turn dies mid-stream; it can't yet know what the turn will spend. The
5232
+ // closing write records the new baseline.
5427
5233
  usageBaseline: ctx.usageBaseline ?? null
5428
5234
  });
5429
5235
  if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
@@ -5717,9 +5523,8 @@ function buildConfig(req, baseInstructionsFile) {
5717
5523
  return {
5718
5524
  mcp_servers,
5719
5525
  experimental_use_rmcp_client: true,
5720
- // CT871: the no-host-access prompt replacement — Cabane's composed prompt
5721
- // becomes Codex's base instructions, and Codex's own environment preamble goes
5722
- // with the harness prompt it belonged to.
5526
+ // The no-host-access prompt replacement — Cabane's composed prompt becomes Codex's base
5527
+ // instructions, and Codex's environment preamble goes with the harness prompt it belonged to.
5723
5528
  ...baseInstructionsFile ? { model_instructions_file: baseInstructionsFile, include_environment_context: false } : {},
5724
5529
  ...Object.keys(shellEnv).length ? { shell_environment_policy: { set: shellEnv } } : {}
5725
5530
  };
@@ -5840,16 +5645,11 @@ function createSdkCodexTransport(opts = {}) {
5840
5645
  const codexOptions = {
5841
5646
  ...opts.apiKey ? { apiKey: opts.apiKey } : {},
5842
5647
  ...opts.codexPathOverride ? { codexPathOverride: opts.codexPathOverride } : {},
5843
- // The `--config` overrides (MCP servers + rmcp flag). CT1501: rendered to
5844
- // finished `--config` strings by US and passed through `configOverrides`,
5845
- // the SDK's raw passthrough — NOT through `config`, which would flatten
5846
- // them itself. A user MCP server named `my.server` cannot go in an
5847
- // override's dotted path at all (the CLI splits the path on `.` and
5848
- // ignores TOML quoting), so it has to ride inside an inline-table value,
5849
- // and the SDK's flattener cannot be steered into producing one. This is
5850
- // also what makes the PUBLISHED companion correct, where the vendored
5851
- // patch never reached — and why that patch is gone. See
5852
- // `config-overrides.ts`.
5648
+ // The `--config` overrides (MCP servers + rmcp flag), rendered to finished `--config` strings
5649
+ // here and passed through `configOverrides`, the SDK's raw passthrough — not `config`, which
5650
+ // flattens them itself. A user MCP server named `my.server` can't go in an override's dotted
5651
+ // path (the CLI splits on `.` and ignores TOML quoting), so it must ride inside an inline-table
5652
+ // value, which the SDK's flattener can't produce. See `config-overrides.ts`.
5853
5653
  configOverrides: buildConfigOverrides(spec.config),
5854
5654
  ...spec.secretEnv ? { env: codexProcessEnv(spec.secretEnv) } : {}
5855
5655
  };
@@ -5873,8 +5673,6 @@ function createCodexAdapter(deps = {}) {
5873
5673
  const transport = deps.transport ?? (deps.enabled ? createSdkCodexTransport({ ...deps.apiKey ? { apiKey: deps.apiKey } : {} }) : null);
5874
5674
  return {
5875
5675
  name: "codex",
5876
- // CT829: one surface, so one addendum and no parameter — CT614's `codeMode`
5877
- // argument went with the named tool surface it selected against.
5878
5676
  promptAddendum: () => CODEX_ADDENDUM,
5879
5677
  dialectSchema: codexDialectSchema,
5880
5678
  async *runTurn(request, signal, hooks) {
@@ -5966,17 +5764,14 @@ function createCodexAdapter(deps = {}) {
5966
5764
  cwd: req.local.cwd,
5967
5765
  resumedThreadId: resumeThreadId,
5968
5766
  degraded,
5969
- // CT1075: which prompt the thread carries once this turn lands. If we sent
5970
- // one, it's that one — a later copy is what the model reads, so a changed
5971
- // prompt supersedes the older copy still sitting above it. If we withheld
5972
- // it, the thread still carries whatever it did before. Read off the spec
5973
- // rather than recomputed, so the recorded fact can't disagree with what
5974
- // was actually sent.
5767
+ // Which prompt the thread carries once this turn lands. If we sent one, it's that one — the
5768
+ // later copy is what the model reads. If we withheld it, the thread carries what it did. Read
5769
+ // off the spec, so the record can't disagree with what was sent.
5975
5770
  promptFingerprint: spec.promptRidesInput ? spec.promptFingerprint : threadPromptFingerprint,
5976
5771
  storedState: req.session,
5977
5772
  usageBaseline: threadUsageBaseline,
5978
- // CT601: Codex reports no model in-stream, so record what the thread was
5979
- // started with (null on the default token). Same for the reasoning effort.
5773
+ // Codex reports no model in-stream, so record what the thread was started with (null on the
5774
+ // default token). Same for the reasoning effort.
5980
5775
  resolvedModel: spec.model,
5981
5776
  resolvedReasoningEffort: spec.modelReasoningEffort ?? null
5982
5777
  });
@@ -6031,11 +5826,9 @@ function makeRequest3(overrides2 = {}) {
6031
5826
  systemPrompt: PROMPT,
6032
5827
  prompt: "hi there",
6033
5828
  content: [{ type: "text", text: "hi there" }],
6034
- // CT601: the resolved model + reasoning effort ride the terminal `result` off
6035
- // the run spec's model. The behavioral fixtures below don't assert them, so the
6036
- // default is the model-less ("let Codex choose") token → no `resolvedModel`
6037
- // emitted → their expected results stay unchanged; a dedicated capture fixture
6038
- // sets a real model + effort.
5829
+ // The resolved model and reasoning effort ride the terminal `result` off the run spec's model.
5830
+ // Behavioural fixtures use the model-less token (no `resolvedModel` emitted); a dedicated capture
5831
+ // fixture sets a real model and effort.
6039
5832
  config: { model: null },
6040
5833
  policy: COMPANION_POLICY2,
6041
5834
  session: null,
@@ -6107,13 +5900,12 @@ var init_conformance4 = __esm({
6107
5900
  });
6108
5901
  CODEX_CONFORMANCE_FIXTURES = [
6109
5902
  {
6110
- // A plain text reply: the held agent message seals as the terminal final at
6111
- // `turn.completed`; the usage counters ride the `result`.
5903
+ // A plain text reply: the held agent message seals as the terminal final at `turn.completed`;
5904
+ // usage rides the `result`.
6112
5905
  //
6113
- // CT1283: the counters NEST — `cached_input_tokens` is a slice of
6114
- // `input_tokens`, `reasoning_output_tokens` a slice of `output_tokens` — so
6115
- // input is 100 (not 120) and output is 50 (not 60), with the cache read broken
6116
- // out beside them. This fixture pinned the double-count before CT1283.
5906
+ // The counters nest — `cached_input_tokens` is a slice of `input_tokens`,
5907
+ // `reasoning_output_tokens` a slice of `output_tokens` — so input is 100 (not 120) and output 50
5908
+ // (not 60), with the cache read broken out beside them.
6117
5909
  name: "clean turn",
6118
5910
  request: makeRequest3(),
6119
5911
  nativeStream: [
@@ -6130,10 +5922,8 @@ var init_conformance4 = __esm({
6130
5922
  expected: [
6131
5923
  sessionEvent3(NEW_THREAD_ID),
6132
5924
  { type: "text", body: "Hello there.", terminal: true },
6133
- // CT1645: the closing session write — the thread's ending totals, recorded
6134
- // as the baseline the NEXT turn subtracts. This is a fresh thread, so the
6135
- // `result` usage below is the frame verbatim (nothing to subtract yet);
6136
- // what makes it per-turn is this figure being carried into turn two.
5925
+ // The closing session write — the thread's ending totals, recorded as the baseline the next turn
5926
+ // subtracts. A fresh thread, so the `result` usage below is the frame verbatim.
6137
5927
  sessionEvent3(NEW_THREAD_ID, DIR2, false, fingerprintPrompt(PROMPT), {
6138
5928
  inputTokens: 100,
6139
5929
  outputTokens: 50,
@@ -6205,8 +5995,8 @@ var init_conformance4 = __esm({
6205
5995
  ]
6206
5996
  },
6207
5997
  {
6208
- // CT1211: recorded MCP success shape. Codex exposes the text compatibility
6209
- // copy and the native object together; normalization must keep the object.
5998
+ // The recorded MCP success shape: Codex exposes the text copy and the native object together;
5999
+ // normalization must keep the object.
6210
6000
  name: "MCP success prefers native structured content",
6211
6001
  request: makeRequest3(),
6212
6002
  nativeStream: [
@@ -6256,8 +6046,8 @@ var init_conformance4 = __esm({
6256
6046
  ]
6257
6047
  },
6258
6048
  {
6259
- // CT1211 incident shape: MCP `isError` keeps the failed phase, while the raw
6260
- // camel-case structured payload survives instead of the generic `error` token.
6049
+ // MCP `isError` keeps the failed phase, while the raw camel-case structured payload survives
6050
+ // instead of the generic `error` token.
6261
6051
  name: "failed MCP call preserves structured receipt while remaining error phase",
6262
6052
  request: makeRequest3(),
6263
6053
  nativeStream: [
@@ -6309,8 +6099,8 @@ var init_conformance4 = __esm({
6309
6099
  ]
6310
6100
  },
6311
6101
  {
6312
- // A transport/tool failure has no MCP result payload and still uses the real
6313
- // adapter error message. CT1211 must not replace this fallback with `undefined`.
6102
+ // A transport/tool failure with no MCP result payload still uses the real adapter error message,
6103
+ // never `undefined`.
6314
6104
  name: "failed MCP call without result keeps error message fallback",
6315
6105
  request: makeRequest3(),
6316
6106
  nativeStream: [
@@ -6350,8 +6140,8 @@ var init_conformance4 = __esm({
6350
6140
  ]
6351
6141
  },
6352
6142
  {
6353
- // CT1147 stays intact when a connector includes structured metadata beside an
6354
- // image-only MCP response: no image/base64 payload enters activity receipts.
6143
+ // A connector's structured metadata beside an image-only MCP response: no image/base64 payload
6144
+ // enters activity receipts.
6355
6145
  name: "image-only MCP result remains empty despite structured metadata",
6356
6146
  request: makeRequest3(),
6357
6147
  nativeStream: [
@@ -6444,12 +6234,10 @@ var init_conformance4 = __esm({
6444
6234
  ]
6445
6235
  },
6446
6236
  {
6447
- // CT715: web_search LIFECYCLE. Unlike the other three tool kinds, a `web_search`
6448
- // item carries NO `status` field — completion is signaled by the frame TYPE
6449
- // (`item.started` → `item.completed`). The `start` fires off the first frame
6450
- // (empty query, no output card); the `done` must resolve off `item.completed`
6451
- // and carry the populated query the completed frame filled in. Before CT715 the
6452
- // card was pinned at "running" forever (status derived from a missing field).
6237
+ // web_search lifecycle. A `web_search` item carries no `status` — completion is signalled by the
6238
+ // frame type (`item.started` → `item.completed`). The `start` fires off the first frame (empty
6239
+ // query); the `done` must resolve off `item.completed` with the populated query, not stay
6240
+ // "running".
6453
6241
  name: "web_search lifecycle \u2014 completes off frame type, populated query on done",
6454
6242
  request: makeRequest3(),
6455
6243
  nativeStream: [
@@ -6480,9 +6268,8 @@ var init_conformance4 = __esm({
6480
6268
  ]
6481
6269
  },
6482
6270
  {
6483
- // CT715: a non-text web action (`action.type: "other"`) legitimately completes
6484
- // with an EMPTY query — that's Codex's own data, not our bug. It must still
6485
- // resolve to `done` (empty query acceptable; stuck-running is not).
6271
+ // A non-text web action (`action.type: "other"`) legitimately completes with an empty query —
6272
+ // Codex's own data. It must still resolve to `done`.
6486
6273
  name: "web_search lifecycle \u2014 empty-query completion still resolves to done",
6487
6274
  request: makeRequest3(),
6488
6275
  nativeStream: [
@@ -6513,9 +6300,8 @@ var init_conformance4 = __esm({
6513
6300
  ]
6514
6301
  },
6515
6302
  {
6516
- // HIGHEST-RISK: cancel mid-stream. The abort lands after the tool result; the
6517
- // adapter stops before the closing reply — no final text, no `result` event.
6518
- // CT1295: the server writes the `kind:'stopped'` marker.
6303
+ // Highest risk: cancel mid-stream. The abort lands after the tool result; the adapter stops before
6304
+ // the closing reply — no final text, no `result` event (the server writes the stopped marker).
6519
6305
  name: "cancel mid-stream",
6520
6306
  request: makeRequest3(),
6521
6307
  nativeStream: [
@@ -6596,11 +6382,8 @@ var init_conformance4 = __esm({
6596
6382
  ]
6597
6383
  },
6598
6384
  {
6599
- // CT592: Codex used to classify NOTHING — every `turn.failed` became a raw
6600
- // `error:<text>` `unknown`. It now routes the failure text through the shared
6601
- // classifier (Codex is text-only), so a provider 429 / outage / auth / cap
6602
- // failure splits into the honest taxonomy instead of all reading `unknown`.
6603
- // This is a real behavior change, not a refactor. One fixture per kind:
6385
+ // The failure text goes through the shared classifier (Codex is text-only), so a provider 429 /
6386
+ // outage / auth / cap failure splits into the honest taxonomy. One fixture per kind:
6604
6387
  name: "turn.failed 429 \u2192 rate_limited (throttle)",
6605
6388
  request: makeRequest3(),
6606
6389
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("429 Too Many Requests")],
@@ -6647,8 +6430,8 @@ var init_conformance4 = __esm({
6647
6430
  ]
6648
6431
  },
6649
6432
  {
6650
- // CT592: a Codex failure text the classifier can't place still keeps today's
6651
- // raw `error:<text>` reason (the `unknown` fallback) — nothing regresses.
6433
+ // A failure text the classifier can't place keeps the raw `error:<text>` reason (the `unknown`
6434
+ // fallback).
6652
6435
  name: "turn.failed unclassified \u2192 raw error reason (unchanged)",
6653
6436
  request: makeRequest3(),
6654
6437
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("something local broke")],
@@ -6672,8 +6455,8 @@ var init_conformance4 = __esm({
6672
6455
  ]
6673
6456
  },
6674
6457
  {
6675
- // CT1351: empty, whitespace-only, and bare status-token failures still set
6676
- // the machine reason but do not add an empty/unhelpful transcript notice.
6458
+ // Empty, whitespace-only and bare status-token failures still set the machine reason but add no
6459
+ // empty or unhelpful transcript notice.
6677
6460
  name: "empty failure text emits no runtime notice",
6678
6461
  request: makeRequest3(),
6679
6462
  nativeStream: [threadStarted(NEW_THREAD_ID), turnFailed("")],
@@ -6702,11 +6485,9 @@ var init_conformance4 = __esm({
6702
6485
  ]
6703
6486
  },
6704
6487
  {
6705
- // CT481: fail LOUD on unknown model metadata. An unknown `--model` makes Codex
6706
- // emit an `error` item ("… Defaulting to fallback metadata …") and then go
6707
- // haywire (the spurious tool call scripted after it MUST never be emitted). The
6708
- // adapter ends the turn the moment the signal lands, with a machine reason — so
6709
- // a future Codex id-drop errors clean instead of degrading mid-tool-turn.
6488
+ // Fail loud on unknown model metadata. An unknown `--model` makes Codex emit an `error` item
6489
+ // ("… Defaulting to fallback metadata …") and misbehave (the spurious tool call scripted after it
6490
+ // must never be emitted). The adapter ends the turn when the signal lands, with a machine reason.
6710
6491
  name: "unknown model metadata \u2192 fail loud (guard)",
6711
6492
  request: makeRequest3(),
6712
6493
  nativeStream: [
@@ -6752,12 +6533,10 @@ var init_conformance4 = __esm({
6752
6533
  ]
6753
6534
  },
6754
6535
  {
6755
- // CT1144: the same zero-content success the claude-code suite pins, in
6756
- // Codex's vocabulary — the thread starts, the turn completes, and nothing is
6757
- // emitted in between. No Codex sample of the anomaly has been seen in the
6758
- // wild; the fixture exists because the host's classification is runtime-
6759
- // neutral, so a runtime that CAN produce this envelope must be shown
6760
- // producing it in the shape the classifier reads.
6536
+ // The zero-content success the claude-code suite pins, in Codex's vocabulary — the thread
6537
+ // starts, the turn completes, nothing in between. No Codex sample has been seen; the fixture
6538
+ // exists because the host's classification is runtime-neutral, so a runtime that can produce
6539
+ // this envelope must be shown producing it in the shape the classifier reads.
6761
6540
  name: "zero-content success (empty runtime result)",
6762
6541
  request: makeRequest3(),
6763
6542
  nativeStream: [threadStarted(NEW_THREAD_ID), turnCompleted()],
@@ -6826,11 +6605,10 @@ var init_conformance4 = __esm({
6826
6605
  ]
6827
6606
  },
6828
6607
  {
6829
- // CT601: Codex reports no model in-stream, so the resolved model + reasoning
6830
- // effort come from what the thread was STARTED with (the run spec) and ride the
6831
- // terminal `result`. A real model + a `runtimeOptions.codex.modelReasoningEffort`
6832
- // are asserted through to `resolvedModel` (bare, `openai/`-stripped) and
6833
- // `resolvedConfig.reasoningEffort`.
6608
+ // Codex reports no model in-stream, so the resolved model and reasoning effort come from what the
6609
+ // thread was started with (the run spec) and ride the terminal `result`: a real model and a
6610
+ // `runtimeOptions.codex.modelReasoningEffort` are asserted through to `resolvedModel` (bare,
6611
+ // `openai/`-stripped) and `resolvedConfig.reasoningEffort`.
6834
6612
  name: "resolved model + reasoning effort captured from run spec",
6835
6613
  request: makeRequest3({
6836
6614
  config: {
@@ -6851,15 +6629,12 @@ var init_conformance4 = __esm({
6851
6629
  ]
6852
6630
  },
6853
6631
  {
6854
- // Session resume: the stored state matches the current directory, so the adapter
6855
- // resumes its thread id and `thread.started` echoes the SAME id — NO session
6856
- // event fires (the id didn't change AND the state is unchanged). This is what
6857
- // carries cross-turn memory.
6632
+ // Session resume: the stored state matches the directory, so the thread id is resumed and
6633
+ // `thread.started` echoes it — no session event fires. This is what carries cross-turn memory.
6858
6634
  //
6859
- // CT1075: the stored state also says the thread already holds the composed
6860
- // prompt, so this turn withholds the preamble and the state it would write is
6861
- // byte-identical to the stored one. The silence is now proof of BOTH: an id that
6862
- // didn't change and a prompt that didn't need re-sending.
6635
+ // The stored state also says the thread already holds the composed prompt, so this turn
6636
+ // withholds the preamble and the state it would write is byte-identical: the silence proves both
6637
+ // an unchanged id and a prompt that needed no re-send.
6863
6638
  name: "session resume (no re-emit on echo)",
6864
6639
  request: makeRequest3({
6865
6640
  session: encodeSession3({
@@ -6876,16 +6651,12 @@ var init_conformance4 = __esm({
6876
6651
  ]
6877
6652
  },
6878
6653
  {
6879
- // CT1075: a resumed thread the platform's state does NOT vouch for — a state
6880
- // written before CT1075 existed, so it carries no `promptFingerprint` field. The
6881
- // adapter reads the absence as "we can't say which prompt this thread holds",
6882
- // sends the full prompt exactly as it did pre-CT1075, and re-emits the state with
6883
- // the fingerprint recorded — on the SAME thread id, which pre-CT1075 emitted
6884
- // nothing at all. That re-emit is the upgrade path: without it a long-lived
6885
- // thread would re-send the whole prompt forever.
6654
+ // A resumed thread whose stored state carries no `promptFingerprint` field. The adapter reads the
6655
+ // absence as "can't say which prompt this thread holds", sends the full prompt, and re-emits the
6656
+ // state with the fingerprint — on the same thread id. That re-emit is the upgrade path: without
6657
+ // it a long-lived thread would re-send the whole prompt forever.
6886
6658
  //
6887
- // This is the fixture that fails if the static half is withheld on a thread we
6888
- // have no evidence about.
6659
+ // This fixture fails if the standing half is withheld on a thread we have no evidence about.
6889
6660
  name: "session resume with a pre-CT1075 state re-emits with the prompt fingerprint",
6890
6661
  request: makeRequest3({
6891
6662
  session: JSON.stringify({ threadId: "th1", cwd: DIR2 })
@@ -6898,14 +6669,12 @@ var init_conformance4 = __esm({
6898
6669
  ]
6899
6670
  },
6900
6671
  {
6901
- // CT1075 (review finding): the thread carries a DIFFERENT prompt than the one
6902
- // this turn composed — a charter edit, a renamed agent, a redeployed prompt
6903
- // body. The stored fingerprint doesn't match, so the new prompt is sent exactly
6904
- // as on a fresh thread and the state is re-recorded against it.
6672
+ // The thread carries a different prompt than this turn composed — a charter edit, a renamed
6673
+ // agent, a redeployed prompt body. The fingerprint doesn't match, so the new prompt is sent as on
6674
+ // a fresh thread and the state re-recorded.
6905
6675
  //
6906
- // This is the fixture that fails if a stale prompt is treated as good enough. A
6907
- // boolean flag would have passed the resume fixture above and silently withheld
6908
- // every future charter edit from this thread.
6676
+ // This fixture fails if a stale prompt is treated as good enough; a boolean flag would pass the
6677
+ // resume fixture above and withhold every future charter edit.
6909
6678
  name: "session resume with a stale prompt fingerprint re-sends the prompt",
6910
6679
  request: makeRequest3({
6911
6680
  session: encodeSession3({
@@ -6923,15 +6692,12 @@ var init_conformance4 = __esm({
6923
6692
  ]
6924
6693
  },
6925
6694
  {
6926
- // Directory-mismatch degrade (the SJ527 analogue): the stored state was created
6927
- // under a DIFFERENT directory, so the adapter refuses to resume and starts
6928
- // fresh — `thread.started` reports a NEW id, and the emitted session event
6929
- // records the CURRENT directory with `degraded: true` so the host rewinds the
6930
- // catch-up mark.
6695
+ // Directory-mismatch degrade: the stored state was created under another directory, so the
6696
+ // adapter starts fresh — `thread.started` reports a new id, and the session event records the
6697
+ // current directory with `degraded: true` so the host rewinds the catch-up mark.
6931
6698
  //
6932
- // CT1075: the refused resume means the new thread is EMPTY, so the full prompt
6933
- // goes in even though the stored state vouched for a copy — of a thread we're
6934
- // no longer in. The emitted state records the fresh thread's own answer.
6699
+ // The refused resume means the new thread is empty, so the full prompt goes in even though the
6700
+ // stored state vouched for a copy in a thread we're no longer in.
6935
6701
  name: "session directory-mismatch degrades to fresh",
6936
6702
  request: makeRequest3({
6937
6703
  session: encodeSession3({
@@ -7320,8 +7086,7 @@ function createTurnContainment(turnId, deps = {}) {
7320
7086
  `--unit=${unit}`,
7321
7087
  `--property=TimeoutStopSec=${CONTAINMENT_STOP_GRACE_SEC}s`,
7322
7088
  // Bind the scope's life to the companion's own unit, so stopping or
7323
- // restarting the companion still takes its in-flight turns with it —
7324
- // which is what happens today, and what the scope would otherwise undo.
7089
+ // restarting the companion still takes its in-flight turns with it.
7325
7090
  // Omitted outside a unit, where there is nothing to bind to.
7326
7091
  ...ownUnit ? [`--property=PartOf=${ownUnit}`] : [],
7327
7092
  "--"
@@ -7672,7 +7437,7 @@ var init_run_config = __esm({
7672
7437
  type: z15.literal("http"),
7673
7438
  url: z15.string().url(),
7674
7439
  headers: z15.record(z15.string(), z15.string()).optional(),
7675
- // CT1698: header → the name of a `secretEnv` entry holding its value. Set by
7440
+ // Header → the name of a `secretEnv` entry holding its value. Set by
7676
7441
  // the server on a room connection's entry, and by `resolveMcpSecrets` on a
7677
7442
  // header whose `${VAR}` it resolved.
7678
7443
  headersFromEnv: z15.record(z15.string(), z15.string()).optional()
@@ -7681,7 +7446,7 @@ var init_run_config = __esm({
7681
7446
  type: z15.literal("sse"),
7682
7447
  url: z15.string().url(),
7683
7448
  headers: z15.record(z15.string(), z15.string()).optional(),
7684
- // CT1698: header → the name of a `secretEnv` entry holding its value. Set by
7449
+ // Header → the name of a `secretEnv` entry holding its value. Set by
7685
7450
  // the server on a room connection's entry, and by `resolveMcpSecrets` on a
7686
7451
  // header whose `${VAR}` it resolved.
7687
7452
  headersFromEnv: z15.record(z15.string(), z15.string()).optional()
@@ -7694,9 +7459,8 @@ var init_run_config = __esm({
7694
7459
  ]);
7695
7460
  effortSchema = z15.enum(["low", "medium", "high", "xhigh", "max"]);
7696
7461
  runConfigSchema = z15.object({
7697
- // CT788: the host-access binary replaced the `assistant`/`coding`/`custom` mode
7698
- // trio + its custom tool lists — `true` grants the host filesystem/shell, absent
7699
- // is the locked surface. Kept in lockstep with `@cabane/shared`'s
7462
+ // `true` grants the host filesystem/shell; absent is the locked surface. Kept
7463
+ // in lockstep with `@cabane/shared`'s
7700
7464
  // `agentRunConfigSchema` (independent zod, forward-compatible: unknown keys are
7701
7465
  // stripped, so an older companion riding a newer server never rejects the config).
7702
7466
  hostAccess: z15.boolean().optional(),
@@ -7787,7 +7551,7 @@ var init_protocol = __esm({
7787
7551
  dispatch: "CABANE_DISPATCH",
7788
7552
  // The turn id the launcher resolved, and whether this is a replay of a turn
7789
7553
  // an earlier process started. The seq floor for a replay is derived inside
7790
- // the turn from the outbox and the server, as it always was.
7554
+ // the turn from the outbox and the server.
7791
7555
  turnId: "CABANE_TURN_ID",
7792
7556
  resumed: "CABANE_RESUMED",
7793
7557
  // Everything else the turn is constructed from, as JSON: see `RunnerSpec`.
@@ -7823,10 +7587,10 @@ var init_protocol = __esm({
7823
7587
  // Whether the harness starts under the OS sandbox. The launcher sends 'os'
7824
7588
  // only when this host can make one.
7825
7589
  sandbox: z16.enum(["os", "none"]),
7826
- // CT1684: the device's sandbox policy, validated by the launcher. Absent
7590
+ // The device's sandbox policy, validated by the launcher. Absent
7827
7591
  // when the sandbox is off.
7828
7592
  sandboxPolicy: sandboxPolicySchema.optional(),
7829
- // CT1691: every companion store the launcher's latest walk found, denied
7593
+ // Every companion store the launcher's latest walk found, denied
7830
7594
  // to the harness. Absent when the sandbox is off.
7831
7595
  companionStores: z16.array(z16.string()).optional(),
7832
7596
  logLevel: z16.string(),
@@ -7835,15 +7599,15 @@ var init_protocol = __esm({
7835
7599
  // True when this (conversation, agent) pair was already told its session
7836
7600
  // could not be saved: the notice is once per pair, not once per process.
7837
7601
  sessionWriteNoticeClaimed: z16.boolean(),
7838
- // CT1680: the runner's identity, minted with its credential. The start
7602
+ // The runner's identity, minted with its credential. The start
7839
7603
  // PATCH carries it so the lease knows which runner holds the span.
7840
7604
  runnerId: z16.string(),
7841
- // CT1681: the launcher keeps the runtime's session files beside the attempt
7605
+ // The launcher keeps the runtime's session files beside the attempt
7842
7606
  // and restores them into this runner's home, so the runner names the attempt
7843
7607
  // each context was composed for and waits for the launcher to say it's in
7844
7608
  // place. Absent: nothing to wait for (a runner on the launcher's own disk).
7845
7609
  attemptState: z16.boolean().optional(),
7846
- // CT1681: MCP `${NAME}` secrets come from the launcher, by name, instead of
7610
+ // MCP `${NAME}` secrets come from the launcher, by name, instead of
7847
7611
  // this machine's secrets.json. Absent: the runner reads the device's file.
7848
7612
  secretsFromLauncher: z16.boolean().optional()
7849
7613
  });
@@ -8432,7 +8196,7 @@ function createTurnControlMcpServer(acts, skipState) {
8432
8196
  return performAct(acts, {
8433
8197
  kind: "ask",
8434
8198
  callId: randomUUID2(),
8435
- // CT1350: the swap rides the ask, so the server commits both halves
8199
+ // The swap rides the ask, so the server commits both halves
8436
8200
  // in one transaction — the person never sees neither or both.
8437
8201
  ...args.replaces ? { replaces: args.replaces } : {},
8438
8202
  ask: hasArray ? {
@@ -8483,7 +8247,7 @@ function createTurnControlMcpServer(acts, skipState) {
8483
8247
  },
8484
8248
  { annotations: ACT_TOOL, alwaysLoad: true }
8485
8249
  ),
8486
- // CT1350: the standalone withdrawal. Not annotated destructive — an ask
8250
+ // The standalone withdrawal. Not annotated destructive — an ask
8487
8251
  // is a question, not data, and a harness that gated this on a live
8488
8252
  // operator would hang a turn nobody is watching (the `cancel_wake`
8489
8253
  // reasoning). Not idempotent either: a second call with the same id is
@@ -8502,7 +8266,7 @@ function createTurnControlMcpServer(acts, skipState) {
8502
8266
  }),
8503
8267
  { annotations: ACT_TOOL, alwaysLoad: true }
8504
8268
  ),
8505
- // CT990: the inverse. No arguments — it can only ever stand down the
8269
+ // The inverse. No arguments — it can only ever stand down the
8506
8270
  // caller's own wake in the conversation it's holding a turn in.
8507
8271
  tool(
8508
8272
  CANCEL_WAKE_TOOL,
@@ -8567,7 +8331,7 @@ function buildCompanionTurnRequest(params) {
8567
8331
  // adapter's `ResolvedMcpServers`.
8568
8332
  ...Object.keys(params.mcpServers).length > 0 ? { mcpServers: params.mcpServers } : {},
8569
8333
  ...params.secretEnv && Object.keys(params.secretEnv).length > 0 ? { secretEnv: params.secretEnv } : {},
8570
- // CT289: the auto-memory escape hatch, when the operator set it.
8334
+ // The auto-memory escape hatch, when the operator set it.
8571
8335
  ...params.claudeCode ? { claudeCode: params.claudeCode } : {}
8572
8336
  },
8573
8337
  // Host-injected turn-control tools, under their own namespace.
@@ -8890,7 +8654,7 @@ var init_transcript_writer = __esm({
8890
8654
  }
8891
8655
  this.line({ type: "_meta", ...meta });
8892
8656
  }
8893
- // Append a raw SDK message verbatim.
8657
+ // Append one event verbatim.
8894
8658
  write(msg) {
8895
8659
  this.line(msg);
8896
8660
  }
@@ -8898,7 +8662,7 @@ var init_transcript_writer = __esm({
8898
8662
  close(outcome) {
8899
8663
  this.line({ type: "_outcome", ...outcome });
8900
8664
  }
8901
- // CT1145: keep an independent copy of an empty successful envelope after the
8665
+ // Keep an independent copy of an empty successful envelope after the
8902
8666
  // footer has landed. The ordinary 200-file FIFO keeps rotating as before;
8903
8667
  // this bucket retains the newest 50 anomalies regardless of ordinary traffic.
8904
8668
  // Best-effort like every other transcript operation: observability can never
@@ -9034,12 +8798,9 @@ var init_turn_committer = __esm({
9034
8798
  // Renders each `TurnEvent` onto the pump's callback sink (tool-log truncation),
9035
8799
  // routing a throwing commit to `onError`.
9036
8800
  emit;
9037
- // CT261: route one `TurnEvent` from the adapter. `text` / `tool` / `thinking`
9038
- // flow through the pump (rendered + committed); `session` / `result` are the
9039
- // dispatcher's concern and ignored here. A throwing commit (a flaky/4xx HTTP
9040
- // POST) is routed to the error hook and swallowed — a failed commit is
9041
- // warn-only and must never fail the turn (the role the classification's
9042
- // `safeEmit` used to play before the adapter owned the decode).
8801
+ // `text` / `tool` / `thinking` flow through the pump; `session` / `result` are
8802
+ // the dispatcher's and ignored here. A throwing commit is routed to the error
8803
+ // hook and swallowed: a failed commit must never fail the turn.
9043
8804
  async ingestEvent(event) {
9044
8805
  if (event.type === "session" || event.type === "result") return;
9045
8806
  try {
@@ -9266,18 +9027,15 @@ var init_turn_execution = __esm({
9266
9027
  turnLog;
9267
9028
  abortController = new AbortController();
9268
9029
  outcome = initialOutcome();
9269
- // CT1503: this turn's shell containment, once the claude-code adapter is built.
9270
- // Null before that and on a turn that never reached adapter construction — a
9271
- // startup failure has nothing to reap, and reaping is a no-op rather than a
9272
- // special case.
9030
+ // This turn's shell containment, once the claude-code adapter is built. Null
9031
+ // before that, so a startup failure's reap is a no-op.
9273
9032
  containment = null;
9274
9033
  seqCounter = 0;
9275
- // CT11: per-turn monotonic counter mirroring the in-process dispatcher's.
9276
- //
9277
- // CT1380: 0 for a NEW turn only — a resume is seeded in `fetchContext` first.
9034
+ // Per-turn monotonic seq. Starts at 0 for a NEW turn only; a resume is seeded
9035
+ // in `fetchContext` first.
9278
9036
  nextSeq = () => ++this.seqCounter;
9279
- // CT1380: a REPLAY, told to us by the supervisor — not derivable here, since
9280
- // `turnId` is set on every dispatch. `resumedFromSeq` is what it seeded from.
9037
+ // A REPLAY, told to us by the supervisor — not derivable here, since `turnId`
9038
+ // is set on every dispatch. `resumedFromSeq` is what it seeded from.
9281
9039
  resumed;
9282
9040
  resumedFromSeq;
9283
9041
  // Whether the span this resumed run continues already has first words —
@@ -9285,8 +9043,8 @@ var init_turn_execution = __esm({
9285
9043
  resumedSpoke = false;
9286
9044
  turnContext;
9287
9045
  resolvedMcpServers;
9288
- // CT1698: the harness's secret environment for this turn. Only ever handed
9289
- // to the adapter; never logged, never merged into `process.env`.
9046
+ // The harness's secret environment for this turn. Only ever handed to the
9047
+ // adapter; never logged, never merged into `process.env`.
9290
9048
  secretEnv = {};
9291
9049
  effectiveCwd;
9292
9050
  hookEnv;
@@ -9300,19 +9058,15 @@ var init_turn_execution = __esm({
9300
9058
  turnControlIntentFetched = false;
9301
9059
  applyRecordedTurnControlIntent = async () => {
9302
9060
  };
9303
- // CT1292: the committer is built before the lease watchdog arms, so the
9304
- // failed-commit hint reaches it through this slot. Safe as a no-op until the
9305
- // watchdog replaces it — the committer only fires it from inside the adapter
9306
- // loop, which runs after.
9061
+ // The committer is built before the lease watchdog arms, so the failed-commit
9062
+ // hint reaches it through this slot. A no-op until then is safe: the committer
9063
+ // only fires it from inside the adapter loop, which runs after.
9307
9064
  noteCommitFailed = () => {
9308
9065
  };
9309
9066
  disarmWatchdogs = () => {
9310
9067
  };
9311
- // CT1503: reap this turn's shell containment. Idempotent (the containment drops
9312
- // what it has already stopped) and total — a reap that fails is reported and
9313
- // swallowed, because a turn that produced a good answer must not be recorded as
9314
- // failed on account of a leftover process, and the failure is visible in the log
9315
- // either way.
9068
+ // Idempotent, and a failed reap is logged and swallowed: a turn that produced a
9069
+ // good answer must not be recorded as failed on account of a leftover process.
9316
9070
  async reapContainment() {
9317
9071
  const containment = this.containment;
9318
9072
  if (!containment) return;
@@ -9338,20 +9092,18 @@ var init_turn_execution = __esm({
9338
9092
  );
9339
9093
  }
9340
9094
  }
9341
- // Codo's stack review, blocking finding #2: set the moment `acquireLease`'s
9342
- // PATCH returns — the lease is granted and this is the pair's running span,
9343
- // so every exit after this point must settle THE SPAN, not just clear the
9095
+ // Set the moment `acquireLease`'s PATCH returns: this is the pair's running
9096
+ // span, so every exit after this must settle THE SPAN, not just clear the
9344
9097
  // participant flag.
9345
9098
  admitted = false;
9346
- // Rooms: one recompose per turn when the lease refuses a stale context.
9099
+ // One recompose per turn when the lease refuses a stale context.
9347
9100
  recomposedForLease = false;
9348
- // CT1677: the execution source the CURRENT context was adopted as. Reassigned
9349
- // when a `dispatch_context_stale` recompose adopts a replacement, so a piece of
9350
- // work — preparation, the committer, a notice — captures it when that work
9351
- // begins and never reads it again: earlier work keeps its source. Undefined
9352
- // only before the first adoption; no work runs until it is set.
9101
+ // The execution source the CURRENT context was adopted as. A
9102
+ // `dispatch_context_stale` recompose reassigns it, so each piece of work
9103
+ // (preparation, the committer, a notice) captures it when it begins and keeps
9104
+ // that source. No work runs before it is set.
9353
9105
  executionSourceId;
9354
- // CT1677: one fresh fetch per turn when adoption is refused.
9106
+ // One fresh fetch per turn when adoption is refused.
9355
9107
  refetchedForAdoption = false;
9356
9108
  concluded(reason, errorReason) {
9357
9109
  return new TurnConcluded(reason, errorReason);
@@ -9419,21 +9171,18 @@ var init_turn_execution = __esm({
9419
9171
  this.supervisor.notifyEnd({ id: payload.messageId, ok: false, durationMs, reason });
9420
9172
  return { ok: false, durationMs, reason, runtime: this.turnContext?.runtime };
9421
9173
  }
9422
- // The post-admission analogue of `concludeBeforeRun` (Codo's stack review,
9423
- // blocking finding #2): this turn WAS admitted — it holds the conversation's
9424
- // lease — so the clear must NAME the turn and DECLARE its terminal outcome.
9425
- // A bare pre-run clear here would reset the participant flag while the span
9426
- // stayed running until the lost sweep. `outcome` follows the settle wire's
9427
- // rule: a failure names its reason; without one the turn settled — the
9428
- // runtime-unavailable exit posts its user-facing `final` before concluding,
9429
- // so the turn genuinely produced its terminal output.
9174
+ // The admitted turn holds the conversation's lease, so the clear must NAME the
9175
+ // turn and DECLARE its outcome; a bare clear resets the participant flag and
9176
+ // leaves the span running until the lost sweep. A failure names its reason;
9177
+ // without one the turn settled (the runtime-unavailable exit has already posted
9178
+ // its `final`).
9430
9179
  async concludeAdmittedRun(reason, errorReason) {
9431
9180
  const { payload, turnLog, startedAt, turnId } = this;
9432
9181
  const body = {
9433
9182
  activeRunStartedAt: null,
9434
9183
  turnId,
9435
- // CT1379: `runtimeIncomplete` is a real failure that deliberately carries
9436
- // no `errorReason`, so the inference alone would read it as settled.
9184
+ // `runtimeIncomplete` is a real failure that deliberately carries no
9185
+ // `errorReason`, so the inference alone would read it as settled.
9437
9186
  outcome: errorReason || this.outcome.runtimeIncomplete ? "failed" : "settled"
9438
9187
  };
9439
9188
  if (errorReason) body.errorReason = errorReason.slice(0, 200);
@@ -9527,11 +9276,10 @@ var init_turn_execution = __esm({
9527
9276
  throw this.concluded(reason, reason);
9528
9277
  }
9529
9278
  }
9530
- // CT1677: register this context's issuance as the execution source of the
9531
- // work about to run on it. A context with no issuance has nothing to adopt,
9532
- // and that ends the turn exactly as a failed adoption does: this companion
9533
- // never commits a row whose provenance it cannot name. Only a refusal —
9534
- // nothing to adopt any more — earns a fresh fetch.
9279
+ // Register this context's issuance as the execution source of the work about to
9280
+ // run on it. No issuance ends the turn as a failed adoption does: this companion
9281
+ // never commits a row whose provenance it cannot name. Only a refusal earns a
9282
+ // fresh fetch.
9535
9283
  async adoptSource(context) {
9536
9284
  const { payload, turnId, turnLog } = this;
9537
9285
  if (!context.issuanceId) {
@@ -9580,9 +9328,8 @@ var init_turn_execution = __esm({
9580
9328
  secretEnv,
9581
9329
  missing
9582
9330
  } = resolveMcpSecrets(
9583
- // CT262: the user MCP DEFINITIONS (placeholder form) come from the turn
9584
- // context now, not a separate `getAgentSelf` run-config fetch. CT1698: and
9585
- // the room connections' entries, whose values arrive in `secretEnv`.
9331
+ // User MCP definitions in placeholder form, plus the room connections'
9332
+ // entries, whose values arrive in `secretEnv`.
9586
9333
  this.turnContext.mcpServers ?? {},
9587
9334
  secretStore,
9588
9335
  this.turnContext.secretEnv
@@ -9695,15 +9442,12 @@ var init_turn_execution = __esm({
9695
9442
  agentUsername: this.opts.agentUsername,
9696
9443
  runtime: this.turnContext.runtime,
9697
9444
  hostAccess: this.turnContext.policy.hostFs,
9698
- // CT317/CT319: the trigger message's referenced-entry paths — what the
9699
- // tasker prepare hook keys its per-task env off. Defaults to `[]` for
9700
- // an older API. The conversation anchor is gone (CT319).
9445
+ // The trigger message's referenced-entry paths, which a prepare hook
9446
+ // may key its per-task env off. `[]` from an older API.
9701
9447
  triggerEntryPaths: this.turnContext.conversation.triggerEntryPaths ?? [],
9702
9448
  title: this.turnContext.conversation.title,
9703
- // CT943: the dispatching message's text — where an `env:` directive
9704
- // rides. The server has always sent the trigger body on the turn
9705
- // context (for the live feed); this is the first thing to read it as
9706
- // an INPUT, so a dispatch can ask for its environment in words.
9449
+ // Where an `env:` directive rides, so a dispatch can ask for its
9450
+ // environment in words.
9707
9451
  messageBody: this.turnContext.message.body
9708
9452
  },
9709
9453
  { turnId, deviceId: this.opts.deviceId },
@@ -9749,37 +9493,30 @@ var init_turn_execution = __esm({
9749
9493
  payload.agentId,
9750
9494
  {
9751
9495
  activeRunStartedAt: (/* @__PURE__ */ new Date()).toISOString(),
9752
- // CT1679: a runner leases under its launcher's boot id — the one the
9753
- // device heartbeats — or the lost-run sweep reads it as a restart.
9496
+ // A runner leases under its launcher's boot id, the one the device
9497
+ // heartbeats, or the lost-run sweep reads it as a restart.
9754
9498
  bootId: this.opts.inRunner?.bootId ?? bootId,
9755
- // CT1680: the runner holding the span, beside the device's boot id.
9756
9499
  ...this.opts.inRunner ? { runnerId: this.opts.inRunner.runnerId } : {},
9757
- // CT1380: the durable record that this turn resumed, and from where.
9500
+ // The durable record that this turn resumed, and from where.
9758
9501
  ...this.resumedFromSeq !== void 0 ? { resumedFromSeq: this.resumedFromSeq } : {},
9759
- // CT1454: the issuance the context THIS attempt runs on came from. Read
9760
- // off `this.turnContext`, which is the fetch that composed this request —
9761
- // so a retransmitted start echoes the same id and reuses the same record,
9762
- // and a resumed attempt echoes its own, newer one. The post-admission
9763
- // fresh-session recompose (`freshRequest`) deliberately does not touch it:
9764
- // it adopts only the prompt, never the run-config, so its issuance must
9765
- // not become this attempt's evidence.
9502
+ // The issuance of the fetch that composed this request, so a
9503
+ // retransmitted start reuses the same record and a resumed attempt
9504
+ // echoes its own. `freshRequest` deliberately leaves it alone: it adopts
9505
+ // only the prompt, never the run-config.
9766
9506
  ...this.turnContext.issuanceId ? { issuanceId: this.turnContext.issuanceId } : {},
9767
- // CT33: hand the server this turn's id so the new-run chokepoint's
9768
- // `closeAbandonedTurns` sweep excludes it. The prepare hook may have
9769
- // already emitted a "preparing" activity row for this turn above (it
9770
- // runs before this flip), which would otherwise make the sweep mistake
9771
- // this live turn for an abandoned one and close it with a `stopped`.
9507
+ // So `closeAbandonedTurns` excludes this turn: a "preparing" row emitted
9508
+ // before this flip would otherwise read as abandoned and be stopped.
9772
9509
  turnId,
9773
9510
  // The newest message this turn's read covered: the lease records it,
9774
9511
  // and settle moves the cursor to it. Omitted when the server sent
9775
9512
  // none (an older API), so the cursor stays where it was.
9776
9513
  ...this.turnContext.readThrough !== void 0 ? { readThrough: this.turnContext.readThrough } : {},
9777
9514
  ...this.turnContext.readRevision !== void 0 ? { readRevision: this.turnContext.readRevision } : {},
9778
- // Rooms: the attempt this request's session belongs to (null = fresh).
9515
+ // The attempt this request's session belongs to (null = fresh).
9779
9516
  ...this.turnContext.attemptId !== void 0 ? { attemptId: this.turnContext.attemptId } : {},
9780
- // CT1543: the frame this start acts on. The turn-context fetch already
9781
- // checked it, but that was before an assembly this turn awaited — the
9782
- // lease checks it again where the stamp is actually taken.
9517
+ // The frame this start acts on. The context fetch checked it before an
9518
+ // assembly this turn awaited; the lease re-checks where the stamp is
9519
+ // taken.
9783
9520
  ...payload.episode !== void 0 ? { dispatchMessageId: payload.messageId, dispatchEpisode: payload.episode } : {}
9784
9521
  }
9785
9522
  );
@@ -9819,21 +9556,18 @@ var init_turn_execution = __esm({
9819
9556
  this.request = buildCompanionTurnRequest({
9820
9557
  turnContext: this.turnContext,
9821
9558
  baseUrl: this.opts.baseUrl,
9822
- // ACC8: always present; `fetchTurnContext` refused a context without one.
9559
+ // Always present: `fetchContext` refuses a context without one.
9823
9560
  turnToken: this.turnContext.turnToken,
9824
- // SJ524: the hook-resolved cwd overrides the static local cwd.
9825
9561
  ...this.effectiveCwd ? { cwd: this.effectiveCwd } : {},
9826
- // CT1103: the prepare-hook env plus the authoritative host-access token.
9827
9562
  env: this.turnEnv,
9828
9563
  mcpServers: this.resolvedMcpServers,
9829
9564
  secretEnv: this.secretEnv,
9830
9565
  turnControlServer,
9831
- // CT238: this turn's conversation, forwarded as the active-conversation
9832
- // header so a cross-thread post/spawn stamps its origin.
9566
+ // Sent as the active-conversation header so a cross-conversation post
9567
+ // stamps its origin.
9833
9568
  activeConversationId: payload.conversationId,
9834
- // Workspace coordinate retained in the compatible TurnRequest envelope.
9835
9569
  workspaceId,
9836
- // CT289: the operator's auto-memory escape hatch (machine-local), when set.
9570
+ // The operator's machine-local auto-memory escape hatch, when set.
9837
9571
  ...this.opts.local.claudeCode ? { claudeCode: this.opts.local.claudeCode } : {}
9838
9572
  });
9839
9573
  }
@@ -9870,8 +9604,8 @@ var init_turn_execution = __esm({
9870
9604
  if (this.opts.claudeCodeAvailable?.() ?? true) {
9871
9605
  this.containment = createTurnContainment(this.turnId, {
9872
9606
  log: (msg, meta) => turnLog.debug(meta ?? {}, msg),
9873
- // CT1679: a group outside the runner's own is reported, so a launcher
9874
- // can stop it if the runner dies without reaping it.
9607
+ // A group outside the runner's own is reported, so a launcher can stop
9608
+ // it if the runner dies without reaping it.
9875
9609
  ...this.opts.inRunner?.onProcessGroup ? { onProcessGroup: this.opts.inRunner.onProcessGroup } : {}
9876
9610
  });
9877
9611
  const containment = this.containment;
@@ -9892,8 +9626,8 @@ var init_turn_execution = __esm({
9892
9626
  const serverUrl = this.opts.opencodeServerUrl;
9893
9627
  adapters.push(
9894
9628
  createOpencodeAdapter(
9895
- // CT1679: the transport's per-server turn mutex is per process; in a
9896
- // runner the turn also holds the device-wide lock every runner shares.
9629
+ // The transport's per-server turn mutex is per process; in a runner the
9630
+ // turn also holds the device-wide lock every runner shares.
9897
9631
  this.opts.inRunner ? {
9898
9632
  transport: this.opts.inRunner.opencodeTransport(serverUrl),
9899
9633
  onWarn
@@ -9940,7 +9674,7 @@ var init_turn_execution = __esm({
9940
9674
  kind: "runtime_notice",
9941
9675
  turnId,
9942
9676
  parentMessageId: payload.messageId,
9943
- // CT1677: the notice is written by this attempt's source, like its speech.
9677
+ // Written by this attempt's source, like its speech.
9944
9678
  ...this.executionSourceId !== void 0 ? { executionSourceId: this.executionSourceId } : {}
9945
9679
  });
9946
9680
  } catch (postErr) {
@@ -9954,16 +9688,13 @@ var init_turn_execution = __esm({
9954
9688
  throw this.concluded(`runtime_unavailable:${err.runtime}`);
9955
9689
  }
9956
9690
  }
9957
- // The adapter refused to resume this pair's stored session and is starting a
9958
- // fresh one. The request it holds was composed for a session that had the
9959
- // conversation so far — the read is what landed since the cursor, the
9960
- // agent's own words left out because the session held them. A fresh session
9961
- // holds none of it, so fetch the context again as a first turn on an empty
9962
- // session (the whole conversation, own words included, the first-turn
9963
- // material) and hand the adapter that to run on — before it speaks or acts.
9964
- // The lease already granted stands; the read reaches at least its mark. A
9965
- // fetch that fails returns null and the adapter falls back to the request
9966
- // it has, flagged degraded, so the server rewinds the cursor at settle.
9691
+ // The adapter refused to resume the stored session and is starting fresh. The
9692
+ // request it holds assumed the session had the conversation so far (only what
9693
+ // landed since the cursor, the agent's own words left out); a fresh session
9694
+ // has none of it. So refetch as a first turn on an empty session and hand the
9695
+ // adapter that, before it speaks or acts. The granted lease stands. A failed
9696
+ // fetch returns null: the adapter runs the request it has, flagged degraded,
9697
+ // and the server rewinds the cursor at settle.
9967
9698
  async freshRequest(reason) {
9968
9699
  const { payload, turnId, turnLog } = this;
9969
9700
  try {
@@ -9974,9 +9705,8 @@ var init_turn_execution = __esm({
9974
9705
  false,
9975
9706
  true,
9976
9707
  void 0,
9977
- // CT1657: the turn's abort. This recompose happens mid-run with the
9978
- // adapter waiting on it, so a Stop here must end the wait rather than
9979
- // hold the turn open for the rest of the retry schedule.
9708
+ // The adapter is waiting on this, so a Stop must end the wait rather
9709
+ // than hold the turn open for the rest of the retry schedule.
9980
9710
  this.abortController.signal
9981
9711
  );
9982
9712
  turnLog.debug(
@@ -10028,19 +9758,17 @@ var init_turn_execution = __esm({
10028
9758
  conversationId: payload.conversationId,
10029
9759
  agentId: payload.agentId,
10030
9760
  turnId,
10031
- // CT113: the message this turn answers — passed back on every commit so
10032
- // the reply records the Q→A edge the in-process dispatcher stamps directly.
9761
+ // The message this turn answers, on every commit so the reply records the
9762
+ // Q→A edge.
10033
9763
  parentMessageId: payload.messageId,
10034
9764
  signal: abortController.signal,
10035
9765
  log: turnLog,
10036
9766
  nextSeq: this.nextSeq,
10037
9767
  spoke: this.resumedSpoke,
10038
- // CT1677: admitted by now, so this is the source the model's work runs on.
9768
+ // Admitted by now, so this is the source the model's work runs on.
10039
9769
  ...this.executionSourceId !== void 0 ? { executionSourceId: this.executionSourceId } : {},
10040
- // CT1292: a commit that didn't land may mean this turn's lease is gone.
10041
- // `noteCommitFailed` is a field slot the lease watchdog fills when it
10042
- // arms (it starts as a no-op) — the committer only fires it from inside
10043
- // the adapter loop, which runs after the watchdog has replaced it.
9770
+ // A commit that didn't land may mean the lease is gone; see
9771
+ // `noteCommitFailed`.
10044
9772
  onCommitFailed: (err) => this.noteCommitFailed(err)
10045
9773
  });
10046
9774
  const usesHttpTurnControl = turnRuntime === "codex" || turnRuntime === "opencode";
@@ -10144,9 +9872,9 @@ var init_turn_execution = __esm({
10144
9872
  workspaceId,
10145
9873
  payload.conversationId,
10146
9874
  payload.agentId,
10147
- // CT1680: named for its turn, which a runner credential
10148
- // requires once its start is admitted; the server then binds
10149
- // the capture to that turn's attempt rather than the pair's.
9875
+ // Named for its turn, which a runner credential requires once
9876
+ // admitted; the server binds the capture to that turn's attempt
9877
+ // rather than the pair's.
10150
9878
  { agentSessionId: event.state, turnId }
10151
9879
  );
10152
9880
  } catch (err) {
@@ -10251,9 +9979,7 @@ var init_turn_execution = __esm({
10251
9979
  }
10252
9980
  const body = {
10253
9981
  activeRunStartedAt: null,
10254
- // CT277: the server keys the turn-end metadata UPDATE (ended_at + tokens)
10255
- // off this turn id, so the settle PATCH must carry it — the start PATCH
10256
- // isn't enough. Always sent (every settle closes a real turn).
9982
+ // The server keys the turn-end metadata (ended_at, tokens) off this id.
10257
9983
  turnId
10258
9984
  };
10259
9985
  if (o.turnUsage) {
@@ -10422,11 +10148,11 @@ var init_dispatcher = __esm({
10422
10148
  this.aborts = opts.aborts ?? /* @__PURE__ */ new Map();
10423
10149
  }
10424
10150
  opts;
10425
- // CT1288: keyed (conversation|agent) → per-TURN controllers. Stop is
10426
- // pair-scoped and must reach every live loop for the pair.
10151
+ // (conversation|agent) → per-TURN controllers. Stop is pair-scoped and must
10152
+ // reach every live loop for the pair.
10427
10153
  aborts;
10428
- // CT1109: pairs already told "your session couldn't be saved" — the notice is
10429
- // once per (conversation, agent), while the rejection repeats every turn.
10154
+ // Pairs already told their session couldn't be saved: the notice is once per
10155
+ // pair, while the rejection repeats every turn.
10430
10156
  sessionWriteNotified = /* @__PURE__ */ new Set();
10431
10157
  notifyStart(info) {
10432
10158
  try {
@@ -10443,11 +10169,6 @@ var init_dispatcher = __esm({
10443
10169
  // The supervisor supplies a stable turnId on both fresh and resumed dispatch.
10444
10170
  // resumed is separate: only replay seeds sequence/speech recovery from the
10445
10171
  // prior span. A direct caller omitting turnId gets a fresh id.
10446
- //
10447
- // CT1261: a short `handle` creates a `TurnExecution` and runs it. The
10448
- // supervisor seam hands the execution exactly the three cross-turn duties —
10449
- // the abort registry, the session-write notice latch, the observer — and
10450
- // nothing else.
10451
10172
  async handle(payload, opts = {}) {
10452
10173
  const key = runKey(payload.conversationId, payload.agentId);
10453
10174
  const supervisor = {
@@ -10493,8 +10214,8 @@ var init_dispatcher = __esm({
10493
10214
  }
10494
10215
  return new TurnExecution(this.opts, supervisor, payload, opts).run();
10495
10216
  }
10496
- // SJ383: cancel a specific (conversation, agent) run if one is in flight in
10497
- // THIS companion process. Returns true if an in-flight run was aborted.
10217
+ // Cancel a (conversation, agent) run in flight in THIS process. True if one
10218
+ // was aborted.
10498
10219
  cancel(conversationId, agentId) {
10499
10220
  const key = runKey(conversationId, agentId);
10500
10221
  const pair2 = this.aborts.get(key);
@@ -11217,7 +10938,7 @@ var init_outbox = __esm({
11217
10938
  }
11218
10939
  workspaceId;
11219
10940
  log;
11220
- // Resolved lazily (per call, like cursor.ts) so tests that swap HOME between
10941
+ // Resolved lazily (per call) so tests that swap HOME between
11221
10942
  // cases route writes at the right tmpdir.
11222
10943
  dir() {
11223
10944
  return join20(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
@@ -11283,7 +11004,7 @@ var init_outbox = __esm({
11283
11004
  );
11284
11005
  return entries;
11285
11006
  }
11286
- // CT1380: the highest `seq` this turn has queued but not yet delivered — the
11007
+ // The highest `seq` this turn has queued but not yet delivered — the
11287
11008
  // half of a resumed turn's seq floor the SERVER CANNOT SEE. A commit that
11288
11009
  // failed transiently before the crash is sitting in this directory with its
11289
11010
  // seq already spent, and the restart's drain is fire-and-forget
@@ -12096,9 +11817,8 @@ function parseHarnessRuntime(raw) {
12096
11817
  function deriveHarnessSnapshot(signals) {
12097
11818
  const advertised = new Set(
12098
11819
  buildCompanionManifest({
12099
- // CT1082 + CT1379: connected AND the bundled binary the SDK launches is
12100
- // there — the manifest's own rule, restated here through the same function
12101
- // rather than re-decided. PATH is deliberately absent from both gates now.
11820
+ // Connected AND the bundled binary present: the manifest's own rule. PATH
11821
+ // is deliberately in neither gate.
12102
11822
  claudeCode: signals.claudeCodeConnected && signals.claudeBundled === "present",
12103
11823
  opencode: signals.opencodeConfigured,
12104
11824
  codex: signals.codexEnabled && signals.codexBundled === "present"
@@ -12136,10 +11856,8 @@ function deriveClaudeCode(signals, manifestHas) {
12136
11856
  ...base,
12137
11857
  state: "detected_not_exposed",
12138
11858
  version: signals.claudeVersion,
12139
- // CT1379: connecting records INTENT; it is not the same as becoming
12140
- // runnable. With the bundled binary missing, a connect succeeds and the
12141
- // device still advertises nothing — so this says so up front rather than
12142
- // promising routing it can't deliver.
11859
+ // Connecting records INTENT, not runnability: with the bundled binary
11860
+ // missing a connect succeeds and nothing is advertised, so say so.
12143
11861
  detail: signals.claudeBundled === "present" ? "Claude Code is installed here but not connected yet. Connect it to offer Claude Code from this device." : `Claude Code is installed here but not connected yet \u2014 and @cabane/companion's bundled Claude Code ${missingNoun(signals.claudeBundled)} is missing, so connecting alone won't make it runnable. ${repairAction()}`,
12144
11862
  enable: "claude-code"
12145
11863
  };
@@ -12177,8 +11895,7 @@ function deriveCodex(signals, manifestHas) {
12177
11895
  ...base,
12178
11896
  state: "detected_not_exposed",
12179
11897
  version: signals.codexVersion,
12180
- // CT1379: same distinction as Claude Code — enabling is intent, not
12181
- // launchability.
11898
+ // As for Claude Code: enabling is intent, not launchability.
12182
11899
  detail: signals.codexBundled === "present" ? "Codex is installed but not enabled yet. Turn it on to offer Codex from this device." : `Codex is installed but not enabled yet \u2014 and @cabane/companion's bundled Codex ${missingNoun(signals.codexBundled)} is missing, so enabling alone won't make it runnable. ${repairAction()}`,
12183
11900
  enable: "codex"
12184
11901
  };
@@ -12259,12 +11976,10 @@ async function probeHarnessSignals(cfg, deps = {}) {
12259
11976
  return {
12260
11977
  claudeOnPath: claudePresence.status === "present",
12261
11978
  claudeVersion,
12262
- // CT1082: the user's opt-in. Presence alone exposes nothing now, so this is
12263
- // the manifest gate and the probe above is only a suggestion.
11979
+ // The user's opt-in, the manifest gate; the probe above only suggests.
12264
11980
  claudeCodeConnected: isClaudeCodeConnected(cfg),
12265
- // CT1379: the executable a Claude turn actually launches. Resolved every time
12266
- // the signals are, so a binary that disappears between beats is caught on the
12267
- // next one rather than at turn time.
11981
+ // The executable a Claude turn launches, resolved with every probe so a
11982
+ // binary that disappears is caught on the next beat, not at turn time.
12268
11983
  claudeBundled: resolveBundled("claude-code").status,
12269
11984
  codexBundled: resolveBundled("codex").status,
12270
11985
  // A parseable `codex --version` is our presence signal (presence alone never
@@ -12273,7 +11988,7 @@ async function probeHarnessSignals(cfg, deps = {}) {
12273
11988
  codexVersion,
12274
11989
  codexEnabled: isCodexEnabled(cfg),
12275
11990
  opencodeConfigured: !!configuredServerUrl,
12276
- // A version came back ⟺ the serve answered its health endpoint (CT584).
11991
+ // A version came back ⟺ the serve answered its health endpoint.
12277
11992
  opencodeReachable: opencodeVersion !== null,
12278
11993
  opencodeVersion,
12279
11994
  opencodeDetectedUrl: opencodeVersion !== null ? opencodeProbeUrl : null
@@ -12842,13 +12557,11 @@ function resolvePairBaseUrl(server) {
12842
12557
  function writePairedConfig(paired) {
12843
12558
  const { local, note, hadPriorConfig } = loadConfigTolerant();
12844
12559
  const config = {
12845
- // CT1082: a device pairs connected to NOTHING — `claudeCode: { enabled: false }`
12846
- // written explicitly, not left absent, because absence is exactly what marks a
12847
- // pre-CT1082 config for the grandfathering migration. Omit it and a brand-new
12848
- // device gets auto-connected on its first start by the behaviour this task
12849
- // removes; stamp it on a RE-pair and an upgrading user's working device gets
12850
- // disconnected instead. So it's written only when there was no config here
12851
- // before. `local` spreads after, so a re-pair keeps the answer already given.
12560
+ // A device pairs connected to NOTHING: `claudeCode: { enabled: false }`
12561
+ // written explicitly, since absence is what the harness migration reads.
12562
+ // Omitted, a new device is auto-connected on first start; stamped on a
12563
+ // RE-pair, an upgrading user's working device is disconnected. So only when
12564
+ // there was no config before; `local` spreads after, keeping a prior answer.
12852
12565
  ...hadPriorConfig ? {} : { claudeCode: { enabled: false } },
12853
12566
  ...local,
12854
12567
  baseUrl: paired.baseUrl,
@@ -13156,7 +12869,7 @@ var CompanionStateHub = class {
13156
12869
  deviceId = null;
13157
12870
  deviceLabel = null;
13158
12871
  deviceError = null;
13159
- // CT586: the latest harness snapshot the supervisor probed, or null before the
12872
+ // The latest harness snapshot the supervisor probed, or null before the
13160
12873
  // first probe (see CompanionStatusJson.harnesses).
13161
12874
  harnesses = null;
13162
12875
  // ---- subscription (SSE) ----
@@ -13183,7 +12896,7 @@ var CompanionStateHub = class {
13183
12896
  this.deviceError = null;
13184
12897
  this.emitStatus();
13185
12898
  }
13186
- // ---- harnesses (CT586) ----
12899
+ // ---- harnesses ----
13187
12900
  // Replace the harness snapshot and push a status change (so the SSE feed flips
13188
12901
  // the Companion's Harnesses surface live). The supervisor calls this on each
13189
12902
  // heartbeat-cadence probe and on an on-demand recheck.
@@ -13801,7 +13514,7 @@ var DeviceApi = class {
13801
13514
  AbortSignal.timeout(5e3)
13802
13515
  );
13803
13516
  }
13804
- // CT1146: report that a dispatch addressed to THIS device can't be run, because
13517
+ // Report that a dispatch addressed to THIS device can't be run, because
13805
13518
  // the agent isn't one this device runs (and still wasn't after a forced
13806
13519
  // assignments refresh). The server clears the Working flag, retires the dispatch
13807
13520
  // and posts an explanatory system message.
@@ -13815,7 +13528,7 @@ var DeviceApi = class {
13815
13528
  return this.request("POST", "/api/companion/dispatch-drop", body);
13816
13529
  }
13817
13530
  // Per-device liveness ping. Reports the companion build version and the env-var
13818
- // names the operator's secret store exposes (never values), so CT30's UI can
13531
+ // names the operator's secret store exposes (never values), so the UI can
13819
13532
  // warn pre-emptively about an agent that needs a secret this device lacks.
13820
13533
  heartbeat(body) {
13821
13534
  return this.request("POST", "/api/companion/heartbeat", body ?? {});
@@ -13998,7 +13711,7 @@ var ConnectorHealthStore = class {
13998
13711
  lastError: outcome.errorReason ?? null
13999
13712
  });
14000
13713
  }
14001
- // CT639: the store's current opinion for one runtime, or undefined when it has
13714
+ // The store's current opinion for one runtime, or undefined when it has
14002
13715
  // no observation. Read by the dispatcher at idle-timeout fire: an idle reap
14003
13716
  // while this runtime is definitively `limited` (a present, future `limitedUntil`
14004
13717
  // recorded by a sibling/earlier turn's classified cap settle) is the account cap
@@ -15579,24 +15292,20 @@ var CompanionSupervisor = class {
15579
15292
  config;
15580
15293
  log;
15581
15294
  hub;
15582
- // CT1379: the bundled-executable resolver — the execution-presence half of both
15583
- // routing gates. Synchronous and cheap (a module resolve plus a stat), so unlike
15584
- // the CLI probes it needs no boot-frozen fallback: a caller that asks before the
15585
- // first harness refresh simply resolves now.
15295
+ // The execution-presence half of both routing gates. Synchronous and cheap (a
15296
+ // module resolve plus a stat), so a caller before the first harness refresh
15297
+ // simply resolves now.
15586
15298
  resolveBundled;
15587
- // CT1379: the last bundled status LOGGED per harness, so a missing binary is
15588
- // reported at startup and on each healthy↔missing transition rather than on
15589
- // every 30s beat (the CT484 latch pattern, per runtime).
15299
+ // The last bundled status LOGGED per harness, so a missing binary is reported
15300
+ // at startup and on each healthy↔missing transition, not every beat.
15590
15301
  loggedBundledStatus = /* @__PURE__ */ new Map();
15591
- // CT571: the probed per-harness versions, reported on each heartbeat manifest.
15592
- // CT586: no longer boot-frozen — the per-heartbeat harness re-probe refreshes
15593
- // it, so a harness updated mid-run reports its new version without a restart.
15302
+ // Refreshed by the per-heartbeat re-probe, so a harness updated mid-run
15303
+ // reports its new version without a restart.
15594
15304
  harnessVersions;
15595
- // CT586: the latest harness detection signals (presence + reachability), probed
15596
- // on the heartbeat cadence + on demand. Feeds BOTH the heartbeat manifest's
15597
- // live presence and the Companion's Harnesses surface off ONE source, so the UI
15598
- // can never disagree with what the manifest advertises. Null until the first
15599
- // probe (the heartbeat then falls back to the boot `claudeCode`).
15305
+ // The latest harness signals (presence, reachability), probed on the heartbeat
15306
+ // cadence and on demand. ONE source for both the manifest and the Harnesses
15307
+ // surface, so the UI never disagrees with what is advertised. Null until the
15308
+ // first probe.
15600
15309
  harnessSignals = null;
15601
15310
  // Fresh per attempt, never the cached heartbeat signal: installing a harness
15602
15311
  // between attempts must be observed immediately.
@@ -15609,8 +15318,8 @@ var CompanionSupervisor = class {
15609
15318
  unrunnableRetryDelaysMs;
15610
15319
  deviceApi = null;
15611
15320
  deviceSub = null;
15612
- // ACC8: whether the device stream is open, which is what every workspace's
15613
- // link on the dashboard reports.
15321
+ // Whether the device stream is open, which is what every workspace's link on
15322
+ // the dashboard reports.
15614
15323
  deviceConnected = false;
15615
15324
  recoveryChecked = false;
15616
15325
  recovering = false;
@@ -15622,8 +15331,7 @@ var CompanionSupervisor = class {
15622
15331
  pollTimer = null;
15623
15332
  refreshing = false;
15624
15333
  stopped = false;
15625
- // CT484: latch so the companion/server version-skew warning is logged once, not
15626
- // on every 30s heartbeat.
15334
+ // The version-skew warning is logged once, not every heartbeat.
15627
15335
  versionSkewWarned = false;
15628
15336
  // This device's id, captured from the heartbeat / assignments response. The SSE
15629
15337
  // router uses it to tell a dispatch addressed to THIS device (a freshly-assigned
@@ -15633,12 +15341,12 @@ var CompanionSupervisor = class {
15633
15341
  // The in-flight assignments pull, exposed so a caller that needs fresh data
15634
15342
  // (pull-on-miss) can `await` the running pull rather than racing past it.
15635
15343
  inFlightRefresh = null;
15636
- // CT566: device-global connector-health memory — every dispatcher records its
15637
- // turn's classified outcome here (per runtime); the heartbeat echoes it so the
15638
- // server's per-connector quota state survives a restart/race.
15344
+ // Device-global connector-health memory: every dispatcher records its turn's
15345
+ // classified outcome per runtime; the heartbeat echoes it so the server's
15346
+ // quota state survives a restart or race.
15639
15347
  connectorHealth = new ConnectorHealthStore();
15640
- // ACC10: which dispatches this process is running, so the copy of a frame that
15641
- // arrives on the other stream doesn't run the turn a second time.
15348
+ // Which dispatches this process is running, so a redelivered frame doesn't
15349
+ // run the turn a second time.
15642
15350
  claims = new DispatchClaims();
15643
15351
  constructor(opts) {
15644
15352
  this.config = opts.config;
@@ -15659,10 +15367,10 @@ var CompanionSupervisor = class {
15659
15367
  }
15660
15368
  this.latchBootBundledState();
15661
15369
  }
15662
- // CT1681: what a runner container said it can run, probed once at start;
15663
- // null until then, and when no container could be started.
15370
+ // What a runner container said it can run, probed once at start; null until
15371
+ // then, and when no container could be started.
15664
15372
  runnerImage = null;
15665
- // CT1681: the companion every container mounts — this one's own install.
15373
+ // The companion every container mounts: this one's own install.
15666
15374
  runnerCompanion() {
15667
15375
  return companionSource({
15668
15376
  configured: this.config.runners?.docker?.companionDir,
@@ -15774,10 +15482,7 @@ var CompanionSupervisor = class {
15774
15482
  this.pollTimer = setInterval(() => void this.refreshAssignments(), ASSIGNMENTS_POLL_MS);
15775
15483
  this.pollTimer.unref?.();
15776
15484
  }
15777
- // The set of SSE subscriber `finished` promises — start.ts awaits these to
15778
- // keep the foreground process alive. With the poll loop running there's always
15779
- // at least the loops keeping the event loop alive; the SSE promises let a
15780
- // clean teardown resolve.
15485
+ // start.ts awaits these; they resolve on a clean teardown.
15781
15486
  finishedPromises() {
15782
15487
  return [...this.deviceSub ? [this.deviceSub.finished] : []];
15783
15488
  }
@@ -15805,39 +15510,26 @@ var CompanionSupervisor = class {
15805
15510
  bootId,
15806
15511
  version: COMPANION_VERSION,
15807
15512
  exposedSecretNames: store.names(),
15808
- // Report each runtime only when this device can actually run it:
15809
- // CT1379 claude-code and codex when the SDK's own bundled binary is there
15810
- // and the user asked for the harness, CT270 opencode when the operator
15811
- // configured an `opencode serve`.
15513
+ // Each runtime only when this device can actually run it.
15812
15514
  manifest: buildCompanionManifest({
15813
- // CT1082/CT1379: connected AND launchable. Execution presence is the
15814
- // live bundled-binary resolution (refreshed on this beat), consent is
15815
- // the user's `claudeCode.enabled`. Claude Code rides neither alone.
15515
+ // Consented (`claudeCode.enabled`) AND launchable (the live bundled
15516
+ // binary). Neither alone.
15816
15517
  claudeCode: this.claudeCodeOffered(),
15817
15518
  opencode: !!this.config.opencode?.serverUrl,
15818
- // CT481/CT1379: advertise codex when the operator enabled it AND its
15819
- // vendored binary is there. The enable flag alone used to be enough,
15820
- // which is how a device with a half-installed 331 MB platform package
15821
- // advertised a runtime that could not start.
15519
+ // Enabled AND its vendored binary present: a half-installed 331 MB
15520
+ // platform package otherwise advertises a runtime that cannot start.
15822
15521
  codex: this.codexOffered(),
15823
- // CT1379: and say WHY when an asked-for harness is absent from the list
15824
- // above. Diagnostic only — the server never routes on it — and always
15522
+ // WHY an asked-for harness is absent above. Diagnostic only, and always
15825
15523
  // sent, so an empty array clears a previously reported issue.
15826
15524
  runtimeIssues: this.runtimeIssues(),
15827
- // CT571/CT586: each runtime's `version` from the latest harness probe
15828
- // (fail-soft to null). Informational only — the server matches on name.
15525
+ // Informational only; the server matches on name.
15829
15526
  versions: this.harnessVersions,
15830
- // CT1681: shown on the device page.
15527
+ // Shown on the device page.
15831
15528
  ...runnersEnabled(this.config) ? { runner: this.runnerManifest() } : {}
15832
15529
  }),
15833
- // CT566: echo the last classified credential state per runtime, when the
15834
- // companion has seen any. Omitted (undefined) until the first observed
15835
- // failure/heal, so a fresh companion's beat is unchanged and the server's
15836
- // manifest synthesis (status-less rows) still runs.
15530
+ // Omitted until the first observed failure or heal, so the server's
15531
+ // status-less manifest synthesis still runs for a fresh companion.
15837
15532
  ...connectorReports.length > 0 ? { connectors: connectorReports } : {},
15838
- // CT584: include enumerated models only when the probe SUCCEEDED (non-null).
15839
- // A null (probe failed / no opencode) omits the field, and the server then
15840
- // leaves this device's stored availability untouched.
15841
15533
  ...opencodeModels !== null ? { models: opencodeModels } : {}
15842
15534
  });
15843
15535
  this.hub.setDevice({ deviceId: res.deviceId });
@@ -15852,11 +15544,8 @@ var CompanionSupervisor = class {
15852
15544
  );
15853
15545
  }
15854
15546
  }
15855
- // CT484: belt-and-suspenders beside shipping the companion in lockstep inside the
15856
- // artifact — if the server reports a build version that differs from this
15857
- // companion's, warn loudly (once). This is exactly the skew that bit the M1 walk:
15858
- // an npm-pinned companion lagging a from-develop server. The elimination (bundled
15859
- // companion) makes it match by construction; this catches a companion run out of band.
15547
+ // Warn once when the server's build version differs from this companion's: an
15548
+ // npm-pinned companion lagging the server is the usual cause of odd failures.
15860
15549
  checkVersionSkew(serverVersion) {
15861
15550
  if (this.versionSkewWarned) return;
15862
15551
  if (!serverVersion || serverVersion === COMPANION_VERSION) return;
@@ -16053,10 +15742,9 @@ var CompanionSupervisor = class {
16053
15742
  `Stopped running ${runner.displayName} (unassigned)`
16054
15743
  );
16055
15744
  }
16056
- // CT1379: can this machine LAUNCH the harness — is the SDK's own bundled
16057
- // executable there? Live (the per-beat re-resolve), falling back to resolving
16058
- // right now before the first refresh lands. Execution presence ONLY; consent is
16059
- // the caller's half.
15745
+ // Can this machine LAUNCH the harness — is the SDK's bundled executable there?
15746
+ // The per-beat result, or resolved now before the first refresh. Execution
15747
+ // presence ONLY; consent is the caller's half.
16060
15748
  bundledStatus(runtime) {
16061
15749
  const cached2 = runtime === "codex" ? this.harnessSignals?.codexBundled : this.harnessSignals?.claudeBundled;
16062
15750
  return cached2 ?? this.resolveBundled(runtime).status;
@@ -16067,23 +15755,19 @@ var CompanionSupervisor = class {
16067
15755
  bundledResolution(runtime) {
16068
15756
  return this.resolveBundled(runtime);
16069
15757
  }
16070
- // CT1082/CT1379: does this device OFFER claude-code — connected by its user, and
16071
- // able to start the binary a turn runs? The ONE signal both the heartbeat
16072
- // manifest and the dispatcher's adapter registry read, so what the device
16073
- // advertises and what it can select can't disagree (the CT833 invariant, with
16074
- // consent in front of it and the real executable underneath it).
15758
+ // Does this device OFFER claude-code — connected by its user, and able to start
15759
+ // the binary a turn runs? The ONE signal both the heartbeat manifest and the
15760
+ // adapter registry read, so what the device advertises and what it can select
15761
+ // can't disagree.
16075
15762
  claudeCodeOffered() {
16076
15763
  return isClaudeCodeConnected(this.config) && this.bundledStatus("claude-code") === "present";
16077
15764
  }
16078
- // CT1379: and the same question for codex, which until now was the enable flag
16079
- // alone — so an enabled device with no vendored binary advertised a runtime it
16080
- // could not start. Same shape as Claude Code, same predicate on both sides.
15765
+ // The same question for codex: enabled AND its vendored binary present.
16081
15766
  codexOffered() {
16082
15767
  return isCodexEnabled(this.config) && this.bundledStatus("codex") === "present";
16083
15768
  }
16084
- // CT1379: the manifest's diagnostic half, derived from the same two facts as the
16085
- // two predicates above so an issue and an advertisement can never both be made
16086
- // for one harness.
15769
+ // Derived from the same two facts as the predicates above, so an issue and an
15770
+ // advertisement are never both made for one harness.
16087
15771
  runtimeIssues() {
16088
15772
  return runtimeIssuesFor({
16089
15773
  claudeCode: {
@@ -16093,26 +15777,16 @@ var CompanionSupervisor = class {
16093
15777
  codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") }
16094
15778
  });
16095
15779
  }
16096
- // CT1379: a missing binary is reported ONCE, and again only when the answer
16097
- // CHANGES — with the runtime, the platform/architecture and the resolution
16098
- // result, which is enough to find the occurrence in `companion.log` without a
16099
- // repro and carries nothing that could be a credential or a vendor's output.
15780
+ // A missing binary is reported ONCE, and again only when the answer CHANGES,
15781
+ // with runtime, platform/arch and status: enough to find it in `companion.log`
15782
+ // without a repro, and nothing that could be a credential or vendor output.
16100
15783
  //
16101
- // Two things this deliberately does NOT do (Codo's review, blocking finding #2):
16102
- //
16103
- // - It does not re-announce the state the device booted in. `warnAboutHarnessReadiness`
16104
- // already said that on stderr at startup, in the specified words, at the one
16105
- // moment a person is watching a terminal — and the `--daemon` launcher runs
16106
- // that preflight in the FOREGROUND for exactly that reason, because the
16107
- // child's output goes to a file. The boot state is latched in the
16108
- // constructor, so the first refresh reports nothing new; it lands here as a
16109
- // debug record instead, present for diagnosis without competing for
16110
- // attention with the warning the operator already read.
16111
- // - It does not report a transition in generic prose. A binary that goes
16112
- // missing at 3am is the case the operator will actually meet, and a line
16113
- // that says only "binary is missing" leaves them with no repair and no
16114
- // restart instruction. Transitions carry the specified copy verbatim — the
16115
- // same sentence the startup path prints.
15784
+ // - The boot state is not re-announced: `warnAboutHarnessReadiness` said it
15785
+ // on stderr at startup, when a person is watching (the `--daemon` launcher
15786
+ // runs that preflight in the FOREGROUND because the child logs to a file).
15787
+ // It is latched in the constructor and recorded at debug.
15788
+ // - A transition carries the same repair copy the startup path prints, not
15789
+ // generic prose: a binary missing at 3am needs the repair and the restart.
16116
15790
  logBundledTransitions() {
16117
15791
  for (const runtime of ["claude-code", "codex"]) {
16118
15792
  const intended = runtime === "codex" ? isCodexEnabled(this.config) : isClaudeCodeConnected(this.config);
@@ -16135,9 +15809,8 @@ var CompanionSupervisor = class {
16135
15809
  this.log.debug(meta, "companion: bundled harness binary is missing (harness not in use)");
16136
15810
  }
16137
15811
  }
16138
- // CT1379: the boot state, latched so the first refresh reports no news, and
16139
- // recorded once as evidence. Runs in the constructor — the resolve is a module
16140
- // lookup plus a stat, so it costs nothing and needs no await.
15812
+ // The boot state, latched so the first refresh reports no news, and recorded
15813
+ // once as evidence. Synchronous: a module lookup plus a stat.
16141
15814
  latchBootBundledState() {
16142
15815
  for (const runtime of ["claude-code", "codex"]) {
16143
15816
  const resolution = this.resolveBundled(runtime);
@@ -16155,35 +15828,34 @@ var CompanionSupervisor = class {
16155
15828
  );
16156
15829
  }
16157
15830
  }
16158
- // CT1679: the locks every runner on this device shares, held here.
15831
+ // The locks every runner on this device shares.
16159
15832
  runnerLocks = new DeviceLocks();
16160
- // CT1679: `runners.sandbox`, checked against this host once. A host that
16161
- // can't make the sandbox says so once and runs its turns unwrapped.
15833
+ // `runners.sandbox`, checked against this host once. A host that can't make
15834
+ // the sandbox says so once and runs its turns unwrapped.
16162
15835
  sandboxUsable;
16163
15836
  sandboxHost = {
16164
15837
  osSandboxUsable: () => this.sandboxUsable ??= osSandboxUsable(this.log)
16165
15838
  };
16166
- // CT1684: `runners.policy`, parsed once. An invalid one is kept as the
16167
- // error, which every sandboxed turn is refused with.
15839
+ // `runners.policy`, parsed once. An invalid one is kept as the error, which
15840
+ // every sandboxed turn is refused with.
16168
15841
  parsedSandboxPolicy;
16169
15842
  sandboxPolicy() {
16170
15843
  this.parsedSandboxPolicy ??= parseSandboxPolicy(this.config.runners?.policy);
16171
15844
  return this.parsedSandboxPolicy;
16172
15845
  }
16173
- // CT1691: the walk for other companions' stores, run off the turn path.
15846
+ // The walk for other companions' stores, run off the turn path.
16174
15847
  storeWatch;
16175
15848
  companionStores() {
16176
15849
  this.storeWatch ??= companionStoreWatch(this.log);
16177
15850
  return this.storeWatch;
16178
15851
  }
16179
- // CT1693: the sandbox one agent's turns run under — its `agents` row, then
16180
- // the device's. Resolved from the current config every time it's asked, so
16181
- // a reload changes it; only the host probe is kept.
15852
+ // The sandbox one agent's turns run under: its `agents` row, then the
15853
+ // device's. Resolved from the current config on every ask, so a reload
15854
+ // changes it; only the host probe is kept.
16182
15855
  runnerSandboxFor(row) {
16183
15856
  return runnerSandboxFor(this.config, row, this.sandboxHost);
16184
15857
  }
16185
- // CT1693: what the heartbeat says about this device's runners, resolved
16186
- // against the agents it runs right now.
15858
+ // Resolved against the agents this device runs right now.
16187
15859
  runnerManifest() {
16188
15860
  const agents = [...this.workspaces.values()].flatMap(
16189
15861
  (wr) => [...wr.agents.values()].map((agent) => ({
@@ -16197,7 +15869,7 @@ var CompanionSupervisor = class {
16197
15869
  ...runnerSandboxReport(this.config, agents, this.sandboxHost)
16198
15870
  };
16199
15871
  }
16200
- // CT1679: one provider for the device, built on first use.
15872
+ // One provider for the device, built on first use.
16201
15873
  runnerProvider() {
16202
15874
  this.providerForRunners ??= this.dockerRunners() ? this.createDockerRunners() : createLocalProcessProvider();
16203
15875
  return this.providerForRunners;
@@ -16239,8 +15911,7 @@ var CompanionSupervisor = class {
16239
15911
  log: this.log
16240
15912
  });
16241
15913
  }
16242
- // CT1681: the runner command for this device's provider: the image's own
16243
- // companion in a container, this process's otherwise.
15914
+ // The image's own companion in a container, this process's otherwise.
16244
15915
  runnerCommandFor() {
16245
15916
  if (this.runnerCommand) return this.runnerCommand;
16246
15917
  return this.dockerRunners() ? ["cabane-companion", "run-turn"] : selfRunnerCommand();
@@ -16272,36 +15943,20 @@ var CompanionSupervisor = class {
16272
15943
  aborts: ctx.aborts,
16273
15944
  runConfig: ctx.runConfig,
16274
15945
  log: this.log.child({ agentName: ctx.agentDisplayName ?? ctx.agentUsername }),
16275
- // CT833: register the claude-code adapter only when this device actually
16276
- // offers claude-code — read per turn (not captured here), so a harness
16277
- // installed or connected after boot works on the next turn exactly as it
16278
- // appears on the next beat. CT1082: "offers" means connected as well as
16279
- // runnable, so a disconnected harness can't be selected either.
16280
- // CT1379: and the same for the bundled binary — the predicate is shared with
16281
- // the heartbeat above, so a runtime advertised by one and refused by the
16282
- // other is not expressible.
15946
+ // Read PER TURN off the heartbeat's own predicates, so a harness installed
15947
+ // or connected after boot works on the next turn exactly as it appears on
15948
+ // the next beat, and a runtime advertised by one and refused by the other
15949
+ // is not expressible.
16283
15950
  claudeCodeAvailable: () => this.claudeCodeOffered(),
16284
- // CT270: the opencode server URL (operator-configured), when this device
16285
- // offers the opencode runtime. Threaded so an opencode turn selects the
16286
- // opencode adapter; unset leaves the device claude-code-only.
16287
15951
  ...this.config.opencode?.serverUrl ? { opencodeServerUrl: this.config.opencode.serverUrl } : {},
16288
- // CT481: register the codex adapter when this device offers codex; a miss
16289
- // leaves an `openai/…` turn to fail loudly (no silent claude-code fallback).
16290
- // CT1379: read PER TURN, like claude-code above, and off the same predicate
16291
- // the heartbeat uses — it was captured here at dispatcher-construction time
16292
- // from the enable flag alone, so a device could register an adapter for a
16293
- // binary it did not have and keep it registered after the binary came back.
16294
15952
  codexAvailable: () => this.codexOffered(),
16295
- // CT556: per-turn timeout watchdog windows, from the companion's own env
16296
- // (`AGENT_IDLE_TIMEOUT_MS` / `AGENT_TOTAL_TIMEOUT_MS`). Unset → the
16297
- // dispatcher's baked-in defaults (CT1503: 15 min idle / 6h total). An
16298
- // override set here still wins, and is deliberately not validated against
16299
- // the tool ceilings it may reorder — see DEFAULT_AGENT_IDLE_TIMEOUT_MS.
15953
+ // Unset uses the dispatcher's defaults. An override is deliberately not
15954
+ // validated against the tool ceilings it may reorder; see
15955
+ // DEFAULT_AGENT_IDLE_TIMEOUT_MS.
16300
15956
  ...positiveIntEnv("AGENT_IDLE_TIMEOUT_MS") !== void 0 ? { idleTimeoutMs: positiveIntEnv("AGENT_IDLE_TIMEOUT_MS") } : {},
16301
15957
  ...positiveIntEnv("AGENT_TOTAL_TIMEOUT_MS") !== void 0 ? { totalTimeoutMs: positiveIntEnv("AGENT_TOTAL_TIMEOUT_MS") } : {},
16302
15958
  observer: this.hub.observerFor(ctx.workspaceId, ctx.agentId, ctx.workspaceSlug),
16303
15959
  transcriptDir: transcriptsDir(),
16304
- // CT566: the shared connector-health memory the turn records into on settle.
16305
15960
  connectorHealth: this.connectorHealth,
16306
15961
  ...runnersEnabled(this.config) ? {
16307
15962
  runner: {
@@ -16325,11 +15980,10 @@ var CompanionSupervisor = class {
16325
15980
  this.workspaces.delete(workspaceId);
16326
15981
  this.hub.removeWorkspace(workspaceId);
16327
15982
  }
16328
- // ---- SSE event routing (per workspace, BY agentId) ----
16329
- // ACC10: the device stream's frames — dispatch and cancel, addressed to this
16330
- // device and authenticated as it. ACC8: the only stream this companion opens.
16331
- // It keeps no cursor: it replays what is still offered on every connect
16332
- // instead of resuming from a `Last-Event-ID`.
15983
+ // ---- SSE event routing (BY agentId) ----
15984
+ // Dispatch and cancel, addressed to this device. The stream keeps no cursor:
15985
+ // it replays what is still offered on every connect instead of resuming from
15986
+ // a `Last-Event-ID`.
16333
15987
  async handleDeviceMessage(ev) {
16334
15988
  const wire = this.parseFrame(ev);
16335
15989
  if (!wire) return;
@@ -16365,9 +16019,9 @@ var CompanionSupervisor = class {
16365
16019
  return null;
16366
16020
  }
16367
16021
  }
16368
- // ACC10: the dedupe gate. A reconnect replays every dispatch still offered,
16369
- // including one whose turn is already running here; the first copy runs the
16370
- // turn and a later one waits for it and then does nothing.
16022
+ // A reconnect replays every dispatch still offered, including one whose turn
16023
+ // is already running here; the first copy runs it and a later one waits for
16024
+ // it and then does nothing.
16371
16025
  //
16372
16026
  // A cancel needs no gate: `dispatcher.cancel` on an already-aborted run is a
16373
16027
  // no-op that reports `false`, so a second copy is inert.
@@ -16383,8 +16037,6 @@ var CompanionSupervisor = class {
16383
16037
  if (claim) this.claims.release(wire.id);
16384
16038
  }
16385
16039
  }
16386
- // Everything below here is the routing as it was: it takes a frame and knows
16387
- // nothing about which stream carried it.
16388
16040
  async routeFrame(wr, wire) {
16389
16041
  if (wire.type === "device:cancel_requested") {
16390
16042
  const payload2 = {
@@ -16450,17 +16102,12 @@ var CompanionSupervisor = class {
16450
16102
  await tail;
16451
16103
  if (wr.chains.get(chainKey) === tail) wr.chains.delete(chainKey);
16452
16104
  }
16453
- // CT1146: tell the server this device cannot run a dispatch it was addressed to.
16454
- //
16105
+ // Tell the server this device cannot run a dispatch it was addressed to.
16455
16106
  // Returns whether the report LANDED. The server checks the recorded dispatch
16456
- // destination even if the agent's assignment has moved. Reporting promptly
16457
- // explains the roster miss; the thirty-minute unanswered-offer deadline is
16458
- // only the backstop when no report lands.
16107
+ // destination even if the assignment has moved.
16459
16108
  //
16460
- // Bounded retry, because the common failure is transient (a 503 mid-deploy, a
16461
- // dropped socket) and one more attempt a second later usually lands. Bounded
16462
- // hard — this runs inline on the event loop, and an unbounded wait here would
16463
- // wedge the workspace's whole stream.
16109
+ // Retried, because the common failure is transient (a 503 mid-deploy), and
16110
+ // bounded hard: this runs inline, and an unbounded wait would wedge the stream.
16464
16111
  async reportUnrunnableDispatch(workspaceId, payload) {
16465
16112
  if (!this.deviceApi) return false;
16466
16113
  let lastErr;
@@ -16500,9 +16147,8 @@ var CompanionSupervisor = class {
16500
16147
  );
16501
16148
  return false;
16502
16149
  }
16503
- // CT1680: a runner is gone without having settled its turn. Same bounded
16504
- // retry as the unrunnable report above, on the device credential, which is
16505
- // the one the launcher still holds whatever became of the runner.
16150
+ // A runner is gone without having settled its turn. Same bounded retry, on the
16151
+ // device credential, which the launcher holds whatever became of the runner.
16506
16152
  async reportRunnerGone(report) {
16507
16153
  if (!this.deviceApi) return;
16508
16154
  let lastErr;
@@ -16527,12 +16173,9 @@ var CompanionSupervisor = class {
16527
16173
  "Couldn't tell Cabane a turn's runner stopped; its credential expires on its own."
16528
16174
  );
16529
16175
  }
16530
- // Pull-on-miss for the assignment-change race: a dispatch arrived for an agent
16531
- // we don't yet run. If it's addressed to THIS device, the assignment just
16532
- // changed under us (the poll hasn't caught it) — force a fresh assignments pull
16533
- // and return the now-live runner. Returns undefined when the dispatch isn't for
16534
- // this device, or the agent still isn't ours after pulling (a genuine
16535
- // not-ours dispatch) — the caller then settles and ignores it as before.
16176
+ // Pull-on-miss: a dispatch addressed to THIS device for an agent we don't run
16177
+ // yet means the assignment changed under us. Undefined when it isn't for this
16178
+ // device, or the agent still isn't ours after the pull.
16536
16179
  async recoverRacedAgent(wr, payload) {
16537
16180
  if (!this.deviceId || payload.deviceId !== this.deviceId) return void 0;
16538
16181
  if (this.refreshing && this.inFlightRefresh) await this.inFlightRefresh;
@@ -16573,9 +16216,8 @@ var CompanionSupervisor = class {
16573
16216
  };
16574
16217
  const tail = this.runDispatch(
16575
16218
  wr,
16576
- // No frame delivered this one — it came off `/runs` — so there is no
16577
- // dispatch id to dedupe on. The `recoveredTurns` set above is this
16578
- // path's own guard.
16219
+ // Off `/runs`, not a frame, so no dispatch id to dedupe on;
16220
+ // `recoveredTurns` is this path's guard.
16579
16221
  "",
16580
16222
  payload,
16581
16223
  agent,
@@ -16597,9 +16239,7 @@ var CompanionSupervisor = class {
16597
16239
  this.recovering = false;
16598
16240
  }
16599
16241
  }
16600
- // One turn: the SJ516 F7 / CT26 skip-vs-resume dedupe, the dispatch, then
16601
- // the console summary. Mirrors the old per-workspace `handleEvent`, now
16602
- // scoped to one agent.
16242
+ // One turn: the skip-vs-resume dedupe, the dispatch, then the console summary.
16603
16243
  async runDispatch(wr, dispatchId, payload, agent, recoveredTurnId) {
16604
16244
  const workspaceId = wr.workspaceId;
16605
16245
  if (dispatchId && hasDispatched(workspaceId, dispatchId)) {
@@ -16684,7 +16324,7 @@ var CompanionSupervisor = class {
16684
16324
  );
16685
16325
  }
16686
16326
  }
16687
- // ---- per-agent outbox drain scheduler (CT25) ----
16327
+ // ---- per-agent outbox drain scheduler ----
16688
16328
  makeDrain(api, agentId) {
16689
16329
  let drainTimer;
16690
16330
  let drainDelay = DRAIN_BASE_MS;
@@ -16753,19 +16393,16 @@ var CompanionSupervisor = class {
16753
16393
  saveConfig(next);
16754
16394
  configureLogger(this.log, next);
16755
16395
  }
16756
- // ---- harnesses (CT586) ----
16757
- // Probe all three harnesses, cache the signals + versions, and push the derived
16758
- // per-harness snapshot to the hub (which flips the Companion's Harnesses surface
16759
- // over SSE). Fail-soft by construction — `probeHarnessSignals` never throws — but
16760
- // wrapped anyway so a harness refresh can never take a heartbeat down. Called on
16761
- // the heartbeat cadence (fresh presence for the manifest + UI) and on demand.
16396
+ // ---- harnesses ----
16397
+ // Probe all three harnesses, cache signals and versions, and push the snapshot
16398
+ // to the hub. `probeHarnessSignals` never throws, but this is wrapped anyway so
16399
+ // a refresh can never take a heartbeat down.
16762
16400
  async refreshHarnessStatuses(presence = {}) {
16763
16401
  try {
16764
16402
  const signals = await probeHarnessSignals(this.config, {
16765
16403
  probePresence: this.probePresence,
16766
16404
  presence,
16767
- // CT1379: the bundled executables are resolved on the same cadence as the
16768
- // CLI probes, through the supervisor's seam so a test pins the host fact.
16405
+ // Through the supervisor's seam, so a test pins the host fact.
16769
16406
  resolveBundled: this.resolveBundled
16770
16407
  });
16771
16408
  this.harnessSignals = signals;
@@ -16788,11 +16425,10 @@ var CompanionSupervisor = class {
16788
16425
  async recheckHarnesses() {
16789
16426
  await this.refreshHarnessStatuses();
16790
16427
  }
16791
- // CT1085 §1 step 3: beat NOW and wait for it to land. `start` calls this
16792
- // between bringing the runtime up and asking the person anything, so the
16793
- // browser's connect step is already showing what this machine has ("your
16794
- // terminal is asking") rather than sitting blank while the terminal blocks on
16795
- // an answer. Coalesces with an in-flight beat rather than stacking a second.
16428
+ // Beat NOW and wait for it to land. `start` calls this before asking the
16429
+ // person anything, so the browser's connect step already shows what this
16430
+ // machine has rather than sitting blank while the terminal waits on an answer.
16431
+ // Coalesces with an in-flight beat.
16796
16432
  async heartbeatNow() {
16797
16433
  if (this.inFlightHeartbeat) {
16798
16434
  await this.inFlightHeartbeat;
@@ -16801,35 +16437,25 @@ var CompanionSupervisor = class {
16801
16437
  this.kickHeartbeat();
16802
16438
  if (this.inFlightHeartbeat) await this.inFlightHeartbeat;
16803
16439
  }
16804
- // CT1085: the config as it stands right now — after any `enableHarness` write.
16805
- // The control socket's connect handler needs it to run the shake-out check
16806
- // against the URL/flag that was just persisted, not the one this process booted
16807
- // with.
16440
+ // The config as it stands now, after any `enableHarness` write: the control
16441
+ // socket's shake-out check needs what was just persisted, not the boot config.
16808
16442
  currentConfig() {
16809
16443
  return this.config;
16810
16444
  }
16811
- // Friendly enable for the config-driven harnesses — flip the flag the app owns in
16812
- // `~/.cabane/config.json`, no hand-edited JSON. It never installs a binary and
16813
- // never drives a login (BYO — Decided).
16445
+ // Enable a config-driven harness by flipping the flag the app owns in
16446
+ // `~/.cabane/config.json`. Never installs a binary or drives a login (BYO).
16814
16447
  //
16815
- // CT1379: this records INTENT; it does not by itself expose anything. A harness
16816
- // is advertised only when the user asked for it AND the SDK's own bundled binary
16817
- // resolves, so a successful enable on a companion whose optional dependency
16818
- // never installed leaves the device advertising nothing — correctly, and with
16819
- // the Harnesses surface saying why. Read every "expose" below as "record the
16820
- // half of the answer the user owns".
16821
- // - claude-code (CT1082): set `claudeCode.enabled`, after checking `claude` is
16822
- // on PATH. This is the callable the terminal offer ("We found Claude Code.
16823
- // Connect it? [Y/n]") and the web pairing flow both wire to.
16824
- // - codex: set `codex.enabled` after checking `codex` is on PATH (the CLI +
16825
- // `codex login` remain the user's).
16826
- // - opencode: set `opencode.serverUrl` — but ONLY after health-probing the URL,
16827
- // so we never advertise a serve that isn't there. An unreachable URL is a
16828
- // typed failure the UI shows in place, not a silent bad write.
16829
- // On success, persist, re-probe, and beat immediately, so a runtime that IS
16830
- // launchable starts taking turns at once rather than on the next tick — and one
16831
- // that isn't reports its issue just as promptly. Returns a typed outcome (never
16832
- // throws for a reachable/valid input).
16448
+ // This records INTENT, the user's half: a harness is advertised only when also
16449
+ // its bundled binary resolves, so an enable on a companion whose optional
16450
+ // dependency never installed advertises nothing, and the Harnesses surface
16451
+ // says why.
16452
+ // - claude-code: `claudeCode.enabled`, after a presence check. The terminal
16453
+ // offer and the web pairing flow both call this.
16454
+ // - codex: `codex.enabled`, after a presence check (login stays the user's).
16455
+ // - opencode: `opencode.serverUrl`, ONLY after health-probing the URL; an
16456
+ // unreachable one is a typed failure shown in place.
16457
+ // On success: persist, re-probe and beat at once, so the answer is true in
16458
+ // Cabane promptly either way. Never throws for a valid input.
16833
16459
  async enableHarness(input) {
16834
16460
  let next;
16835
16461
  let checkedPresence = null;
@@ -16880,9 +16506,8 @@ var CompanionSupervisor = class {
16880
16506
  return { ok: true };
16881
16507
  }
16882
16508
  // Re-create every running agent's Dispatcher from the CURRENT config, keeping
16883
- // the runner (its api/outbox/chains/SSE) intact. Used after a runtime-enabling
16884
- // config change so constructor-captured options — per-agent cwd/prepareHook and
16885
- // the adapter registry — pick up their new values without an app restart.
16509
+ // the runner (its api, outbox, chains) intact, so constructor-captured options
16510
+ // pick up a config change without a restart.
16886
16511
  rebuildDispatchers() {
16887
16512
  for (const wr of this.workspaces.values()) {
16888
16513
  for (const runner of wr.agents.values()) {
@@ -17050,8 +16675,7 @@ async function createCompanionRuntime(opts = {}) {
17050
16675
  }));
17051
16676
  await warnAboutHarnessReadiness(cfg, {
17052
16677
  probeClaude: async () => claudeCode,
17053
- // CT1085: the CLI's onboarding script owns the terminal and says this in
17054
- // its own words (the `!` block, or the per-harness offer), so it hands us a
16678
+ // The CLI's onboarding script says this in its own words, so it hands a
17055
16679
  // sink that logs instead. Every other caller keeps the stderr line.
17056
16680
  ...opts.onReadinessWarning ? { warn: opts.onReadinessWarning } : {}
17057
16681
  });
@@ -17088,11 +16712,10 @@ async function createCompanionRuntime(opts = {}) {
17088
16712
  const clearRuntimeOnExit = () => clearRuntimeStateIfOurs(instanceId);
17089
16713
  process.on("exit", clearRuntimeOnExit);
17090
16714
  const hub = new CompanionStateHub({
17091
- // CT29: one device, one base URL — the cabane instance this device is paired
17092
- // with. The dashboard's connection line shows it.
16715
+ // The dashboard's connection line shows it.
17093
16716
  baseUrl: cfg.baseUrl,
17094
16717
  companionVersion: COMPANION_VERSION,
17095
- // SJ516 F4: surfaced on `/api/status` so `stop`/`status` can confirm the
16718
+ // Surfaced on `/api/status` so `stop`/`status` can confirm the
17096
16719
  // process behind the marker pid is this companion (not a recycled pid).
17097
16720
  instanceId
17098
16721
  });
@@ -17145,7 +16768,7 @@ async function createCompanionRuntime(opts = {}) {
17145
16768
  socket: control.path,
17146
16769
  ...dashboard ? { url: dashboard.url, port: dashboard.port } : {},
17147
16770
  startedAt,
17148
- // SJ495: the daemon launcher sets this env on the detached child, so the
16771
+ // The daemon launcher sets this env on the detached child, so the
17149
16772
  // marker records whether this companion is backgrounded (foreground start
17150
16773
  // leaves it unset → false).
17151
16774
  daemon: process.env.CABANE_COMPANION_DAEMON === "1",
@@ -17249,7 +16872,7 @@ async function startDaemon(opts = {}, deps = {}) {
17249
16872
  });
17250
16873
  await ensureRuntime(cfg, {
17251
16874
  probeClaude: async () => claudeOnPath2,
17252
- // CT1085: the onboarding script already said what's connectable, in its own
16875
+ // The onboarding script already said what's connectable, in its own
17253
16876
  // words. Saying it again here, on stderr, in the middle of the closing block
17254
16877
  // would be the same news twice.
17255
16878
  ...quiet ? { warn: () => {