@oxagen/cli 2.1.4-494 → 2.1.4-498

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 +190 -43
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -4839,6 +4839,16 @@ var init_envelope = __esm({
4839
4839
  ]).optional(),
4840
4840
  tool_denial_kind: short.optional(),
4841
4841
  tool_result_tokens: u32.optional(),
4842
+ /**
4843
+ * How `tool_result_tokens` is known (#5339). `reported` is Claude Code's own
4844
+ * count on its OTel tool span. `estimated` is the recorder's: the result's
4845
+ * UTF-8 bytes over four, from the hook that saw the result. A count with no
4846
+ * basis was written before the member existed, and only the OTel span wrote
4847
+ * a count then, so a reader takes it as `reported`. The recorder writes the
4848
+ * two together. The envelope does not require the pair, so a frame from a
4849
+ * host older than the member still parses.
4850
+ */
4851
+ tool_result_tokens_basis: external_exports.enum(TOKEN_SOURCE_BASES).optional(),
4842
4852
  batch_size: u8.optional(),
4843
4853
  batch_index: u8.optional(),
4844
4854
  attribution_skill: short.optional(),
@@ -11529,7 +11539,10 @@ function normalizeHook(raw, env, options) {
11529
11539
  body2["tool_output_digest"] = digestJcs(
11530
11540
  input.tool_response
11531
11541
  );
11532
- body2["tool_output_bytes"] = jsonByteLength(input.tool_response);
11542
+ const bytes = jsonByteLength(input.tool_response);
11543
+ body2["tool_output_bytes"] = bytes;
11544
+ body2["tool_result_tokens"] = Math.ceil(bytes / 4);
11545
+ body2["tool_result_tokens_basis"] = "estimated";
11533
11546
  }
11534
11547
  if (failed3) {
11535
11548
  const error = input.error ?? input["error_type"];
@@ -14513,7 +14526,24 @@ function steeringFactsOf(steering) {
14513
14526
  steering_tokens_basis: "estimated"
14514
14527
  };
14515
14528
  }
14516
- 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;
14529
+ function contextFactsOf(tokens) {
14530
+ if (tokens <= 0) return {};
14531
+ return {
14532
+ context_frame_tokens: tokens,
14533
+ context_frame_tokens_basis: "estimated"
14534
+ };
14535
+ }
14536
+ function injectedMarksOf(value) {
14537
+ if (!Array.isArray(value)) return [];
14538
+ return value.flatMap((mark) => {
14539
+ if (typeof mark !== "object" || mark === null) return [];
14540
+ const { at, tokens } = mark;
14541
+ if (typeof at !== "string" || typeof tokens !== "number") return [];
14542
+ if (!Number.isFinite(tokens)) return [];
14543
+ return [{ at, tokens: clampU32(tokens) }];
14544
+ });
14545
+ }
14546
+ var REQUEST_FULL_DIGEST_ATTR, SYSTEM_CONTEXT_PARTS_OMITTED_ATTR, MEMORY_REQUESTS, LISTED_PER_TURN, INJECTED_MARKS_MAX, U32_MAX, NAME_MAX, tokenSourceFacts, SystemContextMemory, SHARED_SYSTEM_CONTEXT_MEMORY, CONVERSATION_FIELDS2, STEERING_ASSEMBLY_PART, SystemContextTracker;
14517
14547
  var init_system_context = __esm({
14518
14548
  "../../packages/tacho/src/claude-code/system-context.ts"() {
14519
14549
  "use strict";
@@ -14525,6 +14555,7 @@ var init_system_context = __esm({
14525
14555
  SYSTEM_CONTEXT_PARTS_OMITTED_ATTR = "oxagen.system_context_parts_omitted";
14526
14556
  MEMORY_REQUESTS = 512;
14527
14557
  LISTED_PER_TURN = 16;
14558
+ INJECTED_MARKS_MAX = 32;
14528
14559
  U32_MAX = 4294967295;
14529
14560
  NAME_MAX = 512;
14530
14561
  tokenSourceFacts = modelFacts.pick({
@@ -14581,11 +14612,16 @@ var init_system_context = __esm({
14581
14612
  this.steering = parts === void 0 ? void 0 : { parts, tokens: state?.steeringTokens ?? sum(parts) };
14582
14613
  this.listedTurn = state?.listedTurn;
14583
14614
  this.listed = [...state?.listed ?? []];
14615
+ this.injected = injectedMarksOf(state?.injected);
14616
+ const floor = state?.injectedFloor;
14617
+ this.injectedFloor = typeof floor === "number" && Number.isFinite(floor) ? clampU32(floor) : 0;
14584
14618
  }
14585
14619
  memory;
14586
14620
  steering;
14587
14621
  listedTurn;
14588
14622
  listed;
14623
+ injected;
14624
+ injectedFloor;
14589
14625
  state() {
14590
14626
  const state = {};
14591
14627
  if (this.steering !== void 0) {
@@ -14594,6 +14630,9 @@ var init_system_context = __esm({
14594
14630
  }
14595
14631
  if (this.listedTurn !== void 0) state.listedTurn = this.listedTurn;
14596
14632
  if (this.listed.length > 0) state.listed = [...this.listed];
14633
+ if (this.injected.length > 0)
14634
+ state.injected = this.injected.map((mark) => ({ ...mark }));
14635
+ if (this.injectedFloor > 0) state.injectedFloor = this.injectedFloor;
14597
14636
  return state;
14598
14637
  }
14599
14638
  /** Take the steering a sealed `steering.manifest` frame names. */
@@ -14601,21 +14640,84 @@ var init_system_context = __esm({
14601
14640
  const steering = steeringContext(body2);
14602
14641
  if (steering !== void 0) this.steering = steering;
14603
14642
  }
14643
+ /**
14644
+ * Note text an Oxagen hook answer handed the agent after its start, at
14645
+ * `at`, when the answer left (#5339). The text joins the conversation, so
14646
+ * every call made from `at` on carries it. Empty text notes nothing.
14647
+ *
14648
+ * The caller passes only text the session's own conversation reads. The
14649
+ * start's text is the steering manifest's to count, and a subagent's text
14650
+ * stays in the subagent's conversation.
14651
+ */
14652
+ noteInjectedContext(text4, at) {
14653
+ const tokens = clampU32(budgetTokens(text4));
14654
+ if (tokens === 0) return;
14655
+ this.markInjected(at, this.injectedTotal() + tokens);
14656
+ }
14657
+ /**
14658
+ * Empty the running total at `at`: a compaction or a `/clear` took the
14659
+ * text out of the conversation. A call made before `at` still reads the
14660
+ * total it was made with.
14661
+ */
14662
+ clearInjectedContext(at) {
14663
+ if (this.injected.length === 0 && this.injectedFloor === 0) return;
14664
+ this.markInjected(at, 0);
14665
+ }
14666
+ injectedTotal() {
14667
+ return this.injected.at(-1)?.tokens ?? this.injectedFloor;
14668
+ }
14669
+ markInjected(at, tokens) {
14670
+ this.injected.push({ at, tokens: clampU32(tokens) });
14671
+ while (this.injected.length > INJECTED_MARKS_MAX) {
14672
+ const oldest = this.injected.shift();
14673
+ if (oldest !== void 0) this.injectedFloor = oldest.tokens;
14674
+ }
14675
+ }
14676
+ /**
14677
+ * The context Oxagen's hooks had handed the session when a call was made
14678
+ * at `at`: the latest mark at or before it. A record can arrive after
14679
+ * later answers left, since OTel exports in batches and the transcript is
14680
+ * read behind the session. A call with no readable time reads the latest
14681
+ * total. A call older than every mark kept reads the total before them.
14682
+ */
14683
+ injectedAt(at) {
14684
+ const when = at === void 0 ? Number.NaN : Date.parse(at);
14685
+ if (Number.isNaN(when)) return this.injectedTotal();
14686
+ for (let index = this.injected.length - 1; index >= 0; index -= 1) {
14687
+ const mark = this.injected[index];
14688
+ if (mark === void 0) continue;
14689
+ const markAt = Date.parse(mark.at);
14690
+ if (Number.isNaN(markAt) || markAt <= when) return mark.tokens;
14691
+ }
14692
+ return this.injectedFloor;
14693
+ }
14604
14694
  /**
14605
14695
  * Measure one model call. `content` is the proxy's exchange body, `attrs`
14606
- * the frame's attrs, and `turn` a key that changes when the turn does.
14696
+ * the frame's attrs, `turn` a key that changes when the turn does, and
14697
+ * `at` when the call was made.
14607
14698
  */
14608
- measure(content, attrs2, turn) {
14699
+ measure(content, attrs2, turn, at) {
14609
14700
  const recorded = requestOf(content);
14610
14701
  const resolved = recorded === void 0 ? void 0 : resolveRequest(recorded.request, this.memory);
14611
14702
  if (recorded === void 0 || resolved === void 0)
14612
- return this.checked(steeringFactsOf(this.steering), {}, () => {
14613
- });
14703
+ return this.checked(
14704
+ {
14705
+ ...steeringFactsOf(this.steering),
14706
+ ...contextFactsOf(this.injectedAt(at))
14707
+ },
14708
+ {},
14709
+ () => {
14710
+ }
14711
+ );
14614
14712
  const fullDigest = attrs2?.[REQUEST_FULL_DIGEST_ATTR];
14615
14713
  const key = isSha256Digest(fullDigest) ? fullDigest : recorded.request[PRIOR_MEMBER] === void 0 ? digestBytes(recorded.text) : void 0;
14616
14714
  if (key !== void 0) this.memory.set(key, resolved);
14617
- const steering = resolved.tools.length > 0 ? this.steering : void 0;
14618
- const facts = steeringFactsOf(steering);
14715
+ const main3 = resolved.tools.length > 0;
14716
+ const steering = main3 ? this.steering : void 0;
14717
+ const facts = {
14718
+ ...steeringFactsOf(steering),
14719
+ ...main3 ? contextFactsOf(this.injectedAt(at)) : {}
14720
+ };
14619
14721
  const parts = [
14620
14722
  ...resolved.system,
14621
14723
  ...resolved.instructions,
@@ -14648,15 +14750,23 @@ var init_system_context = __esm({
14648
14750
  /**
14649
14751
  * The token sources of a model call whose request the recorder never saw:
14650
14752
  * the counted OTel or transcript row of a call the proxy did not carry
14651
- * (#4493). Only the steering count is known without the request. The tool
14652
- * definition count, the context frame count, and the system context stay
14653
- * absent, never zero. Nothing changes on the tracker, so there is nothing
14654
- * to commit. The caller asks only for a call that carries the session's
14655
- * conversation, since a side call carries no steering.
14656
- */
14657
- measureUnseen() {
14658
- return this.checked(steeringFactsOf(this.steering), {}, () => {
14659
- }).facts;
14753
+ * (#4493). Two counts are known without the request: the steering, from
14754
+ * the manifest, and the context frames, from the hook answers noted before
14755
+ * `at`, when the call was made (#5339). The tool definition count and the
14756
+ * system context stay absent, never zero. Nothing changes on the tracker,
14757
+ * so there is nothing to commit. The caller asks only for a call that
14758
+ * carries the session's conversation, since a side call carries neither.
14759
+ */
14760
+ measureUnseen(at) {
14761
+ return this.checked(
14762
+ {
14763
+ ...steeringFactsOf(this.steering),
14764
+ ...contextFactsOf(this.injectedAt(at))
14765
+ },
14766
+ {},
14767
+ () => {
14768
+ }
14769
+ ).facts;
14660
14770
  }
14661
14771
  /**
14662
14772
  * Hand back facts the envelope accepts, or none. A body the envelope
@@ -15296,7 +15406,12 @@ function spanDraft(span, resource) {
15296
15406
  ...s(attrs2, "tool_name") !== void 0 ? { tool_name: s(attrs2, "tool_name") } : {},
15297
15407
  ...s(attrs2, "tool_use_id") !== void 0 ? { tool_use_id: s(attrs2, "tool_use_id") } : {},
15298
15408
  ...n(attrs2, "duration_ms") !== void 0 ? { tool_duration_ms: n(attrs2, "duration_ms") } : {},
15299
- ...n(attrs2, "result_tokens") !== void 0 ? { tool_result_tokens: n(attrs2, "result_tokens") } : {},
15409
+ // Claude Code's own count, so it is `reported`. A reader prefers it
15410
+ // to the hook's estimate of the same call (#5339).
15411
+ ...n(attrs2, "result_tokens") !== void 0 ? {
15412
+ tool_result_tokens: n(attrs2, "result_tokens"),
15413
+ tool_result_tokens_basis: "reported"
15414
+ } : {},
15300
15415
  ...s(attrs2, "file_path") !== void 0 ? { tool_target: s(attrs2, "file_path") } : {},
15301
15416
  ...s(attrs2, "full_command") !== void 0 ? { tool_target: (s(attrs2, "full_command") ?? "").slice(0, 512) } : {},
15302
15417
  ...s(attrs2, "skill_name") !== void 0 ? { attribution_skill: s(attrs2, "skill_name") } : {},
@@ -16544,16 +16659,18 @@ var init_recorder = __esm({
16544
16659
  if (kind === "agent_stop") this.turnOpen = false;
16545
16660
  const sighting = kind === "llm_call" ? this.llmCallSighting(body2, fields.source ?? "collector") : NO_SIGHTING;
16546
16661
  const duplicate = sighting.attrs ?? {};
16662
+ const ts2 = fields.ts ?? this.now();
16547
16663
  const measured = kind === "llm_call" ? this.systemContext.measure(
16548
16664
  fields.content,
16549
16665
  fields.attrs,
16550
- this.turnOpen ? `turn:${this.turnSeq}` : `idle:${this.turnSeq}`
16666
+ this.turnOpen ? `turn:${this.turnSeq}` : `idle:${this.turnSeq}`,
16667
+ ts2
16551
16668
  ) : void 0;
16552
16669
  const event = this.seal(
16553
16670
  kind,
16554
16671
  measured !== void 0 ? { ...measured.facts, ...body2 } : body2,
16555
16672
  {
16556
- ts: fields.ts ?? this.now(),
16673
+ ts: ts2,
16557
16674
  source: fields.source ?? "collector",
16558
16675
  ...fields.hook_event_name !== void 0 ? { hook_event_name: fields.hook_event_name } : {},
16559
16676
  attrs: { ...fields.attrs, ...measured?.attrs, ...duplicate },
@@ -16967,19 +17084,32 @@ var init_recorder = __esm({
16967
17084
  * recorder knows without the call's request (#4493). Only the counted row,
16968
17085
  * the first sighting of its call, takes them. A stamped duplicate is never
16969
17086
  * summed, and the rollup joins a stamped proxy row back for the members the
16970
- * counted row lacks. The steering count is the one source known without
16971
- * the request, so the tool definitions and the system context stay absent
16972
- * (ADR-062, amendment of 2026-10-02). A member the producer set itself
16973
- * wins, as on a proxied call. Only a call on the session's own
16974
- * conversation comes here: an OTel record whose `query_source` says so
16975
- * ({@link onSessionConversation}), or a transcript `assistant` record,
16976
- * since Claude Code writes no side call to the transcript.
16977
- */
16978
- withUnseenRequestSources(body2, attrs2) {
17087
+ * counted row lacks. Two sources are known without the request: the
17088
+ * steering count, and the context frames Oxagen's hooks handed the session
17089
+ * before `ts`, when the call was made (#5339). The tool definitions and the
17090
+ * system context stay absent (ADR-062, amendments of 2026-10-02 and
17091
+ * 2026-10-03). A member the producer set itself wins, as on a proxied call.
17092
+ * Only a call on the session's own conversation comes here: an OTel record
17093
+ * whose `query_source` says so ({@link onSessionConversation}), or a
17094
+ * transcript `assistant` record, since Claude Code writes no side call to
17095
+ * the transcript.
17096
+ */
17097
+ withUnseenRequestSources(body2, attrs2, ts2) {
16979
17098
  if (attrs2[LLM_CALL_DUPLICATE_OF_ATTR] !== void 0) return body2;
16980
- const facts = this.systemContext.measureUnseen();
17099
+ const facts = this.systemContext.measureUnseen(ts2);
16981
17100
  return Object.keys(facts).length === 0 ? body2 : { ...facts, ...body2 };
16982
17101
  }
17102
+ /**
17103
+ * Note text an Oxagen hook answer handed this session's agent after its
17104
+ * start, at `at`, when the answer left (#5339). Every model call on the
17105
+ * session's own conversation from then on carries the running total as
17106
+ * `context_frame_tokens`. The hook handler calls this only for a live
17107
+ * answer, since a replay's answer reaches no harness. A rollback of the
17108
+ * chain takes the note back with everything else the hook changed.
17109
+ */
17110
+ noteInjectedContext(text4, at) {
17111
+ this.systemContext.noteInjectedContext(text4, at);
17112
+ }
16983
17113
  /**
16984
17114
  * The attrs a `tool_call` carries, or undefined when the chain already
16985
17115
  * holds this call and the row must not be sealed at all.
@@ -17113,6 +17243,9 @@ var init_recorder = __esm({
17113
17243
  this.started = true;
17114
17244
  this.stopped = false;
17115
17245
  body2 = { ...body2, env_snapshot: this.envSnapshot };
17246
+ const source = body2["session_start_source"];
17247
+ if (source === "compact" || source === "clear")
17248
+ this.systemContext.clearInjectedContext(ts2);
17116
17249
  }
17117
17250
  if (draft.kind === "turn_start") {
17118
17251
  if (this.turnOpen) {
@@ -17389,7 +17522,7 @@ var init_recorder = __esm({
17389
17522
  }
17390
17523
  const event = this.seal(
17391
17524
  draft.kind,
17392
- draft.kind === "llm_call" && draft.source === "otel_log" && onSessionConversation(draft.standard.context.query_source) ? this.withUnseenRequestSources(draft.body, duplicate) : draft.body,
17525
+ draft.kind === "llm_call" && draft.source === "otel_log" && onSessionConversation(draft.standard.context.query_source) ? this.withUnseenRequestSources(draft.body, duplicate, draft.ts) : draft.body,
17393
17526
  {
17394
17527
  ts: draft.ts,
17395
17528
  source: draft.source,
@@ -17472,7 +17605,7 @@ var init_recorder = __esm({
17472
17605
  duplicate = { [LLM_CALL_DUPLICATE_OF_ATTR]: "transcript" };
17473
17606
  }
17474
17607
  if (draft.kind === "llm_call")
17475
- body2 = this.withUnseenRequestSources(body2, duplicate);
17608
+ body2 = this.withUnseenRequestSources(body2, duplicate, draft.ts);
17476
17609
  let content = draft.content;
17477
17610
  if (this.isSealedPrompt(draft.kind, body2)) {
17478
17611
  content = void 0;
@@ -23364,7 +23497,7 @@ import { readFileSync as readFileSync11 } from "node:fs";
23364
23497
  import { dirname as dirname8, resolve as resolve2 } from "node:path";
23365
23498
  import { fileURLToPath } from "node:url";
23366
23499
  function readVersion() {
23367
- if (true) return "2.1.4-494";
23500
+ if (true) return "2.1.4-498";
23368
23501
  try {
23369
23502
  const here = dirname8(fileURLToPath(import.meta.url));
23370
23503
  const pkg = JSON.parse(
@@ -38458,6 +38591,11 @@ function drainMidTurn(input, record, deps, events, replay, used = 0) {
38458
38591
  if (continuation !== void 0) texts.push(continuation);
38459
38592
  return texts;
38460
38593
  }
38594
+ function noteHandedContext(record, input, replay, text4, at) {
38595
+ if (replay !== void 0 || input.agent_id !== void 0) return;
38596
+ if (text4.length === 0) return;
38597
+ record.recorder.noteInjectedContext(text4, at);
38598
+ }
38461
38599
  function hookLedgerKey(raw, harness, hookId) {
38462
38600
  if (hookId !== void 0) return hookId;
38463
38601
  if (harness === "stella" || typeof raw !== "object" || raw === null)
@@ -38760,6 +38898,11 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
38760
38898
  ) : void 0;
38761
38899
  if (block === void 0) record.control.resumeOwed = void 0;
38762
38900
  if (block === void 0) notePrompt(record, promptText(input));
38901
+ const handed = [
38902
+ ...delivered,
38903
+ ...recalled !== void 0 ? [recalled.text] : []
38904
+ ];
38905
+ const context = handed.join(CONTEXT_JOINER);
38763
38906
  events.push(
38764
38907
  ...record.recorder.ingestHook(
38765
38908
  payload,
@@ -38767,11 +38910,16 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
38767
38910
  at,
38768
38911
  (draft) => withReplay({
38769
38912
  ...draft,
38770
- ...recalled !== void 0 ? {
38913
+ ...recalled !== void 0 || handed.length > 0 ? {
38771
38914
  attrs: {
38772
38915
  ...draft.attrs,
38773
- "oxagen.recall_digest": digestText(recalled.text),
38774
- "oxagen.recalled_memories": String(recalled.count)
38916
+ ...recalled !== void 0 ? {
38917
+ "oxagen.recall_digest": digestText(recalled.text),
38918
+ "oxagen.recalled_memories": String(recalled.count)
38919
+ } : {},
38920
+ // How much text the answer hands the agent, as the start
38921
+ // records it (#5339).
38922
+ ...handed.length > 0 ? { "oxagen.delivered_chars": String(context.length) } : {}
38775
38923
  }
38776
38924
  } : {},
38777
38925
  body: {
@@ -38792,16 +38940,13 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
38792
38940
  record
38793
38941
  };
38794
38942
  }
38795
- const context = [
38796
- ...delivered,
38797
- ...recalled !== void 0 ? [recalled.text] : []
38798
- ];
38943
+ noteHandedContext(record, input, replay, context, at);
38799
38944
  return {
38800
38945
  events,
38801
- response: context.length > 0 ? {
38946
+ response: handed.length > 0 ? {
38802
38947
  hookSpecificOutput: {
38803
38948
  hookEventName: "UserPromptSubmit",
38804
- additionalContext: context.join(CONTEXT_JOINER)
38949
+ additionalContext: context
38805
38950
  }
38806
38951
  } : {},
38807
38952
  record
@@ -39070,6 +39215,7 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
39070
39215
  if (operatorBlock(view, record) !== void 0)
39071
39216
  return { events, response: {}, record };
39072
39217
  const texts = drainMidTurn(input, record, deps, events, replay);
39218
+ noteHandedContext(record, input, replay, texts.join(CONTEXT_JOINER), at);
39073
39219
  return {
39074
39220
  events,
39075
39221
  response: texts.length > 0 ? {
@@ -39104,6 +39250,7 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
39104
39250
  ...drainMidTurn(input, record, deps, events, replay, ask?.length ?? 0),
39105
39251
  ...ask !== void 0 ? [ask] : []
39106
39252
  ];
39253
+ noteHandedContext(record, input, replay, texts.join(CONTEXT_JOINER), at);
39107
39254
  return {
39108
39255
  events,
39109
39256
  response: texts.length > 0 ? { decision: "block", reason: texts.join(CONTEXT_JOINER) } : {},
@@ -52121,7 +52268,7 @@ var init_package = __esm({
52121
52268
  "package.json"() {
52122
52269
  package_default = {
52123
52270
  name: "@oxagen/cli",
52124
- version: "2.1.4-494",
52271
+ version: "2.1.4-498",
52125
52272
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
52126
52273
  type: "module",
52127
52274
  main: "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxagen/cli",
3
- "version": "2.1.4-494",
3
+ "version": "2.1.4-498",
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": {