@oxagen/cli 2.1.4-501 → 2.1.4-504

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 +251 -164
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -8747,6 +8747,95 @@ var init_claude_desktop_writer = __esm({
8747
8747
  }
8748
8748
  });
8749
8749
 
8750
+ // ../../packages/tacho/src/claude-code/context.ts
8751
+ import { createHash as createHash3 } from "node:crypto";
8752
+ function digestText(value) {
8753
+ return `sha256:${createHash3("sha256").update(value, "utf8").digest("hex")}`;
8754
+ }
8755
+ function deliveredContext(text4, bundleEtag) {
8756
+ return text4 === null ? { chars: 0, bundle_etag: bundleEtag } : { digest: digestText(text4), chars: text4.length, bundle_etag: bundleEtag };
8757
+ }
8758
+ function readDeliveredContext(value) {
8759
+ if (typeof value !== "object" || value === null) return void 0;
8760
+ const { digest: digest6, chars, bundle_etag } = value;
8761
+ if (typeof chars !== "number" || !Number.isSafeInteger(chars) || chars < 0)
8762
+ return void 0;
8763
+ if (digest6 !== void 0 && !isSha256Digest(digest6)) return void 0;
8764
+ if (bundle_etag !== void 0 && (typeof bundle_etag !== "string" || bundle_etag.length === 0 || bundle_etag.length > 128))
8765
+ return void 0;
8766
+ return {
8767
+ ...digest6 !== void 0 ? { digest: digest6 } : {},
8768
+ chars,
8769
+ ...bundle_etag !== void 0 ? { bundle_etag } : {}
8770
+ };
8771
+ }
8772
+ function snapshotEnv(env, secretPattern = DEFAULT_SECRET_ENV_PATTERN) {
8773
+ const out = {};
8774
+ for (const [key, value] of Object.entries(env)) {
8775
+ if (value === void 0) {
8776
+ continue;
8777
+ }
8778
+ if (!/^(CLAUDE|OTEL|ANTHROPIC|TERM_PROGRAM$|SHELL$|CI$|GITHUB_ACTIONS$)/.test(
8779
+ key
8780
+ )) {
8781
+ continue;
8782
+ }
8783
+ if (secretPattern.test(key)) {
8784
+ continue;
8785
+ }
8786
+ out[key] = value.length > 1024 ? `${value.slice(0, 1024)}...` : value;
8787
+ }
8788
+ return out;
8789
+ }
8790
+ function hostFactsFromEnv(env) {
8791
+ const facts = {};
8792
+ const pid = env["CLAUDE_PID"];
8793
+ if (pid !== void 0 && /^\d+$/.test(pid)) {
8794
+ facts.claude_pid = Number(pid);
8795
+ }
8796
+ if (env["CLAUDE_CODE_EXECPATH"] !== void 0) {
8797
+ facts.claude_execpath = env["CLAUDE_CODE_EXECPATH"];
8798
+ }
8799
+ if (env["CLAUDE_CODE_CHILD_SESSION"] !== void 0) {
8800
+ facts.is_child_session = env["CLAUDE_CODE_CHILD_SESSION"] === "1";
8801
+ }
8802
+ if (env["CLAUDE_CODE_BRIDGE_SESSION_ID"] !== void 0) {
8803
+ facts.bridge_session_id = env["CLAUDE_CODE_BRIDGE_SESSION_ID"];
8804
+ }
8805
+ return facts;
8806
+ }
8807
+ function contextFactsFromEnv(env) {
8808
+ const facts = {};
8809
+ if (env["CLAUDE_CODE_ENTRYPOINT"] !== void 0) {
8810
+ facts.entrypoint = env["CLAUDE_CODE_ENTRYPOINT"];
8811
+ }
8812
+ if (env["CLAUDE_EFFORT"] !== void 0) {
8813
+ facts.effort = env["CLAUDE_EFFORT"];
8814
+ }
8815
+ if (env["CLAUDE_PROJECT_DIR"] !== void 0) {
8816
+ facts.project_dir = env["CLAUDE_PROJECT_DIR"];
8817
+ }
8818
+ if (env["TERM_PROGRAM"] !== void 0) {
8819
+ facts.terminal_type = env["TERM_PROGRAM"];
8820
+ }
8821
+ return facts;
8822
+ }
8823
+ function harnessVersionFromExecPath(execPath) {
8824
+ if (execPath === void 0) {
8825
+ return void 0;
8826
+ }
8827
+ const match = /\/versions\/(\d+\.\d+\.\d+)(?:\/|$)/.exec(execPath);
8828
+ return match?.[1];
8829
+ }
8830
+ var DEFAULT_SECRET_ENV_PATTERN;
8831
+ var init_context2 = __esm({
8832
+ "../../packages/tacho/src/claude-code/context.ts"() {
8833
+ "use strict";
8834
+ init_digest();
8835
+ DEFAULT_SECRET_ENV_PATTERN = /KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|HEADERS|AUTH/i;
8836
+ }
8837
+ });
8838
+
8750
8839
  // ../../packages/tacho/src/claude-code/cursor-adapter.ts
8751
8840
  function isRecord(value) {
8752
8841
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -8780,6 +8869,10 @@ function translateCursorPayload(raw) {
8780
8869
  subagent_id: subagentId,
8781
8870
  subagent_type: subagentType,
8782
8871
  duration,
8872
+ task,
8873
+ description,
8874
+ summary,
8875
+ error_message: errorMessage2,
8783
8876
  ...rest
8784
8877
  } = raw;
8785
8878
  const id = str2(conversationId) ?? str2(cursorSessionId);
@@ -8815,6 +8908,19 @@ function translateCursorPayload(raw) {
8815
8908
  if (typeof duration === "number") out["duration_ms"] = duration;
8816
8909
  if (typeof subagentId === "string") out["agent_id"] = subagentId;
8817
8910
  if (typeof subagentType === "string") out["agent_type"] = subagentType;
8911
+ const digestOnly = [
8912
+ ["task", task],
8913
+ ["description", description],
8914
+ ["summary", summary]
8915
+ ];
8916
+ if (event === "postToolUseFailure" && errorMessage2 !== void 0)
8917
+ out["error"] = errorMessage2;
8918
+ else digestOnly.push(["error_message", errorMessage2]);
8919
+ for (const [name, value] of digestOnly) {
8920
+ if (typeof value !== "string") continue;
8921
+ out[`${name}_digest`] = digestText(value);
8922
+ out[`${name}_length`] = value.length;
8923
+ }
8818
8924
  return out;
8819
8925
  }
8820
8926
  function reasonOf(value, fallback) {
@@ -8881,6 +8987,7 @@ var CURSOR_TO_CLAUDE_EVENT, CURSOR_HOOK_EVENTS, CURSOR_ENFORCEMENT_EVENTS, CURSO
8881
8987
  var init_cursor_adapter = __esm({
8882
8988
  "../../packages/tacho/src/claude-code/cursor-adapter.ts"() {
8883
8989
  "use strict";
8990
+ init_context2();
8884
8991
  CURSOR_TO_CLAUDE_EVENT = {
8885
8992
  sessionStart: "SessionStart",
8886
8993
  sessionEnd: "SessionEnd",
@@ -9349,7 +9456,7 @@ var init_agents = __esm({
9349
9456
  });
9350
9457
 
9351
9458
  // ../../packages/tacho/src/ids.ts
9352
- import { createHash as createHash3, randomBytes as randomBytes2 } from "node:crypto";
9459
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
9353
9460
  function uuidBytes(uuid) {
9354
9461
  const hex = uuid.replace(/-/g, "");
9355
9462
  if (!/^[0-9a-f]{32}$/i.test(hex)) {
@@ -9362,7 +9469,7 @@ function formatUuid(bytes) {
9362
9469
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
9363
9470
  }
9364
9471
  function uuidv5(namespace, name) {
9365
- const hash3 = createHash3("sha1").update(uuidBytes(namespace)).update(Buffer.from(name, "utf8")).digest();
9472
+ const hash3 = createHash4("sha1").update(uuidBytes(namespace)).update(Buffer.from(name, "utf8")).digest();
9366
9473
  const bytes = Buffer.from(hash3.subarray(0, 16));
9367
9474
  bytes[6] = (bytes[6] ?? 0) & 15 | 80;
9368
9475
  bytes[8] = (bytes[8] ?? 0) & 63 | 128;
@@ -9375,11 +9482,11 @@ function eventIdIdem(sessionUuidValue, seq) {
9375
9482
  if (!Number.isInteger(seq) || seq < 0) {
9376
9483
  throw new TypeError(`seq must be a non-negative integer, got ${seq}`);
9377
9484
  }
9378
- const hash3 = createHash3("sha256").update(sessionUuidValue).update(" ").update(String(seq)).digest("hex");
9485
+ const hash3 = createHash4("sha256").update(sessionUuidValue).update(" ").update(String(seq)).digest("hex");
9379
9486
  return `evt_${hash3}`;
9380
9487
  }
9381
9488
  function effectId(sessionUuidValue, toolUseId, target2) {
9382
- return `eff_${createHash3("sha256").update(sessionUuidValue).update(" ").update(toolUseId).update(" ").update(target2).digest("hex")}`;
9489
+ return `eff_${createHash4("sha256").update(sessionUuidValue).update(" ").update(toolUseId).update(" ").update(target2).digest("hex")}`;
9383
9490
  }
9384
9491
  function ulid(now = Date.now()) {
9385
9492
  let time = now;
@@ -9405,7 +9512,7 @@ function backfillEventId(sessionUuidValue, seq, rawSourceDigest, tsMs) {
9405
9512
  timePart = CROCKFORD[time % 32] + timePart;
9406
9513
  time = Math.floor(time / 32);
9407
9514
  }
9408
- const hash3 = createHash3("sha256").update(sessionUuidValue).update(" ").update(String(seq)).update(" ").update(rawSourceDigest).digest();
9515
+ const hash3 = createHash4("sha256").update(sessionUuidValue).update(" ").update(String(seq)).update(" ").update(rawSourceDigest).digest();
9409
9516
  let bits2 = 0n;
9410
9517
  for (let index = 0; index < 10; index += 1)
9411
9518
  bits2 = bits2 << 8n | BigInt(hash3[index] ?? 0);
@@ -9882,95 +9989,6 @@ var init_stella_writer = __esm({
9882
9989
  }
9883
9990
  });
9884
9991
 
9885
- // ../../packages/tacho/src/claude-code/context.ts
9886
- import { createHash as createHash4 } from "node:crypto";
9887
- function digestText(value) {
9888
- return `sha256:${createHash4("sha256").update(value, "utf8").digest("hex")}`;
9889
- }
9890
- function deliveredContext(text4, bundleEtag) {
9891
- return text4 === null ? { chars: 0, bundle_etag: bundleEtag } : { digest: digestText(text4), chars: text4.length, bundle_etag: bundleEtag };
9892
- }
9893
- function readDeliveredContext(value) {
9894
- if (typeof value !== "object" || value === null) return void 0;
9895
- const { digest: digest6, chars, bundle_etag } = value;
9896
- if (typeof chars !== "number" || !Number.isSafeInteger(chars) || chars < 0)
9897
- return void 0;
9898
- if (digest6 !== void 0 && !isSha256Digest(digest6)) return void 0;
9899
- if (bundle_etag !== void 0 && (typeof bundle_etag !== "string" || bundle_etag.length === 0 || bundle_etag.length > 128))
9900
- return void 0;
9901
- return {
9902
- ...digest6 !== void 0 ? { digest: digest6 } : {},
9903
- chars,
9904
- ...bundle_etag !== void 0 ? { bundle_etag } : {}
9905
- };
9906
- }
9907
- function snapshotEnv(env, secretPattern = DEFAULT_SECRET_ENV_PATTERN) {
9908
- const out = {};
9909
- for (const [key, value] of Object.entries(env)) {
9910
- if (value === void 0) {
9911
- continue;
9912
- }
9913
- if (!/^(CLAUDE|OTEL|ANTHROPIC|TERM_PROGRAM$|SHELL$|CI$|GITHUB_ACTIONS$)/.test(
9914
- key
9915
- )) {
9916
- continue;
9917
- }
9918
- if (secretPattern.test(key)) {
9919
- continue;
9920
- }
9921
- out[key] = value.length > 1024 ? `${value.slice(0, 1024)}...` : value;
9922
- }
9923
- return out;
9924
- }
9925
- function hostFactsFromEnv(env) {
9926
- const facts = {};
9927
- const pid = env["CLAUDE_PID"];
9928
- if (pid !== void 0 && /^\d+$/.test(pid)) {
9929
- facts.claude_pid = Number(pid);
9930
- }
9931
- if (env["CLAUDE_CODE_EXECPATH"] !== void 0) {
9932
- facts.claude_execpath = env["CLAUDE_CODE_EXECPATH"];
9933
- }
9934
- if (env["CLAUDE_CODE_CHILD_SESSION"] !== void 0) {
9935
- facts.is_child_session = env["CLAUDE_CODE_CHILD_SESSION"] === "1";
9936
- }
9937
- if (env["CLAUDE_CODE_BRIDGE_SESSION_ID"] !== void 0) {
9938
- facts.bridge_session_id = env["CLAUDE_CODE_BRIDGE_SESSION_ID"];
9939
- }
9940
- return facts;
9941
- }
9942
- function contextFactsFromEnv(env) {
9943
- const facts = {};
9944
- if (env["CLAUDE_CODE_ENTRYPOINT"] !== void 0) {
9945
- facts.entrypoint = env["CLAUDE_CODE_ENTRYPOINT"];
9946
- }
9947
- if (env["CLAUDE_EFFORT"] !== void 0) {
9948
- facts.effort = env["CLAUDE_EFFORT"];
9949
- }
9950
- if (env["CLAUDE_PROJECT_DIR"] !== void 0) {
9951
- facts.project_dir = env["CLAUDE_PROJECT_DIR"];
9952
- }
9953
- if (env["TERM_PROGRAM"] !== void 0) {
9954
- facts.terminal_type = env["TERM_PROGRAM"];
9955
- }
9956
- return facts;
9957
- }
9958
- function harnessVersionFromExecPath(execPath) {
9959
- if (execPath === void 0) {
9960
- return void 0;
9961
- }
9962
- const match = /\/versions\/(\d+\.\d+\.\d+)(?:\/|$)/.exec(execPath);
9963
- return match?.[1];
9964
- }
9965
- var DEFAULT_SECRET_ENV_PATTERN;
9966
- var init_context2 = __esm({
9967
- "../../packages/tacho/src/claude-code/context.ts"() {
9968
- "use strict";
9969
- init_digest();
9970
- DEFAULT_SECRET_ENV_PATTERN = /KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|HEADERS|AUTH/i;
9971
- }
9972
- });
9973
-
9974
9992
  // ../../packages/tacho/src/host/process-scan.ts
9975
9993
  import { execFile, spawnSync } from "node:child_process";
9976
9994
  import { readFileSync as readFileSync4, readlinkSync as readlinkSync2 } from "node:fs";
@@ -10923,11 +10941,18 @@ async function loadCedarRuntimeFrom(sources) {
10923
10941
  return null;
10924
10942
  }
10925
10943
  }
10926
- function loadCedarRuntime() {
10927
- cached ??= loadCedarRuntimeFrom(DEFAULT_SOURCES);
10944
+ function loadCedarRuntime(now = Date.now) {
10945
+ if (failedAt !== void 0 && now() - failedAt >= CEDAR_RETRY_AFTER_MS) {
10946
+ cached = void 0;
10947
+ failedAt = void 0;
10948
+ }
10949
+ cached ??= loadCedarRuntimeFrom(DEFAULT_SOURCES).then((runtime) => {
10950
+ if (runtime === null) failedAt = now();
10951
+ return runtime;
10952
+ });
10928
10953
  return cached;
10929
10954
  }
10930
- var CEDAR_WASM_ASSET, SPECIFIER, DEFAULT_SOURCES, cached;
10955
+ var CEDAR_WASM_ASSET, SPECIFIER, DEFAULT_SOURCES, CEDAR_RETRY_AFTER_MS, cached, failedAt;
10931
10956
  var init_runtime = __esm({
10932
10957
  "../../packages/tacho/src/policy/runtime.ts"() {
10933
10958
  "use strict";
@@ -10948,6 +10973,7 @@ var init_runtime = __esm({
10948
10973
  SPECIFIER
10949
10974
  )
10950
10975
  };
10976
+ CEDAR_RETRY_AFTER_MS = 6e4;
10951
10977
  }
10952
10978
  });
10953
10979
 
@@ -11818,9 +11844,18 @@ function normalizeHook(raw, env, options) {
11818
11844
  }
11819
11845
  case "SessionEnd": {
11820
11846
  const reason3 = str3(input["reason"]) ?? str3(input["end_reason"]) ?? "other";
11847
+ const completed = [
11848
+ "prompt_input_exit",
11849
+ "clear",
11850
+ "other",
11851
+ "resume",
11852
+ "completed",
11853
+ "window_close",
11854
+ "user_close"
11855
+ ].includes(reason3);
11821
11856
  const body2 = {
11822
11857
  session_end_reason: reason3,
11823
- session_outcome: reason3 === "prompt_input_exit" || reason3 === "other" || reason3 === "clear" ? "completed" : "aborted"
11858
+ session_outcome: completed ? "completed" : "aborted"
11824
11859
  };
11825
11860
  return [draft("agent_stop", body2, { hook_source_kind: reason3 })];
11826
11861
  }
@@ -22025,33 +22060,35 @@ function restoreClaude(options, released) {
22025
22060
  else delete settings[HELPER_KEY];
22026
22061
  touched = true;
22027
22062
  }
22063
+ const current = envOf2(settings);
22064
+ const foreignKey = current !== void 0 && (isSecret(current[CLAUDE_API_KEY]) || isSecret(current[CLAUDE_AUTH_TOKEN]));
22065
+ const why = foreignKey ? { reason: "foreign_key_present" } : {};
22028
22066
  let secretRestored = false;
22029
- if (released !== void 0) {
22067
+ if (released !== void 0 && !foreignKey) {
22030
22068
  const member2 = sidecar?.taken.find((t) => t.kind === released.kind)?.member ?? (released.kind === "bearer" ? CLAUDE_AUTH_TOKEN : CLAUDE_API_KEY);
22031
- const env = { ...envOf2(settings) ?? {} };
22069
+ const env = { ...current ?? {} };
22032
22070
  env[member2] = released.secret;
22033
22071
  settings["env"] = env;
22034
22072
  touched = true;
22035
22073
  secretRestored = true;
22036
22074
  } else if (sidecar?.created_env === true) {
22037
- const env = envOf2(settings);
22038
- if (env !== void 0 && Object.keys(env).length === 0) {
22075
+ if (current !== void 0 && Object.keys(current).length === 0) {
22039
22076
  delete settings["env"];
22040
22077
  touched = true;
22041
22078
  }
22042
22079
  }
22043
22080
  if (!touched) {
22044
22081
  dropSidecar();
22045
- return { changed: false, secretRestored };
22082
+ return { changed: false, secretRestored, ...why };
22046
22083
  }
22047
22084
  const next = serializeLike2(text4, settings);
22048
22085
  if (next === text4) {
22049
22086
  dropSidecar();
22050
- return { changed: false, secretRestored };
22087
+ return { changed: false, secretRestored, ...why };
22051
22088
  }
22052
22089
  writeAtomicPreserving2(file, next);
22053
22090
  dropSidecar();
22054
- return { changed: true, secretRestored };
22091
+ return { changed: true, secretRestored, ...why };
22055
22092
  }
22056
22093
  function restoreCodex(options, released) {
22057
22094
  const file = fileFor2("codex", options);
@@ -23497,7 +23534,7 @@ import { readFileSync as readFileSync11 } from "node:fs";
23497
23534
  import { dirname as dirname8, resolve as resolve2 } from "node:path";
23498
23535
  import { fileURLToPath } from "node:url";
23499
23536
  function readVersion() {
23500
- if (true) return "2.1.4-501";
23537
+ if (true) return "2.1.4-504";
23501
23538
  try {
23502
23539
  const here = dirname8(fileURLToPath(import.meta.url));
23503
23540
  const pkg = JSON.parse(
@@ -23644,13 +23681,14 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
23644
23681
  }
23645
23682
  const transient = transientBinDir(binDir, env);
23646
23683
  const flagged = transient !== void 0 ? { transient } : {};
23684
+ const node2 = stableExecutablePath(nodePath, platform, exists);
23647
23685
  return {
23648
- hookCommand: `${shellQuote(nodePath, platform)} ${shellQuote(P.join(binDir, "tacho-hook.mjs"), platform)}`,
23686
+ hookCommand: `${shellQuote(node2, platform)} ${shellQuote(P.join(binDir, "tacho-hook.mjs"), platform)}`,
23649
23687
  credentialHelperCommand: helperCommandFor(
23650
- `${shellQuote(nodePath, platform)} ${shellQuote(P.join(binDir, "tacho.mjs"), platform)}`
23688
+ `${shellQuote(node2, platform)} ${shellQuote(P.join(binDir, "tacho.mjs"), platform)}`
23651
23689
  ),
23652
- daemonCommand: [nodePath, P.join(binDir, "tachod.mjs")],
23653
- mcpStdioCommand: [nodePath, P.join(binDir, "tacho.mjs"), "mcp-stdio"],
23690
+ daemonCommand: [node2, P.join(binDir, "tachod.mjs")],
23691
+ mcpStdioCommand: [node2, P.join(binDir, "tacho.mjs"), "mcp-stdio"],
23654
23692
  binDir,
23655
23693
  ...flagged
23656
23694
  };
@@ -23668,7 +23706,10 @@ function oxagenRuntimeCommands(entry2 = process.argv[1], env = process.env, node
23668
23706
  } else if (native) {
23669
23707
  prefix = [stableExecutablePath(nodePath, platform, exists)];
23670
23708
  } else if (entry2 !== void 0 && !/\.[cm]?tsx?$/.test(entry2)) {
23671
- prefix = [nodePath, P.resolve(entry2)];
23709
+ prefix = [
23710
+ stableExecutablePath(nodePath, platform, exists),
23711
+ P.resolve(entry2)
23712
+ ];
23672
23713
  } else {
23673
23714
  return runtimeCommands(void 0, env, nodePath, platform, false, exists);
23674
23715
  }
@@ -25561,9 +25602,9 @@ ${lines.join("\n")}
25561
25602
  * one torn write (a crash mid-append, before `repairTail` runs at the next
25562
25603
  * startup) must not stop every reader of this session. The retention
25563
25604
  * sweep reads through here. `unshipped` and `stats` read through
25564
- * `eventsAfterShipped`, and `head`, `compact`, `lastSeqOf`, and
25565
- * `appendRecovered` read back from the tail through `eventsBackward`. Both
25566
- * skip a bad line the same way.
25605
+ * `eventsAfterShipped`, and `head`, `eventsSince`, `compact`, `lastSeqOf`,
25606
+ * and `appendRecovered` read back from the tail through `eventsBackward`.
25607
+ * Both skip a bad line the same way.
25567
25608
  */
25568
25609
  read(sessionUuid2) {
25569
25610
  const path2 = this.fileFor(sessionUuid2);
@@ -26036,6 +26077,26 @@ ${lines.join("\n")}
26036
26077
  const suffix = ".bodies.jsonl";
26037
26078
  return readdirSync7(this.dir).filter((name) => name.endsWith(suffix)).map((name) => name.slice(0, -suffix.length));
26038
26079
  }
26080
+ /**
26081
+ * A session's latest events, in seq order: read back from the tail, and
26082
+ * stopped at the first event whose `ts` is before `since` (epoch ms). That
26083
+ * event is left out, and so is everything written before it. An event
26084
+ * whose `ts` does not parse cannot be placed in time, so it is kept and the
26085
+ * walk goes on past it.
26086
+ *
26087
+ * The read costs the lines it walks, not the file. It takes the file's seq
26088
+ * order for time order, and a caller has to accept that: an event written
26089
+ * late with an earlier `ts` ends the walk, so an event written before it
26090
+ * is not returned, whatever its own `ts`.
26091
+ */
26092
+ eventsSince(sessionUuid2, since) {
26093
+ const out = [];
26094
+ for (const event of this.eventsBackward(sessionUuid2)) {
26095
+ if (Date.parse(event.ts) < since) break;
26096
+ out.push(event);
26097
+ }
26098
+ return out.reverse();
26099
+ }
26039
26100
  /**
26040
26101
  * The last sealed event of a session, if any: the last line of its file
26041
26102
  * that parses, read back from the tail.
@@ -37730,34 +37791,33 @@ function createGitLane(deps) {
37730
37791
  function recordReconciliation(session, reading, snapshot) {
37731
37792
  const mark = session.recorder.markChain();
37732
37793
  try {
37733
- record([
37734
- session.recorder.sealCollectorEvent(
37735
- "oxagen:worktree_reconciled",
37736
- worktreeReconciledBody(reading.changes),
37737
- {
37738
- attrs: {
37739
- // What the list measures, so a reader can tell a list that
37740
- // leaves out pulled commits and earlier edits from one that
37741
- // does not (ADR-188).
37742
- changes_basis: reading.basis,
37743
- pre_session_changes: PRE_SESSION_CHANGES[reading.preexisting],
37744
- // Whether the rows of files that held edits before the session
37745
- // count the session's lines alone, or the whole file against
37746
- // `HEAD` for want of a copy.
37747
- ...reading.preSessionCounts !== void 0 ? { pre_session_edit_counts: reading.preSessionCounts } : {},
37748
- ...snapshot ? {
37749
- worktree_root: snapshot.root,
37750
- ...snapshot.repository ? { repository_url: snapshot.repository } : {},
37751
- ...snapshot.baseline ? { diff_base_sha: snapshot.baseline } : {},
37752
- ...snapshot.head ? { diff_head_sha: snapshot.head } : {},
37753
- diff_complete: String(snapshot.complete),
37754
- diff_limitations: snapshot.limitations.join(",")
37755
- } : {}
37756
- },
37757
- ...snapshot ? { content: jsonContent(JSON.stringify(snapshot)) } : {}
37758
- }
37759
- )
37760
- ]);
37794
+ const reconciled = session.recorder.sealCollectorEvent(
37795
+ "oxagen:worktree_reconciled",
37796
+ worktreeReconciledBody(reading.changes),
37797
+ {
37798
+ attrs: {
37799
+ // What the list measures, so a reader can tell a list that
37800
+ // leaves out pulled commits and earlier edits from one that
37801
+ // does not (ADR-188).
37802
+ changes_basis: reading.basis,
37803
+ pre_session_changes: PRE_SESSION_CHANGES[reading.preexisting],
37804
+ // Whether the rows of files that held edits before the session
37805
+ // count the session's lines alone, or the whole file against
37806
+ // `HEAD` for want of a copy.
37807
+ ...reading.preSessionCounts !== void 0 ? { pre_session_edit_counts: reading.preSessionCounts } : {},
37808
+ ...snapshot ? {
37809
+ worktree_root: snapshot.root,
37810
+ ...snapshot.repository ? { repository_url: snapshot.repository } : {},
37811
+ ...snapshot.baseline ? { diff_base_sha: snapshot.baseline } : {},
37812
+ ...snapshot.head ? { diff_head_sha: snapshot.head } : {},
37813
+ diff_complete: String(snapshot.complete),
37814
+ diff_limitations: snapshot.limitations.join(",")
37815
+ } : {}
37816
+ },
37817
+ ...snapshot ? { content: jsonContent(JSON.stringify(snapshot)) } : {}
37818
+ }
37819
+ );
37820
+ record([reconciled], session.recorder.takeBodies());
37761
37821
  } catch (error) {
37762
37822
  session.recorder.rollbackChain(mark);
37763
37823
  requestGitRead(session.recorder.sessionUuid, {
@@ -43563,6 +43623,10 @@ function createDaySpend(deps) {
43563
43623
  let held;
43564
43624
  function own(day) {
43565
43625
  if (held !== void 0 && held.day >= day) return held.ownMicros;
43626
+ if (held !== void 0) {
43627
+ held = { day, ownMicros: 0 };
43628
+ return 0;
43629
+ }
43566
43630
  let seeded = 0;
43567
43631
  try {
43568
43632
  seeded = nonNegative2(deps.priorDaySpendMicros?.(day) ?? 0);
@@ -47729,7 +47793,15 @@ async function initializeDaemon(options, server, port, ready) {
47729
47793
  });
47730
47794
  }
47731
47795
  function record(events, bodies = []) {
47732
- if (events.length === 0) return;
47796
+ if (events.length === 0) {
47797
+ if (bodies.length > 0) {
47798
+ const where = bodies.map((body2) => `session ${body2.session_uuid} seq ${body2.seq}`).join(", ");
47799
+ log(
47800
+ `dropped ${bodies.length} frame ${bodies.length === 1 ? "body" : "bodies"} handed over without an event (${where}). A body ships only with its own event, so this content will not ship.`
47801
+ );
47802
+ }
47803
+ return;
47804
+ }
47733
47805
  const retention = retentionInForce();
47734
47806
  wal.append(
47735
47807
  events,
@@ -49178,6 +49250,24 @@ async function initializeDaemon(options, server, port, ready) {
49178
49250
  }
49179
49251
  }
49180
49252
  await refreshUpstreams();
49253
+ function walDaySpendMicros(day) {
49254
+ const start = Date.parse(`${day}T00:00:00.000Z`);
49255
+ const end = Date.parse(nextUtcDayStart(day));
49256
+ let total2 = 0;
49257
+ for (const session of wal.sessions()) {
49258
+ for (const event of wal.eventsSince(session, start)) {
49259
+ if (event.kind !== "llm_call") continue;
49260
+ if (event.attrs[TACHO_METERING_ATTR] !== TACHO_METERING_OBSERVED)
49261
+ continue;
49262
+ const at = Date.parse(event.ts);
49263
+ if (!(at >= start && at < end)) continue;
49264
+ const cost = event.body.cost_usd_micros;
49265
+ if (typeof cost === "number") total2 += cost;
49266
+ }
49267
+ }
49268
+ return total2;
49269
+ }
49270
+ let startupDaySpend;
49181
49271
  const modelProxy = createModelProxy({
49182
49272
  registry: registry2,
49183
49273
  hostRecorder: () => hostRecorder,
@@ -49191,25 +49281,14 @@ async function initializeDaemon(options, server, port, ready) {
49191
49281
  ...displacedUpstreams,
49192
49282
  ...options.modelUpstreams
49193
49283
  }),
49194
- // A restart must not hand the agent's day back either (ADR-160). Only a
49195
- // session whose last frame is from `day` can hold a frame from it, so
49196
- // the rest of the week the WAL keeps is not parsed.
49284
+ // A restart must not hand the agent's day back either (ADR-160). The day
49285
+ // counter asks this once per process (`createDaySpend`), so the figure
49286
+ // read at startup is used once. A first call on a later day than
49287
+ // startup's reads the WAL for that day.
49197
49288
  priorDaySpendMicros: (day) => {
49198
- const start = Date.parse(`${day}T00:00:00.000Z`);
49199
- let total2 = 0;
49200
- for (const session of wal.sessions()) {
49201
- const last = wal.lastEvent(session);
49202
- if (last !== void 0 && Date.parse(last.ts) < start) continue;
49203
- for (const event of wal.read(session)) {
49204
- if (event.kind !== "llm_call") continue;
49205
- if (event.attrs[TACHO_METERING_ATTR] !== TACHO_METERING_OBSERVED)
49206
- continue;
49207
- if (utcDay(Date.parse(event.ts)) !== day) continue;
49208
- const cost = event.body.cost_usd_micros;
49209
- if (typeof cost === "number") total2 += cost;
49210
- }
49211
- }
49212
- return total2;
49289
+ const seeded = startupDaySpend;
49290
+ startupDaySpend = void 0;
49291
+ return seeded?.day === day ? seeded.micros : walDaySpendMicros(day);
49213
49292
  },
49214
49293
  recordedDaySpend: () => recordedDaySpend === void 0 ? void 0 : {
49215
49294
  day: recordedDaySpend.day,
@@ -49587,6 +49666,14 @@ async function initializeDaemon(options, server, port, ready) {
49587
49666
  );
49588
49667
  }
49589
49668
  }
49669
+ try {
49670
+ const day = utcDay(now());
49671
+ startupDaySpend = { day, micros: walDaySpendMicros(day) };
49672
+ } catch (error) {
49673
+ log(
49674
+ `day spend not read from the WAL at startup: ${error instanceof Error ? error.message : String(error)}`
49675
+ );
49676
+ }
49590
49677
  ready(api);
49591
49678
  servingSince = now();
49592
49679
  if (options.listen ?? true) {
@@ -52268,7 +52355,7 @@ var init_package = __esm({
52268
52355
  "package.json"() {
52269
52356
  package_default = {
52270
52357
  name: "@oxagen/cli",
52271
- version: "2.1.4-501",
52358
+ version: "2.1.4-504",
52272
52359
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
52273
52360
  type: "module",
52274
52361
  main: "./dist/index.js",
@@ -52320,11 +52407,11 @@ var init_package = __esm({
52320
52407
  devDependencies: {
52321
52408
  "@oxagen/telemetry": "workspace:*",
52322
52409
  "@types/node": "25.9.8",
52323
- "@vitest/coverage-v8": "2.1.9",
52410
+ "@vitest/coverage-v8": "3.2.6",
52324
52411
  esbuild: "0.28.2",
52325
52412
  tsx: "4.23.5",
52326
52413
  typescript: "6.0.3",
52327
- vitest: "2.1.9"
52414
+ vitest: "3.2.6"
52328
52415
  }
52329
52416
  };
52330
52417
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxagen/cli",
3
- "version": "2.1.4-501",
3
+ "version": "2.1.4-504",
4
4
  "description": "Oxagen governance CLI — spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
5
5
  "type": "module",
6
6
  "bin": {