@oxagen/cli 2.1.4-383 → 2.1.4-391

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/oxagen.mjs +1528 -653
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -4767,7 +4767,8 @@ var init_envelope = __esm({
4767
4767
  "daemon_down",
4768
4768
  "http_hook_failed",
4769
4769
  "otel_missing",
4770
- "spool_overflow"
4770
+ "spool_overflow",
4771
+ "wal_ceiling"
4771
4772
  ];
4772
4773
  INCIDENT_KINDS = [
4773
4774
  "unobserved_session",
@@ -7375,8 +7376,8 @@ function candidatePaths(path2, context) {
7375
7376
  const base2 = fold(lib.normalize(root)).replace(/\/+$/, "");
7376
7377
  return resolved.startsWith(`${base2}/`) ? resolved.slice(base2.length + 1) : void 0;
7377
7378
  };
7378
- const relative8 = cwd !== void 0 ? under(cwd) : void 0;
7379
- if (relative8 !== void 0) out.push(relative8);
7379
+ const relative9 = cwd !== void 0 ? under(cwd) : void 0;
7380
+ if (relative9 !== void 0) out.push(relative9);
7380
7381
  const fromHome = home !== void 0 ? under(home) : void 0;
7381
7382
  if (fromHome !== void 0) out.push(`~/${fromHome}`);
7382
7383
  return out;
@@ -15346,7 +15347,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
15346
15347
  import { dirname as dirname7, resolve as resolve2 } from "node:path";
15347
15348
  import { fileURLToPath } from "node:url";
15348
15349
  function readVersion() {
15349
- if (true) return "2.1.4-383";
15350
+ if (true) return "2.1.4-391";
15350
15351
  try {
15351
15352
  const here = dirname7(fileURLToPath(import.meta.url));
15352
15353
  const pkg = JSON.parse(
@@ -16982,6 +16983,7 @@ import {
16982
16983
  appendFileSync as appendFileSync2,
16983
16984
  closeSync as closeSync6,
16984
16985
  fdatasyncSync,
16986
+ fstatSync,
16985
16987
  fsyncSync as fsyncSync3,
16986
16988
  openSync as openSync6,
16987
16989
  existsSync as existsSync12,
@@ -16999,7 +17001,10 @@ function activityAt(event) {
16999
17001
  const at = Date.parse(event.ts);
17000
17002
  return Number.isFinite(at) ? at : Number.NEGATIVE_INFINITY;
17001
17003
  }
17002
- var BODY_READ_SLICE, EVENT_TAIL_WINDOW, WalRecoveryConflict, Wal;
17004
+ function sizeOf(path2) {
17005
+ return statSync7(path2, { throwIfNoEntry: false })?.size ?? 0;
17006
+ }
17007
+ var BODY_READ_SLICE, BODY_DROP_SLICE_BYTES, BODY_DROP_SCANS, NEWLINE, EVENT_TAIL_WINDOW, WalRecoveryConflict, Wal;
17003
17008
  var init_wal = __esm({
17004
17009
  "../../packages/tacho/src/host/wal.ts"() {
17005
17010
  "use strict";
@@ -17007,6 +17012,9 @@ var init_wal = __esm({
17007
17012
  init_fs();
17008
17013
  init_wal_index();
17009
17014
  BODY_READ_SLICE = 32;
17015
+ BODY_DROP_SLICE_BYTES = 1024 * 1024;
17016
+ BODY_DROP_SCANS = 3;
17017
+ NEWLINE = 10;
17010
17018
  EVENT_TAIL_WINDOW = 16 * 1024;
17011
17019
  WalRecoveryConflict = class extends Error {
17012
17020
  constructor(sessionUuid2, seq) {
@@ -17616,10 +17624,12 @@ ${lines.join("\n")}
17616
17624
  * returns, and `bodiesOfSession` reports `body_index_unusable` only when
17617
17625
  * that read also finds its index disagreeing with the file. Reaching this
17618
17626
  * takes a file rewritten in place, at the same size, while each awaited
17619
- * build ran, which the daemon never does, since its rewrites go through a
17620
- * temp file and a rename. Shipping those events without bodies instead
17621
- * marked them shipped and lost the stored content for good, and a one-off
17622
- * stall on a path that needs outside tampering costs less than that.
17627
+ * build ran. The daemon's sweeps rewrite through a temp file and a rename.
17628
+ * `dropBodiesAsync` does write in place, but only the shipper calls it,
17629
+ * after this read has returned, and the shipper runs one drain at a time.
17630
+ * Shipping those events without bodies instead marked them shipped and lost
17631
+ * the stored content for good, and a one-off stall on a path that needs
17632
+ * outside tampering costs less than that.
17623
17633
  */
17624
17634
  async bodiesForAsync(events) {
17625
17635
  const found = /* @__PURE__ */ new Map();
@@ -17734,16 +17744,13 @@ ${lines.join("\n")}
17734
17744
  * here. A body file with a second writer would need a tombstone instead,
17735
17745
  * because a read-filter-rename loses a line appended between the read and
17736
17746
  * the rename.
17747
+ *
17748
+ * This reads the whole body file on the synchronous path. The shipper
17749
+ * calls `dropBodiesAsync`, which uses this only as its last resort.
17737
17750
  */
17738
17751
  dropBodies(events) {
17739
- const wanted = /* @__PURE__ */ new Map();
17740
- for (const event of events) {
17741
- const idems = wanted.get(event.session_uuid) ?? /* @__PURE__ */ new Set();
17742
- idems.add(event.event_id_idem);
17743
- wanted.set(event.session_uuid, idems);
17744
- }
17745
17752
  let dropped = 0;
17746
- for (const [session, idems] of wanted) {
17753
+ for (const [session, idems] of _Wal.idemsBySession(events)) {
17747
17754
  dropped += this.rewriteBodies(
17748
17755
  session,
17749
17756
  (stored) => stored !== void 0 && !idems.has(stored.event_id_idem)
@@ -17751,6 +17758,170 @@ ${lines.join("\n")}
17751
17758
  }
17752
17759
  return dropped;
17753
17760
  }
17761
+ /**
17762
+ * Delete the stored bytes of these events' bodies without holding the
17763
+ * daemon's thread on a large file (ADR-231), and report how many lines
17764
+ * went. The shipper calls this when a proven mandate withdraws a body from
17765
+ * its batch.
17766
+ *
17767
+ * `dropBodies` reads the whole body file and rewrites it on the synchronous
17768
+ * path, and a long session's file holds gigabytes. This finds the withdrawn
17769
+ * lines with a scan that awaits every read. Then it overwrites each line in
17770
+ * place with spaces, a slice at a time, with a turn of the event loop
17771
+ * between slices. Every reader skips a line of spaces, so the body reads as
17772
+ * gone. The file keeps its size until `compact` removes it, and no copy of
17773
+ * it is written to a disk that may be nearly full.
17774
+ *
17775
+ * The scan's offsets can be stale by the time a slice runs. Hooks append
17776
+ * between the scan's reads, a failed append is cut back, and a narrowing
17777
+ * sweep can rewrite the file. So a slice reads each line again, in the same
17778
+ * synchronous stretch as the write, and overwrites it only when it is still
17779
+ * one whole stored body for one of these events. A line that is not is left
17780
+ * alone, and the session is scanned again. After `BODY_DROP_SCANS` scans
17781
+ * that each found a line out of place, the rest goes through `dropBodies`
17782
+ * on the synchronous path. Reaching that takes a rewrite of the file during
17783
+ * every scan.
17784
+ *
17785
+ * The scan finds every copy of a body, as `dropBodies` does. It does not
17786
+ * remove a torn line, which `dropBodies` does: a line still being appended
17787
+ * can look torn to an awaited read. `purgeBodiesOutsideMandate` removes torn
17788
+ * lines on a narrowing.
17789
+ */
17790
+ async dropBodiesAsync(events) {
17791
+ let dropped = 0;
17792
+ for (const [session, idems] of _Wal.idemsBySession(events)) {
17793
+ let settled2 = false;
17794
+ for (let scan = 0; scan < BODY_DROP_SCANS && !settled2; scan += 1) {
17795
+ const pass2 = await this.blankBodies(session, idems);
17796
+ dropped += pass2.blanked;
17797
+ settled2 = !pass2.moved;
17798
+ }
17799
+ if (!settled2)
17800
+ dropped += this.dropBodies(
17801
+ events.filter((event) => event.session_uuid === session)
17802
+ );
17803
+ }
17804
+ return dropped;
17805
+ }
17806
+ /**
17807
+ * One scan of `dropBodiesAsync` over one session: find every line that
17808
+ * holds one of these bodies, then overwrite each line that still does.
17809
+ * `moved` says a line the scan found held something else by the time its
17810
+ * slice ran.
17811
+ */
17812
+ async blankBodies(session, idems) {
17813
+ const path2 = this.bodyFileFor(session);
17814
+ if (!existsSync12(path2)) return { blanked: 0, moved: false };
17815
+ const found = [];
17816
+ try {
17817
+ for await (const line of readLinesFromAsync(path2)) {
17818
+ if (!line.terminated) continue;
17819
+ const stored = parseStoredBody(line.text);
17820
+ if (stored !== void 0 && idems.has(stored.event_id_idem))
17821
+ found.push({
17822
+ offset: line.offset,
17823
+ length: line.end - line.offset - 1
17824
+ });
17825
+ }
17826
+ } catch (error) {
17827
+ if (error.code === "ENOENT")
17828
+ return { blanked: 0, moved: false };
17829
+ throw error;
17830
+ }
17831
+ let blanked = 0;
17832
+ let moved = false;
17833
+ for (let next = 0; next < found.length; ) {
17834
+ if (next > 0) await new Promise((resolve19) => setImmediate(resolve19));
17835
+ const slice = this.blankSlice(path2, found, next, idems);
17836
+ if (slice === void 0) break;
17837
+ blanked += slice.blanked;
17838
+ moved ||= slice.moved;
17839
+ next = slice.next;
17840
+ }
17841
+ if (blanked > 0) this.bodyIndexes.invalidate(session);
17842
+ return { blanked, moved };
17843
+ }
17844
+ /**
17845
+ * One synchronous stretch of `blankBodies`: from `from`, overwrite each
17846
+ * found line that still holds one of these bodies, until the slice has
17847
+ * taken `BODY_DROP_SLICE_BYTES`. Nothing else can touch the file while it
17848
+ * runs. Answers undefined when the file is gone.
17849
+ */
17850
+ blankSlice(path2, found, from, idems) {
17851
+ let fd2;
17852
+ try {
17853
+ fd2 = openSync6(path2, "r+");
17854
+ } catch (error) {
17855
+ if (error.code === "ENOENT") return void 0;
17856
+ throw error;
17857
+ }
17858
+ let blanked = 0;
17859
+ let moved = false;
17860
+ let next = from;
17861
+ try {
17862
+ const size = fstatSync(fd2).size;
17863
+ let taken = 0;
17864
+ while (next < found.length && taken < BODY_DROP_SLICE_BYTES) {
17865
+ const at = found[next];
17866
+ next += 1;
17867
+ taken += at.length;
17868
+ if (!_Wal.holdsBodyOf(fd2, size, at, idems)) {
17869
+ moved = true;
17870
+ continue;
17871
+ }
17872
+ _Wal.overwriteWithSpaces(fd2, at);
17873
+ blanked += 1;
17874
+ }
17875
+ if (blanked > 0) fdatasyncSync(fd2);
17876
+ } finally {
17877
+ closeSync6(fd2);
17878
+ }
17879
+ return { blanked, moved, next };
17880
+ }
17881
+ /**
17882
+ * Whether the bytes at `at` are still one whole stored body for one of
17883
+ * `idems`: the start of the file or a newline before them, a newline right
17884
+ * after them, and a line between that parses and names one of the events.
17885
+ * Costs a read of that one line.
17886
+ */
17887
+ static holdsBodyOf(fd2, size, at, idems) {
17888
+ const start = at.offset === 0 ? 0 : at.offset - 1;
17889
+ const end = at.offset + at.length + 1;
17890
+ if (at.length <= 0 || end > size) return false;
17891
+ const buffer = Buffer.allocUnsafe(end - start);
17892
+ let filled = 0;
17893
+ while (filled < buffer.length) {
17894
+ const read2 = readSync3(
17895
+ fd2,
17896
+ buffer,
17897
+ filled,
17898
+ buffer.length - filled,
17899
+ start + filled
17900
+ );
17901
+ if (read2 <= 0) return false;
17902
+ filled += read2;
17903
+ }
17904
+ if (at.offset > 0 && buffer[0] !== NEWLINE) return false;
17905
+ if (buffer[buffer.length - 1] !== NEWLINE) return false;
17906
+ const stored = parseStoredBody(
17907
+ buffer.subarray(at.offset - start, buffer.length - 1).toString("utf8")
17908
+ );
17909
+ return stored !== void 0 && idems.has(stored.event_id_idem);
17910
+ }
17911
+ /** Overwrite the bytes at `at` with spaces, leaving its newline. */
17912
+ static overwriteWithSpaces(fd2, at) {
17913
+ const spaces = Buffer.alloc(Math.min(at.length, 64 * 1024), " ");
17914
+ let written = 0;
17915
+ while (written < at.length) {
17916
+ written += writeSync4(
17917
+ fd2,
17918
+ spaces,
17919
+ 0,
17920
+ Math.min(spaces.length, at.length - written),
17921
+ at.offset + written
17922
+ );
17923
+ }
17924
+ }
17754
17925
  /** Sessions that have a body file, whether or not they have an event file. */
17755
17926
  sessionsWithBodies() {
17756
17927
  const suffix = ".bodies.jsonl";
@@ -18134,20 +18305,75 @@ ${lines.join("\n")}
18134
18305
  ...oldest !== void 0 ? { oldestUnshippedAt: oldest } : {}
18135
18306
  };
18136
18307
  }
18308
+ /**
18309
+ * Every session with an event past its shipped cursor, and the bytes its
18310
+ * files hold.
18311
+ *
18312
+ * The WAL ceiling asks for this once a minute (ADR-261). It costs two `stat`
18313
+ * calls per session, plus the tail read `lastSeqOf` already keeps, so it
18314
+ * reads no event or body. The byte counts are whole files, shipped lines
18315
+ * included, because nothing this cheap says where a session's unshipped
18316
+ * bodies start.
18317
+ */
18318
+ holdings() {
18319
+ const out = [];
18320
+ for (const session of this.sessions()) {
18321
+ const last = this.lastSeqOf(session);
18322
+ const through = this.shippedThrough(session);
18323
+ if (last <= through) continue;
18324
+ out.push({
18325
+ sessionUuid: session,
18326
+ shippedThrough: through,
18327
+ lastSeq: last,
18328
+ eventBytes: sizeOf(this.fileFor(session)),
18329
+ bodyBytes: sizeOf(this.bodyFileFor(session)),
18330
+ lastActivityAt: this.lastActivity.get(session) ?? Number.NEGATIVE_INFINITY
18331
+ });
18332
+ }
18333
+ return out;
18334
+ }
18335
+ /**
18336
+ * Remove one session's body file and its index, and answer how many bytes
18337
+ * went. Only the WAL ceiling calls this (ADR-261).
18338
+ *
18339
+ * The file is removed, not rewritten. `rewriteBodies` reads every line on
18340
+ * the daemon's only thread, and a session over the ceiling can hold
18341
+ * gigabytes, which ADR-231 rules out. The event file stays, so the chain
18342
+ * still verifies. Each event whose body went ships without it, and the
18343
+ * control plane records a `body_missing` gap for it.
18344
+ *
18345
+ * What each reader sees afterwards:
18346
+ *
18347
+ * - `bodiesFor` finds no file, so a batch read after this ships its events
18348
+ * with no bodies. A batch already read keeps the bodies it holds.
18349
+ * - An index build running for the session sees the generation move, and
18350
+ * keeps nothing.
18351
+ * - `appendRecovered` finds no stored body, so a journaled terminal batch
18352
+ * writes its own few bodies again.
18353
+ * - The session's next `append` starts a new body file.
18354
+ *
18355
+ * A removal that fails is reported, answers 0, and the next check tries
18356
+ * again. On Windows that happens while another reader holds the file open.
18357
+ */
18358
+ dropSessionBodies(sessionUuid2) {
18359
+ const bytes = sizeOf(this.bodyFileFor(sessionUuid2));
18360
+ if (bytes === 0) return 0;
18361
+ return this.removeBodies(sessionUuid2) ? bytes : 0;
18362
+ }
18137
18363
  /**
18138
18364
  * Erase every stored body whose class `retention` does not cover, in every
18139
18365
  * session this host holds, and answer how many were erased.
18140
18366
  *
18141
- * Why this exists beside `dropBodies`. `dropBodies` reaches the bodies of
18142
- * the events in a drain's batch, which is every body the shipper still has
18143
- * an unshipped event for. Two kinds of body sit outside that reach: one
18144
- * whose event already shipped, since `markShipped` advanced the cursor past
18145
- * it, and one in a session the drain is not looking at. `compact` frees
18146
- * those only once their session is sealed, fully shipped, and older than
18147
- * the retention window, so a session that never seals kept them for as long
18148
- * as the host ran, or for ever on a host that never came back. This sweep
18149
- * walks every session and every stored line, so a narrowing reaches all of
18150
- * them.
18367
+ * Why this exists beside `dropBodiesAsync`. The shipper's drop reaches the
18368
+ * bodies of the events in a drain's batch, which is every body the shipper
18369
+ * still has an unshipped event for. Two kinds of body sit outside that
18370
+ * reach: one whose event already shipped, since `markShipped` advanced the
18371
+ * cursor past it, and one in a session the drain is not looking at.
18372
+ * `compact` frees those only once their session is sealed, fully shipped,
18373
+ * and older than the retention window, so a session that never seals kept
18374
+ * them for as long as the host ran, or for ever on a host that never came
18375
+ * back. This sweep walks every session and every stored line, so a
18376
+ * narrowing reaches all of them.
18151
18377
  *
18152
18378
  * The write is the one in `rewriteBodies`, which `dropBodies` uses as well:
18153
18379
  * atomic, and safe because these files have one writer. The module header
@@ -18682,6 +18908,252 @@ var init_policy_mode = __esm({
18682
18908
  }
18683
18909
  });
18684
18910
 
18911
+ // ../../packages/tacho/src/host/wal-ceiling.ts
18912
+ import { statfsSync } from "node:fs";
18913
+ import { join as join16 } from "node:path";
18914
+ function freeBytesOn(dir) {
18915
+ try {
18916
+ const stats2 = statfsSync(dir);
18917
+ return stats2.bavail * stats2.bsize;
18918
+ } catch {
18919
+ return void 0;
18920
+ }
18921
+ }
18922
+ function isCount(value) {
18923
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
18924
+ }
18925
+ function isClock(value) {
18926
+ if (typeof value !== "object" || value === null) return false;
18927
+ const clock = value;
18928
+ const since = clock["since"];
18929
+ return Number.isSafeInteger(clock["shipped_through"]) && typeof since === "string" && Number.isFinite(Date.parse(since));
18930
+ }
18931
+ function clocksFrom(value) {
18932
+ const clocks = {};
18933
+ if (typeof value !== "object" || value === null || Array.isArray(value))
18934
+ return clocks;
18935
+ for (const [session, clock] of Object.entries(value))
18936
+ if (isClock(clock))
18937
+ clocks[session] = {
18938
+ shipped_through: clock.shipped_through,
18939
+ since: clock.since
18940
+ };
18941
+ return clocks;
18942
+ }
18943
+ function sameClocks(a, b5) {
18944
+ const keys = Object.keys(a);
18945
+ if (keys.length !== Object.keys(b5).length) return false;
18946
+ return keys.every((session) => {
18947
+ const other = b5[session];
18948
+ const clock = a[session];
18949
+ return other !== void 0 && clock !== void 0 && other.shipped_through === clock.shipped_through && other.since === clock.since;
18950
+ });
18951
+ }
18952
+ function isDrop(value) {
18953
+ if (typeof value !== "object" || value === null) return false;
18954
+ const drop = value;
18955
+ return typeof drop["session_uuid"] === "string" && isCount(drop["bytes"]) && typeof drop["shipped_through"] === "number" && typeof drop["last_seq"] === "number" && typeof drop["stalled_since"] === "string" && typeof drop["dropped_at"] === "string" && isCount(drop["stalled_bytes"]) && isCount(drop["ceiling_bytes"]);
18956
+ }
18957
+ function readWalCeilingState(walDir) {
18958
+ let raw;
18959
+ try {
18960
+ raw = readJsonFileIfExists(join16(walDir, STATE_FILE));
18961
+ } catch {
18962
+ return void 0;
18963
+ }
18964
+ if (typeof raw !== "object" || raw === null) return void 0;
18965
+ const state = raw;
18966
+ const checkedAt = state["checked_at"];
18967
+ const ceilingBytes = state["ceiling_bytes"];
18968
+ const stallGraceMs = state["stall_grace_ms"];
18969
+ const stalledBytes = state["stalled_bytes"];
18970
+ const stalledSessions = state["stalled_sessions"];
18971
+ const drops = state["drops"];
18972
+ if (state["schema"] !== STATE_SCHEMA || typeof checkedAt !== "string" || !isCount(ceilingBytes) || !isCount(stallGraceMs) || !isCount(stalledBytes) || !isCount(stalledSessions) || !Array.isArray(drops))
18973
+ return void 0;
18974
+ return {
18975
+ schema: STATE_SCHEMA,
18976
+ checked_at: checkedAt,
18977
+ ceiling_bytes: ceilingBytes,
18978
+ stall_grace_ms: stallGraceMs,
18979
+ stalled_bytes: stalledBytes,
18980
+ stalled_sessions: stalledSessions,
18981
+ drops: drops.filter(isDrop),
18982
+ stall_clocks: clocksFrom(state["stall_clocks"])
18983
+ };
18984
+ }
18985
+ function ceilingFor(fixed, free, stalled) {
18986
+ if (free === void 0 || !Number.isFinite(free)) return fixed;
18987
+ return Math.min(fixed, Math.floor((free + stalled) / 2));
18988
+ }
18989
+ var DEFAULT_WAL_CEILING, STATE_FILE, STATE_SCHEMA, DROPS_KEPT, WalCeiling;
18990
+ var init_wal_ceiling = __esm({
18991
+ "../../packages/tacho/src/host/wal-ceiling.ts"() {
18992
+ "use strict";
18993
+ init_fs();
18994
+ DEFAULT_WAL_CEILING = {
18995
+ ceilingBytes: 8 * 1024 ** 3,
18996
+ stallGraceMs: 60 * 6e4,
18997
+ checkEveryMs: 6e4,
18998
+ freeBytes: freeBytesOn
18999
+ };
19000
+ STATE_FILE = "ceiling.json";
19001
+ STATE_SCHEMA = "tacho.wal-ceiling.v1";
19002
+ DROPS_KEPT = 20;
19003
+ WalCeiling = class {
19004
+ constructor(wal, walDir, policy, keepDropsMs, log) {
19005
+ this.wal = wal;
19006
+ this.walDir = walDir;
19007
+ this.policy = policy;
19008
+ this.keepDropsMs = keepDropsMs;
19009
+ this.log = log;
19010
+ this.state = readWalCeilingState(walDir);
19011
+ for (const [session, clock] of Object.entries(
19012
+ this.state?.stall_clocks ?? {}
19013
+ ))
19014
+ this.progress.set(session, {
19015
+ through: clock.shipped_through,
19016
+ since: Date.parse(clock.since)
19017
+ });
19018
+ }
19019
+ wal;
19020
+ walDir;
19021
+ policy;
19022
+ keepDropsMs;
19023
+ log;
19024
+ /**
19025
+ * Each waiting session's shipped cursor as the last check saw it, and when
19026
+ * it was first seen there.
19027
+ *
19028
+ * A clock whose cursor stood still between two checks is also written to
19029
+ * `ceiling.json`, and the constructor reads it back, so a restarted daemon
19030
+ * resumes the clock. A daemon that restarted more often than `stallGraceMs`
19031
+ * used to start every clock again each time, and never dropped anything.
19032
+ * A clock first seen by the last check before a restart is not written yet,
19033
+ * so a restart costs it at most one `checkEveryMs`.
19034
+ */
19035
+ progress = /* @__PURE__ */ new Map();
19036
+ state;
19037
+ /** Set when the last write of `ceiling.json` failed, so the next check writes. */
19038
+ unsaved = false;
19039
+ lastCheckAt = Number.NEGATIVE_INFINITY;
19040
+ /** Whether a check is due at `now`. */
19041
+ due(now) {
19042
+ return now - this.lastCheckAt >= this.policy.checkEveryMs;
19043
+ }
19044
+ /**
19045
+ * One check, and the drops it made.
19046
+ *
19047
+ * `onDrop` runs right after each body file goes, in the same synchronous
19048
+ * stretch, so the daemon seals the frame that records the drop before
19049
+ * anything else runs. A frame that fails does not bring the bodies back:
19050
+ * the drop stands, and the daemon logs the failure.
19051
+ */
19052
+ check(now, onDrop) {
19053
+ this.lastCheckAt = now;
19054
+ const holdings = this.wal.holdings();
19055
+ const waiting = /* @__PURE__ */ new Set();
19056
+ const clocks = {};
19057
+ for (const holding of holdings) {
19058
+ waiting.add(holding.sessionUuid);
19059
+ const known = this.progress.get(holding.sessionUuid);
19060
+ if (known === void 0 || known.through !== holding.shippedThrough) {
19061
+ this.progress.set(holding.sessionUuid, {
19062
+ through: holding.shippedThrough,
19063
+ since: now
19064
+ });
19065
+ continue;
19066
+ }
19067
+ clocks[holding.sessionUuid] = {
19068
+ shipped_through: known.through,
19069
+ since: new Date(known.since).toISOString()
19070
+ };
19071
+ }
19072
+ for (const session of [...this.progress.keys()])
19073
+ if (!waiting.has(session)) this.progress.delete(session);
19074
+ const stalled = holdings.map((holding) => ({
19075
+ holding,
19076
+ since: this.progress.get(holding.sessionUuid)?.since ?? now
19077
+ })).filter((entry2) => now - entry2.since >= this.policy.stallGraceMs);
19078
+ let held = 0;
19079
+ for (const { holding } of stalled)
19080
+ held += holding.eventBytes + holding.bodyBytes;
19081
+ const ceiling = ceilingFor(
19082
+ this.policy.ceilingBytes,
19083
+ this.policy.freeBytes(this.walDir),
19084
+ held
19085
+ );
19086
+ const drops = [];
19087
+ if (held > ceiling) {
19088
+ stalled.sort(
19089
+ (a, b5) => a.since - b5.since || a.holding.lastActivityAt - b5.holding.lastActivityAt || (a.holding.sessionUuid < b5.holding.sessionUuid ? -1 : 1)
19090
+ );
19091
+ for (const { holding, since } of stalled) {
19092
+ if (held <= ceiling) break;
19093
+ if (holding.bodyBytes === 0) continue;
19094
+ const bytes = this.wal.dropSessionBodies(holding.sessionUuid);
19095
+ if (bytes === 0) continue;
19096
+ const drop = {
19097
+ session_uuid: holding.sessionUuid,
19098
+ bytes,
19099
+ shipped_through: holding.shippedThrough,
19100
+ last_seq: holding.lastSeq,
19101
+ stalled_since: new Date(since).toISOString(),
19102
+ dropped_at: new Date(now).toISOString(),
19103
+ stalled_bytes: held,
19104
+ ceiling_bytes: ceiling
19105
+ };
19106
+ held -= bytes;
19107
+ drops.push(drop);
19108
+ try {
19109
+ onDrop(drop);
19110
+ } catch {
19111
+ }
19112
+ }
19113
+ }
19114
+ this.remember(now, ceiling, held, stalled.length, drops, clocks);
19115
+ return drops;
19116
+ }
19117
+ /**
19118
+ * Write what this check saw to `ceiling.json`, when it says something the
19119
+ * file does not. A host where no cursor stood still between two checks,
19120
+ * and nothing was dropped, writes no file at all.
19121
+ */
19122
+ remember(now, ceiling, held, stalledSessions, drops, clocks) {
19123
+ const prior = this.state;
19124
+ const kept = [...prior?.drops ?? [], ...drops].filter((drop) => now - Date.parse(drop.dropped_at) < this.keepDropsMs).slice(-DROPS_KEPT);
19125
+ if (prior === void 0 && stalledSessions === 0 && kept.length === 0 && Object.keys(clocks).length === 0)
19126
+ return;
19127
+ const next = {
19128
+ schema: STATE_SCHEMA,
19129
+ checked_at: new Date(now).toISOString(),
19130
+ ceiling_bytes: ceiling,
19131
+ stall_grace_ms: this.policy.stallGraceMs,
19132
+ stalled_bytes: held,
19133
+ stalled_sessions: stalledSessions,
19134
+ drops: kept,
19135
+ stall_clocks: clocks
19136
+ };
19137
+ const changed = this.unsaved || prior === void 0 || prior.stalled_sessions !== next.stalled_sessions || prior.drops.length !== next.drops.length || prior.drops.at(-1)?.dropped_at !== next.drops.at(-1)?.dropped_at || !sameClocks(prior.stall_clocks, next.stall_clocks) || next.stalled_sessions > 0 && (prior.stalled_bytes !== next.stalled_bytes || prior.ceiling_bytes !== next.ceiling_bytes);
19138
+ this.state = next;
19139
+ if (!changed) return;
19140
+ try {
19141
+ writeSensitiveFileAtomic(
19142
+ join16(this.walDir, STATE_FILE),
19143
+ JSON.stringify(next)
19144
+ );
19145
+ this.unsaved = false;
19146
+ } catch (error) {
19147
+ this.unsaved = true;
19148
+ this.log(
19149
+ `WAL ceiling: could not write ${STATE_FILE}: ${error instanceof Error ? error.message : String(error)}`
19150
+ );
19151
+ }
19152
+ }
19153
+ };
19154
+ }
19155
+ });
19156
+
18685
19157
  // ../../packages/tacho/src/cli/status.ts
18686
19158
  import { existsSync as existsSync14 } from "node:fs";
18687
19159
  import { dirname as dirname10 } from "node:path";
@@ -18828,6 +19300,7 @@ async function agentStatus(deps) {
18828
19300
  host.host_enrollment_id
18829
19301
  ) : void 0;
18830
19302
  const walStats = new Wal(deps.paths.wal).stats();
19303
+ const ceilingState = readWalCeilingState(deps.paths.wal);
18831
19304
  const gateway = gatewayOf(daemon);
18832
19305
  const routedHarnesses = host.harnesses.filter(isBrokerableHarness);
18833
19306
  const baseUrlHarnesses = host.harnesses.filter(isModelRoutedHarness);
@@ -18924,12 +19397,49 @@ async function agentStatus(deps) {
18924
19397
  wal: {
18925
19398
  sessions: walStats.sessions,
18926
19399
  unshipped: walStats.unshipped,
18927
- ...walStats.oldestUnshippedAt !== void 0 ? { oldest_unshipped_at: walStats.oldestUnshippedAt } : {}
19400
+ ...walStats.oldestUnshippedAt !== void 0 ? { oldest_unshipped_at: walStats.oldestUnshippedAt } : {},
19401
+ ...ceilingState !== void 0 ? {
19402
+ ceiling: {
19403
+ checked_at: ceilingState.checked_at,
19404
+ ceiling_bytes: ceilingState.ceiling_bytes,
19405
+ stall_grace_ms: ceilingState.stall_grace_ms,
19406
+ stalled_bytes: ceilingState.stalled_bytes,
19407
+ stalled_sessions: ceilingState.stalled_sessions,
19408
+ drops: ceilingState.drops
19409
+ }
19410
+ } : {}
18928
19411
  },
18929
19412
  ...shipping !== void 0 ? { shipping } : {}
18930
19413
  };
18931
19414
  return report;
18932
19415
  }
19416
+ function formatBytes(bytes) {
19417
+ if (bytes < 1024) return `${bytes} bytes`;
19418
+ if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KiB`;
19419
+ if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MiB`;
19420
+ return `${(bytes / 1024 ** 3).toFixed(1)} GiB`;
19421
+ }
19422
+ function ceilingLines(ceiling) {
19423
+ const lines = [];
19424
+ if (ceiling.stalled_sessions > 0) {
19425
+ const minutes = Math.round(ceiling.stall_grace_ms / 6e4);
19426
+ const one = ceiling.stalled_sessions === 1;
19427
+ const who = `${ceiling.stalled_sessions} session${one ? " has" : "s have"} shipped nothing for at least ${minutes} minute${minutes === 1 ? "" : "s"} and ${one ? "holds" : "hold"} ${formatBytes(ceiling.stalled_bytes)}`;
19428
+ lines.push(
19429
+ ceiling.stalled_bytes > ceiling.ceiling_bytes ? `Ceiling OVER: ${who}, more than the ${formatBytes(ceiling.ceiling_bytes)} ceiling` : `Ceiling ${who} of the ${formatBytes(ceiling.ceiling_bytes)} ceiling`
19430
+ );
19431
+ }
19432
+ const newestFirst = [...ceiling.drops].reverse();
19433
+ for (const drop of newestFirst.slice(0, CEILING_DROPS_SHOWN))
19434
+ lines.push(
19435
+ `Dropped session ${drop.session_uuid} went over the WAL ceiling at ${drop.dropped_at}: ${formatBytes(drop.bytes)} of stored content removed, events ${drop.shipped_through + 1} to ${drop.last_seq} ship without it`
19436
+ );
19437
+ if (newestFirst.length > CEILING_DROPS_SHOWN)
19438
+ lines.push(
19439
+ ` ${newestFirst.length - CEILING_DROPS_SHOWN} earlier drop${newestFirst.length - CEILING_DROPS_SHOWN === 1 ? "" : "s"} in \`oxagen agent status --json\``
19440
+ );
19441
+ return lines;
19442
+ }
18933
19443
  function printStatus(report, deps) {
18934
19444
  const h = report.host;
18935
19445
  if (h === void 0) {
@@ -19061,12 +19571,14 @@ function printStatus(report, deps) {
19061
19571
  deps.out(
19062
19572
  `WAL ${wal.sessions} session files, ${wal.unshipped} events unshipped${wal.oldest_unshipped_at !== void 0 ? ` (oldest ${wal.oldest_unshipped_at})` : ""}`
19063
19573
  );
19574
+ if (wal.ceiling !== void 0)
19575
+ for (const line of ceilingLines(wal.ceiling)) deps.out(line);
19064
19576
  if (shipping !== void 0)
19065
19577
  deps.out(
19066
19578
  `Shipping ${shipping.healthy ? "ok" : "FAILING"}: ${shipping.detail}`
19067
19579
  );
19068
19580
  }
19069
- var SHIPPING_STALL_MS, TIER_RANK;
19581
+ var SHIPPING_STALL_MS, TIER_RANK, CEILING_DROPS_SHOWN;
19070
19582
  var init_status = __esm({
19071
19583
  "../../packages/tacho/src/cli/status.ts"() {
19072
19584
  "use strict";
@@ -19078,6 +19590,7 @@ var init_status = __esm({
19078
19590
  init_settings_writer();
19079
19591
  init_stella_writer();
19080
19592
  init_wal();
19593
+ init_wal_ceiling();
19081
19594
  init_agents();
19082
19595
  init_wire();
19083
19596
  init_deps();
@@ -19086,6 +19599,7 @@ var init_status = __esm({
19086
19599
  init_policy_mode();
19087
19600
  SHIPPING_STALL_MS = 10 * 6e4;
19088
19601
  TIER_RANK = { observe: 0, harness: 1, gateway: 2 };
19602
+ CEILING_DROPS_SHOWN = 5;
19089
19603
  }
19090
19604
  });
19091
19605
 
@@ -19864,8 +20378,8 @@ function harnessFileProblems(harnesses, deps) {
19864
20378
  if (harnesses.includes("codex"))
19865
20379
  check2(deps.paths.codexHooks, deps.readCodexHooks, hooksShapeProblem);
19866
20380
  if (harnesses.includes("cursor")) {
19867
- const relative8 = absoluteHookCommandProblem(deps.runtime.hookCommand);
19868
- if (relative8 !== void 0) problems.push(relative8);
20381
+ const relative9 = absoluteHookCommandProblem(deps.runtime.hookCommand);
20382
+ if (relative9 !== void 0) problems.push(relative9);
19869
20383
  for (const path2 of deps.paths.cursorHooks)
19870
20384
  check2(path2, () => deps.readCursorHooks(path2), cursorHooksShapeProblem);
19871
20385
  }
@@ -22342,6 +22856,13 @@ function steeringContext(body2) {
22342
22856
  tokens: clampU32(assembly.tokens + delivered)
22343
22857
  };
22344
22858
  }
22859
+ function steeringFactsOf(steering) {
22860
+ if (steering === void 0) return {};
22861
+ return {
22862
+ steering_tokens: steering.tokens,
22863
+ steering_tokens_basis: "estimated"
22864
+ };
22865
+ }
22345
22866
  var REQUEST_FULL_DIGEST_ATTR, SYSTEM_CONTEXT_PARTS_OMITTED_ATTR, MEMORY_REQUESTS, LISTED_PER_TURN, U32_MAX, NAME_MAX, tokenSourceFacts, SystemContextMemory, SHARED_SYSTEM_CONTEXT_MEMORY, CONVERSATION_FIELDS2, STEERING_ASSEMBLY_PART, SystemContextTracker;
22346
22867
  var init_system_context = __esm({
22347
22868
  "../../packages/tacho/src/claude-code/system-context.ts"() {
@@ -22435,20 +22956,16 @@ var init_system_context = __esm({
22435
22956
  * the frame's attrs, and `turn` a key that changes when the turn does.
22436
22957
  */
22437
22958
  measure(content, attrs2, turn) {
22438
- const facts = {};
22439
- const steering = this.steering;
22440
- if (steering !== void 0) {
22441
- facts.steering_tokens = steering.tokens;
22442
- facts.steering_tokens_basis = "estimated";
22443
- }
22444
22959
  const recorded = requestOf(content);
22445
22960
  const resolved = recorded === void 0 ? void 0 : resolveRequest(recorded.request, this.memory);
22446
22961
  if (recorded === void 0 || resolved === void 0)
22447
- return this.checked(facts, {}, () => {
22962
+ return this.checked(steeringFactsOf(this.steering), {}, () => {
22448
22963
  });
22449
22964
  const fullDigest = attrs2?.[REQUEST_FULL_DIGEST_ATTR];
22450
22965
  const key = isSha256Digest(fullDigest) ? fullDigest : recorded.request[PRIOR_MEMBER] === void 0 ? digestBytes(recorded.text) : void 0;
22451
22966
  if (key !== void 0) this.memory.set(key, resolved);
22967
+ const steering = resolved.tools.length > 0 ? this.steering : void 0;
22968
+ const facts = steeringFactsOf(steering);
22452
22969
  const parts = [
22453
22970
  ...resolved.system,
22454
22971
  ...resolved.instructions,
@@ -22478,6 +22995,19 @@ var init_system_context = __esm({
22478
22995
  if (this.listed.length > LISTED_PER_TURN) this.listed.shift();
22479
22996
  });
22480
22997
  }
22998
+ /**
22999
+ * The token sources of a model call whose request the recorder never saw:
23000
+ * the counted OTel or transcript row of a call the proxy did not carry
23001
+ * (#4493). Only the steering count is known without the request. The tool
23002
+ * definition count, the context frame count, and the system context stay
23003
+ * absent, never zero. Nothing changes on the tracker, so there is nothing
23004
+ * to commit. The caller asks only for a call that carries the session's
23005
+ * conversation, since a side call carries no steering.
23006
+ */
23007
+ measureUnseen() {
23008
+ return this.checked(steeringFactsOf(this.steering), {}, () => {
23009
+ }).facts;
23010
+ }
22481
23011
  /**
22482
23012
  * Hand back facts the envelope accepts, or none. A body the envelope
22483
23013
  * refuses loses its frame, so a measurement that fails the schema is
@@ -23841,6 +24371,10 @@ var init_transcript = __esm({
23841
24371
  });
23842
24372
 
23843
24373
  // ../../packages/tacho/src/claude-code/recorder.ts
24374
+ function onSessionConversation(querySource) {
24375
+ if (querySource === void 0) return false;
24376
+ return MAIN_CONVERSATION_QUERY_SOURCES.has(querySource) || querySource.startsWith("repl_main_thread:");
24377
+ }
23844
24378
  function compact(value) {
23845
24379
  const out = {};
23846
24380
  for (const [key, member2] of Object.entries(value)) {
@@ -23917,7 +24451,7 @@ function inChainOrder(events) {
23917
24451
  }
23918
24452
  return out;
23919
24453
  }
23920
- var NO_SIGHTING, PROMPT_DUPLICATE_OF_ATTR, AFTER_STOP_ATTR, SUBAGENT_TYPE_AMBIGUOUS_ATTR, SessionRecorder;
24454
+ var NO_SIGHTING, MAIN_CONVERSATION_QUERY_SOURCES, PROMPT_DUPLICATE_OF_ATTR, AFTER_STOP_ATTR, SUBAGENT_TYPE_AMBIGUOUS_ATTR, SessionRecorder;
23921
24455
  var init_recorder = __esm({
23922
24456
  "../../packages/tacho/src/claude-code/recorder.ts"() {
23923
24457
  "use strict";
@@ -23938,6 +24472,10 @@ var init_recorder = __esm({
23938
24472
  init_transcript();
23939
24473
  NO_SIGHTING = { attrs: {}, commit: () => {
23940
24474
  } };
24475
+ MAIN_CONVERSATION_QUERY_SOURCES = /* @__PURE__ */ new Set([
24476
+ "repl_main_thread",
24477
+ "sdk"
24478
+ ]);
23941
24479
  PROMPT_DUPLICATE_OF_ATTR = "oxagen.prompt_duplicate_of";
23942
24480
  AFTER_STOP_ATTR = "oxagen.after_stop";
23943
24481
  SUBAGENT_TYPE_AMBIGUOUS_ATTR = "oxagen.subagent_type_ambiguous";
@@ -24740,6 +25278,24 @@ var init_recorder = __esm({
24740
25278
  return { attrs: { [LLM_CALL_DUPLICATE_OF_ATTR]: verdict.of }, commit };
24741
25279
  return { attrs: {}, commit };
24742
25280
  }
25281
+ /**
25282
+ * The body of an OTel or transcript model call, with the token sources the
25283
+ * recorder knows without the call's request (#4493). Only the counted row,
25284
+ * the first sighting of its call, takes them. A stamped duplicate is never
25285
+ * summed, and the rollup joins a stamped proxy row back for the members the
25286
+ * counted row lacks. The steering count is the one source known without
25287
+ * the request, so the tool definitions and the system context stay absent
25288
+ * (ADR-062, amendment of 2026-10-02). A member the producer set itself
25289
+ * wins, as on a proxied call. Only a call on the session's own
25290
+ * conversation comes here: an OTel record whose `query_source` says so
25291
+ * ({@link onSessionConversation}), or a transcript `assistant` record,
25292
+ * since Claude Code writes no side call to the transcript.
25293
+ */
25294
+ withUnseenRequestSources(body2, attrs2) {
25295
+ if (attrs2[LLM_CALL_DUPLICATE_OF_ATTR] !== void 0) return body2;
25296
+ const facts = this.systemContext.measureUnseen();
25297
+ return Object.keys(facts).length === 0 ? body2 : { ...facts, ...body2 };
25298
+ }
24743
25299
  /**
24744
25300
  * The attrs a `tool_call` carries, or undefined when the chain already
24745
25301
  * holds this call and the row must not be sealed at all.
@@ -25149,7 +25705,7 @@ var init_recorder = __esm({
25149
25705
  }
25150
25706
  const event = this.seal(
25151
25707
  draft.kind,
25152
- draft.body,
25708
+ draft.kind === "llm_call" && draft.source === "otel_log" && onSessionConversation(draft.standard.context.query_source) ? this.withUnseenRequestSources(draft.body, duplicate) : draft.body,
25153
25709
  {
25154
25710
  ts: draft.ts,
25155
25711
  source: draft.source,
@@ -25205,6 +25761,8 @@ var init_recorder = __esm({
25205
25761
  body2 = withoutUsage(body2);
25206
25762
  duplicate = { [LLM_CALL_DUPLICATE_OF_ATTR]: "transcript" };
25207
25763
  }
25764
+ if (draft.kind === "llm_call")
25765
+ body2 = this.withUnseenRequestSources(body2, duplicate);
25208
25766
  let content = draft.content;
25209
25767
  if (this.isSealedPrompt(draft.kind, body2)) {
25210
25768
  content = void 0;
@@ -26598,25 +27156,19 @@ async function runContained(command2, deps) {
26598
27156
  return 1;
26599
27157
  }
26600
27158
  const token = deps.env[CONTAINED_GITHUB_TOKEN_ENV];
26601
- const repository = command2.githubRepository ?? (token !== void 0 ? deps.env["GITHUB_REPOSITORY"] : void 0);
26602
- if (repository !== void 0 && (token === void 0 || token.length === 0)) {
27159
+ if (token !== void 0 && token.length > 0) {
26603
27160
  deps.err(
26604
- `--github-repository needs an installation token for it in ${CONTAINED_GITHUB_TOKEN_ENV}.`
26605
- );
26606
- return 2;
26607
- }
26608
- if (token !== void 0 && token.length > 0 && repository === void 0) {
26609
- deps.err(
26610
- `${CONTAINED_GITHUB_TOKEN_ENV} is set; name its one repository with --github-repository owner/name.`
27161
+ `${CONTAINED_GITHUB_TOKEN_ENV} is no longer read. A contained run reaches GitHub through Oxagen's Git custody, which gets a token from the workspace's GitHub App for each request. Remove the variable and the step that mints it, and bind the repository to the workspace.`
26611
27162
  );
26612
27163
  return 2;
26613
27164
  }
27165
+ const repository = command2.githubRepository;
26614
27166
  const body2 = JSON.stringify({
26615
27167
  workspace: command2.workspace ?? deps.cwd,
26616
27168
  harness,
26617
27169
  args: command2.args,
26618
27170
  image,
26619
- ...repository !== void 0 && token !== void 0 ? { github: { repository, token } } : {}
27171
+ ...repository !== void 0 ? { github: { repository } } : {}
26620
27172
  });
26621
27173
  let exitCode;
26622
27174
  let failure2;
@@ -26936,11 +27488,11 @@ var init_move_commands = __esm({
26936
27488
 
26937
27489
  // ../../packages/tacho/src/host/work-orders.ts
26938
27490
  import { readdirSync as readdirSync9, rmSync as rmSync4 } from "node:fs";
26939
- import { join as join16 } from "node:path";
27491
+ import { join as join17 } from "node:path";
26940
27492
  function orderFile(paths, id) {
26941
27493
  if (!WORK_ORDER_ID_PATTERN.test(id))
26942
27494
  throw new Error(`${JSON.stringify(id)} is not a work order id`);
26943
- return join16(paths.workOrders, `${id}.json`);
27495
+ return join17(paths.workOrders, `${id}.json`);
26944
27496
  }
26945
27497
  function readWorkOrder(paths, id) {
26946
27498
  let raw;
@@ -30756,135 +31308,27 @@ var init_configuration = __esm({
30756
31308
  });
30757
31309
 
30758
31310
  // ../../packages/tacho/src/contained/github.ts
30759
- import {
30760
- request as httpRequest
30761
- } from "node:http";
30762
- import { request as httpsRequest } from "node:https";
30763
- async function verifyRunGitHubToken(grant, fetch2, upstreams = GITHUB_UPSTREAMS) {
30764
- const response = await fetch2(
30765
- `${upstreams.api}/installation/repositories?per_page=2`,
30766
- {
30767
- method: "GET",
30768
- headers: { ...API_HEADERS, authorization: `Bearer ${grant.token}` },
30769
- signal: AbortSignal.timeout(15e3)
30770
- }
30771
- );
30772
- if (!response.ok)
30773
- throw new Error(
30774
- `GitHub refused the run's installation token (${response.status}); mint a new one for this run`
30775
- );
30776
- const body2 = JSON.parse(await response.text());
30777
- const only = body2.repositories?.[0]?.full_name;
30778
- if (body2.total_count !== 1 || only === void 0 || only.toLowerCase() !== grant.repository.toLowerCase())
30779
- throw new Error(
30780
- `The run's GitHub token must reach ${grant.repository} and no other repository; mint it with repositories: [${grant.repository.split("/")[1]}]`
30781
- );
30782
- }
30783
- async function revokeRunGitHubToken(token, fetch2, upstreams = GITHUB_UPSTREAMS) {
30784
- const response = await fetch2(`${upstreams.api}/installation/token`, {
30785
- method: "DELETE",
30786
- headers: { ...API_HEADERS, authorization: `Bearer ${token}` },
30787
- signal: AbortSignal.timeout(15e3)
30788
- });
30789
- if (response.status === 204) return "revoked";
30790
- if (response.status === 401) return "already_invalid";
30791
- throw new Error(`GitHub did not revoke the run's token (${response.status})`);
30792
- }
30793
- function gitHubTarget(path2, repository, upstreams = GITHUB_UPSTREAMS) {
30794
- const wanted = repository.toLowerCase();
31311
+ function custodyGitPath(path2, repository) {
30795
31312
  const git6 = GIT_ROUTE.exec(path2);
30796
- if (git6) {
30797
- const [, owner, name, rest] = git6;
30798
- const repo = name.replace(/\.git$/, "");
30799
- if (`${owner}/${repo}`.toLowerCase() !== wanted) return void 0;
30800
- return {
30801
- kind: "git",
30802
- url: `${upstreams.git}/${owner}/${repo}.git/${rest}`
30803
- };
30804
- }
30805
- const api = API_ROUTE.exec(path2);
30806
- if (api) {
30807
- const [, owner, name, rest = "", query = ""] = api;
30808
- if (`${owner}/${name}`.toLowerCase() !== wanted) return void 0;
30809
- if (rest.split("/").some((segment) => segment === "." || segment === ".."))
30810
- return void 0;
30811
- return {
30812
- kind: "api",
30813
- url: `${upstreams.api}/repos/${owner}/${name}${rest}${query}`
30814
- };
30815
- }
30816
- return void 0;
30817
- }
30818
- function forwardToGitHub(target2, token, incoming, outgoing) {
30819
- const url = new URL(target2.url);
30820
- const headers = {
30821
- "user-agent": "oxagen-contained-launcher",
30822
- authorization: target2.kind === "git" ? `Basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}` : `Bearer ${token}`
30823
- };
30824
- for (const name of FORWARDED) {
30825
- const value = incoming.headers[name];
30826
- if (typeof value === "string") headers[name] = value;
30827
- }
30828
- const send4 = url.protocol === "https:" ? httpsRequest : httpRequest;
30829
- const upstream = send4(
30830
- url,
30831
- { method: incoming.method, headers, timeout: 12e4 },
30832
- (response) => {
30833
- const passed = {};
30834
- for (const [name, value] of Object.entries(response.headers)) {
30835
- if (value === void 0) continue;
30836
- if (name === "set-cookie" || name === "connection") continue;
30837
- passed[name] = value;
30838
- }
30839
- outgoing.writeHead(response.statusCode ?? 502, passed);
30840
- response.pipe(outgoing);
30841
- }
30842
- );
30843
- upstream.on("timeout", () => upstream.destroy(new Error("GitHub timed out")));
30844
- upstream.on("error", () => {
30845
- if (!outgoing.headersSent) {
30846
- outgoing.writeHead(502, { "content-type": "application/json" });
30847
- outgoing.end(JSON.stringify({ error: "GitHub could not be reached" }));
30848
- } else outgoing.destroy();
30849
- });
30850
- outgoing.on("close", () => upstream.destroy());
30851
- incoming.pipe(upstream);
31313
+ if (!git6) return void 0;
31314
+ const [, owner, name, rest] = git6;
31315
+ const repo = name.replace(/\.git$/, "");
31316
+ if (`${owner}/${repo}`.toLowerCase() !== repository.toLowerCase())
31317
+ return void 0;
31318
+ return `/github/${owner}/${repo}.git/${rest}`;
30852
31319
  }
30853
- var containedGitHubSchema, GITHUB_UPSTREAMS, API_HEADERS, SEGMENT, GIT_ROUTE, API_ROUTE, FORWARDED;
31320
+ var containedGitHubSchema, SEGMENT, GIT_ROUTE;
30854
31321
  var init_github2 = __esm({
30855
31322
  "../../packages/tacho/src/contained/github.ts"() {
30856
31323
  "use strict";
30857
31324
  init_zod();
30858
31325
  containedGitHubSchema = external_exports.object({
30859
- repository: external_exports.string().regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9._-]{1,100}$/).refine((value) => !/\/\.\.?$/.test(value), "Invalid repository name"),
30860
- /** Installation access tokens carry the `ghs_` prefix. */
30861
- token: external_exports.string().regex(/^ghs_[A-Za-z0-9_]{20,255}$/)
31326
+ repository: external_exports.string().regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9._-]{1,100}$/).refine((value) => !/\/\.\.?$/.test(value), "Invalid repository name")
30862
31327
  }).strict();
30863
- GITHUB_UPSTREAMS = {
30864
- api: "https://api.github.com",
30865
- git: "https://github.com"
30866
- };
30867
- API_HEADERS = {
30868
- accept: "application/vnd.github+json",
30869
- "x-github-api-version": "2022-11-28",
30870
- "user-agent": "oxagen-contained-launcher"
30871
- };
30872
31328
  SEGMENT = "[A-Za-z0-9_-][A-Za-z0-9._-]*";
30873
31329
  GIT_ROUTE = new RegExp(
30874
31330
  `^/github/git/(${SEGMENT})/(${SEGMENT})/(info/refs\\?service=git-(?:upload|receive)-pack|git-upload-pack|git-receive-pack)$`
30875
31331
  );
30876
- API_ROUTE = new RegExp(
30877
- `^/github/api/repos/(${SEGMENT})/(${SEGMENT})((?:/${SEGMENT})*)(\\?[A-Za-z0-9_.,=&%+-]*)?$`
30878
- );
30879
- FORWARDED = [
30880
- "accept",
30881
- "accept-encoding",
30882
- "content-type",
30883
- "content-length",
30884
- "content-encoding",
30885
- "git-protocol",
30886
- "x-github-api-version"
30887
- ];
30888
31332
  }
30889
31333
  });
30890
31334
 
@@ -30962,17 +31406,38 @@ function containedBridgeHandler(options) {
30962
31406
  );
30963
31407
  return;
30964
31408
  }
30965
- if (options.github && path2.startsWith("/github/")) {
30966
- const target2 = gitHubTarget(
30967
- path2,
30968
- options.github.repository,
30969
- options.githubUpstreams
30970
- );
30971
- if (target2) {
30972
- options.forwarded?.(request3.method ?? "GET", path2);
30973
- forwardToGitHub(target2, options.github.token, request3, response);
31409
+ const github = options.github;
31410
+ const target2 = github === void 0 ? void 0 : custodyGitPath(path2, github.repository);
31411
+ if (github !== void 0 && target2 !== void 0) {
31412
+ const lease = github.lease();
31413
+ const body2 = lease.body;
31414
+ const token = lease.status === 200 ? body2?.token : void 0;
31415
+ if (typeof token !== "string") {
31416
+ options.githubRefused?.(path2);
31417
+ response.writeHead(403, {
31418
+ "content-type": "text/plain",
31419
+ "cache-control": "no-store"
31420
+ });
31421
+ response.end(
31422
+ `Oxagen issued no GitHub credential for this run. ${typeof body2?.error === "string" ? body2.error : "Check the session and the host's enrollment."}`
31423
+ );
30974
31424
  return;
30975
31425
  }
31426
+ try {
31427
+ const headers = {
31428
+ authorization: `Basic ${Buffer.from(`oxagen:${token}`).toString("base64")}`
31429
+ };
31430
+ for (const name of GIT_HEADERS) {
31431
+ const value = request3.headers[name];
31432
+ if (typeof value === "string") headers[name] = value;
31433
+ }
31434
+ request3.headers = headers;
31435
+ request3.url = target2;
31436
+ await github.handle(request3, response);
31437
+ } finally {
31438
+ github.release(token);
31439
+ }
31440
+ return;
30976
31441
  }
30977
31442
  options.refused(path2);
30978
31443
  send(response, 403, {
@@ -31010,16 +31475,23 @@ async function startContainedBridge(options) {
31010
31475
  }
31011
31476
  };
31012
31477
  }
31478
+ var GIT_HEADERS;
31013
31479
  var init_bridge = __esm({
31014
31480
  "../../packages/tacho/src/contained/bridge.ts"() {
31015
31481
  "use strict";
31016
31482
  init_github2();
31483
+ GIT_HEADERS = [
31484
+ "content-type",
31485
+ "content-encoding",
31486
+ "git-protocol",
31487
+ "user-agent"
31488
+ ];
31017
31489
  }
31018
31490
  });
31019
31491
 
31020
31492
  // ../../packages/tacho/src/contained/workspace.ts
31021
31493
  import { lstatSync as lstatSync5, readFileSync as readFileSync14, readdirSync as readdirSync10, realpathSync as realpathSync2 } from "node:fs";
31022
- import { isAbsolute, join as join17, relative, resolve as resolve6 } from "node:path";
31494
+ import { isAbsolute, join as join18, relative, resolve as resolve6 } from "node:path";
31023
31495
  function within(root, path2) {
31024
31496
  const rel = relative(root, path2);
31025
31497
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
@@ -31050,7 +31522,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
31050
31522
  throw new Error(
31051
31523
  "Remove local credential files from the contained checkout before launch"
31052
31524
  );
31053
- const path2 = join17(directory, entry2);
31525
+ const path2 = join18(directory, entry2);
31054
31526
  const stat5 = lstatSync5(path2);
31055
31527
  if (stat5.isSymbolicLink()) {
31056
31528
  let target2;
@@ -31204,7 +31676,7 @@ import {
31204
31676
  writeFileSync as writeFileSync2
31205
31677
  } from "node:fs";
31206
31678
  import { tmpdir } from "node:os";
31207
- import { isAbsolute as isAbsolute2, join as join18, relative as relative2 } from "node:path";
31679
+ import { isAbsolute as isAbsolute2, join as join19, relative as relative2 } from "node:path";
31208
31680
  import { promisify } from "node:util";
31209
31681
  async function launchContainedAgent(options) {
31210
31682
  validateContainedArguments(options.request.harness, options.request.args);
@@ -31237,7 +31709,7 @@ async function launchContainedAgent(options) {
31237
31709
  if (digest6 === void 0)
31238
31710
  throw new Error("The operator's contained image is not installed");
31239
31711
  const sessionId = `contained-${randomBytes8(16).toString("hex")}`;
31240
- const directory = mkdtempSync(join18(tmpdir(), "oxagen-contained-"));
31712
+ const directory = mkdtempSync(join19(tmpdir(), "oxagen-contained-"));
31241
31713
  chmodSync6(directory, 448);
31242
31714
  let prepared;
31243
31715
  let containerCreated = false;
@@ -31258,7 +31730,7 @@ async function launchContainedAgent(options) {
31258
31730
  for (const [name2, contents] of entries2) {
31259
31731
  if (!/^[a-z0-9.-]+$/.test(name2))
31260
31732
  throw new Error("Invalid contained configuration filename");
31261
- writeFileSync2(join18(directory, name2), contents, {
31733
+ writeFileSync2(join19(directory, name2), contents, {
31262
31734
  mode: 256,
31263
31735
  flag: "wx"
31264
31736
  });
@@ -31337,7 +31809,7 @@ var init_launcher = __esm({
31337
31809
  });
31338
31810
 
31339
31811
  // ../../packages/tacho/src/contained/runner.ts
31340
- import { join as join19 } from "node:path";
31812
+ import { join as join20 } from "node:path";
31341
31813
  function containedLaunchEndpoint(host) {
31342
31814
  const ingest = new URL(host.endpoints.ingest);
31343
31815
  if (!ingest.pathname.endsWith("/tacho/events"))
@@ -31348,211 +31820,214 @@ function containedLaunchEndpoint(host) {
31348
31820
  }
31349
31821
  function createContainedRunner(options) {
31350
31822
  const launched = /* @__PURE__ */ new Set();
31823
+ const repositories = /* @__PURE__ */ new Map();
31351
31824
  const active2 = /* @__PURE__ */ new Map();
31352
31825
  return {
31353
31826
  launched: (harnessSessionId) => launched.has(harnessSessionId),
31827
+ githubRepository: (harnessSessionId) => repositories.get(harnessSessionId),
31354
31828
  stop: (uuid) => active2.get(uuid)?.abort(),
31355
31829
  stopAll: () => {
31356
31830
  for (const controller of active2.values()) controller.abort();
31357
31831
  },
31358
31832
  run: async (raw, output, signal) => {
31359
31833
  const input = containedRunRequestSchema.parse(raw);
31834
+ const host = options.host();
31835
+ if (host.host_status !== "active" || host.revoked_at !== null || Date.parse(host.expires_at) <= Date.now())
31836
+ throw new Error("This enrollment cannot start a contained run");
31837
+ if (!host.gateway_api_key)
31838
+ throw new Error(
31839
+ "Re-enroll this runner to obtain a daemon gateway credential"
31840
+ );
31841
+ const credential = options.issueCredential(input.harness);
31842
+ if (credential.status !== 200)
31843
+ throw new Error(
31844
+ "The daemon must hold this harness's model credential before containment can start"
31845
+ );
31846
+ const repository = input.github?.repository;
31847
+ const controller = new AbortController();
31848
+ const abort = () => controller.abort();
31849
+ signal?.addEventListener("abort", abort, { once: true });
31850
+ if (signal?.aborted) abort();
31851
+ let sessionUuid2;
31852
+ let launchedSessionId;
31853
+ const session = (id) => {
31854
+ const found = options.registry.get(id);
31855
+ if (!found) throw new Error("Contained session was not recorded");
31856
+ return found;
31857
+ };
31360
31858
  try {
31361
- const host = options.host();
31362
- if (host.host_status !== "active" || host.revoked_at !== null || Date.parse(host.expires_at) <= Date.now())
31363
- throw new Error("This enrollment cannot start a contained run");
31364
- if (!host.gateway_api_key)
31365
- throw new Error(
31366
- "Re-enroll this runner to obtain a daemon gateway credential"
31367
- );
31368
- const credential = options.issueCredential(input.harness);
31369
- if (credential.status !== 200)
31370
- throw new Error(
31371
- "The daemon must hold this harness's model credential before containment can start"
31372
- );
31373
- if (input.github)
31374
- await verifyRunGitHubToken(input.github, options.fetch);
31375
- const controller = new AbortController();
31376
- const abort = () => controller.abort();
31377
- signal?.addEventListener("abort", abort, { once: true });
31378
- if (signal?.aborted) abort();
31379
- let sessionUuid2;
31380
- let launchedSessionId;
31381
- const session = (id) => {
31382
- const found = options.registry.get(id);
31383
- if (!found) throw new Error("Contained session was not recorded");
31384
- return found;
31385
- };
31386
- try {
31387
- return await launchContainedAgent({
31388
- image: input.image,
31389
- request: input,
31390
- output,
31391
- signal: controller.signal,
31392
- prepare: async ({ sessionId, directory, workspace }) => {
31393
- launched.add(sessionId);
31394
- launchedSessionId = sessionId;
31395
- await options.hook({
31396
- harness: input.harness,
31397
- payload: {
31398
- hook_event_name: "SessionStart",
31399
- session_id: sessionId,
31400
- cwd: workspace,
31401
- source: "startup"
31402
- }
31403
- });
31404
- const started = session(sessionId);
31405
- sessionUuid2 = started.recorder.sessionUuid;
31406
- active2.set(sessionUuid2, controller);
31407
- const gateway = createMcpGateway({
31408
- attribution: () => {
31409
- const current = options.host();
31410
- if (current.host_status !== "active" || !current.gateway_api_key)
31411
- return void 0;
31412
- return {
31413
- organizationId: current.organization_id,
31414
- workspaceId: current.workspace_id,
31415
- orgSlug: current.org_slug,
31416
- workspaceSlug: current.workspace_slug,
31417
- apiKey: current.gateway_api_key,
31418
- hostEnrollmentId: current.host_enrollment_id,
31419
- chainSessionUuid: started.recorder.sessionUuid,
31420
- chainGenesisHash: options.genesis(
31421
- started.recorder.sessionUuid
31422
- )
31423
- };
31424
- },
31425
- endpoint: mcpEndpointFor(options.host()),
31426
- fetch: options.fetch,
31427
- bundle: () => options.host().bundle,
31428
- log: options.log,
31429
- record: (call) => options.record([
31430
- started.recorder.sealCollectorEvent("tool_call", {
31431
- tool_name: call.toolName,
31432
- tool_source: "mcp",
31433
- tool_status: call.status,
31434
- tool_duration_ms: call.durationMs
31435
- })
31436
- ])
31437
- });
31438
- const bridge = await startContainedBridge({
31439
- socketPath: join19(directory, "bridge.sock"),
31859
+ return await launchContainedAgent({
31860
+ image: input.image,
31861
+ request: input,
31862
+ output,
31863
+ signal: controller.signal,
31864
+ prepare: async ({ sessionId, directory, workspace }) => {
31865
+ launched.add(sessionId);
31866
+ launchedSessionId = sessionId;
31867
+ if (repository !== void 0)
31868
+ repositories.set(sessionId, repository);
31869
+ await options.hook({
31870
+ harness: input.harness,
31871
+ payload: {
31872
+ hook_event_name: "SessionStart",
31873
+ session_id: sessionId,
31874
+ cwd: workspace,
31875
+ source: "startup"
31876
+ }
31877
+ });
31878
+ const started = session(sessionId);
31879
+ sessionUuid2 = started.recorder.sessionUuid;
31880
+ active2.set(sessionUuid2, controller);
31881
+ const gateway = createMcpGateway({
31882
+ attribution: () => {
31883
+ const current = options.host();
31884
+ if (current.host_status !== "active" || !current.gateway_api_key)
31885
+ return void 0;
31886
+ return {
31887
+ organizationId: current.organization_id,
31888
+ workspaceId: current.workspace_id,
31889
+ orgSlug: current.org_slug,
31890
+ workspaceSlug: current.workspace_slug,
31891
+ apiKey: current.gateway_api_key,
31892
+ hostEnrollmentId: current.host_enrollment_id,
31893
+ chainSessionUuid: started.recorder.sessionUuid,
31894
+ chainGenesisHash: options.genesis(
31895
+ started.recorder.sessionUuid
31896
+ )
31897
+ };
31898
+ },
31899
+ endpoint: mcpEndpointFor(options.host()),
31900
+ fetch: options.fetch,
31901
+ bundle: () => options.host().bundle,
31902
+ log: options.log,
31903
+ record: (call) => options.record([
31904
+ started.recorder.sealCollectorEvent("tool_call", {
31905
+ tool_name: call.toolName,
31906
+ tool_source: "mcp",
31907
+ tool_status: call.status,
31908
+ tool_duration_ms: call.durationMs
31909
+ })
31910
+ ])
31911
+ });
31912
+ const bridge = await startContainedBridge({
31913
+ socketPath: join20(directory, "bridge.sock"),
31914
+ sessionId,
31915
+ workspace,
31916
+ harness: input.harness,
31917
+ modelPort: options.modelPort(),
31918
+ issueCredential: () => {
31919
+ const answer = options.issueCredential(input.harness);
31920
+ if (answer.status !== 200)
31921
+ throw new Error("Credential custody unavailable");
31922
+ return answer.body.token;
31923
+ },
31924
+ model: (request3, response) => options.model.handle(request3, response),
31925
+ hook: options.hook,
31926
+ mcp: (body2) => gateway.handle(body2, {
31440
31927
  sessionId,
31441
- workspace,
31442
- harness: input.harness,
31443
- modelPort: options.modelPort(),
31444
- issueCredential: () => {
31445
- const answer = options.issueCredential(input.harness);
31446
- if (answer.status !== 200)
31447
- throw new Error("Credential custody unavailable");
31448
- return answer.body.token;
31449
- },
31450
- model: (request3, response) => options.model.handle(request3, response),
31451
- hook: options.hook,
31452
- mcp: (body2) => gateway.handle(body2, {
31453
- sessionId,
31454
- enrollmentId: options.host().host_enrollment_id
31455
- }),
31456
- ...input.github ? { github: input.github } : {},
31457
- forwarded: (method, path2) => options.record([
31458
- started.recorder.sealCollectorEvent("policy_decision", {
31459
- policy_decision: "allow",
31460
- policy_source: "bundle",
31461
- policy_reason_code: "contained_github_route",
31462
- tool_name: `${method} ${path2.split("?")[0] ?? ""}`.slice(
31463
- 0,
31464
- 256
31465
- )
31466
- })
31467
- ]),
31468
- refused: (path2) => options.record([
31469
- started.recorder.sealCollectorEvent("policy_decision", {
31470
- policy_decision: "deny",
31471
- policy_source: "bundle",
31472
- policy_reason_code: "contained_gateway_route",
31473
- tool_name: path2.split("?")[0]?.slice(0, 256) ?? "unknown"
31474
- })
31475
- ])
31476
- });
31477
- return {
31478
- files: containedConfiguration(
31479
- input.harness,
31480
- input.github?.repository
31481
- ),
31482
- close: bridge.close
31483
- };
31484
- },
31485
- measured: async (sessionId, measurement) => {
31486
- const started = session(sessionId);
31487
- const genesis = options.genesis(started.recorder.sessionUuid);
31488
- if (!genesis)
31489
- throw new Error("Contained session has no recorded genesis");
31490
- const current = options.host();
31491
- const response = await options.fetch(
31492
- containedLaunchEndpoint(current),
31493
- {
31494
- method: "POST",
31495
- headers: {
31496
- authorization: `Bearer ${current.gateway_api_key}`,
31497
- "content-type": "application/json"
31498
- },
31499
- body: JSON.stringify({
31500
- host_enrollment_id: current.host_enrollment_id,
31501
- session_uuid: started.recorder.sessionUuid,
31502
- genesis_hash: genesis,
31503
- measurement
31928
+ enrollmentId: options.host().host_enrollment_id
31929
+ }),
31930
+ // The lease is keyed by this launcher's own session, never by
31931
+ // a working directory or a port, neither of which the
31932
+ // container shares with the host (ADR-254).
31933
+ ...repository !== void 0 ? {
31934
+ github: {
31935
+ repository,
31936
+ lease: () => options.github.issueForSession({
31937
+ session: started.recorder.sessionUuid,
31938
+ repository
31504
31939
  }),
31505
- signal: AbortSignal.timeout(3e4)
31940
+ handle: options.github.handle,
31941
+ release: options.github.release
31506
31942
  }
31943
+ } : {},
31944
+ githubRefused: (path2) => options.record([
31945
+ started.recorder.sealCollectorEvent("policy_decision", {
31946
+ policy_decision: "deny",
31947
+ policy_source: "kernel",
31948
+ policy_reason_code: "contained_github_custody",
31949
+ tool_name: path2.split("?")[0]?.slice(0, 256) ?? "unknown"
31950
+ })
31951
+ ]),
31952
+ refused: (path2) => options.record([
31953
+ started.recorder.sealCollectorEvent("policy_decision", {
31954
+ policy_decision: "deny",
31955
+ policy_source: "bundle",
31956
+ policy_reason_code: "contained_gateway_route",
31957
+ tool_name: path2.split("?")[0]?.slice(0, 256) ?? "unknown"
31958
+ })
31959
+ ])
31960
+ });
31961
+ return {
31962
+ files: containedConfiguration(input.harness, repository),
31963
+ close: bridge.close
31964
+ };
31965
+ },
31966
+ measured: async (sessionId, measurement) => {
31967
+ const started = session(sessionId);
31968
+ const genesis = options.genesis(started.recorder.sessionUuid);
31969
+ if (!genesis)
31970
+ throw new Error("Contained session has no recorded genesis");
31971
+ const current = options.host();
31972
+ const response = await options.fetch(
31973
+ containedLaunchEndpoint(current),
31974
+ {
31975
+ method: "POST",
31976
+ headers: {
31977
+ authorization: `Bearer ${current.gateway_api_key}`,
31978
+ "content-type": "application/json"
31979
+ },
31980
+ body: JSON.stringify({
31981
+ host_enrollment_id: current.host_enrollment_id,
31982
+ session_uuid: started.recorder.sessionUuid,
31983
+ genesis_hash: genesis,
31984
+ measurement
31985
+ }),
31986
+ signal: AbortSignal.timeout(3e4)
31987
+ }
31988
+ );
31989
+ if (!response.ok)
31990
+ throw new Error(
31991
+ `Contained launch registration failed (${response.status}); the agent was not started`
31507
31992
  );
31508
- if (!response.ok)
31509
- throw new Error(
31510
- `Contained launch registration failed (${response.status}); the agent was not started`
31511
- );
31512
- options.record([
31513
- started.recorder.sealCollectorEvent(
31514
- "policy_decision",
31515
- {
31516
- policy_decision: "allow",
31517
- policy_source: "kernel",
31518
- policy_reason_code: "contained_launch_registered"
31519
- },
31520
- {
31521
- attrs: {
31522
- "oxagen.containment.profile": measurement.profile,
31523
- "oxagen.containment.image": measurement.imageDigest,
31524
- "oxagen.containment.configuration": measurement.configurationDigest
31525
- }
31993
+ options.record([
31994
+ started.recorder.sealCollectorEvent(
31995
+ "policy_decision",
31996
+ {
31997
+ policy_decision: "allow",
31998
+ policy_source: "kernel",
31999
+ policy_reason_code: "contained_launch_registered"
32000
+ },
32001
+ {
32002
+ attrs: {
32003
+ "oxagen.containment.profile": measurement.profile,
32004
+ "oxagen.containment.image": measurement.imageDigest,
32005
+ "oxagen.containment.configuration": measurement.configurationDigest
31526
32006
  }
31527
- )
31528
- ]);
31529
- },
31530
- sealed: async (sessionId, exitCode) => {
31531
- await options.hook({
31532
- harness: input.harness,
31533
- payload: {
31534
- hook_event_name: "SessionEnd",
31535
- session_id: sessionId,
31536
- cwd: input.workspace,
31537
- reason: "other",
31538
- exit_code: exitCode
31539
32007
  }
31540
- });
31541
- }
31542
- });
31543
- } finally {
31544
- signal?.removeEventListener("abort", abort);
31545
- if (sessionUuid2) active2.delete(sessionUuid2);
31546
- if (launchedSessionId) launched.delete(launchedSessionId);
31547
- }
32008
+ )
32009
+ ]);
32010
+ },
32011
+ sealed: async (sessionId, exitCode) => {
32012
+ await options.hook({
32013
+ harness: input.harness,
32014
+ payload: {
32015
+ hook_event_name: "SessionEnd",
32016
+ session_id: sessionId,
32017
+ cwd: input.workspace,
32018
+ reason: "other",
32019
+ exit_code: exitCode
32020
+ }
32021
+ });
32022
+ }
32023
+ });
31548
32024
  } finally {
31549
- if (input.github)
31550
- await revokeRunGitHubToken(input.github.token, options.fetch).then(
31551
- (outcome) => options.log(`Contained run GitHub token ${outcome}`),
31552
- (error) => options.log(
31553
- `Contained run GitHub token was not revoked: ${error instanceof Error ? error.message : String(error)}`
31554
- )
31555
- );
32025
+ signal?.removeEventListener("abort", abort);
32026
+ if (sessionUuid2) active2.delete(sessionUuid2);
32027
+ if (launchedSessionId) {
32028
+ launched.delete(launchedSessionId);
32029
+ repositories.delete(launchedSessionId);
32030
+ }
31556
32031
  }
31557
32032
  }
31558
32033
  };
@@ -31573,7 +32048,10 @@ var init_runner = __esm({
31573
32048
  harness: external_exports.enum(["claude-code", "codex"]),
31574
32049
  args: external_exports.array(external_exports.string().max(65536)).max(128),
31575
32050
  image: external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9/_.:@-]{0,255}$/),
31576
- /** Optional: one installation token for one repository (ADR-152). */
32051
+ /**
32052
+ * Optional: the one repository the run may reach, through the daemon's
32053
+ * Git custody (ADR-254). The request carries no token.
32054
+ */
31577
32055
  github: containedGitHubSchema.optional()
31578
32056
  }).strict();
31579
32057
  }
@@ -31581,7 +32059,7 @@ var init_runner = __esm({
31581
32059
 
31582
32060
  // ../../packages/tacho/src/collector/detector.ts
31583
32061
  import { promises as fs } from "node:fs";
31584
- import { basename as basename4, join as join20 } from "node:path";
32062
+ import { basename as basename4, join as join21 } from "node:path";
31585
32063
  async function eachLimited(items, limit, task) {
31586
32064
  let next = 0;
31587
32065
  const workers = Array.from(
@@ -31607,7 +32085,7 @@ async function scanProject(dir) {
31607
32085
  await eachLimited(names, SCAN_CONCURRENCY, async (name) => {
31608
32086
  const match = SESSION_FILE.exec(name);
31609
32087
  if (match === null) return;
31610
- const path2 = join20(dir, name);
32088
+ const path2 = join21(dir, name);
31611
32089
  try {
31612
32090
  const st = await fs.stat(path2);
31613
32091
  out.push({ sessionId: match[1], path: path2, mtimeMs: st.mtimeMs });
@@ -31648,7 +32126,7 @@ var init_detector = __esm({
31648
32126
  continue;
31649
32127
  }
31650
32128
  await eachLimited(names, SCAN_CONCURRENCY, async (name) => {
31651
- const dir = join20(root, name);
32129
+ const dir = join21(root, name);
31652
32130
  try {
31653
32131
  const st = await fs.stat(dir);
31654
32132
  if (st.isDirectory()) dirs.push({ dir, mtimeMs: st.mtimeMs });
@@ -31874,7 +32352,7 @@ var init_detector = __esm({
31874
32352
  transcript_path: transcript.path,
31875
32353
  transcript_mtime: new Date(transcript.mtimeMs).toISOString(),
31876
32354
  claude_pids: processes.map((p) => p.pid),
31877
- project: basename4(join20(transcript.path, ".."))
32355
+ project: basename4(join21(transcript.path, ".."))
31878
32356
  }
31879
32357
  })
31880
32358
  );
@@ -32247,7 +32725,7 @@ import {
32247
32725
  rm,
32248
32726
  unlink
32249
32727
  } from "node:fs/promises";
32250
- import { dirname as dirname13, join as join21 } from "node:path";
32728
+ import { dirname as dirname13, join as join22 } from "node:path";
32251
32729
  async function statOf(absolutePath) {
32252
32730
  try {
32253
32731
  const info = await lstat(absolutePath);
@@ -32287,7 +32765,7 @@ async function contentHash(absolutePath, kind, keep) {
32287
32765
  if (keep !== void 0) {
32288
32766
  try {
32289
32767
  await mkdir(keep.dir, { recursive: true, mode: 448 });
32290
- const temp2 = join21(keep.dir, `.partial-${randomUUID3()}`);
32768
+ const temp2 = join22(keep.dir, `.partial-${randomUUID3()}`);
32291
32769
  copy = {
32292
32770
  temp: temp2,
32293
32771
  handle: await open3(temp2, "wx", keep.executable ? 448 : 384)
@@ -32331,7 +32809,7 @@ async function settleCopy(temp, name) {
32331
32809
  if (temp === void 0) return;
32332
32810
  if (name !== void 0)
32333
32811
  try {
32334
- await rename(temp, join21(dirname13(temp), name));
32812
+ await rename(temp, join22(dirname13(temp), name));
32335
32813
  return;
32336
32814
  } catch {
32337
32815
  }
@@ -32347,7 +32825,7 @@ async function copiedBytes(dir) {
32347
32825
  let total2 = 0;
32348
32826
  for (const name of names) {
32349
32827
  try {
32350
- total2 += (await lstat(join21(dir, name))).size;
32828
+ total2 += (await lstat(join22(dir, name))).size;
32351
32829
  } catch {
32352
32830
  }
32353
32831
  }
@@ -32363,7 +32841,7 @@ async function removeCopiesOutside(root, live) {
32363
32841
  const keep = new Set(live());
32364
32842
  await Promise.all(
32365
32843
  names.filter((name) => !keep.has(name)).map(
32366
- (name) => rm(join21(root, name), { recursive: true, force: true }).catch(
32844
+ (name) => rm(join22(root, name), { recursive: true, force: true }).catch(
32367
32845
  () => void 0
32368
32846
  )
32369
32847
  )
@@ -32471,7 +32949,7 @@ async function contributionOf(exec4, cwd, root, repoRelative, entry2, copies) {
32471
32949
  let copy = null;
32472
32950
  if (entry2 !== null) {
32473
32951
  if (copies === void 0 || !COPY_NAME.test(entry2[0])) return void 0;
32474
- copy = join21(copies, entry2[0]);
32952
+ copy = join22(copies, entry2[0]);
32475
32953
  const kept = await statOf(copy);
32476
32954
  if (kept === void 0 || kept === "absent" || kept.kind !== "file")
32477
32955
  return void 0;
@@ -32799,7 +33277,7 @@ var init_session_changes = __esm({
32799
33277
 
32800
33278
  // ../../packages/tacho/src/collector/worktree-snapshot.ts
32801
33279
  import { lstat as lstat2 } from "node:fs/promises";
32802
- import { join as join22 } from "node:path";
33280
+ import { join as join23 } from "node:path";
32803
33281
  function headerPath(prefix, path2) {
32804
33282
  const name = `${prefix}${path2}`;
32805
33283
  let quoted2 = false;
@@ -32874,7 +33352,7 @@ async function worktreeFingerprint(status2, root) {
32874
33352
  const path2 = statusPath(record);
32875
33353
  if (path2 === null) return "";
32876
33354
  try {
32877
- const info = await lstat2(join22(root, path2));
33355
+ const info = await lstat2(join23(root, path2));
32878
33356
  return `${info.size}:${info.mtimeMs}:${info.ctimeMs}`;
32879
33357
  } catch {
32880
33358
  return "absent";
@@ -32980,7 +33458,7 @@ async function readWorktreeSnapshot(exec4, cwd, baseline, measured) {
32980
33458
  }
32981
33459
  let present;
32982
33460
  try {
32983
- present = (await lstat2(join22(root, path2))).isFile();
33461
+ present = (await lstat2(join23(root, path2))).isFile();
32984
33462
  } catch {
32985
33463
  present = false;
32986
33464
  }
@@ -33081,10 +33559,10 @@ var init_worktree_snapshot = __esm({
33081
33559
  });
33082
33560
 
33083
33561
  // ../../packages/tacho/src/collector/git-lane.ts
33084
- import { join as join23 } from "node:path";
33562
+ import { join as join24 } from "node:path";
33085
33563
  function copiesDirOf(root, session) {
33086
33564
  const uuid = session.recorder.sessionUuid;
33087
- return /^[0-9a-f-]{36}$/i.test(uuid) ? join23(root, uuid) : void 0;
33565
+ return /^[0-9a-f-]{36}$/i.test(uuid) ? join24(root, uuid) : void 0;
33088
33566
  }
33089
33567
  function holdsBaseline(session) {
33090
33568
  return session.baselineCommit !== void 0 || Object.keys(session.baselines ?? {}).length > 0;
@@ -34227,7 +34705,11 @@ async function gitPushBasis(input, record, deps) {
34227
34705
  if (deps.pushCredentialBasis === void 0)
34228
34706
  return TACHO_CREDENTIAL_HARNESS_HELD;
34229
34707
  try {
34230
- return await deps.pushCredentialBasis(command2, record.cwd ?? input.cwd);
34708
+ return await deps.pushCredentialBasis(
34709
+ command2,
34710
+ record.cwd ?? input.cwd,
34711
+ record.harnessSessionId
34712
+ );
34231
34713
  } catch {
34232
34714
  return TACHO_CREDENTIAL_HARNESS_HELD;
34233
34715
  }
@@ -35451,15 +35933,41 @@ function createGithubProxy(deps) {
35451
35933
  const fetchUpstream = deps.fetch ?? globalThis.fetch;
35452
35934
  function live(lease) {
35453
35935
  const host = deps.host();
35454
- if (host.github_broker_enabled !== true || host.host_status !== "active" || !Number.isFinite(Date.parse(host.expires_at)) || Date.parse(host.expires_at) <= deps.now() || lease.expires <= deps.now())
35936
+ if (
35937
+ // `tacho github configure` turns this on for a configured checkout.
35938
+ // A contained run opts in by naming its repository at launch instead.
35939
+ lease.launcher !== true && host.github_broker_enabled !== true || host.host_status !== "active" || !Number.isFinite(Date.parse(host.expires_at)) || Date.parse(host.expires_at) <= deps.now() || lease.expires <= deps.now()
35940
+ )
35455
35941
  return void 0;
35456
35942
  const session = deps.registry.byUuid(lease.session);
35457
- return session && !session.sealed && !session.pendingTerminal && session.control.paused == null && session.control.cancelled == null && receiptMatches(
35943
+ return session && !session.sealed && !session.pendingTerminal && session.control.paused == null && session.control.cancelled == null && (lease.launcher === true || receiptMatches(
35458
35944
  host,
35459
35945
  lease.repository,
35460
35946
  session.cwd,
35461
35947
  deps.registry.agentOf(session).harness
35462
- ) ? session : void 0;
35948
+ )) ? session : void 0;
35949
+ }
35950
+ function validRepository(value) {
35951
+ return typeof value === "string" && REPO.test(value) && ![".", ".."].includes(value.split("/")[1] ?? "");
35952
+ }
35953
+ function grant(fields, host) {
35954
+ const token = `oxgit_${randomBytes9(32).toString("base64url")}`;
35955
+ const lease = {
35956
+ ...fields,
35957
+ id: `rt_${randomBytes9(10).toString("hex")}`,
35958
+ repository: fields.repository.toLowerCase(),
35959
+ expires: Math.min(deps.now() + LIFE_MS, Date.parse(host.expires_at))
35960
+ };
35961
+ if (!live(lease))
35962
+ return {
35963
+ status: 403,
35964
+ body: { error: "The session or enrollment is not active" }
35965
+ };
35966
+ leases.set(hash(token), lease);
35967
+ return {
35968
+ status: 200,
35969
+ body: { token, expires_at: new Date(lease.expires).toISOString() }
35970
+ };
35463
35971
  }
35464
35972
  function receiptMatches(host, repository, cwd, harness) {
35465
35973
  return cwd !== void 0 && (host.github_repositories ?? []).some(
@@ -35472,14 +35980,14 @@ function createGithubProxy(deps) {
35472
35980
  status: 400,
35473
35981
  body: { error: "A GitHub lease request must be an object" }
35474
35982
  };
35475
- for (const [key, lease2] of leases) if (!live(lease2)) leases.delete(key);
35983
+ for (const [key, lease] of leases) if (!live(lease)) leases.delete(key);
35476
35984
  const host = deps.host();
35477
35985
  if (host.github_broker_enabled !== true || host.host_status !== "active")
35478
35986
  return {
35479
35987
  status: 403,
35480
35988
  body: { error: "GitHub custody is not enabled on this host" }
35481
35989
  };
35482
- if (typeof input.repository !== "string" || !REPO.test(input.repository) || [".", ".."].includes(input.repository.split("/")[1] ?? "") || typeof input.cwd !== "string" || typeof input.harness !== "string" || !isWrappedHarness(input.harness))
35990
+ if (!validRepository(input.repository) || typeof input.cwd !== "string" || typeof input.harness !== "string" || !isWrappedHarness(input.harness))
35483
35991
  return {
35484
35992
  status: 400,
35485
35993
  body: {
@@ -35503,24 +36011,39 @@ function createGithubProxy(deps) {
35503
36011
  error: "GitHub custody needs exactly one live session in this working directory"
35504
36012
  }
35505
36013
  };
35506
- const session = matches[0];
35507
- const token = `oxgit_${randomBytes9(32).toString("base64url")}`;
35508
- const lease = {
35509
- id: `rt_${randomBytes9(10).toString("hex")}`,
35510
- repository: input.repository.toLowerCase(),
35511
- session: session.recorder.sessionUuid,
35512
- expires: Math.min(deps.now() + LIFE_MS, Date.parse(host.expires_at))
35513
- };
35514
- if (!live(lease))
36014
+ return grant(
36015
+ {
36016
+ repository: input.repository,
36017
+ session: matches[0].recorder.sessionUuid
36018
+ },
36019
+ host
36020
+ );
36021
+ }
36022
+ function issueForSession(input) {
36023
+ for (const [key, lease] of leases) if (!live(lease)) leases.delete(key);
36024
+ const host = deps.host();
36025
+ if (host.host_status !== "active")
35515
36026
  return {
35516
36027
  status: 403,
35517
- body: { error: "The session or enrollment is not active" }
36028
+ body: { error: "This host's enrollment is not active" }
35518
36029
  };
35519
- leases.set(hash(token), lease);
35520
- return {
35521
- status: 200,
35522
- body: { token, expires_at: new Date(lease.expires).toISOString() }
35523
- };
36030
+ if (!validRepository(input.repository))
36031
+ return {
36032
+ status: 400,
36033
+ body: { error: "Name the GitHub repository as owner/name" }
36034
+ };
36035
+ if (leases.size >= MAX_LEASES)
36036
+ return {
36037
+ status: 409,
36038
+ body: { error: "Too many GitHub run credentials are open on this host" }
36039
+ };
36040
+ return grant(
36041
+ { repository: input.repository, session: input.session, launcher: true },
36042
+ host
36043
+ );
36044
+ }
36045
+ function release2(token) {
36046
+ leases.delete(hash(token));
35524
36047
  }
35525
36048
  async function handle(req, res) {
35526
36049
  const token = bearer(req);
@@ -35758,7 +36281,7 @@ function createGithubProxy(deps) {
35758
36281
  }
35759
36282
  }
35760
36283
  }
35761
- return { issue, handle };
36284
+ return { issue, issueForSession, release: release2, handle };
35762
36285
  }
35763
36286
  var LIFE_MS, MAX_LEASES, MAX_REQUEST_BYTES, REPO, MAX_REFUSAL_CHARS, REFUSED;
35764
36287
  var init_github_proxy = __esm({
@@ -35866,7 +36389,7 @@ var init_hook_queues = __esm({
35866
36389
  });
35867
36390
 
35868
36391
  // ../../packages/tacho/src/collector/memory-capture/memory-reader.ts
35869
- import { isAbsolute as isAbsolute5, join as join24, sep as sep2 } from "node:path";
36392
+ import { isAbsolute as isAbsolute5, join as join25, sep as sep2 } from "node:path";
35870
36393
  function claudeCodeLocation(projectsDir, memoryDir) {
35871
36394
  return {
35872
36395
  harness: "claude-code",
@@ -35878,13 +36401,13 @@ function claudeCodeLocation(projectsDir, memoryDir) {
35878
36401
  }
35879
36402
  function claudeCodeMemoryLocations(configDir, projectDirs = []) {
35880
36403
  const locations = [
35881
- claudeCodeLocation(() => join24(configDir, "projects"), "memory"),
35882
- claudeCodeLocation(() => join24(configDir, "agent-memory"), "")
36404
+ claudeCodeLocation(() => join25(configDir, "projects"), "memory"),
36405
+ claudeCodeLocation(() => join25(configDir, "agent-memory"), "")
35883
36406
  ];
35884
36407
  for (const dir of projectDirs.slice(0, MEMORY_PROJECT_DIRS_MAX))
35885
36408
  locations.push(
35886
- claudeCodeLocation(() => join24(dir, ".claude", "agent-memory"), ""),
35887
- claudeCodeLocation(() => join24(dir, ".claude", "agent-memory-local"), "")
36409
+ claudeCodeLocation(() => join25(dir, ".claude", "agent-memory"), ""),
36410
+ claudeCodeLocation(() => join25(dir, ".claude", "agent-memory-local"), "")
35888
36411
  );
35889
36412
  return locations;
35890
36413
  }
@@ -36063,13 +36586,13 @@ function createMemoryReader(deps) {
36063
36586
  const projects = await list(top);
36064
36587
  let complete = projects.complete;
36065
36588
  for (const project2 of projects.names) {
36066
- const dir = location.memoryDir === "" ? join24(top, project2) : join24(top, project2, location.memoryDir);
36589
+ const dir = location.memoryDir === "" ? join25(top, project2) : join25(top, project2, location.memoryDir);
36067
36590
  const files2 = await list(dir);
36068
36591
  if (!files2.complete) complete = false;
36069
36592
  for (const name of files2.names) {
36070
36593
  if (!name.endsWith(location.extension)) continue;
36071
36594
  if (location.skip.includes(name)) continue;
36072
- const path2 = join24(dir, name);
36595
+ const path2 = join25(dir, name);
36073
36596
  const file = await readAt2(location.harness, path2);
36074
36597
  if (file.kind === "absent" || file.kind === "empty") continue;
36075
36598
  paths.push(path2);
@@ -36155,8 +36678,8 @@ var init_memory_reader = __esm({
36155
36678
  MEMORY_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
36156
36679
  MEMORY_PROJECT_DIRS_MAX = 200;
36157
36680
  HARNESS_MEMORY_LOCATIONS = [
36158
- claudeCodeLocation((home) => join24(home, ".claude", "projects"), "memory"),
36159
- claudeCodeLocation((home) => join24(home, ".claude", "agent-memory"), "")
36681
+ claudeCodeLocation((home) => join25(home, ".claude", "projects"), "memory"),
36682
+ claudeCodeLocation((home) => join25(home, ".claude", "agent-memory"), "")
36160
36683
  ];
36161
36684
  FRONTMATTER = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
36162
36685
  FRONTMATTER_LINE = /^([ \t]*)([A-Za-z_][\w-]*)[ \t]*:[ \t]*(.*)$/;
@@ -36635,7 +37158,7 @@ var init_memory_upload = __esm({
36635
37158
  });
36636
37159
 
36637
37160
  // ../../packages/tacho/src/collector/memory-capture/memory-uses.ts
36638
- import { basename as basename5, dirname as dirname15, isAbsolute as isAbsolute6, join as join25, resolve as resolve9 } from "node:path";
37161
+ import { basename as basename5, dirname as dirname15, isAbsolute as isAbsolute6, join as join26, resolve as resolve9 } from "node:path";
36639
37162
  function memoryFileOf(path2, locations, home) {
36640
37163
  if (!isAbsolute6(path2)) return void 0;
36641
37164
  const file = resolve9(path2);
@@ -36653,7 +37176,7 @@ function memoryFileOf(path2, locations, home) {
36653
37176
  return void 0;
36654
37177
  }
36655
37178
  function pathOf(word, cwd, home) {
36656
- const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join25(home, word.slice(word.indexOf("/") + 1)) : word;
37179
+ const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join26(home, word.slice(word.indexOf("/") + 1)) : word;
36657
37180
  if (isAbsolute6(expanded)) return expanded;
36658
37181
  if (cwd !== void 0 && isAbsolute6(cwd)) return resolve9(cwd, expanded);
36659
37182
  return void 0;
@@ -36932,7 +37455,7 @@ ${use.path}`;
36932
37455
 
36933
37456
  // ../../packages/tacho/src/collector/memory-capture/stella-store.ts
36934
37457
  import { createRequire as createRequire3 } from "node:module";
36935
- import { dirname as dirname16, isAbsolute as isAbsolute7, join as join26, resolve as resolve10 } from "node:path";
37458
+ import { dirname as dirname16, isAbsolute as isAbsolute7, join as join27, resolve as resolve10 } from "node:path";
36936
37459
  function sqlite2() {
36937
37460
  sqliteModule2 ??= createRequire3(import.meta.url)(
36938
37461
  "node:sqlite"
@@ -37035,7 +37558,7 @@ async function stellaStoreOf(dir, exists) {
37035
37558
  if (!isAbsolute7(dir)) return void 0;
37036
37559
  let at = resolve10(dir);
37037
37560
  for (let depth = 0; depth < WORKSPACE_SEARCH_DEPTH; depth += 1) {
37038
- const candidate = join26(at, STELLA_CONTEXT_DB);
37561
+ const candidate = join27(at, STELLA_CONTEXT_DB);
37039
37562
  if (await exists(candidate)) return candidate;
37040
37563
  const up = dirname16(at);
37041
37564
  if (up === at) return void 0;
@@ -37048,7 +37571,7 @@ var init_stella_store = __esm({
37048
37571
  "../../packages/tacho/src/collector/memory-capture/stella-store.ts"() {
37049
37572
  "use strict";
37050
37573
  init_memory_reader();
37051
- STELLA_CONTEXT_DB = join26(".stella", "private", "context.db");
37574
+ STELLA_CONTEXT_DB = join27(".stella", "private", "context.db");
37052
37575
  STELLA_MEMORIES_VIEW = "export_memories_v1";
37053
37576
  STELLA_USES_VIEW = "export_memory_uses_v1";
37054
37577
  STELLA_USES_PER_READ = 5e3;
@@ -37627,7 +38150,7 @@ var init_cloud_link = __esm({
37627
38150
  });
37628
38151
 
37629
38152
  // ../../packages/tacho/src/collector/local-servers/digest.ts
37630
- import { join as join27 } from "node:path";
38153
+ import { join as join28 } from "node:path";
37631
38154
  function objectOf(value) {
37632
38155
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
37633
38156
  }
@@ -37652,7 +38175,7 @@ function npmNameSegment(name) {
37652
38175
  return name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : encodeURIComponent(name);
37653
38176
  }
37654
38177
  async function whichOnPath(command2, pathValue, search) {
37655
- const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join27(dir, command2));
38178
+ const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join28(dir, command2));
37656
38179
  for (const candidate of candidates) {
37657
38180
  for (const extension of search.extensions) {
37658
38181
  if (await search.isExecutable(candidate + extension)) return candidate + extension;
@@ -38498,7 +39021,7 @@ var init_machine = __esm({
38498
39021
  });
38499
39022
 
38500
39023
  // ../../packages/tacho/src/collector/push-basis.ts
38501
- import { resolve as resolve11 } from "node:path";
39024
+ import { isAbsolute as isAbsolute8, relative as relative4, resolve as resolve11 } from "node:path";
38502
39025
  function gitPushTarget(command2) {
38503
39026
  const tokens = tokenizeSimpleCommand(command2);
38504
39027
  if (tokens === void 0 || tokens[0] !== "git") return void 0;
@@ -38538,6 +39061,8 @@ function gitPushTarget(command2) {
38538
39061
  return { chdir, ...remote !== void 0 ? { remote } : {} };
38539
39062
  }
38540
39063
  async function pushCredentialBasis(command2, cwd, deps) {
39064
+ if (deps.containedRepository !== void 0)
39065
+ return containedPushBasis(command2, cwd, deps.containedRepository, deps);
38541
39066
  const receipts = deps.receipts();
38542
39067
  if (cwd === void 0 || receipts.length === 0)
38543
39068
  return TACHO_CREDENTIAL_HARNESS_HELD;
@@ -38550,6 +39075,25 @@ async function pushCredentialBasis(command2, cwd, deps) {
38550
39075
  const urls = await pushUrls(dir, remote, deps);
38551
39076
  return urls.length > 0 && urls.every((url) => url === receipt.url) ? TACHO_CREDENTIAL_GATEWAY_BROKERED : TACHO_CREDENTIAL_HARNESS_HELD;
38552
39077
  }
39078
+ function githubRepositoryOf(url) {
39079
+ const match = /^https:\/\/github\.com\/([A-Za-z0-9-]+\/[A-Za-z0-9._-]+?)(?:\.git)?$/.exec(
39080
+ url
39081
+ );
39082
+ return match?.[1]?.toLowerCase();
39083
+ }
39084
+ async function containedPushBasis(command2, cwd, repository, deps) {
39085
+ const target2 = gitPushTarget(command2);
39086
+ if (cwd === void 0 || target2 === void 0)
39087
+ return TACHO_CREDENTIAL_HARNESS_HELD;
39088
+ const dir = target2.chdir.reduce((from, to) => resolve11(from, to), cwd);
39089
+ const inside2 = relative4(cwd, dir);
39090
+ if (inside2.startsWith("..") || isAbsolute8(inside2))
39091
+ return TACHO_CREDENTIAL_HARNESS_HELD;
39092
+ const remote = target2.remote ?? await defaultPushRemote(dir, deps);
39093
+ const urls = await pushUrls(dir, remote, deps);
39094
+ const wanted = repository.toLowerCase();
39095
+ return urls.length > 0 && urls.every((url) => githubRepositoryOf(url) === wanted) ? TACHO_CREDENTIAL_GATEWAY_BROKERED : TACHO_CREDENTIAL_HARNESS_HELD;
39096
+ }
38553
39097
  async function gitOut(dir, args, deps) {
38554
39098
  const result2 = await deps.execAsync("git", ["-C", dir, ...args]);
38555
39099
  return result2.status === 0 ? result2.stdout.trim() : void 0;
@@ -38625,7 +39169,7 @@ var init_codec = __esm({
38625
39169
  import { createHash as createHash12 } from "node:crypto";
38626
39170
  import { mkdir as mkdir2, readdir as readdir2, readFile as readFile2, rm as rm2, writeFile } from "node:fs/promises";
38627
39171
  import { homedir as homedir5 } from "node:os";
38628
- import { dirname as dirname17, join as join28 } from "node:path";
39172
+ import { dirname as dirname17, join as join29 } from "node:path";
38629
39173
  function envDir(env, key) {
38630
39174
  const value = env[key];
38631
39175
  return value === void 0 || value.trim() === "" ? null : value;
@@ -38633,13 +39177,13 @@ function envDir(env, key) {
38633
39177
  function skillsRoot(harness, env = process.env, home = homedir5()) {
38634
39178
  switch (harness) {
38635
39179
  case "claude-code":
38636
- return join28(envDir(env, "CLAUDE_CONFIG_DIR") ?? join28(home, ".claude"), "skills");
39180
+ return join29(envDir(env, "CLAUDE_CONFIG_DIR") ?? join29(home, ".claude"), "skills");
38637
39181
  case "codex": {
38638
39182
  const codexHome = envDir(env, "CODEX_HOME");
38639
- return codexHome === null ? join28(home, ".agents", "skills") : join28(codexHome, "skills");
39183
+ return codexHome === null ? join29(home, ".agents", "skills") : join29(codexHome, "skills");
38640
39184
  }
38641
39185
  case "stella":
38642
- return join28(envDir(env, "STELLA_HOME") ?? join28(home, ".stella"), "skills");
39186
+ return join29(envDir(env, "STELLA_HOME") ?? join29(home, ".stella"), "skills");
38643
39187
  case "cursor":
38644
39188
  case "claude-desktop":
38645
39189
  return null;
@@ -38692,7 +39236,7 @@ async function readMarker(folder) {
38692
39236
  }
38693
39237
  if (!entries2.includes(SESSION_MARKER)) return { state: "foreign" };
38694
39238
  try {
38695
- const parsed = JSON.parse(await readFile2(join28(folder, SESSION_MARKER), "utf8"));
39239
+ const parsed = JSON.parse(await readFile2(join29(folder, SESSION_MARKER), "utf8"));
38696
39240
  return isMarker(parsed) ? { state: "ours", marker: parsed } : { state: "foreign" };
38697
39241
  } catch {
38698
39242
  return { state: "foreign" };
@@ -38707,15 +39251,15 @@ function liveSessions(marker, now) {
38707
39251
  return live;
38708
39252
  }
38709
39253
  async function writeMarker(folder, marker) {
38710
- await writeFile(join28(folder, SESSION_MARKER), `${JSON.stringify(marker, null, 2)}
39254
+ await writeFile(join29(folder, SESSION_MARKER), `${JSON.stringify(marker, null, 2)}
38711
39255
  `);
38712
39256
  }
38713
39257
  async function writeSkill(folder, skill) {
38714
39258
  await rm2(folder, { recursive: true, force: true });
38715
39259
  await mkdir2(folder, { recursive: true });
38716
- await writeFile(join28(folder, "SKILL.md"), skillMarkdown(skill));
39260
+ await writeFile(join29(folder, "SKILL.md"), skillMarkdown(skill));
38717
39261
  for (const file of skill.files) {
38718
- const target2 = join28(folder, file.path);
39262
+ const target2 = join29(folder, file.path);
38719
39263
  await mkdir2(dirname17(target2), { recursive: true });
38720
39264
  await writeFile(target2, file.content);
38721
39265
  }
@@ -38743,7 +39287,7 @@ async function placeSkills(root, sessionId, skills, now = /* @__PURE__ */ new Da
38743
39287
  result2.warnings.push(refusal);
38744
39288
  continue;
38745
39289
  }
38746
- const folder = join28(root, skill.name);
39290
+ const folder = join29(root, skill.name);
38747
39291
  if (taken.has(skill.name)) {
38748
39292
  result2.skipped.push(skill.name);
38749
39293
  result2.warnings.push(
@@ -38801,7 +39345,7 @@ async function removeSkills(root, sessionId, now = /* @__PURE__ */ new Date()) {
38801
39345
  throw error;
38802
39346
  }
38803
39347
  for (const name of names.sort(compareText)) {
38804
- const folder = join28(root, name);
39348
+ const folder = join29(root, name);
38805
39349
  const found = await readMarker(folder);
38806
39350
  if (found.state !== "ours") continue;
38807
39351
  const sessions = liveSessions(found.marker, now);
@@ -40041,10 +40585,10 @@ var init_model_usage = __esm({
40041
40585
  // ../../packages/tacho/src/collector/model-proxy.ts
40042
40586
  import { createHash as createHash14 } from "node:crypto";
40043
40587
  import {
40044
- request as httpRequest2,
40588
+ request as httpRequest,
40045
40589
  Agent as HttpAgent
40046
40590
  } from "node:http";
40047
- import { Agent as HttpsAgent, request as httpsRequest2 } from "node:https";
40591
+ import { Agent as HttpsAgent, request as httpsRequest } from "node:https";
40048
40592
  import {
40049
40593
  gunzipSync,
40050
40594
  brotliDecompressSync,
@@ -40226,7 +40770,7 @@ function createModelProxy(deps) {
40226
40770
  for (const call of calls) held += call.reserved;
40227
40771
  return held;
40228
40772
  }
40229
- function ceilingFor(route, model, requestBytes, json2, hasSession) {
40773
+ function ceilingFor2(route, model, requestBytes, json2, hasSession) {
40230
40774
  const { budget, model_prices: prices } = deps.policy().bundle;
40231
40775
  if (budget.mode !== "enforced") return 0;
40232
40776
  const limited = hasSession && budget.session_limit_usd !== void 0 || budget.daily_limit_usd !== void 0;
@@ -40593,7 +41137,7 @@ function createModelProxy(deps) {
40593
41137
  resolve19(outcome);
40594
41138
  };
40595
41139
  try {
40596
- upstream = (secure ? httpsRequest2 : httpRequest2)({
41140
+ upstream = (secure ? httpsRequest : httpRequest)({
40597
41141
  protocol: target2.protocol,
40598
41142
  hostname: target2.hostname,
40599
41143
  port: target2.port.length > 0 ? Number(target2.port) : secure ? 443 : 80,
@@ -40929,7 +41473,7 @@ function createModelProxy(deps) {
40929
41473
  let abortReason;
40930
41474
  let upstreamReq;
40931
41475
  const entry2 = {
40932
- reserved: metered ? ceilingFor(
41476
+ reserved: metered ? ceilingFor2(
40933
41477
  route,
40934
41478
  askedModel,
40935
41479
  requestTextBytes,
@@ -41358,7 +41902,7 @@ function createModelProxy(deps) {
41358
41902
  );
41359
41903
  };
41360
41904
  const openUpstream = (fresh) => {
41361
- const created = (secure ? httpsRequest2 : httpRequest2)({
41905
+ const created = (secure ? httpsRequest : httpRequest)({
41362
41906
  protocol: target2.protocol,
41363
41907
  hostname: target2.hostname,
41364
41908
  port: target2.port.length > 0 ? Number(target2.port) : secure ? 443 : 80,
@@ -42066,7 +42610,7 @@ var init_server = __esm({
42066
42610
 
42067
42611
  // ../../packages/tacho/src/collector/spool.ts
42068
42612
  import { existsSync as existsSync19, readFileSync as readFileSync16 } from "node:fs";
42069
- import { join as join29 } from "node:path";
42613
+ import { join as join30 } from "node:path";
42070
42614
  function sessionOwnedElsewhere(error) {
42071
42615
  return error.status === 403 && error.body.includes(SESSION_OWNED_ELSEWHERE);
42072
42616
  }
@@ -42226,7 +42770,7 @@ var init_spool = __esm({
42226
42770
  * gone for good.
42227
42771
  */
42228
42772
  stash(event, reason3, body2) {
42229
- const path2 = join29(
42773
+ const path2 = join30(
42230
42774
  this.options.quarantineDir,
42231
42775
  `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`
42232
42776
  );
@@ -42244,7 +42788,7 @@ var init_spool = __esm({
42244
42788
  }
42245
42789
  /** Add a body to a quarantine record that was written without one. */
42246
42790
  stashBodyBeside(event, body2) {
42247
- const path2 = join29(
42791
+ const path2 = join30(
42248
42792
  this.options.quarantineDir,
42249
42793
  `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`
42250
42794
  );
@@ -42373,7 +42917,7 @@ var init_spool = __esm({
42373
42917
  if (event !== void 0) withdrawn.push(event);
42374
42918
  }
42375
42919
  if (retention.proven && withdrawn.length > 0) {
42376
- const dropped = this.options.wal.dropBodies(withdrawn);
42920
+ const dropped = await this.options.wal.dropBodiesAsync(withdrawn);
42377
42921
  if (dropped > 0)
42378
42922
  this.options.log(
42379
42923
  `retention: dropped ${String(dropped)} body(ies) the mandate no longer covers`
@@ -42653,13 +43197,13 @@ var init_spool = __esm({
42653
43197
 
42654
43198
  // ../../packages/tacho/src/collector/transcript-tailer.ts
42655
43199
  import { promises as fs2 } from "node:fs";
42656
- import { basename as basename6, join as join30 } from "node:path";
43200
+ import { basename as basename6, join as join31 } from "node:path";
42657
43201
  function hasTranscriptNormalizer(session) {
42658
43202
  return session.harness === void 0 || TRANSCRIPT_NORMALIZED_HARNESSES.has(session.harness);
42659
43203
  }
42660
43204
  function subagentDirOf(transcriptPath) {
42661
43205
  if (!transcriptPath.endsWith(".jsonl")) return void 0;
42662
- return join30(transcriptPath.slice(0, -".jsonl".length), "subagents");
43206
+ return join31(transcriptPath.slice(0, -".jsonl".length), "subagents");
42663
43207
  }
42664
43208
  function subagentIdOf(path2) {
42665
43209
  return /^agent-(.+)\.jsonl$/.exec(basename6(path2))?.[1];
@@ -43179,11 +43723,11 @@ var init_transcript_tailer = __esm({
43179
43723
  * those were 3,634 of 5,059 subagent transcripts.
43180
43724
  */
43181
43725
  async subagentTranscripts(dir) {
43182
- const paths = (await this.listDir(dir)).map((name) => join30(dir, name));
43183
- const workflows = join30(dir, "workflows");
43726
+ const paths = (await this.listDir(dir)).map((name) => join31(dir, name));
43727
+ const workflows = join31(dir, "workflows");
43184
43728
  for (const workflow of await this.listDir(workflows)) {
43185
- const at = join30(workflows, workflow);
43186
- for (const name of await this.listDir(at)) paths.push(join30(at, name));
43729
+ const at = join31(workflows, workflow);
43730
+ for (const name of await this.listDir(at)) paths.push(join31(at, name));
43187
43731
  }
43188
43732
  return paths.filter((path2) => subagentIdOf(path2) !== void 0).sort();
43189
43733
  }
@@ -43468,7 +44012,7 @@ import {
43468
44012
  } from "node:fs";
43469
44013
  import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "node:fs/promises";
43470
44014
  import { homedir as homedir7, hostname as osHostname2 } from "node:os";
43471
- import { dirname as dirname18, join as join31 } from "node:path";
44015
+ import { dirname as dirname18, join as join32 } from "node:path";
43472
44016
  import { execFile as execFile3, spawnSync as spawnSync5 } from "node:child_process";
43473
44017
  function defaultExec(command2, args) {
43474
44018
  const result2 = spawnSync5(command2, args, {
@@ -43984,6 +44528,7 @@ async function initializeDaemon(options, server, port, ready) {
43984
44528
  };
43985
44529
  }
43986
44530
  let launchedContained = () => false;
44531
+ let containedGitHubRepository = () => void 0;
43987
44532
  function policy() {
43988
44533
  const current = host;
43989
44534
  return {
@@ -43996,7 +44541,7 @@ async function initializeDaemon(options, server, port, ready) {
43996
44541
  controlReachable: lastControlAt !== void 0 && now() - lastControlAt < 2 * timers.bundleRefreshMs
43997
44542
  };
43998
44543
  }
43999
- const bodyPurgeOwedPath = join31(paths.wal, "body-purge-owed");
44544
+ const bodyPurgeOwedPath = join32(paths.wal, "body-purge-owed");
44000
44545
  function markBodyPurgeOwed(retention, dropped) {
44001
44546
  try {
44002
44547
  ensureDir(paths.wal);
@@ -44646,7 +45191,7 @@ async function initializeDaemon(options, server, port, ready) {
44646
45191
  let sessionId;
44647
45192
  try {
44648
45193
  const file = JSON.parse(
44649
- readFileSync17(join31(paths.spool, name), "utf8")
45194
+ readFileSync17(join32(paths.spool, name), "utf8")
44650
45195
  );
44651
45196
  const named = file.payload?.session_id;
44652
45197
  sessionId = typeof named === "string" ? named : void 0;
@@ -44696,10 +45241,14 @@ async function initializeDaemon(options, server, port, ready) {
44696
45241
  now,
44697
45242
  // Both reads hold this hook's session queue, and both answer no
44698
45243
  // answer once `stop` begins (`hookExecAsync`).
44699
- pushCredentialBasis: (command2, cwd) => pushCredentialBasis(command2, cwd, {
44700
- receipts: () => readHostFile(paths.hostFile)?.github_repositories ?? [],
44701
- execAsync: hookExecAsync
44702
- }),
45244
+ pushCredentialBasis: (command2, cwd, harnessSessionId) => {
45245
+ const containedRepository = containedGitHubRepository(harnessSessionId);
45246
+ return pushCredentialBasis(command2, cwd, {
45247
+ receipts: () => readHostFile(paths.hostFile)?.github_repositories ?? [],
45248
+ ...containedRepository !== void 0 ? { containedRepository } : {},
45249
+ execAsync: hookExecAsync
45250
+ });
45251
+ },
44703
45252
  repositoryRemote: (cwd) => readRepositoryRemote(hookExecAsync, cwd),
44704
45253
  cedar: loadCedarRuntime,
44705
45254
  skills,
@@ -44786,7 +45335,7 @@ async function initializeDaemon(options, server, port, ready) {
44786
45335
  function setAsideConflictingTerminal(uuid, pending, conflict) {
44787
45336
  const name = `${uuid}-terminal-conflict-${String(conflict.seq).padStart(8, "0")}.json`;
44788
45337
  writeSensitiveFileAtomic(
44789
- join31(paths.quarantine, name),
45338
+ join32(paths.quarantine, name),
44790
45339
  JSON.stringify(
44791
45340
  {
44792
45341
  reason: conflict.message,
@@ -44865,8 +45414,8 @@ async function initializeDaemon(options, server, port, ready) {
44865
45414
  function moveToFailedSpool(path2, name, reason3) {
44866
45415
  log(`spool file ${name} moved to spool/failed/: ${reason3}`);
44867
45416
  try {
44868
- ensureDir(join31(paths.spool, "failed"));
44869
- renameSync5(path2, join31(paths.spool, "failed", name));
45417
+ ensureDir(join32(paths.spool, "failed"));
45418
+ renameSync5(path2, join32(paths.spool, "failed", name));
44870
45419
  } catch (error) {
44871
45420
  log(
44872
45421
  `spool file ${name} could not be moved to spool/failed/: ${error instanceof Error ? error.message : String(error)}`
@@ -44915,7 +45464,7 @@ async function initializeDaemon(options, server, port, ready) {
44915
45464
  continue;
44916
45465
  }
44917
45466
  reads += 1;
44918
- const path2 = join31(paths.spool, name);
45467
+ const path2 = join32(paths.spool, name);
44919
45468
  let file;
44920
45469
  try {
44921
45470
  file = JSON.parse(readFileSync17(path2, "utf8"));
@@ -45128,7 +45677,7 @@ async function initializeDaemon(options, server, port, ready) {
45128
45677
  let requested;
45129
45678
  try {
45130
45679
  const file = JSON.parse(
45131
- await readFile3(join31(paths.spool, name), "utf8")
45680
+ await readFile3(join32(paths.spool, name), "utf8")
45132
45681
  );
45133
45682
  const payload = file.payload;
45134
45683
  requested = payload?.hook_event_name === "PreToolUse" && typeof payload.tool_use_id === "string" ? payload.tool_use_id : void 0;
@@ -45311,6 +45860,23 @@ async function initializeDaemon(options, server, port, ready) {
45311
45860
  port: options.modelProxyPort ?? (options.port === 0 ? 0 : modelProxyPortFor(host)),
45312
45861
  log
45313
45862
  });
45863
+ const githubProxy = createGithubProxy({
45864
+ host: () => {
45865
+ const current = readHostFile(paths.hostFile);
45866
+ return {
45867
+ ...host,
45868
+ github_broker_enabled: current?.github_broker_enabled === true,
45869
+ github_repositories: current?.github_repositories ?? []
45870
+ };
45871
+ },
45872
+ policy,
45873
+ refreshBundle,
45874
+ registry: registry2,
45875
+ controlFetch: options.fetch ?? globalThis.fetch,
45876
+ now,
45877
+ record,
45878
+ log
45879
+ });
45314
45880
  const contained = createContainedRunner({
45315
45881
  host: () => host,
45316
45882
  registry: registry2,
@@ -45341,10 +45907,12 @@ async function initializeDaemon(options, server, port, ready) {
45341
45907
  log
45342
45908
  }
45343
45909
  ),
45910
+ github: githubProxy,
45344
45911
  fetch: options.fetch ?? ((input, init) => fetch(input, init)),
45345
45912
  log
45346
45913
  });
45347
45914
  launchedContained = contained.launched;
45915
+ containedGitHubRepository = contained.githubRepository;
45348
45916
  function interruptModelCalls(commands) {
45349
45917
  for (const command2 of commands) {
45350
45918
  const cuts = command2.command === "pause" || command2.command === "cancel" || command2.command === "kill" || command2.command === "steer" && command2.delivery_mode === "interrupt";
@@ -45409,23 +45977,6 @@ async function initializeDaemon(options, server, port, ready) {
45409
45977
  for (const refusal of session.recorder.takeOtelRefusals())
45410
45978
  log(`OTel record not sealed for session ${sessionId}: ${refusal}`);
45411
45979
  }
45412
- const githubProxy = createGithubProxy({
45413
- host: () => {
45414
- const current = readHostFile(paths.hostFile);
45415
- return {
45416
- ...host,
45417
- github_broker_enabled: current?.github_broker_enabled === true,
45418
- github_repositories: current?.github_repositories ?? []
45419
- };
45420
- },
45421
- policy,
45422
- refreshBundle,
45423
- registry: registry2,
45424
- controlFetch: options.fetch ?? globalThis.fetch,
45425
- now,
45426
- record,
45427
- log
45428
- });
45429
45980
  const api = {
45430
45981
  runContained: contained.run,
45431
45982
  githubLease: (input) => githubProxy.issue(input),
@@ -45648,7 +46199,7 @@ async function initializeDaemon(options, server, port, ready) {
45648
46199
  let removed = 0;
45649
46200
  for (const name of readdirSync11(paths.quarantine)) {
45650
46201
  if (!name.endsWith(".json")) continue;
45651
- const file = join31(paths.quarantine, name);
46202
+ const file = join32(paths.quarantine, name);
45652
46203
  try {
45653
46204
  if (at - statSync9(file).mtimeMs < retainMs) continue;
45654
46205
  unlinkSync12(file);
@@ -45665,6 +46216,42 @@ async function initializeDaemon(options, server, port, ready) {
45665
46216
  let lastCompact = 0;
45666
46217
  const COMPACT_EVERY_MS = 60 * 6e4;
45667
46218
  const COMPACT_RETRY_MS = 6e4;
46219
+ const walCeiling = new WalCeiling(
46220
+ wal,
46221
+ paths.wal,
46222
+ { ...DEFAULT_WAL_CEILING, ...options.walCeiling },
46223
+ timers.walRetainMs,
46224
+ log
46225
+ );
46226
+ function recordCeilingDrop(drop) {
46227
+ const mark = hostRecorder.markChain();
46228
+ try {
46229
+ record([
46230
+ hostRecorder.sealCollectorEvent("telemetry_gap", {
46231
+ gap_cause: "wal_ceiling",
46232
+ // How long the session went without shipping, capped at what the
46233
+ // column holds.
46234
+ gap_duration_ms: Math.min(
46235
+ Math.max(
46236
+ 0,
46237
+ Date.parse(drop.dropped_at) - Date.parse(drop.stalled_since)
46238
+ ),
46239
+ 4294967295
46240
+ ),
46241
+ incident_evidence: { ...drop }
46242
+ })
46243
+ ]);
46244
+ } catch (error) {
46245
+ hostRecorder.rollbackChain(mark);
46246
+ log(
46247
+ `WAL ceiling: removed the stored bodies of session ${drop.session_uuid}, but could not record it on the daemon chain: ${error instanceof Error ? error.message : String(error)}`
46248
+ );
46249
+ return;
46250
+ }
46251
+ log(
46252
+ `WAL ceiling: removed ${drop.bytes} bytes of stored bodies from session ${drop.session_uuid}, which has shipped nothing since ${drop.stalled_since}; events ${drop.shipped_through + 1} to ${drop.last_seq} ship without their bodies`
46253
+ );
46254
+ }
45668
46255
  retryOwedBodyPurge();
45669
46256
  let gitLane;
45670
46257
  function startGitReads() {
@@ -45811,6 +46398,11 @@ async function initializeDaemon(options, server, port, ready) {
45811
46398
  });
45812
46399
  await stage("acks", () => sendAcks());
45813
46400
  void startGitReads();
46401
+ await stage("wal ceiling", () => {
46402
+ if (!walCeiling.due(now())) return;
46403
+ if (queues.busy(hostRecord.harnessSessionId)) return;
46404
+ walCeiling.check(now(), recordCeilingDrop);
46405
+ });
45814
46406
  await stage("compact", () => {
45815
46407
  if (now() - lastCompact < COMPACT_EVERY_MS) return;
45816
46408
  lastCompact = now();
@@ -45904,7 +46496,7 @@ async function initializeDaemon(options, server, port, ready) {
45904
46496
  let memoryTimer;
45905
46497
  if (options.listen ?? true) {
45906
46498
  const codexMemories = createCodexMemoryStore({
45907
- path: join31(dirname18(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
46499
+ path: join32(dirname18(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
45908
46500
  stat: (path2) => stat2(path2),
45909
46501
  log
45910
46502
  });
@@ -46045,6 +46637,7 @@ var init_daemon = __esm({
46045
46637
  init_process_scan();
46046
46638
  init_retention();
46047
46639
  init_wal();
46640
+ init_wal_ceiling();
46048
46641
  init_work_orders();
46049
46642
  init_ids();
46050
46643
  init_timestamp();
@@ -46467,7 +47060,7 @@ var init_schema = __esm({
46467
47060
  import {
46468
47061
  closeSync as closeSync7,
46469
47062
  constants as constants3,
46470
- fstatSync,
47063
+ fstatSync as fstatSync2,
46471
47064
  lstatSync as lstatSync6,
46472
47065
  mkdirSync as mkdirSync7,
46473
47066
  openSync as openSync7,
@@ -46475,8 +47068,8 @@ import {
46475
47068
  realpathSync as realpathSync4,
46476
47069
  writeFileSync as writeFileSync5
46477
47070
  } from "node:fs";
46478
- import { dirname as dirname19, join as join32, resolve as resolve12 } from "node:path";
46479
- function bundlePath(root, relative8, create2 = false) {
47071
+ import { dirname as dirname19, join as join33, resolve as resolve12 } from "node:path";
47072
+ function bundlePath(root, relative9, create2 = false) {
46480
47073
  const absolute = resolve12(root);
46481
47074
  if (create2) mkdirSync7(absolute, { recursive: true, mode: 448 });
46482
47075
  if (lstatSync6(absolute).isSymbolicLink() || !lstatSync6(absolute).isDirectory()) {
@@ -46484,9 +47077,9 @@ function bundlePath(root, relative8, create2 = false) {
46484
47077
  }
46485
47078
  const base2 = realpathSync4(absolute);
46486
47079
  let current = base2;
46487
- const parts = relative8.split("/");
47080
+ const parts = relative9.split("/");
46488
47081
  for (let i2 = 0; i2 < parts.length; i2++) {
46489
- current = join32(current, parts[i2]);
47082
+ current = join33(current, parts[i2]);
46490
47083
  try {
46491
47084
  const stat5 = lstatSync6(current);
46492
47085
  if (stat5.isSymbolicLink() || i2 < parts.length - 1 && !stat5.isDirectory()) {
@@ -46503,11 +47096,11 @@ function bundlePath(root, relative8, create2 = false) {
46503
47096
  }
46504
47097
  return current;
46505
47098
  }
46506
- function readBounded(root, relative8, limit = ARP_MAX_BLOB_BYTES) {
46507
- const path2 = bundlePath(root, relative8);
47099
+ function readBounded(root, relative9, limit = ARP_MAX_BLOB_BYTES) {
47100
+ const path2 = bundlePath(root, relative9);
46508
47101
  const fd2 = openSync7(path2, constants3.O_RDONLY | constants3.O_NOFOLLOW);
46509
47102
  try {
46510
- const stat5 = fstatSync(fd2);
47103
+ const stat5 = fstatSync2(fd2);
46511
47104
  if (!stat5.isFile() || stat5.size > limit)
46512
47105
  throw new Error(
46513
47106
  "ARP file exceeds its size limit or is not a regular file"
@@ -46526,8 +47119,8 @@ function readBounded(root, relative8, limit = ARP_MAX_BLOB_BYTES) {
46526
47119
  closeSync7(fd2);
46527
47120
  }
46528
47121
  }
46529
- function writeNew(root, relative8, bytes) {
46530
- const path2 = bundlePath(root, relative8, true);
47122
+ function writeNew(root, relative9, bytes) {
47123
+ const path2 = bundlePath(root, relative9, true);
46531
47124
  const fd2 = openSync7(
46532
47125
  path2,
46533
47126
  constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW,
@@ -47356,7 +47949,7 @@ import { randomUUID as randomUUID4 } from "node:crypto";
47356
47949
  import {
47357
47950
  closeSync as closeSync8,
47358
47951
  constants as constants4,
47359
- fstatSync as fstatSync2,
47952
+ fstatSync as fstatSync3,
47360
47953
  lstatSync as lstatSync7,
47361
47954
  mkdirSync as mkdirSync8,
47362
47955
  openSync as openSync8,
@@ -47365,14 +47958,14 @@ import {
47365
47958
  rmSync as rmSync7,
47366
47959
  writeFileSync as writeFileSync6
47367
47960
  } from "node:fs";
47368
- import { dirname as dirname20, isAbsolute as isAbsolute8, join as join33, relative as relative4, resolve as resolve13, sep as sep3 } from "node:path";
47961
+ import { dirname as dirname20, isAbsolute as isAbsolute9, join as join34, relative as relative5, resolve as resolve13, sep as sep3 } from "node:path";
47369
47962
  function fail2(message) {
47370
47963
  throw new Error(`ARP: ${message}`);
47371
47964
  }
47372
47965
  function readRegular(path2, limit) {
47373
47966
  const fd2 = openSync8(path2, constants4.O_RDONLY | constants4.O_NOFOLLOW);
47374
47967
  try {
47375
- const before = fstatSync2(fd2);
47968
+ const before = fstatSync3(fd2);
47376
47969
  if (!before.isFile() || before.size > limit)
47377
47970
  fail2("Input is not a bounded regular file.");
47378
47971
  const bytes = Buffer.alloc(before.size);
@@ -47384,7 +47977,7 @@ function readRegular(path2, limit) {
47384
47977
  }
47385
47978
  if (readSync5(fd2, Buffer.alloc(1), 0, 1, offset) !== 0)
47386
47979
  fail2("Input grew while it was read.");
47387
- const after = fstatSync2(fd2);
47980
+ const after = fstatSync3(fd2);
47388
47981
  if (bytes.length > limit || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) {
47389
47982
  fail2(
47390
47983
  "Input changed while it was read. Stop its writer and capture again."
@@ -47403,12 +47996,12 @@ function rejectSecrets(bytes) {
47403
47996
  }
47404
47997
  }
47405
47998
  function within2(root, path2) {
47406
- const rel = relative4(root, path2);
47407
- return rel === "" || !isAbsolute8(rel) && rel !== ".." && !rel.startsWith(`..${sep3}`);
47999
+ const rel = relative5(root, path2);
48000
+ return rel === "" || !isAbsolute9(rel) && rel !== ".." && !rel.startsWith(`..${sep3}`);
47408
48001
  }
47409
48002
  function newPath(path2) {
47410
48003
  const full = resolve13(path2);
47411
- return join33(
48004
+ return join34(
47412
48005
  realpathSync5(dirname20(full)),
47413
48006
  full.slice(dirname20(full).length + 1)
47414
48007
  );
@@ -47424,7 +48017,7 @@ function sourcePath(root, path2) {
47424
48017
  }
47425
48018
  let current = root;
47426
48019
  for (const part2 of path2.split("/")) {
47427
- current = join33(current, part2);
48020
+ current = join34(current, part2);
47428
48021
  if (lstatSync7(current).isSymbolicLink())
47429
48022
  fail2("Symlinks require a resource adapter.");
47430
48023
  }
@@ -47677,10 +48270,10 @@ function prepareCheckpoint(options) {
47677
48270
  });
47678
48271
  mkdirSync8(destination, { mode: 448 });
47679
48272
  try {
47680
- const restored = join33(destination, "workspace");
48273
+ const restored = join34(destination, "workspace");
47681
48274
  mkdirSync8(restored, { mode: 448 });
47682
48275
  for (const { entry: entry2, bytes } of files2) {
47683
- const path2 = join33(restored, entry2.path);
48276
+ const path2 = join34(restored, entry2.path);
47684
48277
  if (entry2.kind === "directory")
47685
48278
  mkdirSync8(path2, { recursive: true, mode: 448 });
47686
48279
  else {
@@ -47692,9 +48285,9 @@ function prepareCheckpoint(options) {
47692
48285
  });
47693
48286
  }
47694
48287
  }
47695
- const promptDir = join33(restored, ".arp");
48288
+ const promptDir = join34(restored, ".arp");
47696
48289
  mkdirSync8(promptDir, { mode: 448 });
47697
- const promptFile = join33(promptDir, "handoff.json");
48290
+ const promptFile = join34(promptDir, "handoff.json");
47698
48291
  const prompt = {
47699
48292
  checkpoint_digest: checkpointDigest,
47700
48293
  task: task.request,
@@ -47711,7 +48304,7 @@ function prepareCheckpoint(options) {
47711
48304
  flag: "wx",
47712
48305
  mode: 384
47713
48306
  });
47714
- const reportPath = join33(destination, "report.json");
48307
+ const reportPath = join34(destination, "report.json");
47715
48308
  writeFileSync6(
47716
48309
  reportPath,
47717
48310
  jcs({
@@ -48128,7 +48721,7 @@ function buildTachoProgram(options = {}) {
48128
48721
  "The repository root to mount at /workspace (default: the current directory)"
48129
48722
  ).option(
48130
48723
  "--github-repository <owner/name>",
48131
- "The one repository the run may reach, with a token in OXAGEN_CONTAINED_GITHUB_TOKEN"
48724
+ "The one repository the run may fetch and push, through Oxagen's Git custody. The workspace must have it bound."
48132
48725
  ).argument("<agent>", "claude or codex").argument("[args...]", "Arguments for the agent, after --").action(
48133
48726
  async (agent, args, opts) => {
48134
48727
  const controller = new AbortController();
@@ -48235,7 +48828,7 @@ var init_package = __esm({
48235
48828
  "package.json"() {
48236
48829
  package_default = {
48237
48830
  name: "@oxagen/cli",
48238
- version: "2.1.4-383",
48831
+ version: "2.1.4-391",
48239
48832
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
48240
48833
  type: "module",
48241
48834
  main: "./dist/index.js",
@@ -48320,10 +48913,10 @@ var init_retired = __esm({
48320
48913
 
48321
48914
  // src/lib/atomic-write.ts
48322
48915
  import { writeFileSync as writeFileSync7, renameSync as renameSync6, unlinkSync as unlinkSync14, existsSync as existsSync21 } from "node:fs";
48323
- import { dirname as dirname21, join as join34 } from "node:path";
48916
+ import { dirname as dirname21, join as join35 } from "node:path";
48324
48917
  function atomicWriteFileSync(path2, data) {
48325
48918
  const dir = dirname21(path2);
48326
- const tmp = join34(
48919
+ const tmp = join35(
48327
48920
  dir,
48328
48921
  `.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`
48329
48922
  );
@@ -48345,7 +48938,7 @@ var init_atomic_write = __esm({
48345
48938
 
48346
48939
  // src/lib/config.ts
48347
48940
  import { homedir as homedir8 } from "os";
48348
- import { join as join35 } from "path";
48941
+ import { join as join36 } from "path";
48349
48942
  import { readFileSync as readFileSync20, mkdirSync as mkdirSync9, existsSync as existsSync22 } from "fs";
48350
48943
  function getConfigDir() {
48351
48944
  return CONFIG_DIR;
@@ -48396,8 +48989,8 @@ var CONFIG_DIR, CONFIG_FILE;
48396
48989
  var init_config = __esm({
48397
48990
  "src/lib/config.ts"() {
48398
48991
  init_atomic_write();
48399
- CONFIG_DIR = join35(homedir8(), ".config", "oxagen");
48400
- CONFIG_FILE = join35(CONFIG_DIR, "config.json");
48992
+ CONFIG_DIR = join36(homedir8(), ".config", "oxagen");
48993
+ CONFIG_FILE = join36(CONFIG_DIR, "config.json");
48401
48994
  }
48402
48995
  });
48403
48996
 
@@ -48429,16 +49022,16 @@ import {
48429
49022
  writeFile as writeFile2
48430
49023
  } from "node:fs/promises";
48431
49024
  import { homedir as homedir9 } from "node:os";
48432
- import { join as join36 } from "node:path";
49025
+ import { join as join37 } from "node:path";
48433
49026
  function isDebugEnabled() {
48434
49027
  const v = process.env[DEBUG_ENV];
48435
49028
  return v === "1" || v === "true";
48436
49029
  }
48437
49030
  function logsDir() {
48438
- return join36(homedir9(), ".oxagen", "logs");
49031
+ return join37(homedir9(), ".oxagen", "logs");
48439
49032
  }
48440
49033
  function debugLogFile() {
48441
- return join36(logsDir(), "cli.output");
49034
+ return join37(logsDir(), "cli.output");
48442
49035
  }
48443
49036
  function sanitize(value, depth = 0) {
48444
49037
  if (value === null || value === void 0) return value;
@@ -51459,9 +52052,9 @@ var init_git = __esm({
51459
52052
 
51460
52053
  // ../../packages/steering-freshness/src/cache.ts
51461
52054
  import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
51462
- import { dirname as dirname22, join as join37 } from "node:path";
52055
+ import { dirname as dirname22, join as join38 } from "node:path";
51463
52056
  function stampPath(gitCommonDir) {
51464
- return join37(gitCommonDir, "oxagen", "steering-freshness.json");
52057
+ return join38(gitCommonDir, "oxagen", "steering-freshness.json");
51465
52058
  }
51466
52059
  async function readFetchStamp(gitCommonDir, io = defaultIo) {
51467
52060
  try {
@@ -51515,24 +52108,24 @@ var init_cache = __esm({
51515
52108
  // ../../packages/steering-freshness/src/hooks.ts
51516
52109
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
51517
52110
  import { homedir as homedir10 } from "node:os";
51518
- import { dirname as dirname23, join as join38 } from "node:path";
52111
+ import { dirname as dirname23, join as join39 } from "node:path";
51519
52112
  function hookCommand(harness) {
51520
52113
  return `oxagen steering gate --harness ${harness}`;
51521
52114
  }
51522
52115
  function hookConfigPath(projectRoot, harness, env = process.env, home = homedir10()) {
51523
52116
  switch (harness) {
51524
52117
  case "claude-code":
51525
- return join38(projectRoot, ".claude", "settings.json");
52118
+ return join39(projectRoot, ".claude", "settings.json");
51526
52119
  case "codex":
51527
- return join38(env["CODEX_HOME"] ?? join38(home, ".codex"), "hooks.json");
52120
+ return join39(env["CODEX_HOME"] ?? join39(home, ".codex"), "hooks.json");
51528
52121
  case "cursor":
51529
- return join38(home, ".cursor", "hooks.json");
52122
+ return join39(home, ".cursor", "hooks.json");
51530
52123
  case "stella":
51531
- return join38(env["STELLA_HOME"] ?? join38(home, ".stella"), "stella.toml");
52124
+ return join39(env["STELLA_HOME"] ?? join39(home, ".stella"), "stella.toml");
51532
52125
  }
51533
52126
  }
51534
52127
  function stellaJsonPath(env, home) {
51535
- return join38(env["STELLA_HOME"] ?? join38(home, ".stella"), "settings.json");
52128
+ return join39(env["STELLA_HOME"] ?? join39(home, ".stella"), "settings.json");
51536
52129
  }
51537
52130
  function isRecord7(v) {
51538
52131
  return v !== null && typeof v === "object" && !Array.isArray(v);
@@ -52261,7 +52854,7 @@ var init_policy = __esm({
52261
52854
  // ../../packages/steering-freshness/src/settings.ts
52262
52855
  import { readFile as readFile8 } from "node:fs/promises";
52263
52856
  import { homedir as homedir11 } from "node:os";
52264
- import { join as join39 } from "node:path";
52857
+ import { join as join40 } from "node:path";
52265
52858
  async function readLayer(scope, path2, read2) {
52266
52859
  let text4;
52267
52860
  try {
@@ -52313,14 +52906,14 @@ function parseLayer(scope, path2, text4) {
52313
52906
  }
52314
52907
  async function loadSteeringSettings({
52315
52908
  projectRoot,
52316
- userSettingsPath = join39(homedir11(), USER_SETTINGS_RELATIVE),
52909
+ userSettingsPath = join40(homedir11(), USER_SETTINGS_RELATIVE),
52317
52910
  read: read2 = readFile8,
52318
52911
  workspacePolicy = null
52319
52912
  }) {
52320
52913
  const targets = [
52321
52914
  ["user", userSettingsPath],
52322
- ["project", join39(projectRoot, PROJECT_DIR_NAME, PROJECT_SETTINGS_FILE)],
52323
- ["local", join39(projectRoot, PROJECT_DIR_NAME, LOCAL_SETTINGS_FILE)]
52915
+ ["project", join40(projectRoot, PROJECT_DIR_NAME, PROJECT_SETTINGS_FILE)],
52916
+ ["local", join40(projectRoot, PROJECT_DIR_NAME, LOCAL_SETTINGS_FILE)]
52324
52917
  ];
52325
52918
  const layers = [];
52326
52919
  const warnings = [];
@@ -52368,7 +52961,7 @@ var init_settings = __esm({
52368
52961
  "use strict";
52369
52962
  init_git();
52370
52963
  init_policy();
52371
- USER_SETTINGS_RELATIVE = join39(
52964
+ USER_SETTINGS_RELATIVE = join40(
52372
52965
  ".config",
52373
52966
  "oxagen",
52374
52967
  "settings.json"
@@ -52381,7 +52974,7 @@ var init_settings = __esm({
52381
52974
 
52382
52975
  // ../../packages/steering-freshness/src/check.ts
52383
52976
  import { lstat as lstat3 } from "node:fs/promises";
52384
- import { isAbsolute as isAbsolute9, join as join40, resolve as resolve14 } from "node:path";
52977
+ import { isAbsolute as isAbsolute10, join as join41, resolve as resolve14 } from "node:path";
52385
52978
  function publishedCommits(platform) {
52386
52979
  if (platform.headCommits && platform.headCommits.length > 0)
52387
52980
  return platform.headCommits;
@@ -52473,7 +53066,7 @@ async function checkSteeringFreshness(opts) {
52473
53066
  );
52474
53067
  }
52475
53068
  const commonDir = allowNetwork ? await gitOrNull(repoCtx, "rev-parse", "--git-common-dir") : null;
52476
- const stampDir = commonDir ? isAbsolute9(commonDir) ? commonDir : resolve14(root, commonDir) : null;
53069
+ const stampDir = commonDir ? isAbsolute10(commonDir) ? commonDir : resolve14(root, commonDir) : null;
52477
53070
  const stamp = stampDir ? await readFetchStamp(stampDir, cacheIo) : null;
52478
53071
  const remoteContactDue = allowNetwork && shouldContactRemote(stamp, policy.fetchIntervalSeconds, now());
52479
53072
  const branch = policy.branch ?? await defaultBranch(networkCtx(repoCtx), policy.remote, {
@@ -52695,7 +53288,7 @@ async function absentFromDisk(root, paths) {
52695
53288
  const flags = await Promise.all(
52696
53289
  paths.map(async (path2) => {
52697
53290
  try {
52698
- await lstat3(join40(root, path2));
53291
+ await lstat3(join41(root, path2));
52699
53292
  return false;
52700
53293
  } catch {
52701
53294
  return true;
@@ -53348,9 +53941,9 @@ import {
53348
53941
  rmSync as rmSync8,
53349
53942
  writeFileSync as writeFileSync8
53350
53943
  } from "node:fs";
53351
- import { join as join41 } from "node:path";
53944
+ import { join as join42 } from "node:path";
53352
53945
  function workspaceLinkPath(cwd) {
53353
- return join41(cwd, ".oxagen", "workspace.json");
53946
+ return join42(cwd, ".oxagen", "workspace.json");
53354
53947
  }
53355
53948
  function readWorkspaceLink(cwd) {
53356
53949
  const filePath = workspaceLinkPath(cwd);
@@ -53367,7 +53960,7 @@ function readWorkspaceLink(cwd) {
53367
53960
  }
53368
53961
  }
53369
53962
  function writeWorkspaceLink(cwd, link) {
53370
- const dir = join41(cwd, ".oxagen");
53963
+ const dir = join42(cwd, ".oxagen");
53371
53964
  mkdirSync10(dir, { recursive: true });
53372
53965
  writeFileSync8(workspaceLinkPath(cwd), JSON.stringify(link, null, 2), "utf8");
53373
53966
  }
@@ -53393,7 +53986,7 @@ __export(steering_exports, {
53393
53986
  });
53394
53987
  import { execFileSync } from "node:child_process";
53395
53988
  import { existsSync as existsSync24, realpathSync as realpathSync6 } from "node:fs";
53396
- import { dirname as dirname24, join as join42, normalize, parse as parsePath } from "node:path";
53989
+ import { dirname as dirname24, join as join43, normalize, parse as parsePath } from "node:path";
53397
53990
  function findProjectRoot(start) {
53398
53991
  const { root } = parsePath(start);
53399
53992
  const rawGitRoot = gitRootOr(start);
@@ -53402,7 +53995,7 @@ function findProjectRoot(start) {
53402
53995
  if (gitRoot !== null) return gitRoot;
53403
53996
  let dir = from;
53404
53997
  for (; ; ) {
53405
- if (existsSync24(join42(dir, PROJECT_DIR_NAME))) return dir;
53998
+ if (existsSync24(join43(dir, PROJECT_DIR_NAME))) return dir;
53406
53999
  if (dir === root) return from;
53407
54000
  const parent = dirname24(dir);
53408
54001
  if (parent === dir) return from;
@@ -64334,7 +64927,7 @@ var init_record = __esm({
64334
64927
  external_exports.object({
64335
64928
  source: external_exports.enum(["proposal", "run", "import"]),
64336
64929
  uri: external_exports.string(),
64337
- agent: external_exports.string().optional().describe("The agent that proposed the record with steering_propose."),
64930
+ agent: external_exports.string().optional().describe("The agent that proposed the record with propose_steering."),
64338
64931
  memories: external_exports.array(provenanceMemorySchema).min(1).optional().describe(
64339
64932
  "The memories the curator cited for the record. Required when source is run."
64340
64933
  )
@@ -68748,14 +69341,14 @@ var init_memory_client = __esm({
68748
69341
 
68749
69342
  // src/lib/markdown-import.ts
68750
69343
  import { readFile as readFile9 } from "node:fs/promises";
68751
- import { basename as basename7, dirname as dirname25, isAbsolute as isAbsolute10, relative as relative5, resolve as resolve15, sep as sep4 } from "node:path";
69344
+ import { basename as basename7, dirname as dirname25, isAbsolute as isAbsolute11, relative as relative6, resolve as resolve15, sep as sep4 } from "node:path";
68752
69345
  function inside(rel) {
68753
- return rel !== "" && !rel.startsWith("..") && !isAbsolute10(rel);
69346
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute11(rel);
68754
69347
  }
68755
69348
  function importFilename(path2, folder) {
68756
- let rel = relative5(process.cwd(), path2);
69349
+ let rel = relative6(process.cwd(), path2);
68757
69350
  if (!inside(rel) && folder !== void 0) {
68758
- rel = relative5(dirname25(resolve15(folder)), resolve15(path2));
69351
+ rel = relative6(dirname25(resolve15(folder)), resolve15(path2));
68759
69352
  }
68760
69353
  const name = inside(rel) ? rel.split(sep4).join("/") : basename7(path2);
68761
69354
  return name.length > 256 ? basename7(path2).slice(-256) : name;
@@ -68973,7 +69566,7 @@ __export(steering_import_exports, {
68973
69566
  handleSteeringImport: () => handleSteeringImport
68974
69567
  });
68975
69568
  import { readdir as readdir4, realpath as realpath2, stat as stat4 } from "node:fs/promises";
68976
- import { extname, join as join43 } from "node:path";
69569
+ import { extname, join as join44 } from "node:path";
68977
69570
  function byName(a, b5) {
68978
69571
  return a.name < b5.name ? -1 : 1;
68979
69572
  }
@@ -69001,7 +69594,7 @@ async function findMarkdownFiles(paths) {
69001
69594
  }
69002
69595
  entries2.sort(byName);
69003
69596
  for (const entry2 of entries2) {
69004
- const path2 = join43(dir, entry2.name);
69597
+ const path2 = join44(dir, entry2.name);
69005
69598
  if (entry2.isDirectory()) {
69006
69599
  if (entry2.name.startsWith(".") || entry2.name === "node_modules") continue;
69007
69600
  await walk(path2, folder);
@@ -69071,6 +69664,268 @@ var init_steering_import = __esm({
69071
69664
  }
69072
69665
  });
69073
69666
 
69667
+ // src/commands/steering-findings.ts
69668
+ var steering_findings_exports = {};
69669
+ __export(steering_findings_exports, {
69670
+ PROPOSAL_STATUS_WORDS: () => PROPOSAL_STATUS_WORDS,
69671
+ STEERING_FINDINGS_CAPABILITIES: () => STEERING_FINDINGS_CAPABILITIES,
69672
+ apiRefusal: () => apiRefusal,
69673
+ reportApiFailure: () => reportApiFailure,
69674
+ steeringFindings: () => steeringFindings,
69675
+ usageError: () => usageError
69676
+ });
69677
+ function isRecord9(value) {
69678
+ return typeof value === "object" && value !== null && !Array.isArray(value);
69679
+ }
69680
+ function textOrNull(value) {
69681
+ return typeof value === "string" && value.length > 0 ? value : null;
69682
+ }
69683
+ function apiRefusal(err2) {
69684
+ const message = errorMessage(err2);
69685
+ const start = message.indexOf("{");
69686
+ const end = message.lastIndexOf("}");
69687
+ if (start >= 0 && end > start) {
69688
+ try {
69689
+ const body2 = JSON.parse(message.slice(start, end + 1));
69690
+ const error = isRecord9(body2) && isRecord9(body2.error) ? body2.error : null;
69691
+ const said = error === null ? null : textOrNull(error.message);
69692
+ if (error !== null && said !== null) {
69693
+ return {
69694
+ code: textOrNull(error.code),
69695
+ reason: textOrNull(error.reason),
69696
+ message: said
69697
+ };
69698
+ }
69699
+ } catch {
69700
+ }
69701
+ }
69702
+ return { code: null, reason: null, message };
69703
+ }
69704
+ function reportApiFailure(out, writer, err2, nextSteps = {}) {
69705
+ const refusal = apiRefusal(err2);
69706
+ out.error(refusal.message, refusal.reason ?? refusal.code ?? "api");
69707
+ if (out.isJson || refusal.reason === null) return;
69708
+ const next = nextSteps[refusal.reason];
69709
+ if (next !== void 0) writer.writeErr(next);
69710
+ }
69711
+ function usageError(writer, message, line) {
69712
+ writer.writeErr(`error: ${message}`);
69713
+ writer.writeErr(`usage: ${line}`);
69714
+ process.exitCode = 2;
69715
+ }
69716
+ function plural(count7, one, many) {
69717
+ return `${count7} ${count7 === 1 ? one : many}`;
69718
+ }
69719
+ function findingRow(finding) {
69720
+ const pr = finding.pull_request;
69721
+ const proposal = finding.proposal;
69722
+ return [
69723
+ finding.id,
69724
+ finding.kind,
69725
+ `${finding.path}:${finding.line}`,
69726
+ finding.record.label ?? finding.record.lineage,
69727
+ `#${pr.number} ${pr.state}`,
69728
+ proposal === null ? "none" : `${proposal.id} (${PROPOSAL_STATUS_WORDS[proposal.status]})`
69729
+ ];
69730
+ }
69731
+ async function steeringFindings(opts = {}, writer = stdoutWriter) {
69732
+ const out = createOutput({ json: opts.json }, writer);
69733
+ let result2;
69734
+ try {
69735
+ result2 = await apiGetOrThrow(
69736
+ "repository/findings"
69737
+ );
69738
+ } catch (err2) {
69739
+ reportApiFailure(out, writer, err2);
69740
+ return;
69741
+ }
69742
+ if (out.isJson) {
69743
+ out.data(result2);
69744
+ return;
69745
+ }
69746
+ const repositories = result2.repositories.filter((r) => r.findings.length > 0);
69747
+ if (repositories.length === 0) {
69748
+ writer.write(
69749
+ "No findings. The Oxagen check found no statement in a linked repository's instruction files that repeats or contradicts a steering record."
69750
+ );
69751
+ return;
69752
+ }
69753
+ let total2 = 0;
69754
+ for (const [index, repository] of repositories.entries()) {
69755
+ if (index > 0) writer.write("");
69756
+ writer.write(`${repository.full_name} (${repository.provider})`);
69757
+ printTable(
69758
+ ["FINDING", "KIND", "FILE", "RECORD", "PULL REQUEST", "PROPOSAL"],
69759
+ repository.findings.map(findingRow),
69760
+ writer
69761
+ );
69762
+ total2 += repository.findings.length;
69763
+ }
69764
+ writer.write("");
69765
+ writer.write(
69766
+ `${plural(total2, "finding", "findings")} in ${plural(repositories.length, "repository", "repositories")}.`
69767
+ );
69768
+ const promotable = repositories.some(
69769
+ (r) => r.findings.some((f) => f.kind === "contradiction" && f.proposal === null)
69770
+ );
69771
+ if (promotable) {
69772
+ writer.write(
69773
+ "Promote a contradiction to a proposal with `oxagen steering promote <finding-id>`."
69774
+ );
69775
+ }
69776
+ }
69777
+ var STEERING_FINDINGS_CAPABILITIES, PROPOSAL_STATUS_WORDS;
69778
+ var init_steering_findings = __esm({
69779
+ "src/commands/steering-findings.ts"() {
69780
+ init_api();
69781
+ init_capture_writer();
69782
+ init_output();
69783
+ STEERING_FINDINGS_CAPABILITIES = [
69784
+ "list_code_repository_findings"
69785
+ ];
69786
+ PROPOSAL_STATUS_WORDS = {
69787
+ proposed: "waiting for its steering PR",
69788
+ pr_open: "steering PR open",
69789
+ checks_running: "checks running",
69790
+ checks_passed: "checks passed",
69791
+ checks_failed: "checks failed",
69792
+ merged: "merged",
69793
+ rejected: "rejected"
69794
+ };
69795
+ }
69796
+ });
69797
+
69798
+ // src/commands/steering-promote.ts
69799
+ var steering_promote_exports = {};
69800
+ __export(steering_promote_exports, {
69801
+ STEERING_PROMOTE_CAPABILITIES: () => STEERING_PROMOTE_CAPABILITIES,
69802
+ steeringPromote: () => steeringPromote
69803
+ });
69804
+ async function steeringPromote(findingId, opts = {}, writer = stdoutWriter) {
69805
+ const id = findingId.trim();
69806
+ if (!FINDING_ID.test(id)) {
69807
+ usageError(
69808
+ writer,
69809
+ `expected a finding id that starts with crf_, got ${JSON.stringify(findingId)}. Run \`oxagen steering findings\` to list them.`,
69810
+ USAGE
69811
+ );
69812
+ return;
69813
+ }
69814
+ const out = createOutput({ json: opts.json }, writer);
69815
+ let result2;
69816
+ try {
69817
+ result2 = await apiPostOrThrow(
69818
+ "repository/findings/promote",
69819
+ { finding_id: id }
69820
+ );
69821
+ } catch (err2) {
69822
+ reportApiFailure(out, writer, err2, NEXT_STEPS);
69823
+ return;
69824
+ }
69825
+ if (out.isJson) {
69826
+ out.data(result2);
69827
+ return;
69828
+ }
69829
+ writer.write(`Opened proposal ${result2.proposal_id} for record ${result2.lineage}.`);
69830
+ const pr = result2.pull_request;
69831
+ writer.write(
69832
+ pr === null ? "Steering PR: none yet" : `Steering PR: #${pr.number} ${pr.url}`
69833
+ );
69834
+ writer.write(`Status: ${PROPOSAL_STATUS_WORDS[result2.status]}`);
69835
+ writer.write("Nothing steers until a person merges the steering PR.");
69836
+ }
69837
+ var STEERING_PROMOTE_CAPABILITIES, FINDING_ID, USAGE, NEXT_STEPS;
69838
+ var init_steering_promote = __esm({
69839
+ "src/commands/steering-promote.ts"() {
69840
+ init_api();
69841
+ init_capture_writer();
69842
+ init_output();
69843
+ init_steering_findings();
69844
+ STEERING_PROMOTE_CAPABILITIES = [
69845
+ "promote_instruction_to_steering"
69846
+ ];
69847
+ FINDING_ID = /^crf_[0-9A-Za-z]+$/;
69848
+ USAGE = "oxagen steering promote <finding-id> [--json]";
69849
+ NEXT_STEPS = {
69850
+ finding_resolved: "Run `oxagen steering findings` to see the statements that still match a record.",
69851
+ already_proposed: "Run `oxagen steering findings` to see the open proposal and its steering PR.",
69852
+ lineage_pr_open: "Merge or close the PR that is open on the record, then promote the finding again."
69853
+ };
69854
+ }
69855
+ });
69856
+
69857
+ // src/commands/steering-restore-block.ts
69858
+ var steering_restore_block_exports = {};
69859
+ __export(steering_restore_block_exports, {
69860
+ STEERING_RESTORE_BLOCK_CAPABILITIES: () => STEERING_RESTORE_BLOCK_CAPABILITIES,
69861
+ managedBlockFile: () => managedBlockFile,
69862
+ steeringRestoreBlock: () => steeringRestoreBlock
69863
+ });
69864
+ function managedBlockFile(path2) {
69865
+ const name = path2.trim().replace(/^\.\//, "").toLowerCase();
69866
+ const files2 = Object.keys(MANAGED_BLOCK_FILES);
69867
+ return files2.find((file) => file.toLowerCase() === name) ?? null;
69868
+ }
69869
+ async function steeringRestoreBlock(proposalId, path2, opts = {}, writer = stdoutWriter) {
69870
+ const id = proposalId.trim();
69871
+ if (!PROPOSAL_ID2.test(id)) {
69872
+ usageError(
69873
+ writer,
69874
+ `expected a proposal id that starts with prp_, got ${JSON.stringify(proposalId)}`,
69875
+ USAGE2
69876
+ );
69877
+ return;
69878
+ }
69879
+ const file = managedBlockFile(path2);
69880
+ if (file === null) {
69881
+ usageError(
69882
+ writer,
69883
+ `expected AGENTS.md, CLAUDE.md, or README.md, got ${JSON.stringify(path2)}`,
69884
+ USAGE2
69885
+ );
69886
+ return;
69887
+ }
69888
+ const out = createOutput({ json: opts.json }, writer);
69889
+ let result2;
69890
+ try {
69891
+ result2 = await apiPostOrThrow(
69892
+ "context/prs/restore-block",
69893
+ { proposalId: id, path: file }
69894
+ );
69895
+ } catch (err2) {
69896
+ reportApiFailure(out, writer, err2);
69897
+ return;
69898
+ }
69899
+ if (out.isJson) {
69900
+ out.data(result2);
69901
+ return;
69902
+ }
69903
+ writer.write(
69904
+ `Restored the managed block in ${file} on the steering PR for ${id}.`
69905
+ );
69906
+ writer.write(`Commit: ${result2.commit_sha}`);
69907
+ writer.write(`Status: ${PROPOSAL_STATUS_WORDS[result2.status]}`);
69908
+ }
69909
+ var STEERING_RESTORE_BLOCK_CAPABILITIES, MANAGED_BLOCK_FILES, PROPOSAL_ID2, USAGE2;
69910
+ var init_steering_restore_block = __esm({
69911
+ "src/commands/steering-restore-block.ts"() {
69912
+ init_api();
69913
+ init_capture_writer();
69914
+ init_output();
69915
+ init_steering_findings();
69916
+ STEERING_RESTORE_BLOCK_CAPABILITIES = [
69917
+ "restore_managed_block"
69918
+ ];
69919
+ MANAGED_BLOCK_FILES = {
69920
+ "AGENTS.md": true,
69921
+ "CLAUDE.md": true,
69922
+ "README.md": true
69923
+ };
69924
+ PROPOSAL_ID2 = /^prp_[0-9A-Za-z]+$/;
69925
+ USAGE2 = "oxagen steering restore-block <proposal-id> <AGENTS.md|CLAUDE.md|README.md> [--json]";
69926
+ }
69927
+ });
69928
+
69074
69929
  // src/commands/tools.ts
69075
69930
  var tools_exports = {};
69076
69931
  __export(tools_exports, {
@@ -69149,7 +70004,7 @@ function resolveDownloadUrl(url, apiUrl) {
69149
70004
  const base2 = apiUrl.replace(/\/+$/, "");
69150
70005
  return `${base2}${url.startsWith("/") ? "" : "/"}${url}`;
69151
70006
  }
69152
- function formatBytes(bytes) {
70007
+ function formatBytes2(bytes) {
69153
70008
  return bytes === null ? "not recorded" : `${bytes.toLocaleString("en-US")} bytes`;
69154
70009
  }
69155
70010
  async function runExportStatus(exportId, opts = {}, writer = stdoutWriter) {
@@ -69183,7 +70038,7 @@ async function runExportStatus(exportId, opts = {}, writer = stdoutWriter) {
69183
70038
  );
69184
70039
  return;
69185
70040
  }
69186
- writer.write(`Size: ${formatBytes(result2.bundleBytes)}`);
70041
+ writer.write(`Size: ${formatBytes2(result2.bundleBytes)}`);
69187
70042
  writer.write(`Digest: ${result2.bundleDigest ?? "not recorded"}`);
69188
70043
  writer.write(`Frames: ${result2.frameCount ?? "not recorded"}`);
69189
70044
  writer.write(`Merkle root: ${result2.merkleRoot ?? "not recorded"}`);
@@ -69275,7 +70130,7 @@ async function runDownload(exportId, opts = {}, writer = stdoutWriter) {
69275
70130
  out.data(result2);
69276
70131
  return;
69277
70132
  }
69278
- writer.write(`Wrote ${path2} (${formatBytes(bytes.byteLength)}).`);
70133
+ writer.write(`Wrote ${path2} (${formatBytes2(bytes.byteLength)}).`);
69279
70134
  writer.write(`Digest ${actual} matches the recorded bundle digest.`);
69280
70135
  writer.write(`Check every frame offline with \`oxagen verify ${path2}\`.`);
69281
70136
  }
@@ -70446,10 +71301,10 @@ __export(verify_exports, {
70446
71301
  verifyBundle: () => verifyBundle3
70447
71302
  });
70448
71303
  import { existsSync as existsSync25, readFileSync as readFileSync22, statSync as statSync10 } from "node:fs";
70449
- import { join as join44 } from "node:path";
71304
+ import { join as join45 } from "node:path";
70450
71305
  function readDirectory(dir) {
70451
71306
  const read2 = (name) => {
70452
- const path2 = join44(dir, name);
71307
+ const path2 = join45(dir, name);
70453
71308
  return existsSync25(path2) ? readFileSync22(path2, "utf8") : void 0;
70454
71309
  };
70455
71310
  return assemble(read2, dir);
@@ -70509,7 +71364,7 @@ function formatFrame(frame) {
70509
71364
  return parts.join(" ");
70510
71365
  }
70511
71366
  function formatCounts(counts) {
70512
- return counts.map((c) => `${c.kind} ${c.count} (in ${plural(c.frames, "frame")})`).join(", ");
71367
+ return counts.map((c) => `${c.kind} ${c.count} (in ${plural2(c.frames, "frame")})`).join(", ");
70513
71368
  }
70514
71369
  function formatVerification(result2) {
70515
71370
  const lines = [];
@@ -70522,7 +71377,7 @@ function formatVerification(result2) {
70522
71377
  ).length;
70523
71378
  if (notCarried > 0) {
70524
71379
  lines.push(
70525
- ` Digest not carried on ${plural(notCarried, "wrapped (tse_) frame")}. A wrapped frame is hashed over its full event, and the bundle does not hold that event for these frames: an export before format 3 never did, and a format-3 export leaves it out when the stored row cannot be proven to rebuild it. For these frames, only the links and the Merkle root can be checked.`
71380
+ ` Digest not carried on ${plural2(notCarried, "wrapped (tse_) frame")}. A wrapped frame is hashed over its full event, and the bundle does not hold that event for these frames: an export before format 3 never did, and a format-3 export leaves it out when the stored row cannot be proven to rebuild it. For these frames, only the links and the Merkle root can be checked.`
70526
71381
  );
70527
71382
  }
70528
71383
  lines.push("");
@@ -70551,7 +71406,7 @@ function formatVerification(result2) {
70551
71406
  (c) => c.status === "broken"
70552
71407
  ).length;
70553
71408
  lines.push(
70554
- result2.ok ? `HELD ${runId}: ${plural(result2.frames.length, "frame")}` : `BROKEN ${runId}: ${brokenFrames} of ${plural(result2.frames.length, "frame")} broken, ${plural(brokenChecks, "check")} broken`
71409
+ result2.ok ? `HELD ${runId}: ${plural2(result2.frames.length, "frame")}` : `BROKEN ${runId}: ${brokenFrames} of ${plural2(result2.frames.length, "frame")} broken, ${plural2(brokenChecks, "check")} broken`
70555
71410
  );
70556
71411
  return lines;
70557
71412
  }
@@ -70569,7 +71424,7 @@ async function verifyBundle3(bundle2, opts = {}, writer = stdoutWriter) {
70569
71424
  if (!result2.ok && (process.exitCode === void 0 || process.exitCode === 0))
70570
71425
  process.exitCode = 1;
70571
71426
  }
70572
- var REQUIRED, OPTIONAL, BundleReadError, plural;
71427
+ var REQUIRED, OPTIONAL, BundleReadError, plural2;
70573
71428
  var init_verify2 = __esm({
70574
71429
  "src/commands/verify.ts"() {
70575
71430
  init_esm2();
@@ -70584,7 +71439,7 @@ var init_verify2 = __esm({
70584
71439
  OPTIONAL = "redactions.json";
70585
71440
  BundleReadError = class extends Error {
70586
71441
  };
70587
- plural = (n4, word) => `${n4} ${word}${n4 === 1 ? "" : "s"}`;
71442
+ plural2 = (n4, word) => `${n4} ${word}${n4 === 1 ? "" : "s"}`;
70588
71443
  }
70589
71444
  });
70590
71445
 
@@ -70796,7 +71651,7 @@ function ago(iso, now = /* @__PURE__ */ new Date()) {
70796
71651
  if (hours < 48) return `${hours}h ago`;
70797
71652
  return `${Math.round(hours / 24)}d ago`;
70798
71653
  }
70799
- function plural2(count7, one, many) {
71654
+ function plural3(count7, one, many) {
70800
71655
  return `${count7} ${count7 === 1 ? one : many}`;
70801
71656
  }
70802
71657
  function table(headers, rows) {
@@ -70808,7 +71663,7 @@ function table(headers, rows) {
70808
71663
  }
70809
71664
  function formatWorkspaceMemories(result2, now = /* @__PURE__ */ new Date()) {
70810
71665
  if (result2.groups.length === 0) {
70811
- return result2.total_groups === 0 ? "No memories yet. Oxagen collects them from enrolled hosts every five minutes." : `No memories on this page. The list holds ${plural2(result2.total_groups, "group", "groups")}.`;
71666
+ return result2.total_groups === 0 ? "No memories yet. Oxagen collects them from enrolled hosts every five minutes." : `No memories on this page. The list holds ${plural3(result2.total_groups, "group", "groups")}.`;
70812
71667
  }
70813
71668
  const rows = result2.groups.map((group) => {
70814
71669
  const { memory } = group;
@@ -70827,13 +71682,13 @@ function formatWorkspaceMemories(result2, now = /* @__PURE__ */ new Date()) {
70827
71682
  const shown2 = result2.groups.length;
70828
71683
  lines.push(
70829
71684
  "",
70830
- shown2 < result2.total_groups ? `Showing ${shown2} of ${plural2(result2.total_groups, "group", "groups")}. Use --limit and --offset to page.` : `${plural2(result2.total_groups, "group", "groups")}.`
71685
+ shown2 < result2.total_groups ? `Showing ${shown2} of ${plural3(result2.total_groups, "group", "groups")}. Use --limit and --offset to page.` : `${plural3(result2.total_groups, "group", "groups")}.`
70831
71686
  );
70832
71687
  if (result2.truncated)
70833
71688
  lines.push(
70834
71689
  `More than ${result2.total_memories} memories matched, and the list groups the top ${result2.total_memories}. Narrow the filters to see the rest.`
70835
71690
  );
70836
- lines.push(`${plural2(result2.waiting, "memory waits", "memories wait")} in this workspace.`);
71691
+ lines.push(`${plural3(result2.waiting, "memory waits", "memories wait")} in this workspace.`);
70837
71692
  return lines.join("\n");
70838
71693
  }
70839
71694
  function formatWorkspaceMemory(result2, now = /* @__PURE__ */ new Date()) {
@@ -70891,7 +71746,7 @@ function formatDismissResult(result2, restore) {
70891
71746
  const lines = [];
70892
71747
  if (result2.changed.length > 0)
70893
71748
  lines.push(
70894
- `${restore ? "Restored" : "Dismissed"} ${plural2(result2.changed.length, "memory", "memories")}: ${result2.changed.join(", ")}.`
71749
+ `${restore ? "Restored" : "Dismissed"} ${plural3(result2.changed.length, "memory", "memories")}: ${result2.changed.join(", ")}.`
70895
71750
  );
70896
71751
  else lines.push(`No memory was ${restore ? "restored" : "dismissed"}.`);
70897
71752
  if (!restore && result2.rejections > 0)
@@ -71360,7 +72215,7 @@ import {
71360
72215
  writeFileSync as writeFileSync10
71361
72216
  } from "node:fs";
71362
72217
  import { homedir as homedir12, hostname as osHostname3 } from "node:os";
71363
- import { join as join45, relative as relative6, resolve as resolve17, sep as sep5 } from "node:path";
72218
+ import { join as join46, relative as relative7, resolve as resolve17, sep as sep5 } from "node:path";
71364
72219
  function git4(cwd, args) {
71365
72220
  return new Promise((done) => {
71366
72221
  try {
@@ -71407,7 +72262,7 @@ function parseRemoteRepository(url) {
71407
72262
  }
71408
72263
  function oxagenPresent(root) {
71409
72264
  try {
71410
- return readdirSync12(join45(root, ".oxagen")).some(
72265
+ return readdirSync12(join46(root, ".oxagen")).some(
71411
72266
  (name) => name !== "workspace.json"
71412
72267
  );
71413
72268
  } catch {
@@ -71416,18 +72271,18 @@ function oxagenPresent(root) {
71416
72271
  }
71417
72272
  function symlinkState(root) {
71418
72273
  try {
71419
- if (!lstatSync8(join45(root, ".stella")).isDirectory()) return "none";
72274
+ if (!lstatSync8(join46(root, ".stella")).isDirectory()) return "none";
71420
72275
  } catch {
71421
72276
  return "none";
71422
72277
  }
71423
72278
  let oxagenReal;
71424
72279
  try {
71425
- oxagenReal = realpathSync7(join45(root, ".oxagen"));
72280
+ oxagenReal = realpathSync7(join46(root, ".oxagen"));
71426
72281
  } catch {
71427
72282
  return "missing";
71428
72283
  }
71429
72284
  for (const link of STELLA_LINKS) {
71430
- const at = join45(root, link);
72285
+ const at = join46(root, link);
71431
72286
  try {
71432
72287
  if (!lstatSync8(at).isSymbolicLink()) return "missing";
71433
72288
  const target2 = realpathSync7(at);
@@ -71441,7 +72296,7 @@ function symlinkState(root) {
71441
72296
  return "linked";
71442
72297
  }
71443
72298
  function machineId(configDir = getConfigDir()) {
71444
- const file = join45(configDir, MACHINE_ID_FILE);
72299
+ const file = join46(configDir, MACHINE_ID_FILE);
71445
72300
  let secret = null;
71446
72301
  try {
71447
72302
  if (existsSync26(file)) {
@@ -71521,14 +72376,14 @@ async function ensureWorkspaceLinkIgnored(root) {
71521
72376
  const top = await git4(root, ["rev-parse", "--show-toplevel"]);
71522
72377
  if (!top) return null;
71523
72378
  const topDir = resolve17(top);
71524
- const entry2 = relative6(
72379
+ const entry2 = relative7(
71525
72380
  topDir,
71526
- join45(realpathOr2(root), ".oxagen", "workspace.json")
72381
+ join46(realpathOr2(root), ".oxagen", "workspace.json")
71527
72382
  ).split(sep5).join("/");
71528
72383
  if (entry2.startsWith("../")) return null;
71529
72384
  const rule = await git4(topDir, ["check-ignore", "--no-index", "-v", entry2]);
71530
72385
  if (rule !== null) return null;
71531
- const gitignore = join45(resolve17(top), ".gitignore");
72386
+ const gitignore = join46(resolve17(top), ".gitignore");
71532
72387
  let existing = "";
71533
72388
  try {
71534
72389
  existing = readFileSync23(gitignore, "utf8");
@@ -72011,8 +72866,8 @@ import {
72011
72866
  } from "node:fs";
72012
72867
  import {
72013
72868
  dirname as dirname26,
72014
- isAbsolute as isAbsolute11,
72015
- join as join46,
72869
+ isAbsolute as isAbsolute12,
72870
+ join as join47,
72016
72871
  parse as parsePath2,
72017
72872
  posix as posix5,
72018
72873
  sep as sep6
@@ -72025,7 +72880,7 @@ function checkSteeringPath(path2) {
72025
72880
  if (path2.includes("\\")) {
72026
72881
  return { ok: false, reason: "contains a backslash" };
72027
72882
  }
72028
- if (posix5.isAbsolute(path2) || isAbsolute11(path2) || /^[A-Za-z]:/.test(path2)) {
72883
+ if (posix5.isAbsolute(path2) || isAbsolute12(path2) || /^[A-Za-z]:/.test(path2)) {
72029
72884
  return { ok: false, reason: "absolute path" };
72030
72885
  }
72031
72886
  const normalized = posix5.normalize(path2);
@@ -72125,7 +72980,7 @@ function findLinkedRoot(start) {
72125
72980
  }
72126
72981
  function fsReader(root) {
72127
72982
  return (path2) => {
72128
- const abs = join46(root, ...path2.split("/"));
72983
+ const abs = join47(root, ...path2.split("/"));
72129
72984
  try {
72130
72985
  const st = lstatSync9(abs);
72131
72986
  if (!st.isFile()) return Buffer.from(`\0not-a-file:${abs}`);
@@ -72136,14 +72991,14 @@ function fsReader(root) {
72136
72991
  };
72137
72992
  }
72138
72993
  function assertInsideOxagen(root, path2) {
72139
- const oxagenAbs = join46(root, OXAGEN_DIR);
72994
+ const oxagenAbs = join47(root, OXAGEN_DIR);
72140
72995
  let oxagenReal;
72141
72996
  try {
72142
72997
  oxagenReal = realpathSync8(oxagenAbs);
72143
72998
  } catch {
72144
72999
  return;
72145
73000
  }
72146
- const abs = join46(root, ...path2.split("/"));
73001
+ const abs = join47(root, ...path2.split("/"));
72147
73002
  try {
72148
73003
  if (lstatSync9(abs).isSymbolicLink()) {
72149
73004
  throw new Error(
@@ -72173,8 +73028,8 @@ function assertInsideOxagen(root, path2) {
72173
73028
  }
72174
73029
  }
72175
73030
  function pruneEmptyDirs(root, path2) {
72176
- const stop = join46(root, OXAGEN_DIR);
72177
- let dir = dirname26(join46(root, ...path2.split("/")));
73031
+ const stop = join47(root, OXAGEN_DIR);
73032
+ let dir = dirname26(join47(root, ...path2.split("/")));
72178
73033
  while (dir.startsWith(stop + sep6)) {
72179
73034
  try {
72180
73035
  if (readdirSync13(dir).length > 0) return;
@@ -72189,12 +73044,12 @@ function applyPlan(root, plan) {
72189
73044
  for (const w of plan.writes) assertInsideOxagen(root, w.path);
72190
73045
  for (const d of plan.deletes) assertInsideOxagen(root, d);
72191
73046
  for (const w of plan.writes) {
72192
- const abs = join46(root, ...w.path.split("/"));
73047
+ const abs = join47(root, ...w.path.split("/"));
72193
73048
  mkdirSync12(dirname26(abs), { recursive: true });
72194
73049
  atomicWriteFileSync(abs, w.content);
72195
73050
  }
72196
73051
  for (const d of plan.deletes) {
72197
- rmSync9(join46(root, ...d.split("/")), { force: true });
73052
+ rmSync9(join47(root, ...d.split("/")), { force: true });
72198
73053
  pruneEmptyDirs(root, d);
72199
73054
  }
72200
73055
  }
@@ -74292,7 +75147,7 @@ var init_registry_launch = __esm({
74292
75147
  function messageOf2(error) {
74293
75148
  return error instanceof Error ? error.message : String(error);
74294
75149
  }
74295
- function isRecord9(value) {
75150
+ function isRecord10(value) {
74296
75151
  return typeof value === "object" && value !== null && !Array.isArray(value);
74297
75152
  }
74298
75153
  function isList(value) {
@@ -74303,7 +75158,7 @@ function jsonEqual(a, b5) {
74303
75158
  if (Array.isArray(a)) {
74304
75159
  return Array.isArray(b5) && a.length === b5.length && a.every((item, index) => jsonEqual(item, b5[index]));
74305
75160
  }
74306
- if (isRecord9(a) && isRecord9(b5)) {
75161
+ if (isRecord10(a) && isRecord10(b5)) {
74307
75162
  const keys = Object.keys(a);
74308
75163
  return keys.length === Object.keys(b5).length && keys.every((key) => Object.hasOwn(b5, key) && jsonEqual(a[key], b5[key]));
74309
75164
  }
@@ -74312,16 +75167,16 @@ function jsonEqual(a, b5) {
74312
75167
  function valueAt2(value, path2) {
74313
75168
  let current = value;
74314
75169
  for (const step of path2.split(".")) {
74315
- if (step.endsWith("[]") || !isRecord9(current) || !Object.hasOwn(current, step)) return void 0;
75170
+ if (step.endsWith("[]") || !isRecord10(current) || !Object.hasOwn(current, step)) return void 0;
74316
75171
  current = current[step];
74317
75172
  }
74318
75173
  return current;
74319
75174
  }
74320
75175
  function withValueAt(value, path2, replacement) {
74321
75176
  const [step, ...rest] = path2.split(".");
74322
- if (step === void 0 || !isRecord9(value)) return value;
75177
+ if (step === void 0 || !isRecord10(value)) return value;
74323
75178
  const inner = Object.hasOwn(value, step) ? value[step] : void 0;
74324
- if (rest.length > 0 && !isRecord9(inner)) return value;
75179
+ if (rest.length > 0 && !isRecord10(inner)) return value;
74325
75180
  const next = rest.length === 0 ? replacement : withValueAt(inner, rest.join("."), replacement);
74326
75181
  return { ...value, [step]: next };
74327
75182
  }
@@ -74996,11 +75851,11 @@ var init_openapi = __esm({
74996
75851
  });
74997
75852
 
74998
75853
  // ../../packages/mcp-studio/src/compile/json-schema.ts
74999
- function isRecord11(value) {
75854
+ function isRecord12(value) {
75000
75855
  return typeof value === "object" && value !== null && !Array.isArray(value);
75001
75856
  }
75002
75857
  function propertiesOf(schema) {
75003
- return isRecord11(schema.properties) ? schema.properties : {};
75858
+ return isRecord12(schema.properties) ? schema.properties : {};
75004
75859
  }
75005
75860
  function requiredOf(schema) {
75006
75861
  if (!Array.isArray(schema.required)) return [];
@@ -82612,13 +83467,13 @@ var require_path = __commonJS({
82612
83467
  "../../node_modules/.pnpm/@protobufjs+path@1.1.2/node_modules/@protobufjs/path/index.js"(exports) {
82613
83468
  "use strict";
82614
83469
  var path2 = exports;
82615
- var isAbsolute13 = (
83470
+ var isAbsolute14 = (
82616
83471
  /**
82617
83472
  * Tests if the specified path is absolute.
82618
83473
  * @param {string} path Path to test
82619
83474
  * @returns {boolean} `true` if path is absolute
82620
83475
  */
82621
- path2.isAbsolute = function isAbsolute14(path3) {
83476
+ path2.isAbsolute = function isAbsolute15(path3) {
82622
83477
  return /^(?:\/|\w+:)/.test(path3);
82623
83478
  }
82624
83479
  );
@@ -82630,7 +83485,7 @@ var require_path = __commonJS({
82630
83485
  */
82631
83486
  path2.normalize = function normalize3(path3) {
82632
83487
  path3 = path3.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
82633
- var parts = path3.split("/"), absolute = isAbsolute13(path3), prefix = "";
83488
+ var parts = path3.split("/"), absolute = isAbsolute14(path3), prefix = "";
82634
83489
  if (absolute)
82635
83490
  prefix = parts.shift() + "/";
82636
83491
  for (var i2 = 0; i2 < parts.length; ) {
@@ -82652,7 +83507,7 @@ var require_path = __commonJS({
82652
83507
  path2.resolve = function resolve19(originPath, includePath, alreadyNormalized) {
82653
83508
  if (!alreadyNormalized)
82654
83509
  includePath = normalize2(includePath);
82655
- if (isAbsolute13(includePath))
83510
+ if (isAbsolute14(includePath))
82656
83511
  return includePath;
82657
83512
  if (!alreadyNormalized)
82658
83513
  originPath = normalize2(originPath);
@@ -86926,7 +87781,7 @@ function shapeInput(key, entry2, upstream, supplied, issues) {
86926
87781
  for (const [name, value] of defaults) {
86927
87782
  if (!usable("defaults", name)) continue;
86928
87783
  const property = shaped[name];
86929
- shaped[name] = { ...isRecord11(property) ? property : {}, default: value };
87784
+ shaped[name] = { ...isRecord12(property) ? property : {}, default: value };
86930
87785
  required = required.filter((other) => other !== name);
86931
87786
  }
86932
87787
  const renamed = /* @__PURE__ */ new Map();
@@ -86972,7 +87827,7 @@ function selectTree(paths) {
86972
87827
  return root;
86973
87828
  }
86974
87829
  function trimObject(schema, children) {
86975
- if (!isRecord11(schema.properties)) return schema;
87830
+ if (!isRecord12(schema.properties)) return schema;
86976
87831
  const properties = schema.properties;
86977
87832
  const kept = {};
86978
87833
  for (const [name, node2] of children) {
@@ -86986,9 +87841,9 @@ function trimObject(schema, children) {
86986
87841
  return out;
86987
87842
  }
86988
87843
  function trimNode(schema, node2) {
86989
- if (node2.whole || !isRecord11(schema)) return schema;
87844
+ if (node2.whole || !isRecord12(schema)) return schema;
86990
87845
  if (!node2.array) return trimObject(schema, node2.children);
86991
- return isRecord11(schema.items) ? { ...schema, items: trimObject(schema.items, node2.children) } : schema;
87846
+ return isRecord12(schema.items) ? { ...schema, items: trimObject(schema.items, node2.children) } : schema;
86992
87847
  }
86993
87848
  function selectOutput(outputSchema, select) {
86994
87849
  if (select.length === 0) return outputSchema;
@@ -87660,12 +88515,12 @@ function walkSchemas(root, at, visit2) {
87660
88515
  node2.forEach((item, index) => walk(item, `${path2}[${index}]`, depth + 1));
87661
88516
  return;
87662
88517
  }
87663
- if (!isRecord11(node2) || above.has(node2)) return;
88518
+ if (!isRecord12(node2) || above.has(node2)) return;
87664
88519
  visit2(node2, path2);
87665
88520
  above.add(node2);
87666
88521
  for (const [key, value] of Object.entries(node2)) {
87667
88522
  if (DATA.has(key)) continue;
87668
- if (SCHEMA_MAPS.has(key) && isRecord11(value)) {
88523
+ if (SCHEMA_MAPS.has(key) && isRecord12(value)) {
87669
88524
  for (const [name, child] of Object.entries(value)) walk(child, `${path2}.${key}.${name}`, depth + 1);
87670
88525
  } else {
87671
88526
  walk(value, `${path2}.${key}`, depth + 1);
@@ -87676,14 +88531,14 @@ function walkSchemas(root, at, visit2) {
87676
88531
  walk(root, at, 0);
87677
88532
  }
87678
88533
  function tagOf(schema) {
87679
- if (!isRecord11(schema)) return void 0;
88534
+ if (!isRecord12(schema)) return void 0;
87680
88535
  if (Object.hasOwn(schema, "const")) return JSON.stringify(schema.const);
87681
88536
  if (Array.isArray(schema.enum) && schema.enum.length === 1) return JSON.stringify(schema.enum[0]);
87682
88537
  return void 0;
87683
88538
  }
87684
88539
  function distinguishable(branches) {
87685
88540
  if (branches.length < 2) return true;
87686
- const records = branches.filter(isRecord11);
88541
+ const records = branches.filter(isRecord12);
87687
88542
  const [first] = records;
87688
88543
  if (first === void 0 || records.length < branches.length) return false;
87689
88544
  const types = records.map((branch) => branch.type);
@@ -87774,7 +88629,7 @@ function shapedInput(entry2, upstream) {
87774
88629
  for (const [name, value] of defaults) {
87775
88630
  if (!Object.hasOwn(shaped, name)) continue;
87776
88631
  const property = shaped[name];
87777
- shaped[name] = { ...isRecord11(property) ? property : {}, default: value };
88632
+ shaped[name] = { ...isRecord12(property) ? property : {}, default: value };
87778
88633
  required = required.filter((other) => other !== name);
87779
88634
  }
87780
88635
  const renamed = new Map(rename3.filter(([from]) => Object.hasOwn(shaped, from)));
@@ -87804,7 +88659,7 @@ function selectTree2(paths) {
87804
88659
  return root;
87805
88660
  }
87806
88661
  function trimObject2(schema, children) {
87807
- if (!isRecord11(schema.properties)) return schema;
88662
+ if (!isRecord12(schema.properties)) return schema;
87808
88663
  const properties = schema.properties;
87809
88664
  const kept = {};
87810
88665
  for (const [name, node2] of children) {
@@ -87818,9 +88673,9 @@ function trimObject2(schema, children) {
87818
88673
  return out;
87819
88674
  }
87820
88675
  function trimNode2(schema, node2) {
87821
- if (node2.whole || !isRecord11(schema)) return schema;
88676
+ if (node2.whole || !isRecord12(schema)) return schema;
87822
88677
  if (!node2.array) return trimObject2(schema, node2.children);
87823
- return isRecord11(schema.items) ? { ...schema, items: trimObject2(schema.items, node2.children) } : schema;
88678
+ return isRecord12(schema.items) ? { ...schema, items: trimObject2(schema.items, node2.children) } : schema;
87824
88679
  }
87825
88680
  function servedOutput(outputSchema, select) {
87826
88681
  if (select.length === 0) return outputSchema;
@@ -87870,7 +88725,7 @@ function quoted(values) {
87870
88725
  return orList2(values.map((value) => JSON.stringify(value)));
87871
88726
  }
87872
88727
  function isArraySchema(schema) {
87873
- if (!isRecord11(schema)) return false;
88728
+ if (!isRecord12(schema)) return false;
87874
88729
  return schema.type === "array" || Array.isArray(schema.type) && schema.type.includes("array");
87875
88730
  }
87876
88731
  function lintTools(folder, context, report) {
@@ -88047,7 +88902,7 @@ function lintUpstream(folder, context, key, entry2, upstream, report) {
88047
88902
  });
88048
88903
  }
88049
88904
  const oneOf2 = (node2, at) => {
88050
- if (!Array.isArray(node2.oneOf) || isRecord11(node2.discriminator) || distinguishable(node2.oneOf)) return;
88905
+ if (!Array.isArray(node2.oneOf) || isRecord12(node2.discriminator) || distinguishable(node2.oneOf)) return;
88051
88906
  report("undiscriminated_one_of", {
88052
88907
  tool: key,
88053
88908
  field: `${at}.oneOf`,
@@ -88657,7 +89512,7 @@ function upstreamError(response, bytes) {
88657
89512
  let problem;
88658
89513
  if (text4 !== "" && isJsonMediaType(headerValue(response.headers, "content-type"))) {
88659
89514
  const parsed = parseJson3(text4);
88660
- if (parsed.ok && isRecord9(parsed.value)) problem = parsed.value;
89515
+ if (parsed.ok && isRecord10(parsed.value)) problem = parsed.value;
88661
89516
  }
88662
89517
  const title = problem === void 0 ? void 0 : firstText(problem, ["title"]);
88663
89518
  const detail = problem === void 0 ? text4.trim() === "" ? void 0 : text4 : firstText(problem, ["detail", "message", "error_description", "error"]);
@@ -88841,11 +89696,11 @@ var init_apply_credential = __esm({
88841
89696
 
88842
89697
  // ../../packages/mcp-studio/src/execute/mcp.ts
88843
89698
  function toolResult(value) {
88844
- if (!isRecord9(value)) return { ok: false, detail: "The tools/call result is not an object." };
89699
+ if (!isRecord10(value)) return { ok: false, detail: "The tools/call result is not an object." };
88845
89700
  if (!isList(value.content)) return { ok: false, detail: "The tools/call result has no content list." };
88846
89701
  const content = [];
88847
89702
  for (const [index, item] of value.content.entries()) {
88848
- if (!isRecord9(item) || typeof item.type !== "string") {
89703
+ if (!isRecord10(item) || typeof item.type !== "string") {
88849
89704
  return { ok: false, detail: `Content item ${index} is not an object with a string type.` };
88850
89705
  }
88851
89706
  content.push({ ...item, type: item.type });
@@ -88853,7 +89708,7 @@ function toolResult(value) {
88853
89708
  const result2 = { content };
88854
89709
  const structured = value.structuredContent;
88855
89710
  if (structured !== void 0 && structured !== null) {
88856
- if (!isRecord9(structured)) return { ok: false, detail: "The tools/call result's structuredContent is not an object." };
89711
+ if (!isRecord10(structured)) return { ok: false, detail: "The tools/call result's structuredContent is not an object." };
88857
89712
  result2.structuredContent = structured;
88858
89713
  }
88859
89714
  const isError = value.isError;
@@ -88972,7 +89827,7 @@ function rejectedAttempt(response, failure2) {
88972
89827
  function findResponse(value, id) {
88973
89828
  const messages = isList(value) ? value : [value];
88974
89829
  for (const message of messages) {
88975
- if (isRecord9(message) && message.id === id && !Object.hasOwn(message, "method")) return message;
89830
+ if (isRecord10(message) && message.id === id && !Object.hasOwn(message, "method")) return message;
88976
89831
  }
88977
89832
  return void 0;
88978
89833
  }
@@ -89022,8 +89877,8 @@ async function readReply(session, response, id, what) {
89022
89877
  return { ok: true, value: message };
89023
89878
  }
89024
89879
  function rpcError2(error) {
89025
- const message = isRecord9(error) && typeof error.message === "string" ? error.message : JSON.stringify(error);
89026
- const code = isRecord9(error) && typeof error.code === "number" ? ` (code ${error.code})` : "";
89880
+ const message = isRecord10(error) && typeof error.message === "string" ? error.message : JSON.stringify(error);
89881
+ const code = isRecord10(error) && typeof error.code === "number" ? ` (code ${error.code})` : "";
89027
89882
  return { title: "MCP error", detail: cutDetail(`${message}${code}`), status: void 0 };
89028
89883
  }
89029
89884
  async function initialize(session) {
@@ -89050,7 +89905,7 @@ async function initialize(session) {
89050
89905
  if (!reply2.ok) return reply2;
89051
89906
  if (Object.hasOwn(reply2.value, "error")) return { ok: false, error: rpcError2(reply2.value.error) };
89052
89907
  const result2 = reply2.value.result;
89053
- if (!isRecord9(result2)) return invalidResponse("The MCP server's initialize result is not an object.", response.status);
89908
+ if (!isRecord10(result2)) return invalidResponse("The MCP server's initialize result is not an object.", response.status);
89054
89909
  const version2 = result2.protocolVersion;
89055
89910
  session.protocol = typeof version2 === "string" && PROTOCOL_VERSION2.test(version2) ? version2 : MCP_PROTOCOL_VERSION2;
89056
89911
  return { ok: true, value: void 0 };
@@ -89252,8 +90107,8 @@ function firstPosition(paging, args, options) {
89252
90107
  if (paging.style === "offset") return 0;
89253
90108
  const properties = options.inputSchema.properties;
89254
90109
  const name = options.agentName(paging.input);
89255
- const property = isRecord9(properties) && Object.hasOwn(properties, name) ? properties[name] : void 0;
89256
- if (isRecord9(property)) {
90110
+ const property = isRecord10(properties) && Object.hasOwn(properties, name) ? properties[name] : void 0;
90111
+ if (isRecord10(property)) {
89257
90112
  const fallback = wholeNumber(property.default) ?? wholeNumber(property.minimum);
89258
90113
  if (fallback !== void 0) return fallback;
89259
90114
  }
@@ -89351,11 +90206,11 @@ function errorText4(error) {
89351
90206
  function asSentence(text4) {
89352
90207
  return /[.!?…]$/.test(text4) ? text4 : `${text4}.`;
89353
90208
  }
89354
- function plural3(count7, one, many) {
90209
+ function plural4(count7, one, many) {
89355
90210
  return `${count7} ${count7 === 1 ? one : many}`;
89356
90211
  }
89357
90212
  function pagingNote(paged, options) {
89358
- const read2 = `${plural3(paged.items, "item", "items")} from ${plural3(paged.pages, "page", "pages")}`;
90213
+ const read2 = `${plural4(paged.items, "item", "items")} from ${plural4(paged.pages, "page", "pages")}`;
89359
90214
  switch (paged.stop) {
89360
90215
  case "last_page":
89361
90216
  return void 0;
@@ -89403,7 +90258,7 @@ function graphqlDocument(template, args) {
89403
90258
  return { query, variables: Object.fromEntries(present.map((a) => [a.name, args[a.property]])) };
89404
90259
  }
89405
90260
  function errorMessages(errors) {
89406
- return errors.map((entry2) => isRecord9(entry2) && typeof entry2.message === "string" ? entry2.message : JSON.stringify(entry2)).join("; ");
90261
+ return errors.map((entry2) => isRecord10(entry2) && typeof entry2.message === "string" ? entry2.message : JSON.stringify(entry2)).join("; ");
89407
90262
  }
89408
90263
  function invalidResponse2(detail, status2) {
89409
90264
  return { ok: false, error: { title: "Invalid response", detail, status: status2 } };
@@ -89414,14 +90269,14 @@ function interpret(template) {
89414
90269
  const status2 = response.status;
89415
90270
  const parsed = bytes.byteLength === 0 ? void 0 : parseJson3(decodeText2(bytes));
89416
90271
  const body2 = parsed?.ok === true ? parsed.value : void 0;
89417
- if (isRecord9(body2) && isList(body2.errors) && body2.errors.length > 0) {
90272
+ if (isRecord10(body2) && isList(body2.errors) && body2.errors.length > 0) {
89418
90273
  return { ok: false, error: { title: "GraphQL error", detail: cutDetail(errorMessages(body2.errors)), status: status2 } };
89419
90274
  }
89420
90275
  if (status2 < 200 || status2 >= 300) return { ok: false, error: upstreamError(response, bytes) };
89421
90276
  if (parsed === void 0) return invalidResponse2(`The upstream answered ${status2} with no body.`, status2);
89422
90277
  if (!parsed.ok) return invalidResponse2(`The upstream answered ${status2} with JSON that does not parse: ${parsed.message}`, status2);
89423
- const data = isRecord9(body2) ? body2.data : void 0;
89424
- if (!isRecord9(data)) return invalidResponse2(`The upstream answered ${status2} with no data object.`, status2);
90278
+ const data = isRecord10(body2) ? body2.data : void 0;
90279
+ if (!isRecord10(data)) return invalidResponse2(`The upstream answered ${status2} with no data object.`, status2);
89425
90280
  if (!Object.hasOwn(data, field)) return invalidResponse2(`The response's data has no ${field} field.`, status2);
89426
90281
  const value = data[field];
89427
90282
  return { ok: true, value: isList(value) ? { items: value } : value };
@@ -107716,7 +108571,7 @@ function scalarText(value, name) {
107716
108571
  }
107717
108572
  function shapeOf(value, name) {
107718
108573
  if (isList(value)) return { kind: "list", items: value.map((item) => scalarText(item, name)) };
107719
- if (isRecord9(value)) {
108574
+ if (isRecord10(value)) {
107720
108575
  return {
107721
108576
  kind: "object",
107722
108577
  entries: Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => [key, scalarText(item, name)])
@@ -107837,7 +108692,7 @@ function formText(value) {
107837
108692
  throw invalidArguments(`A form body cannot carry a ${typeof value} value.`);
107838
108693
  }
107839
108694
  function formBody(value) {
107840
- if (!isRecord9(value)) throw invalidArguments("A form body takes an object.");
108695
+ if (!isRecord10(value)) throw invalidArguments("A form body takes an object.");
107841
108696
  const form = new URLSearchParams();
107842
108697
  for (const [key, item] of Object.entries(value)) {
107843
108698
  const items = (isList(item) ? item : [item]).filter((one) => one !== void 0);
@@ -108065,7 +108920,7 @@ function project(value, at) {
108065
108920
  const each = at.each;
108066
108921
  return value.map((item) => project(item, each)).filter((item) => item !== ABSENT);
108067
108922
  }
108068
- if (isRecord9(value) && at.fields.size > 0) {
108923
+ if (isRecord10(value) && at.fields.size > 0) {
108069
108924
  const kept = [];
108070
108925
  for (const [name, child] of at.fields) {
108071
108926
  if (!Object.hasOwn(value, name)) continue;
@@ -108082,7 +108937,7 @@ function remove(value, at) {
108082
108937
  const each = at.each;
108083
108938
  return value.map((item) => remove(item, each)).filter((item) => item !== ABSENT);
108084
108939
  }
108085
- if (isRecord9(value) && at.fields.size > 0) {
108940
+ if (isRecord10(value) && at.fields.size > 0) {
108086
108941
  const kept = [];
108087
108942
  for (const [name, item] of Object.entries(value)) {
108088
108943
  const child = at.fields.get(name);
@@ -108118,10 +108973,10 @@ function shapeRecord(value, rules, isError) {
108118
108973
  let out = value;
108119
108974
  if (rules.select !== void 0 && !isError) out = project(out, rules.select);
108120
108975
  if (rules.redact !== void 0) out = remove(out, rules.redact);
108121
- return isRecord9(out) ? out : {};
108976
+ return isRecord10(out) ? out : {};
108122
108977
  }
108123
108978
  function shapeJson(value, rules) {
108124
- return isRecord9(value) ? shapeRecord(value, rules, false) : value;
108979
+ return isRecord10(value) ? shapeRecord(value, rules, false) : value;
108125
108980
  }
108126
108981
  function byteLength(text4) {
108127
108982
  return encodeText(text4).byteLength;
@@ -108148,7 +109003,7 @@ function resultBytes(content, structured) {
108148
109003
  }
108149
109004
  function jsonResult(value, text4) {
108150
109005
  const result2 = { content: [textItem(text4)] };
108151
- if (isRecord9(value)) result2.structuredContent = value;
109006
+ if (isRecord10(value)) result2.structuredContent = value;
108152
109007
  return result2;
108153
109008
  }
108154
109009
  function fitsCap(result2, rules) {
@@ -108203,7 +109058,7 @@ function shapeTextItem(item, rules, isError) {
108203
109058
  const text4 = item.text;
108204
109059
  if (typeof text4 !== "string") return item;
108205
109060
  const parsed = parseJson3(text4);
108206
- if (!parsed.ok || !isRecord9(parsed.value)) return item;
109061
+ if (!parsed.ok || !isRecord10(parsed.value)) return item;
108207
109062
  return { ...item, text: JSON.stringify(shapeRecord(parsed.value, rules, isError)) };
108208
109063
  }
108209
109064
  function shapeToolResult(result2, rules, notes) {
@@ -108268,7 +109123,7 @@ function hasType(type, value) {
108268
109123
  case "boolean":
108269
109124
  return typeof value === "boolean";
108270
109125
  case "object":
108271
- return isRecord9(value);
109126
+ return isRecord10(value);
108272
109127
  case "array":
108273
109128
  return isList(value);
108274
109129
  default:
@@ -108279,10 +109134,10 @@ function checkString(schema, value, path2) {
108279
109134
  const issues = [];
108280
109135
  const length = [...value].length;
108281
109136
  if (typeof schema.minLength === "number" && length < schema.minLength) {
108282
- issues.push(`${path2} must be at least ${schema.minLength} ${plural4(schema.minLength, "character")} long.`);
109137
+ issues.push(`${path2} must be at least ${schema.minLength} ${plural5(schema.minLength, "character")} long.`);
108283
109138
  }
108284
109139
  if (typeof schema.maxLength === "number" && length > schema.maxLength) {
108285
- issues.push(`${path2} must be at most ${schema.maxLength} ${plural4(schema.maxLength, "character")} long.`);
109140
+ issues.push(`${path2} must be at most ${schema.maxLength} ${plural5(schema.maxLength, "character")} long.`);
108286
109141
  }
108287
109142
  if (typeof schema.pattern === "string" && testPattern(schema.pattern, value) === false) {
108288
109143
  issues.push(`${path2} must match the pattern ${schema.pattern}.`);
@@ -108340,7 +109195,7 @@ function resolveRef(root, ref2) {
108340
109195
  const index = Number(token);
108341
109196
  if (!Number.isInteger(index) || index < 0 || index >= current.length) return void 0;
108342
109197
  current = current[index];
108343
- } else if (isRecord9(current) && Object.hasOwn(current, token)) {
109198
+ } else if (isRecord10(current) && Object.hasOwn(current, token)) {
108344
109199
  current = current[token];
108345
109200
  } else {
108346
109201
  return void 0;
@@ -108350,7 +109205,7 @@ function resolveRef(root, ref2) {
108350
109205
  }
108351
109206
  function canonical2(value) {
108352
109207
  if (isList(value)) return `[${value.map(canonical2).join(",")}]`;
108353
- if (isRecord9(value)) {
109208
+ if (isRecord10(value)) {
108354
109209
  const keys = Object.keys(value).sort();
108355
109210
  return `{${keys.map((key) => `${JSON.stringify(key)}:${canonical2(value[key])}`).join(",")}}`;
108356
109211
  }
@@ -108364,7 +109219,7 @@ function listOf(values) {
108364
109219
  const more = values.length - shown2.length;
108365
109220
  return more > 0 ? `${shown2.join(", ")}, and ${more} more` : shown2.join(", ");
108366
109221
  }
108367
- function plural4(count7, one, many = `${one}s`) {
109222
+ function plural5(count7, one, many = `${one}s`) {
108368
109223
  return count7 === 1 ? one : many;
108369
109224
  }
108370
109225
  var MAX_DEPTH2, MAX_STEPS, MAX_ISSUES, TYPE_NAMES, Checker;
@@ -108394,7 +109249,7 @@ var init_validate = __esm({
108394
109249
  this.steps += 1;
108395
109250
  if (depth > MAX_DEPTH2 || this.steps > MAX_STEPS) return [];
108396
109251
  if (schema === false) return [`${path2} is not allowed.`];
108397
- if (!isRecord9(schema)) return [];
109252
+ if (!isRecord10(schema)) return [];
108398
109253
  if (value === null && schema.nullable === true) return [];
108399
109254
  const issues = [];
108400
109255
  if (typeof schema.$ref === "string") {
@@ -108412,7 +109267,7 @@ var init_validate = __esm({
108412
109267
  if (typeof value === "string") issues.push(...checkString(schema, value, path2));
108413
109268
  else if (typeof value === "number") issues.push(...checkNumber(schema, value, path2));
108414
109269
  else if (isList(value)) issues.push(...this.checkArray(schema, value, path2, depth));
108415
- else if (isRecord9(value)) issues.push(...this.checkObject(schema, value, path2, depth));
109270
+ else if (isRecord10(value)) issues.push(...this.checkObject(schema, value, path2, depth));
108416
109271
  issues.push(...this.checkCombinators(schema, value, path2, depth));
108417
109272
  return issues;
108418
109273
  }
@@ -108422,10 +109277,10 @@ var init_validate = __esm({
108422
109277
  checkArray(schema, value, path2, depth) {
108423
109278
  const issues = [];
108424
109279
  if (typeof schema.minItems === "number" && value.length < schema.minItems) {
108425
- issues.push(`${path2} must have at least ${schema.minItems} ${plural4(schema.minItems, "item")}.`);
109280
+ issues.push(`${path2} must have at least ${schema.minItems} ${plural5(schema.minItems, "item")}.`);
108426
109281
  }
108427
109282
  if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
108428
- issues.push(`${path2} must have at most ${schema.maxItems} ${plural4(schema.maxItems, "item")}.`);
109283
+ issues.push(`${path2} must have at most ${schema.maxItems} ${plural5(schema.maxItems, "item")}.`);
108429
109284
  }
108430
109285
  if (schema.uniqueItems === true) {
108431
109286
  const seen = /* @__PURE__ */ new Set();
@@ -108457,13 +109312,13 @@ var init_validate = __esm({
108457
109312
  }
108458
109313
  }
108459
109314
  if (typeof schema.minProperties === "number" && present.length < schema.minProperties) {
108460
- issues.push(`${path2} must have at least ${schema.minProperties} ${plural4(schema.minProperties, "property", "properties")}.`);
109315
+ issues.push(`${path2} must have at least ${schema.minProperties} ${plural5(schema.minProperties, "property", "properties")}.`);
108461
109316
  }
108462
109317
  if (typeof schema.maxProperties === "number" && present.length > schema.maxProperties) {
108463
- issues.push(`${path2} must have at most ${schema.maxProperties} ${plural4(schema.maxProperties, "property", "properties")}.`);
109318
+ issues.push(`${path2} must have at most ${schema.maxProperties} ${plural5(schema.maxProperties, "property", "properties")}.`);
108464
109319
  }
108465
- const properties = isRecord9(schema.properties) ? schema.properties : {};
108466
- const patterns = isRecord9(schema.patternProperties) ? Object.entries(schema.patternProperties) : [];
109320
+ const properties = isRecord10(schema.properties) ? schema.properties : {};
109321
+ const patterns = isRecord10(schema.patternProperties) ? Object.entries(schema.patternProperties) : [];
108467
109322
  for (const key of present) {
108468
109323
  const at = propertyPath(path2, key);
108469
109324
  let known = false;
@@ -108743,7 +109598,7 @@ function differenceAt(path2, expected, actual) {
108743
109598
  if (expected.length === actual.length) return void 0;
108744
109599
  return { path: `${path2}[${shared}]`, expected: expected[shared], actual: actual[shared] };
108745
109600
  }
108746
- if (isRecord9(expected) && isRecord9(actual)) {
109601
+ if (isRecord10(expected) && isRecord10(actual)) {
108747
109602
  for (const key of Object.keys(expected)) {
108748
109603
  const found = differenceAt(keyPath(path2, key), expected[key], Object.hasOwn(actual, key) ? actual[key] : void 0);
108749
109604
  if (found !== void 0) return found;
@@ -108821,7 +109676,7 @@ function rpcMessage(request3) {
108821
109676
  const parsed = parseJson3(decodeText2(request3.body));
108822
109677
  if (!parsed.ok) return void 0;
108823
109678
  const value = isList(parsed.value) ? parsed.value[0] : parsed.value;
108824
- return isRecord9(value) ? value : void 0;
109679
+ return isRecord10(value) ? value : void 0;
108825
109680
  }
108826
109681
  function replayTransport(exchanges, route) {
108827
109682
  let next = 0;
@@ -109530,7 +110385,7 @@ import {
109530
110385
  readlinkSync as readlinkSync3,
109531
110386
  statSync as statSync11
109532
110387
  } from "node:fs";
109533
- import { dirname as dirname27, isAbsolute as isAbsolute12, join as join47, relative as relative7, resolve as resolve18, sep as sep7 } from "node:path";
110388
+ import { dirname as dirname27, isAbsolute as isAbsolute13, join as join48, relative as relative8, resolve as resolve18, sep as sep7 } from "node:path";
109534
110389
  function git5(cwd, args, input) {
109535
110390
  try {
109536
110391
  return execFileSync2("git", args, {
@@ -109557,7 +110412,7 @@ function commitOf(root, ref2) {
109557
110412
  return sha.length > 0 ? sha : null;
109558
110413
  }
109559
110414
  function readWorkingFile(root, path2) {
109560
- const abs = join47(root, ...path2.split("/"));
110415
+ const abs = join48(root, ...path2.split("/"));
109561
110416
  try {
109562
110417
  const stat5 = lstatSync10(abs);
109563
110418
  if (stat5.isSymbolicLink()) return readlinkSync3(abs, "utf8");
@@ -109651,7 +110506,7 @@ function isDir(path2) {
109651
110506
  function findSteeringRoot(start) {
109652
110507
  let dir = resolve18(start);
109653
110508
  for (; ; ) {
109654
- if (isFile(join47(dir, WORKSPACE_TOML_PATH)) || isFile(join47(dir, AGENTS_MD_PATH)) && isDir(join47(dir, STEERING_DIR))) {
110509
+ if (isFile(join48(dir, WORKSPACE_TOML_PATH)) || isFile(join48(dir, AGENTS_MD_PATH)) && isDir(join48(dir, STEERING_DIR))) {
109655
110510
  return dir;
109656
110511
  }
109657
110512
  const parent = dirname27(dir);
@@ -109665,8 +110520,8 @@ function within3(path2, scope) {
109665
110520
  function scopesFor(root, cwd, paths, trees) {
109666
110521
  const scopes = [];
109667
110522
  for (const path2 of paths) {
109668
- const rel = relative7(root, resolve18(cwd, path2));
109669
- if (rel === ".." || rel.startsWith(`..${sep7}`) || isAbsolute12(rel)) {
110523
+ const rel = relative8(root, resolve18(cwd, path2));
110524
+ if (rel === ".." || rel.startsWith(`..${sep7}`) || isAbsolute13(rel)) {
109670
110525
  return { error: `${path2} is outside the steering repo at ${root}.` };
109671
110526
  }
109672
110527
  const scope = rel.split(sep7).join("/");
@@ -109731,7 +110586,7 @@ function cacheFile(cacheDir, root) {
109731
110586
  key: token === void 0 ? null : createHash20("sha256").update(token).digest("hex").slice(0, 16)
109732
110587
  };
109733
110588
  const key = createHash20("sha256").update(JSON.stringify([root, identity])).digest("hex").slice(0, 32);
109734
- return join47(cacheDir, `${key}.json`);
110589
+ return join48(cacheDir, `${key}.json`);
109735
110590
  }
109736
110591
  function readCache(file, now) {
109737
110592
  let entry2;
@@ -109830,7 +110685,7 @@ async function fetchSteeringIndex(root) {
109830
110685
  function defaultDeps2() {
109831
110686
  return {
109832
110687
  fetchPublished: fetchSteeringIndex,
109833
- cacheDir: join47(getConfigDir(), "cache", "steering-check"),
110688
+ cacheDir: join48(getConfigDir(), "cache", "steering-check"),
109834
110689
  now: () => Date.now()
109835
110690
  };
109836
110691
  }
@@ -112086,7 +112941,7 @@ function buildProgram() {
112086
112941
  }
112087
112942
  );
112088
112943
  const steeringCmd = program2.command("steering").description(
112089
- "Steering records: whether .oxagen/ carries the ones merged on the production branch, and Markdown import"
112944
+ "Steering records: whether .oxagen/ carries the ones merged on the production branch, Markdown import, and instruction file findings"
112090
112945
  );
112091
112946
  steeringCmd.command("status").description(
112092
112947
  "Compare .oxagen/ against the remote production branch and show the two gates"
@@ -112142,6 +112997,26 @@ function buildProgram() {
112142
112997
  await handleSteeringImport2(paths, opts);
112143
112998
  }
112144
112999
  );
113000
+ steeringCmd.command("findings").description(
113001
+ "List the statements in linked repositories' instruction files that repeat or contradict a steering record"
113002
+ ).option("--json", "Output JSON").action(async (opts) => {
113003
+ const { steeringFindings: steeringFindings2 } = await Promise.resolve().then(() => (init_steering_findings(), steering_findings_exports));
113004
+ await steeringFindings2(opts);
113005
+ });
113006
+ steeringCmd.command("promote").argument("<finding-id>", "The crf_\u2026 id `oxagen steering findings` shows").description(
113007
+ "Propose a new version of the record a statement contradicts, with the statement as its text, and open its steering PR"
113008
+ ).option("--json", "Output JSON").action(async (findingId, opts) => {
113009
+ const { steeringPromote: steeringPromote2 } = await Promise.resolve().then(() => (init_steering_promote(), steering_promote_exports));
113010
+ await steeringPromote2(findingId, opts);
113011
+ });
113012
+ steeringCmd.command("restore-block").argument("<proposal-id>", "The prp_\u2026 proposal whose steering PR holds the file").argument("<path>", "AGENTS.md, CLAUDE.md, or README.md").description(
113013
+ "Restore Oxagen's managed block in one file of an open steering PR, as one commit from the production branch"
113014
+ ).option("--json", "Output JSON").action(
113015
+ async (proposalId, path2, opts) => {
113016
+ const { steeringRestoreBlock: steeringRestoreBlock2 } = await Promise.resolve().then(() => (init_steering_restore_block(), steering_restore_block_exports));
113017
+ await steeringRestoreBlock2(proposalId, path2, opts);
113018
+ }
113019
+ );
112145
113020
  const toolsCmd = program2.command("tools").description("The workspace's connected MCP servers and their tools");
112146
113021
  toolsCmd.command("migrate").description(
112147
113022
  "Open the pull request that moves the workspace's MCP servers into its steering repo, or print the one already open"
@@ -112156,7 +113031,7 @@ function buildProgram() {
112156
113031
  "The repository root to mount at /workspace (default: the current directory)"
112157
113032
  ).option(
112158
113033
  "--github-repository <owner/name>",
112159
- "The one repository the run may reach, with a token in OXAGEN_CONTAINED_GITHUB_TOKEN"
113034
+ "The one repository the run may fetch and push, through Oxagen's Git custody. The workspace must have it bound."
112160
113035
  ).argument("[command...]", "After --: claude or codex, then its arguments").action(
112161
113036
  async (command2, opts) => {
112162
113037
  if (command2.length === 0) {
@@ -112724,7 +113599,7 @@ function buildProgram() {
112724
113599
  "With --contained: the repository root to mount at /workspace (default: the current directory)"
112725
113600
  ).option(
112726
113601
  "--github-repository <owner/name>",
112727
- "With --contained: the one repository the run may reach, with a token in OXAGEN_CONTAINED_GITHUB_TOKEN"
113602
+ "With --contained: the one repository the run may fetch and push, through Oxagen's Git custody. The workspace must have it bound."
112728
113603
  ).argument("[command...]", "After --: the agent's command and its arguments").action(
112729
113604
  async (command2, opts) => {
112730
113605
  const { handleAgentRun: handleAgentRun2 } = await Promise.resolve().then(() => (init_tacho(), tacho_exports));