@oxagen/cli 2.1.5-60 → 2.1.5-69

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 +979 -317
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -3589,15 +3589,15 @@ var init_types = __esm({
3589
3589
  }
3590
3590
  const valueType = this._def.valueType;
3591
3591
  function finalizeSet(elements2) {
3592
- const parsedSet = /* @__PURE__ */ new Set();
3592
+ const parsedSet2 = /* @__PURE__ */ new Set();
3593
3593
  for (const element of elements2) {
3594
3594
  if (element.status === "aborted")
3595
3595
  return INVALID;
3596
3596
  if (element.status === "dirty")
3597
3597
  status2.dirty();
3598
- parsedSet.add(element.value);
3598
+ parsedSet2.add(element.value);
3599
3599
  }
3600
- return { status: status2.value, value: parsedSet };
3600
+ return { status: status2.value, value: parsedSet2 };
3601
3601
  }
3602
3602
  const elements = [...ctx.data.values()].map((item, i2) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i2)));
3603
3603
  if (ctx.common.async) {
@@ -5656,7 +5656,7 @@ function bundleSkillChars(skill) {
5656
5656
  chars += file.path.length + file.content.length;
5657
5657
  return chars;
5658
5658
  }
5659
- var protocolTimestampSchema, machineGroupSchema, TACHO_BATCH_SCHEMA, TACHO_BUNDLE_SCHEMA, TACHO_COMMANDS_SCHEMA, TACHO_ENROLLMENT_CLAIMS_SCHEMA, TACHO_ENFORCEMENT_TIER_ATTR, TACHO_GATEWAY_TIER, TACHO_GATEWAY_SESSION_HEADER, TACHO_GATEWAY_GENESIS_HEADER, REFUSAL_META_KEY, MCP_STREAMABLE_HTTP_ACCEPT, TACHO_METERING_ATTR, TACHO_METERING_OBSERVED, TACHO_MODEL_SESSION_HEADER, TACHO_CREDENTIAL_BASIS_ATTR, TACHO_CREDENTIAL_GATEWAY_BROKERED, TACHO_CREDENTIAL_HARNESS_HELD, TACHO_CREDENTIAL_BASES, TACHO_RUN_TOKEN_ATTR, BUNDLE_FEATURE_GATEWAY_TOOLS, BUNDLE_FEATURE_MODEL_PRICES, BUNDLE_FEATURE_MODEL_ALLOWLIST, BUNDLE_FEATURE_INDEPENDENT_MODELS, BUNDLE_FEATURE_HOOK_FAIL_OPEN, BUNDLE_FEATURE_STEERING_MANIFEST, BUNDLE_FEATURE_STEERING_INCOMPLETE, BUNDLE_FEATURE_CONTAINMENT, BUNDLE_FEATURE_DAILY_BUDGET, BUNDLE_FEATURE_STEER_NEXT_STEP, BUNDLE_FEATURE_UNBOUND_REPO, BUNDLE_FEATURE_CEDAR, BUNDLE_FEATURE_CEDAR_TOOLBELT, BUNDLE_FEATURE_SKILLS, BUNDLE_FEATURE_CACHE_KEEP_ALIVE, BUNDLE_FEATURE_WORK_ORDERS, BUNDLE_FEATURE_WORK_ORDER_START, BUNDLE_FEATURE_GATEWAY_POLICY, GATEWAY_MIN_TIERS, SENSITIVE_SCREEN_ACTIONS, PROMPT_REDACTION_PATTERNS_MAX, PROMPT_REDACTION_PATTERN_MAX, gatewayPolicySchema, TACHO_BUNDLE_FEATURES, bundleFeaturesSchema, hostEnrollmentIdSchema, tachoPlatformSchema, tachoHarnessSchema, TACHO_HARNESS_LABELS, TACHO_HARNESS_TIERS, WRAPPED_HARNESSES, CONNECTED_HARNESSES, HARNESS_BINARY, MODEL_HARNESS_ROUTES, MODEL_ROUTED_HARNESSES, BROKERABLE_HARNESSES, BROKERABLE_HARNESS_PROVIDER, TACHO_TIER_SUMMARY, CUSTOM_AGENT_NAME_PATTERN, RESERVED_AGENT_NAMES, tachoHostStatusSchema, tachoBundleModeSchema, tachoCommandSchema, tachoCommandStatusSchema, tachoCommandAckStatusSchema, tachoDeliveryModeSchema, denyGenerationSchema, deliveredCommandSchema, deliveredCommandResponseSchema, WORK_ORDER_ID_PATTERN, WORK_ITEM_ID_PATTERN, workOrderCommandPayloadSchema, WORK_ORDER_CLAIM_PATH, WORK_ORDER_REJECT_PATH, workOrderClaimResponseSchema, workOrderRejectResponseSchema, WORK_CRITERION_CLAIM_PATH, WORK_CRITERION_ID_PATTERN, WORK_HEAD_SHA_PATTERN, WORK_CRITERION_CLAIM_TEXT_MAX, workCriterionClaimResponseSchema, WORK_ORDER_ENV, WORK_ORDER_ATTR, steeringForceSchema, steeringItemKindSchema, steeringCutReasonSchema, steeringManifestItemSchema, STEERING_MANIFEST_SCHEMA, steeringManifestSchema, steeringManifestFrameSchema, cedarIdSchema, cedarPrincipalSchema, cedarToolSchema, cedarBundleSchema, BUNDLE_SKILLS_MAX, BUNDLE_SKILL_FILES_MAX, BUNDLE_SKILLS_CHARS_MAX, BASE64_PATTERN, bundleSkillFileSchema, bundleSkillSchema, policyBundleSchema, enrollmentClaimsSchema, tachoEventWireSchema, daemonHealthSchema, TACHO_MAX_BATCH, TACHO_MAX_BODY_BYTES, TACHO_MAX_REQUEST_BYTES, TACHO_REQUEST_ENVELOPE_BYTES, tachoBodySchema, tachoBatchSchema, agentDaySpendSchema, controlEnvelopeSchema, ingestResponseSchema, bundleResponseSchema, commandAcknowledgementSchema, commandsResponseSchema, enrollmentResponseSchema, tokenEnrollmentResponseSchema;
5659
+ var protocolTimestampSchema, machineGroupSchema, TACHO_BATCH_SCHEMA, TACHO_BUNDLE_SCHEMA, TACHO_COMMANDS_SCHEMA, TACHO_ENROLLMENT_CLAIMS_SCHEMA, TACHO_ENFORCEMENT_TIER_ATTR, TACHO_GATEWAY_TIER, TACHO_GATEWAY_SESSION_HEADER, TACHO_GATEWAY_GENESIS_HEADER, REFUSAL_META_KEY, MCP_STREAMABLE_HTTP_ACCEPT, TACHO_METERING_ATTR, TACHO_METERING_OBSERVED, TACHO_MODEL_SESSION_HEADER, TACHO_CREDENTIAL_BASIS_ATTR, TACHO_CREDENTIAL_GATEWAY_BROKERED, TACHO_CREDENTIAL_HARNESS_HELD, TACHO_CREDENTIAL_BASES, TACHO_RUN_TOKEN_ATTR, BUNDLE_FEATURE_GATEWAY_TOOLS, BUNDLE_FEATURE_MODEL_PRICES, BUNDLE_FEATURE_MODEL_ALLOWLIST, BUNDLE_FEATURE_INDEPENDENT_MODELS, BUNDLE_FEATURE_HOOK_FAIL_OPEN, BUNDLE_FEATURE_STEERING_MANIFEST, BUNDLE_FEATURE_STEERING_INCOMPLETE, BUNDLE_FEATURE_CONTAINMENT, BUNDLE_FEATURE_DAILY_BUDGET, BUNDLE_FEATURE_STEER_NEXT_STEP, BUNDLE_FEATURE_UNBOUND_REPO, BUNDLE_FEATURE_CEDAR, BUNDLE_FEATURE_CEDAR_TOOLBELT, BUNDLE_FEATURE_SKILLS, BUNDLE_FEATURE_CACHE_KEEP_ALIVE, BUNDLE_FEATURE_WORK_ORDERS, BUNDLE_FEATURE_WORK_ORDER_START, BUNDLE_FEATURE_MCP_RULE_SETS, BUNDLE_FEATURE_GATEWAY_POLICY, GATEWAY_MIN_TIERS, SENSITIVE_SCREEN_ACTIONS, PROMPT_REDACTION_PATTERNS_MAX, PROMPT_REDACTION_PATTERN_MAX, gatewayPolicySchema, TACHO_BUNDLE_FEATURES, bundleFeaturesSchema, hostEnrollmentIdSchema, tachoPlatformSchema, tachoHarnessSchema, TACHO_HARNESS_LABELS, TACHO_HARNESS_TIERS, WRAPPED_HARNESSES, CONNECTED_HARNESSES, HARNESS_BINARY, MODEL_HARNESS_ROUTES, MODEL_ROUTED_HARNESSES, BROKERABLE_HARNESSES, BROKERABLE_HARNESS_PROVIDER, TACHO_TIER_SUMMARY, CUSTOM_AGENT_NAME_PATTERN, RESERVED_AGENT_NAMES, tachoHostStatusSchema, tachoBundleModeSchema, tachoCommandSchema, tachoCommandStatusSchema, tachoCommandAckStatusSchema, tachoDeliveryModeSchema, denyGenerationSchema, deliveredCommandSchema, deliveredCommandResponseSchema, WORK_ORDER_ID_PATTERN, WORK_ITEM_ID_PATTERN, workOrderCommandPayloadSchema, WORK_ORDER_CLAIM_PATH, WORK_ORDER_REJECT_PATH, workOrderClaimResponseSchema, workOrderRejectResponseSchema, WORK_CRITERION_CLAIM_PATH, WORK_CRITERION_ID_PATTERN, WORK_HEAD_SHA_PATTERN, WORK_CRITERION_CLAIM_TEXT_MAX, workCriterionClaimResponseSchema, WORK_ORDER_ENV, WORK_ORDER_ATTR, steeringForceSchema, steeringItemKindSchema, steeringCutReasonSchema, steeringManifestItemSchema, STEERING_MANIFEST_SCHEMA, steeringManifestSchema, steeringManifestFrameSchema, cedarIdSchema, cedarPrincipalSchema, cedarToolSchema, cedarBundleSchema, BUNDLE_SKILLS_MAX, BUNDLE_SKILL_FILES_MAX, BUNDLE_SKILLS_CHARS_MAX, BASE64_PATTERN, bundleSkillFileSchema, bundleSkillSchema, policyBundleSchema, enrollmentClaimsSchema, tachoEventWireSchema, daemonHealthSchema, TACHO_MAX_BATCH, TACHO_MAX_BODY_BYTES, TACHO_MAX_REQUEST_BYTES, TACHO_REQUEST_ENVELOPE_BYTES, tachoBodySchema, tachoBatchSchema, agentDaySpendSchema, controlEnvelopeSchema, ingestResponseSchema, bundleResponseSchema, commandAcknowledgementSchema, commandsResponseSchema, enrollmentResponseSchema, tokenEnrollmentResponseSchema;
5660
5660
  var init_wire = __esm({
5661
5661
  "../../packages/tacho/src/wire.ts"() {
5662
5662
  "use strict";
@@ -5709,6 +5709,7 @@ var init_wire = __esm({
5709
5709
  BUNDLE_FEATURE_CACHE_KEEP_ALIVE = "cache_keep_alive";
5710
5710
  BUNDLE_FEATURE_WORK_ORDERS = "work_orders";
5711
5711
  BUNDLE_FEATURE_WORK_ORDER_START = "work_order_start";
5712
+ BUNDLE_FEATURE_MCP_RULE_SETS = "mcp_rule_sets";
5712
5713
  BUNDLE_FEATURE_GATEWAY_POLICY = "gateway_policy";
5713
5714
  GATEWAY_MIN_TIERS = ["harness", "gateway", "contained"];
5714
5715
  SENSITIVE_SCREEN_ACTIONS = ["flag", "strip", "reject"];
@@ -5755,6 +5756,7 @@ var init_wire = __esm({
5755
5756
  BUNDLE_FEATURE_WORK_ORDERS,
5756
5757
  BUNDLE_FEATURE_WORK_ORDER_START,
5757
5758
  BUNDLE_FEATURE_STEERING_INCOMPLETE,
5759
+ BUNDLE_FEATURE_MCP_RULE_SETS,
5758
5760
  BUNDLE_FEATURE_GATEWAY_POLICY
5759
5761
  ];
5760
5762
  bundleFeaturesSchema = external_exports.array(external_exports.string().max(64)).max(32);
@@ -6072,6 +6074,25 @@ var init_wire = __esm({
6072
6074
  deny: external_exports.array(external_exports.string().max(512)).max(1024),
6073
6075
  ask: external_exports.array(external_exports.string().max(512)).max(1024)
6074
6076
  }).strict(),
6077
+ /**
6078
+ * The agent's MCP tool rules as the app holds them (#5398): one list per
6079
+ * rule set, each rule in its set's order. The host takes the first rule
6080
+ * that matches in each set and the most restrictive answer across the
6081
+ * sets (deny, then ask, then allow), and a set with no matching rule says
6082
+ * nothing. These rules are not also in `permissions`, whose fixed
6083
+ * deny-before-allow order would undo theirs.
6084
+ *
6085
+ * Emitted only to a host that advertised `BUNDLE_FEATURE_MCP_RULE_SETS`.
6086
+ * When it is absent, the same rules are sorted into `permissions`.
6087
+ */
6088
+ mcp_rule_sets: external_exports.array(
6089
+ external_exports.array(
6090
+ external_exports.object({
6091
+ rule: external_exports.string().max(512),
6092
+ effect: external_exports.enum(["allow", "deny", "ask"])
6093
+ }).strict()
6094
+ ).max(1024)
6095
+ ).max(256).optional(),
6075
6096
  tools: external_exports.record(
6076
6097
  external_exports.string().max(256),
6077
6098
  external_exports.object({
@@ -8121,12 +8142,12 @@ function cacheKeepAliveFinding(bundle2) {
8121
8142
  }
8122
8143
  function parseRule(raw) {
8123
8144
  const rule = raw.trim();
8124
- const open6 = rule.indexOf("(");
8125
- if (open6 === -1 || !rule.endsWith(")")) return { raw, tool: rule };
8145
+ const open7 = rule.indexOf("(");
8146
+ if (open7 === -1 || !rule.endsWith(")")) return { raw, tool: rule };
8126
8147
  return {
8127
8148
  raw,
8128
- tool: rule.slice(0, open6).trim(),
8129
- spec: rule.slice(open6 + 1, -1)
8149
+ tool: rule.slice(0, open7).trim(),
8150
+ spec: rule.slice(open7 + 1, -1)
8130
8151
  };
8131
8152
  }
8132
8153
  function escapeRegex(value) {
@@ -8427,6 +8448,29 @@ function firstMatch(rules, call2, context, effect) {
8427
8448
  }
8428
8449
  return void 0;
8429
8450
  }
8451
+ function parsedSet(set) {
8452
+ const cached3 = parsedSetCache.get(set);
8453
+ if (cached3 !== void 0 && cached3.length === set.length && cached3.every((rule, index) => rule.raw === set[index]?.rule))
8454
+ return cached3;
8455
+ const parsed2 = set.map((entry2) => parseRule(entry2.rule));
8456
+ parsedSetCache.set(set, parsed2);
8457
+ return parsed2;
8458
+ }
8459
+ function ruleSetsMatch(sets, call2, context) {
8460
+ if (sets === void 0) return void 0;
8461
+ let result2;
8462
+ for (const set of sets) {
8463
+ const parsed2 = parsedSet(set);
8464
+ for (const [index, rule] of parsed2.entries()) {
8465
+ const effect = set[index].effect;
8466
+ if (!callMatches(rule, call2, context ?? {}, effect)) continue;
8467
+ if (result2 === void 0 || EFFECT_RANK[effect] > EFFECT_RANK[result2.effect])
8468
+ result2 = { effect, rule: rule.raw };
8469
+ break;
8470
+ }
8471
+ }
8472
+ return result2;
8473
+ }
8430
8474
  function allowMatch(rules, call2, context) {
8431
8475
  const single = firstMatch(rules, call2, context, "allow");
8432
8476
  if (single !== void 0) return [single];
@@ -8559,12 +8603,8 @@ function evaluatePreToolUse(input) {
8559
8603
  };
8560
8604
  }
8561
8605
  const call2 = toolCallOf(toolName3, toolInput);
8562
- const denied = firstMatch(
8563
- bundle2.permissions.deny,
8564
- call2,
8565
- input.context,
8566
- "deny"
8567
- );
8606
+ const ordered = ruleSetsMatch(bundle2.mcp_rule_sets, call2, input.context);
8607
+ const denied = firstMatch(bundle2.permissions.deny, call2, input.context, "deny") ?? (ordered?.effect === "deny" ? ordered.rule : void 0);
8568
8608
  if (denied !== void 0) {
8569
8609
  return {
8570
8610
  decision: bundle2.mode === "observe" ? "allow" : "deny",
@@ -8588,7 +8628,7 @@ function evaluatePreToolUse(input) {
8588
8628
  stale
8589
8629
  };
8590
8630
  }
8591
- const ask = firstMatch(bundle2.permissions.ask, call2, input.context, "ask");
8631
+ const ask = firstMatch(bundle2.permissions.ask, call2, input.context, "ask") ?? (ordered?.effect === "ask" ? ordered.rule : void 0);
8592
8632
  if (ask !== void 0) {
8593
8633
  return {
8594
8634
  decision: bundle2.mode === "observe" ? "allow" : "ask",
@@ -8603,7 +8643,7 @@ function evaluatePreToolUse(input) {
8603
8643
  stale
8604
8644
  };
8605
8645
  }
8606
- const allowed = allowMatch(bundle2.permissions.allow, call2, input.context);
8646
+ const allowed = allowMatch(bundle2.permissions.allow, call2, input.context) ?? (ordered?.effect === "allow" ? [ordered.rule] : void 0);
8607
8647
  if (allowed !== void 0) {
8608
8648
  const rule = allowed.join(" and ");
8609
8649
  return {
@@ -8753,7 +8793,7 @@ function decideWithCedar(input) {
8753
8793
  };
8754
8794
  }
8755
8795
  }
8756
- var SHELL_OPERATORS, SHELL_LEADING_WORDS, WINDOWS_PATH, SHELL_TOOLS2, DECLARATION_SYNONYMS, GLOB_CACHE_LIMIT, globCache, parsedRuleCache, WRITE_EFFECTS;
8796
+ var SHELL_OPERATORS, SHELL_LEADING_WORDS, WINDOWS_PATH, SHELL_TOOLS2, DECLARATION_SYNONYMS, GLOB_CACHE_LIMIT, globCache, parsedRuleCache, EFFECT_RANK, parsedSetCache, WRITE_EFFECTS;
8757
8797
  var init_bundle = __esm({
8758
8798
  "../../packages/tacho/src/host/bundle.ts"() {
8759
8799
  "use strict";
@@ -8795,6 +8835,8 @@ var init_bundle = __esm({
8795
8835
  GLOB_CACHE_LIMIT = 1024;
8796
8836
  globCache = /* @__PURE__ */ new Map();
8797
8837
  parsedRuleCache = /* @__PURE__ */ new WeakMap();
8838
+ EFFECT_RANK = { allow: 0, ask: 1, deny: 2 };
8839
+ parsedSetCache = /* @__PURE__ */ new WeakMap();
8798
8840
  WRITE_EFFECTS = /* @__PURE__ */ new Set([
8799
8841
  "file_write",
8800
8842
  "file_edit",
@@ -8853,6 +8895,13 @@ function withRecordedHarnessFiles(paths, host) {
8853
8895
  claudeDesktopConfig: record.claude_desktop_config !== void 0 ? record.claude_desktop_config ?? void 0 : paths.claudeDesktopConfig
8854
8896
  };
8855
8897
  }
8898
+ function recordedHarnessHomes(host) {
8899
+ const record = host?.harness_files;
8900
+ return {
8901
+ ...record?.claude_settings !== void 0 ? { claudeConfigDir: dirname2(record.claude_settings) } : {},
8902
+ ...record?.codex_hooks !== void 0 ? { codexHome: dirname2(record.codex_hooks) } : {}
8903
+ };
8904
+ }
8856
8905
  function mcpEndpointOverrideRequestFrom(env) {
8857
8906
  const raw = env["TACHO_MCP_ENDPOINT"];
8858
8907
  if (typeof raw !== "string" || raw.length === 0)
@@ -10917,12 +10966,12 @@ function procStartTicks(stat7) {
10917
10966
  return ticks !== void 0 && /^\d+$/.test(ticks) ? ticks : void 0;
10918
10967
  }
10919
10968
  function procParent(stat7) {
10920
- const open6 = stat7.indexOf("(");
10969
+ const open7 = stat7.indexOf("(");
10921
10970
  const close = stat7.lastIndexOf(")");
10922
- if (open6 < 0 || close < open6) return void 0;
10971
+ if (open7 < 0 || close < open7) return void 0;
10923
10972
  const ppid = stat7.slice(close + 1).trim().split(/\s+/)[1];
10924
10973
  if (ppid === void 0 || !/^\d+$/.test(ppid)) return void 0;
10925
- return { ppid: Number(ppid), comm: stat7.slice(open6 + 1, close) };
10974
+ return { ppid: Number(ppid), comm: stat7.slice(open7 + 1, close) };
10926
10975
  }
10927
10976
  function readProcParent(pid, read2 = readText) {
10928
10977
  const stat7 = read2(`/proc/${pid}/stat`);
@@ -23229,7 +23278,7 @@ function openCredentialStore(paths) {
23229
23278
  entriesSeen = { version: version2, entries: parsed2.data.entries };
23230
23279
  return parsed2.data.entries;
23231
23280
  }
23232
- function writeAll2(entries2) {
23281
+ function writeAll3(entries2) {
23233
23282
  if (entries2.length === 0) {
23234
23283
  if (existsSync5(paths.file)) unlinkSync4(paths.file);
23235
23284
  return;
@@ -23273,7 +23322,7 @@ function openCredentialStore(paths) {
23273
23322
  prefix: kind === "chatgpt" ? "login" : credentialPrefix(secret),
23274
23323
  sealed: seal(keyForWrite(), secret, sealingContext(provider, owner))
23275
23324
  };
23276
- writeAll2([
23325
+ writeAll3([
23277
23326
  ...readAll().filter((e) => !matches(e, provider, owner)),
23278
23327
  entry2
23279
23328
  ]);
@@ -23289,7 +23338,7 @@ function openCredentialStore(paths) {
23289
23338
  entry2.sealed,
23290
23339
  sealingContext(provider, owner)
23291
23340
  );
23292
- writeAll2(entries2.filter((e) => !matches(e, provider, owner)));
23341
+ writeAll3(entries2.filter((e) => !matches(e, provider, owner)));
23293
23342
  return { kind: entry2.kind, secret };
23294
23343
  },
23295
23344
  status: () => readAll().map(custodyOf),
@@ -26623,7 +26672,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
26623
26672
  import { dirname as dirname9, resolve as resolve3 } from "node:path";
26624
26673
  import { fileURLToPath } from "node:url";
26625
26674
  function readVersion() {
26626
- if (true) return "2.1.5-60";
26675
+ if (true) return "2.1.5-69";
26627
26676
  try {
26628
26677
  const here = dirname9(fileURLToPath(import.meta.url));
26629
26678
  const pkg = JSON.parse(
@@ -28194,13 +28243,15 @@ function* readLinesFrom(path2, from = 0, chunkBytes = SCAN_CHUNK_BYTES) {
28194
28243
  closeSync5(fd2);
28195
28244
  }
28196
28245
  }
28197
- async function* readLinesFromAsync(path2, from = 0) {
28246
+ async function* readLinesFromAsync(path2, from = 0, to = Number.POSITIVE_INFINITY) {
28198
28247
  const handle = await open2(path2, "r");
28199
28248
  try {
28200
28249
  const splitter = new LineSplitter(from);
28201
28250
  let position = from;
28202
- for (; ; ) {
28203
- const chunk2 = Buffer.allocUnsafe(SCAN_CHUNK_BYTES);
28251
+ while (position < to) {
28252
+ const chunk2 = Buffer.allocUnsafe(
28253
+ Math.min(SCAN_CHUNK_BYTES, to - position)
28254
+ );
28204
28255
  const { bytesRead } = await handle.read(chunk2, 0, chunk2.length, position);
28205
28256
  if (bytesRead <= 0) break;
28206
28257
  position += bytesRead;
@@ -28542,6 +28593,7 @@ import {
28542
28593
  writeSync as writeSync4
28543
28594
  } from "node:fs";
28544
28595
  import { randomUUID } from "node:crypto";
28596
+ import { open as open3 } from "node:fs/promises";
28545
28597
  import { join as join18 } from "node:path";
28546
28598
  function activityAt(event) {
28547
28599
  const at2 = Date.parse(event.ts);
@@ -28576,7 +28628,66 @@ function isResumeMark(value) {
28576
28628
  function sizeOf(path2) {
28577
28629
  return statSync6(path2, { throwIfNoEntry: false })?.size ?? 0;
28578
28630
  }
28579
- var BODY_READ_SLICE, BODY_DROP_SLICE_BYTES, BODY_DROP_SCANS, NEWLINE, EVENT_TAIL_WINDOW, WalRecoveryConflict, Wal;
28631
+ function statIfExists2(path2) {
28632
+ return statSync6(path2, { throwIfNoEntry: false });
28633
+ }
28634
+ async function writeAll2(out, bytes) {
28635
+ let written2 = 0;
28636
+ while (written2 < bytes.length) {
28637
+ const { bytesWritten } = await out.write(
28638
+ bytes,
28639
+ written2,
28640
+ bytes.length - written2
28641
+ );
28642
+ written2 += bytesWritten;
28643
+ }
28644
+ }
28645
+ async function copyRange(path2, out, from, to, signal) {
28646
+ if (to <= from) return;
28647
+ const source = await open3(path2, "r");
28648
+ try {
28649
+ const buffer = Buffer.allocUnsafe(
28650
+ Math.min(BODY_PURGE_SLICE_BYTES, to - from)
28651
+ );
28652
+ for (let at2 = from; at2 < to; ) {
28653
+ signal?.throwIfAborted();
28654
+ const { bytesRead } = await source.read(
28655
+ buffer,
28656
+ 0,
28657
+ Math.min(buffer.length, to - at2),
28658
+ at2
28659
+ );
28660
+ if (bytesRead <= 0) return;
28661
+ await writeAll2(out, buffer.subarray(0, bytesRead));
28662
+ at2 += bytesRead;
28663
+ }
28664
+ } finally {
28665
+ await source.close();
28666
+ }
28667
+ }
28668
+ function readRangeSync(path2, from, to) {
28669
+ const buffer = Buffer.alloc(Math.max(0, to - from));
28670
+ if (buffer.length === 0) return buffer;
28671
+ const fd2 = openSync6(path2, "r");
28672
+ try {
28673
+ let filled = 0;
28674
+ while (filled < buffer.length) {
28675
+ const read2 = readSync3(
28676
+ fd2,
28677
+ buffer,
28678
+ filled,
28679
+ buffer.length - filled,
28680
+ from + filled
28681
+ );
28682
+ if (read2 <= 0) break;
28683
+ filled += read2;
28684
+ }
28685
+ return buffer.subarray(0, filled);
28686
+ } finally {
28687
+ closeSync6(fd2);
28688
+ }
28689
+ }
28690
+ var BODY_READ_SLICE, BODY_DROP_SLICE_BYTES, BODY_DROP_SCANS, BODY_PURGE_SLICE_BYTES, BODY_PURGE_SCANS, BODY_PURGE_CATCH_UPS, NEWLINE, EVENT_TAIL_WINDOW, WalRecoveryConflict, Wal;
28580
28691
  var init_wal = __esm({
28581
28692
  "../../packages/tacho/src/host/wal.ts"() {
28582
28693
  "use strict";
@@ -28586,6 +28697,9 @@ var init_wal = __esm({
28586
28697
  BODY_READ_SLICE = 32;
28587
28698
  BODY_DROP_SLICE_BYTES = 1024 * 1024;
28588
28699
  BODY_DROP_SCANS = 3;
28700
+ BODY_PURGE_SLICE_BYTES = 1024 * 1024;
28701
+ BODY_PURGE_SCANS = 3;
28702
+ BODY_PURGE_CATCH_UPS = 3;
28589
28703
  NEWLINE = 10;
28590
28704
  EVENT_TAIL_WINDOW = 16 * 1024;
28591
28705
  WalRecoveryConflict = class extends Error {
@@ -28685,6 +28799,18 @@ var init_wal = __esm({
28685
28799
  * WAL has ever opened. See `flush` for why this exists.
28686
28800
  */
28687
28801
  dirtyPaths = /* @__PURE__ */ new Set();
28802
+ /**
28803
+ * The awaited body sweeps running now, one entry per session scan
28804
+ * (`purgeBodiesOutsideMandateAsync`). Every write to a body file other than
28805
+ * an append marks the entries for its session, so a scan whose bytes moved
28806
+ * under it does not rename its copy over the file.
28807
+ */
28808
+ bodyWatches = /* @__PURE__ */ new Set();
28809
+ /**
28810
+ * The temporary files those sweeps are writing. `compact` removes a
28811
+ * temporary file a crashed rewrite left, and must not remove one of these.
28812
+ */
28813
+ liveTemps = /* @__PURE__ */ new Set();
28688
28814
  fileFor(sessionUuid2) {
28689
28815
  return join18(this.dir, `${sessionUuid2}.ndjson`);
28690
28816
  }
@@ -29407,6 +29533,7 @@ ${lines.join("\n")}
29407
29533
  if (next > 0) await new Promise((resolve26) => setImmediate(resolve26));
29408
29534
  const slice = this.blankSlice(path2, found, next, idems);
29409
29535
  if (slice === void 0) break;
29536
+ if (slice.blanked > 0) this.bodyRewritten(session);
29410
29537
  blanked += slice.blanked;
29411
29538
  moved ||= slice.moved;
29412
29539
  next = slice.next;
@@ -29663,6 +29790,7 @@ ${lines.join("\n")}
29663
29790
  this.dirtyPaths.add(path2);
29664
29791
  }
29665
29792
  this.bodyIndexes.invalidate(sessionUuid2);
29793
+ this.bodyRewritten(sessionUuid2);
29666
29794
  return cut;
29667
29795
  }
29668
29796
  /**
@@ -29989,27 +30117,241 @@ ${lines.join("\n")}
29989
30117
  * that withholds a body from a shipment. See `purgeBodiesNarrowedOut` in
29990
30118
  * the daemon, which calls this when a replacement bundle verifies and
29991
30119
  * narrows the clause.
30120
+ *
30121
+ * This reads every event file and every body file on the synchronous path,
30122
+ * which ADR-231 rules out on the daemon's thread. The daemon calls
30123
+ * `purgeBodiesOutsideMandateAsync`. It calls this only when it cannot record
30124
+ * the purge it owes, the one case where the erase has to finish before the
30125
+ * narrowed bundle is cached.
29992
30126
  */
29993
30127
  purgeBodiesOutsideMandate(retention) {
29994
30128
  let purged = 0;
29995
- const sessions = /* @__PURE__ */ new Set([
29996
- ...this.sessions(),
29997
- ...this.sessionsWithBodies()
29998
- ]);
29999
- for (const session of sessions) {
30129
+ for (const session of this.sessionsToPurge())
30130
+ purged += this.purgeSessionSync(session, retention);
30131
+ return purged;
30132
+ }
30133
+ /**
30134
+ * The same sweep, without holding the daemon's thread (#5176, ADR-231).
30135
+ * Every read is awaited, and a turn of the event loop separates two
30136
+ * sessions, so a hook or a proxied model call waits for one read and not
30137
+ * for the sweep.
30138
+ *
30139
+ * Each session is rewritten the way `rewriteBodies` rewrites it: the lines
30140
+ * kept go to a temporary file, and a rename puts it over the body file. A
30141
+ * crash part way leaves the old file whole, and the caller's record of the
30142
+ * purge it owes runs the sweep again. Hooks append to the file while the
30143
+ * scan awaits its reads, and three rules keep those appends:
30144
+ *
30145
+ * - The scan measures the body file and the event file in one synchronous
30146
+ * stretch, and reads each only up to that size. `append` writes a body
30147
+ * and then its event in one synchronous stretch too, so every body the
30148
+ * scan reads has its event in the part of the event file the scan reads,
30149
+ * or is a crash orphan. A body appended later is never judged an orphan.
30150
+ * - Bytes appended after that moment go to the new file as they are. They
30151
+ * were recorded under the mandate in force when they were appended, and
30152
+ * the daemon caches the narrowed mandate before it starts this sweep.
30153
+ * - The rename happens in one synchronous stretch, after a last check. When
30154
+ * the file was replaced, cut short, or written other than by an append
30155
+ * while the scan read it (`dropBodiesAsync` blanks lines in place), the
30156
+ * copy is thrown away and the session is scanned again. After
30157
+ * `BODY_PURGE_SCANS` scans the session goes through the synchronous sweep.
30158
+ *
30159
+ * The copy is synced to disk with an awaited call before that stretch, so
30160
+ * the stretch copies and syncs only what was appended since, about
30161
+ * `BODY_PURGE_SLICE_BYTES` at most.
30162
+ */
30163
+ async purgeBodiesOutsideMandateAsync(retention, options = {}) {
30164
+ const { signal } = options;
30165
+ let purged = 0;
30166
+ for (const session of this.sessionsToPurge()) {
30167
+ await new Promise((resolve26) => setImmediate(resolve26));
30168
+ signal?.throwIfAborted();
30169
+ await options.beforeSession?.(session);
30170
+ signal?.throwIfAborted();
30000
30171
  if (!existsSync12(this.bodyFileFor(session))) continue;
30001
- const kindOf2 = new Map(
30002
- this.read(session).map(
30003
- (event) => [event.event_id_idem, event.kind]
30004
- )
30005
- );
30006
- purged += this.rewriteBodies(
30007
- session,
30008
- (stored) => this.keeps(stored, kindOf2, retention)
30009
- );
30172
+ let swept = "moved";
30173
+ for (let scan = 0; scan < BODY_PURGE_SCANS && swept === "moved"; scan++)
30174
+ swept = await this.purgeSessionAsync(session, retention, signal);
30175
+ purged += swept === "moved" ? this.purgeSessionSync(session, retention) : swept;
30010
30176
  }
30011
30177
  return purged;
30012
30178
  }
30179
+ /**
30180
+ * Every session that has an event file OR a body file. `sessions()` lists
30181
+ * `.ndjson` only, so a crash between `append`'s body write and its first
30182
+ * event write leaves a `<uuid>.bodies.jsonl` with no `.ndjson` beside it,
30183
+ * invisible to a sweep that walked `sessions()` and to `compact`, which
30184
+ * does. Those bytes would outlive every mechanism meant to remove them.
30185
+ */
30186
+ sessionsToPurge() {
30187
+ return /* @__PURE__ */ new Set([...this.sessions(), ...this.sessionsWithBodies()]);
30188
+ }
30189
+ /** One session of `purgeBodiesOutsideMandate`, read and rewritten at once. */
30190
+ purgeSessionSync(session, retention) {
30191
+ if (!existsSync12(this.bodyFileFor(session))) return 0;
30192
+ const kindOf2 = new Map(
30193
+ this.read(session).map(
30194
+ (event) => [event.event_id_idem, event.kind]
30195
+ )
30196
+ );
30197
+ return this.rewriteBodies(
30198
+ session,
30199
+ (stored) => this.keeps(stored, kindOf2, retention)
30200
+ );
30201
+ }
30202
+ /**
30203
+ * One scan of `purgeBodiesOutsideMandateAsync` over one session. Answers
30204
+ * how many lines went, or `moved` when the file changed under the scan
30205
+ * other than by an append, in which case nothing was renamed.
30206
+ */
30207
+ async purgeSessionAsync(session, retention, signal) {
30208
+ const path2 = this.bodyFileFor(session);
30209
+ const start = statIfExists2(path2);
30210
+ if (start === void 0) return 0;
30211
+ const eventEnd = statIfExists2(this.fileFor(session))?.size ?? 0;
30212
+ const watch2 = { session, moved: false };
30213
+ this.bodyWatches.add(watch2);
30214
+ const tmp = `${path2}.${randomUUID()}.tmp`;
30215
+ let out;
30216
+ try {
30217
+ const kindOf2 = await this.eventKindsUpTo(session, eventEnd, signal);
30218
+ let cut = 0;
30219
+ let kept = 0;
30220
+ let held = [];
30221
+ let heldBytes = 0;
30222
+ const writeHeld = async (to) => {
30223
+ if (heldBytes === 0) return;
30224
+ await writeAll2(to, Buffer.concat(held, heldBytes));
30225
+ held = [];
30226
+ heldBytes = 0;
30227
+ };
30228
+ for await (const line of readLinesFromAsync(path2, 0, start.size)) {
30229
+ signal?.throwIfAborted();
30230
+ if (line.text.trim().length === 0) continue;
30231
+ if (this.keeps(parseStoredBody(line.text), kindOf2, retention)) {
30232
+ kept += 1;
30233
+ if (out === void 0) continue;
30234
+ const bytes = Buffer.from(`${line.text}
30235
+ `);
30236
+ held.push(bytes);
30237
+ heldBytes += bytes.length;
30238
+ if (heldBytes >= BODY_PURGE_SLICE_BYTES) await writeHeld(out);
30239
+ continue;
30240
+ }
30241
+ cut += 1;
30242
+ if (out === void 0) {
30243
+ this.liveTemps.add(tmp);
30244
+ out = await open3(tmp, "wx", 384);
30245
+ await copyRange(path2, out, 0, line.offset, signal);
30246
+ }
30247
+ }
30248
+ if (out === void 0) return watch2.moved ? "moved" : 0;
30249
+ await writeHeld(out);
30250
+ let copied = start.size;
30251
+ for (let round = 0; round < BODY_PURGE_CATCH_UPS; round += 1) {
30252
+ const size = statIfExists2(path2)?.size;
30253
+ if (size === void 0 || size - copied <= BODY_PURGE_SLICE_BYTES)
30254
+ break;
30255
+ await copyRange(path2, out, copied, size, signal);
30256
+ copied = size;
30257
+ }
30258
+ await out.sync();
30259
+ await out.close();
30260
+ out = void 0;
30261
+ signal?.throwIfAborted();
30262
+ if (watch2.moved) return "moved";
30263
+ return this.commitPurge(session, tmp, {
30264
+ start,
30265
+ copied,
30266
+ keptAny: kept > 0 || copied > start.size,
30267
+ cut
30268
+ });
30269
+ } catch (error) {
30270
+ if (error.code === "ENOENT" && !existsSync12(path2))
30271
+ return 0;
30272
+ throw error;
30273
+ } finally {
30274
+ this.bodyWatches.delete(watch2);
30275
+ if (out !== void 0) await out.close().catch(() => void 0);
30276
+ if (existsSync12(tmp)) unlinkSync9(tmp);
30277
+ this.liveTemps.delete(tmp);
30278
+ }
30279
+ }
30280
+ /**
30281
+ * The kind of every event in the first `end` bytes of a session's event
30282
+ * file, by event id, read with awaited reads. A line that does not parse is
30283
+ * reported and skipped, as `read` does. A line cut off at `end` was torn by
30284
+ * a crash, since `append` ends every write with a newline, and is skipped.
30285
+ */
30286
+ async eventKindsUpTo(session, end, signal) {
30287
+ const kindOf2 = /* @__PURE__ */ new Map();
30288
+ if (end === 0) return kindOf2;
30289
+ for await (const line of readLinesFromAsync(
30290
+ this.fileFor(session),
30291
+ 0,
30292
+ end
30293
+ )) {
30294
+ signal?.throwIfAborted();
30295
+ if (!line.terminated || line.text.trim().length === 0) continue;
30296
+ try {
30297
+ const event = parseEventLine(line.text);
30298
+ kindOf2.set(event.event_id_idem, event.kind);
30299
+ } catch (error) {
30300
+ this.reportEventParseFailure({
30301
+ session_uuid: session,
30302
+ reason: error instanceof Error ? error.message : String(error)
30303
+ });
30304
+ }
30305
+ }
30306
+ return kindOf2;
30307
+ }
30308
+ /**
30309
+ * The rename of `purgeSessionAsync`, in one synchronous stretch, so nothing
30310
+ * else can write the body file between the check and the rename. Copies
30311
+ * what was appended since `copied`, syncs it, and puts the copy over the
30312
+ * file, or removes the file when nothing in it is left.
30313
+ */
30314
+ commitPurge(session, tmp, scan) {
30315
+ const path2 = this.bodyFileFor(session);
30316
+ const now = statIfExists2(path2);
30317
+ if (now === void 0) return 0;
30318
+ if (now.ino !== scan.start.ino || now.dev !== scan.start.dev || now.size < scan.copied)
30319
+ return "moved";
30320
+ const tail = readRangeSync(path2, scan.copied, now.size);
30321
+ if (!scan.keptAny && tail.toString("utf8").trim().length === 0) {
30322
+ unlinkSync9(path2);
30323
+ } else {
30324
+ const fd2 = openSync6(tmp, "r+");
30325
+ try {
30326
+ let written2 = 0;
30327
+ const at2 = fstatSync(fd2).size;
30328
+ while (written2 < tail.length)
30329
+ written2 += writeSync4(
30330
+ fd2,
30331
+ tail,
30332
+ written2,
30333
+ tail.length - written2,
30334
+ at2 + written2
30335
+ );
30336
+ fdatasyncSync(fd2);
30337
+ } finally {
30338
+ closeSync6(fd2);
30339
+ }
30340
+ renameSync3(tmp, path2);
30341
+ }
30342
+ this.bodyIndexes.invalidate(session);
30343
+ this.bodyRewritten(session);
30344
+ return scan.cut;
30345
+ }
30346
+ /**
30347
+ * Tell every awaited sweep of this session that its body file changed
30348
+ * other than by an append. Each write that cuts, blanks, or replaces body
30349
+ * bytes calls this in the same synchronous stretch as the write.
30350
+ */
30351
+ bodyRewritten(session) {
30352
+ for (const watch2 of this.bodyWatches)
30353
+ if (watch2.session === session) watch2.moved = true;
30354
+ }
30013
30355
  /**
30014
30356
  * Whether one stored body survives this mandate. Three answers, each for a
30015
30357
  * reason:
@@ -30027,7 +30369,9 @@ ${lines.join("\n")}
30027
30369
  * the two writes and no event will ever arrive for it. Keeping it kept
30028
30370
  * prompt or tool bytes that no mandate covers and that nothing would
30029
30371
  * ever ship, delete or compact, because every one of those paths starts
30030
- * from the event.
30372
+ * from the event. The awaited sweep does let appends land while it reads,
30373
+ * so it judges only the bodies written before it measured the files, and
30374
+ * their events are all in the part of the event file it reads.
30031
30375
  * - Otherwise the class comes from the event's kind and the one retention
30032
30376
  * gate answers. A kind the table does not name has no class, so no mandate
30033
30377
  * can cover it and the body goes.
@@ -30089,6 +30433,7 @@ ${lines.join("\n")}
30089
30433
  if (kept === 0) unlinkSync9(path2);
30090
30434
  else renameSync3(tmp, path2);
30091
30435
  this.bodyIndexes.invalidate(sessionUuid2);
30436
+ this.bodyRewritten(sessionUuid2);
30092
30437
  return cut;
30093
30438
  } finally {
30094
30439
  if (existsSync12(tmp)) unlinkSync9(tmp);
@@ -30116,6 +30461,7 @@ ${lines.join("\n")}
30116
30461
  `^${uuid}\\.bodies\\.jsonl\\.${uuid}\\.tmp$`
30117
30462
  );
30118
30463
  for (const name of readdirSync7(this.dir)) {
30464
+ if (this.liveTemps.has(join18(this.dir, name))) continue;
30119
30465
  if (abandonedRewrite.test(name)) {
30120
30466
  try {
30121
30467
  unlinkSync9(join18(this.dir, name));
@@ -30199,6 +30545,7 @@ ${lines.join("\n")}
30199
30545
  const path2 = this.bodyFileFor(session);
30200
30546
  if (existsSync12(path2)) unlinkSync9(path2);
30201
30547
  this.bodyIndexes.invalidate(session);
30548
+ this.bodyRewritten(session);
30202
30549
  return true;
30203
30550
  } catch (error) {
30204
30551
  this.bodyFailure(session, "cleanup", error);
@@ -30551,8 +30898,12 @@ function daemonServiceSpec(daemonCommand, deps) {
30551
30898
  command: daemonCommand,
30552
30899
  env: {
30553
30900
  ...deps.env["TACHO_HOME"] !== void 0 ? { TACHO_HOME: deps.env["TACHO_HOME"] } : {},
30554
- // The harness homes the enrolling shell moved, so tachod reads the
30555
- // same files the hooks were written to.
30901
+ // The harness homes this shell moved. Each collector reads its agent's
30902
+ // harness files where that agent's host.json recorded them at enroll
30903
+ // (`harness_files`), so these decide nothing for an agent with a
30904
+ // record. They stay for an agent enrolled before the record existed.
30905
+ // One service serves every agent, so they come from whichever shell
30906
+ // installed it last (#5409).
30556
30907
  ...Object.fromEntries(
30557
30908
  HARNESS_HOME_VARS.flatMap((key) => {
30558
30909
  const value = deps.env[key];
@@ -43081,7 +43432,11 @@ async function renewStaticToken(harness, deps) {
43081
43432
  const label2 = TACHO_HARNESS_LABELS[harness];
43082
43433
  if (deps.host.host_status !== "active" || !deps.inCustody(provider, harness))
43083
43434
  return "skipped";
43084
- const dirs = deps.stellaHome !== void 0 ? { stellaHome: deps.stellaHome } : {};
43435
+ const dirs = {
43436
+ ...deps.stellaHome !== void 0 ? { stellaHome: deps.stellaHome } : {},
43437
+ ...deps.codexHome !== void 0 ? { codexHome: deps.codexHome } : {},
43438
+ ...deps.claudeConfigDir !== void 0 ? { claudeConfigDir: deps.claudeConfigDir } : {}
43439
+ };
43085
43440
  let brokered = false;
43086
43441
  try {
43087
43442
  brokered = (await readModelCredentialState({
@@ -44021,8 +44376,33 @@ var init_git_facts = __esm({
44021
44376
 
44022
44377
  // ../../packages/tacho/src/collector/work-order-autostart.ts
44023
44378
  import { spawn as spawn7 } from "node:child_process";
44024
- import { closeSync as closeSync7, existsSync as existsSync21, openSync as openSync7 } from "node:fs";
44379
+ import { closeSync as closeSync7, existsSync as existsSync21, openSync as openSync7, readSync as readSync4, statSync as statSync12 } from "node:fs";
44025
44380
  import { dirname as dirname20, join as join36 } from "node:path";
44381
+ function lastLogLine(path2) {
44382
+ let fd2;
44383
+ try {
44384
+ const size = statSync12(path2).size;
44385
+ const length = Math.min(size, 4096);
44386
+ const buffer = Buffer.alloc(length);
44387
+ fd2 = openSync7(path2, "r");
44388
+ readSync4(fd2, buffer, 0, length, size - length);
44389
+ const line = buffer.toString("utf8").replace(TERMINAL_CODE, "").split(/\r?\n/).map((text5) => text5.trim()).filter((text5) => text5.length > 0).pop();
44390
+ if (line === void 0) return void 0;
44391
+ return line.length > EXIT_REPORT_QUOTE ? `${line.slice(0, EXIT_REPORT_QUOTE - 1)}\u2026` : line;
44392
+ } catch {
44393
+ return void 0;
44394
+ } finally {
44395
+ if (fd2 !== void 0) {
44396
+ try {
44397
+ closeSync7(fd2);
44398
+ } catch {
44399
+ }
44400
+ }
44401
+ }
44402
+ }
44403
+ function exitPhrase(exit) {
44404
+ return exit.signal !== null ? `ended on ${exit.signal}` : `exited with code ${String(exit.code)}`;
44405
+ }
44026
44406
  function asError(error) {
44027
44407
  return error instanceof Error ? error : new Error(String(error));
44028
44408
  }
@@ -44037,6 +44417,10 @@ function isPlainBranchName(name) {
44037
44417
  function createWorkOrderAutostart(deps) {
44038
44418
  const { paths, log } = deps;
44039
44419
  const isAlive = deps.isAlive ?? processIsAlive;
44420
+ const wait = deps.wait ?? ((ms) => new Promise((done) => {
44421
+ setTimeout(done, ms).unref();
44422
+ }));
44423
+ const sessions = /* @__PURE__ */ new Set();
44040
44424
  const spawnHarness = deps.spawn ?? spawnDetached;
44041
44425
  let searchPath;
44042
44426
  const pathForHarness = () => {
@@ -44222,7 +44606,7 @@ function createWorkOrderAutostart(deps) {
44222
44606
  `work order ${id2}: could not clear its running mark: ${asError(error).message}`
44223
44607
  );
44224
44608
  }
44225
- const ended = exit === void 0 ? "ended while the daemon was stopped" : "never" in exit ? `never started (${exit.never})` : exit.signal !== null ? `ended on ${exit.signal}` : `exited with code ${String(exit.code)}`;
44609
+ const ended = exit === void 0 ? "ended while the daemon was stopped" : "never" in exit ? `never started (${exit.never})` : exitPhrase(exit);
44226
44610
  const { worktree, root, branch, base_sha: base2 } = mark;
44227
44611
  if (worktree === void 0 || !existsSync21(worktree)) {
44228
44612
  log(`work order ${id2}: the harness ${ended}`);
@@ -44276,13 +44660,28 @@ function createWorkOrderAutostart(deps) {
44276
44660
  `work order ${id2}: the harness ${ended} and left nothing that is not on a remote, so its worktree is removed${mark.log !== void 0 ? `. Its output is in ${mark.log}` : ""}.`
44277
44661
  );
44278
44662
  }
44279
- function watch2(id2, workspace, exited) {
44663
+ async function reportExit(id2, binary, exit, logPath) {
44664
+ if (sessions.has(id2)) return;
44665
+ await wait(EXIT_REPORT_GRACE_MS);
44666
+ if (sessions.has(id2)) return;
44667
+ const said = lastLogLine(logPath);
44668
+ const reason3 = `${binary} ${exitPhrase(exit)} before it opened a run.${said !== void 0 ? ` Its last output: ${said}` : ""} The full output is in ${logPath} on this machine.`;
44669
+ const failed3 = await refuseWorkOrder(deps.client, id2, reason3.slice(0, 512));
44670
+ log(
44671
+ failed3 === void 0 ? `work order ${id2}: ${reason3} Oxagen ended the send.` : `work order ${id2}: ${reason3} Oxagen kept the send (${failed3}).`
44672
+ );
44673
+ }
44674
+ function watch2(id2, binary, workspace, exited) {
44280
44675
  void exited.then((exit) => {
44281
- const done = cleanUp({ work_order: id2, ...workspace }, exit).catch((error) => {
44676
+ const done = Promise.all([
44677
+ cleanUp({ work_order: id2, ...workspace }, exit),
44678
+ reportExit(id2, binary, exit, workspace.log)
44679
+ ]).then(() => void 0).catch((error) => {
44282
44680
  log(
44283
44681
  `work order ${id2}: cleanup after the harness failed: ${asError(error).message}`
44284
44682
  );
44285
44683
  }).finally(() => {
44684
+ sessions.delete(id2);
44286
44685
  cleanups.delete(done);
44287
44686
  });
44288
44687
  cleanups.add(done);
@@ -44292,8 +44691,8 @@ function createWorkOrderAutostart(deps) {
44292
44691
  const id2 = order.work_order;
44293
44692
  const running = runningWorkOrder(paths, id2, isAlive);
44294
44693
  if (running !== void 0) return;
44295
- const wait = retry.get(id2);
44296
- if (wait !== void 0 && deps.now() < wait.at) return;
44694
+ const wait2 = retry.get(id2);
44695
+ if (wait2 !== void 0 && deps.now() < wait2.at) return;
44297
44696
  const outcome = await claimForStart(deps.client, deps.host(), id2);
44298
44697
  if (outcome.kind === "refused" && outcome.final) {
44299
44698
  log(
@@ -44389,7 +44788,7 @@ function createWorkOrderAutostart(deps) {
44389
44788
  log(
44390
44789
  `work order ${id2} for ${claim.work_order.item_number} started ${binary} (process ${started.pid}) in ${workspace.worktree}; output goes to ${workspace.log}`
44391
44790
  );
44392
- watch2(id2, workspace, started.exited);
44791
+ watch2(id2, binary, workspace, started.exited);
44393
44792
  }
44394
44793
  async function runPass() {
44395
44794
  let orders;
@@ -44438,6 +44837,9 @@ function createWorkOrderAutostart(deps) {
44438
44837
  }
44439
44838
  }
44440
44839
  },
44840
+ noteSession: (workOrder) => {
44841
+ sessions.add(workOrder);
44842
+ },
44441
44843
  idle: async () => {
44442
44844
  while (pass2 !== void 0 || cleanups.size > 0) {
44443
44845
  await Promise.allSettled([pass2, ...cleanups]);
@@ -44445,7 +44847,7 @@ function createWorkOrderAutostart(deps) {
44445
44847
  }
44446
44848
  };
44447
44849
  }
44448
- var SLOW_GIT_MS, RETRY_MIN_MS, RETRY_MAX_MS, spawnDetached;
44850
+ var SLOW_GIT_MS, RETRY_MIN_MS, RETRY_MAX_MS, EXIT_REPORT_GRACE_MS, TERMINAL_CODE, EXIT_REPORT_QUOTE, spawnDetached;
44449
44851
  var init_work_order_autostart = __esm({
44450
44852
  "../../packages/tacho/src/collector/work-order-autostart.ts"() {
44451
44853
  "use strict";
@@ -44459,6 +44861,12 @@ var init_work_order_autostart = __esm({
44459
44861
  SLOW_GIT_MS = 12e4;
44460
44862
  RETRY_MIN_MS = 15e3;
44461
44863
  RETRY_MAX_MS = 5 * 6e4;
44864
+ EXIT_REPORT_GRACE_MS = 6e4;
44865
+ TERMINAL_CODE = new RegExp(
44866
+ `${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`,
44867
+ "g"
44868
+ );
44869
+ EXIT_REPORT_QUOTE = 240;
44462
44870
  spawnDetached = (command2, args, options) => new Promise((resolve26) => {
44463
44871
  let fd2;
44464
44872
  try {
@@ -44468,10 +44876,10 @@ var init_work_order_autostart = __esm({
44468
44876
  resolve26({ ok: false, error: asError(error) });
44469
44877
  return;
44470
44878
  }
44471
- let open6 = true;
44879
+ let open7 = true;
44472
44880
  const close = () => {
44473
- if (!open6) return;
44474
- open6 = false;
44881
+ if (!open7) return;
44882
+ open7 = false;
44475
44883
  try {
44476
44884
  closeSync7(fd2);
44477
44885
  } catch {
@@ -44559,7 +44967,7 @@ import { createReadStream } from "node:fs";
44559
44967
  import {
44560
44968
  lstat,
44561
44969
  mkdir,
44562
- open as open3,
44970
+ open as open4,
44563
44971
  readdir,
44564
44972
  readlink,
44565
44973
  rename,
@@ -44612,7 +45020,7 @@ async function contentHash(absolutePath, kind, keep) {
44612
45020
  const temp2 = join37(keep.dir, `.partial-${randomUUID3()}`);
44613
45021
  copy = {
44614
45022
  temp: temp2,
44615
- handle: await open3(temp2, "wx", keep.executable ? 448 : 384)
45023
+ handle: await open4(temp2, "wx", keep.executable ? 448 : 384)
44616
45024
  };
44617
45025
  } catch {
44618
45026
  copy = null;
@@ -46380,22 +46788,41 @@ var init_interjection = __esm({
46380
46788
  function showsRefusedPrompt(harness) {
46381
46789
  return harness !== "stella";
46382
46790
  }
46791
+ function questionBoundary(harness) {
46792
+ return showsRefusedPrompt(harness) ? "UserPromptSubmit" : "SessionStart";
46793
+ }
46794
+ function holdToolCall(evaluation, record) {
46795
+ const held = record.control.interjection;
46796
+ if (held === void 0 || evaluation.decision === "deny") return evaluation;
46797
+ const refused2 = {
46798
+ ...evaluation,
46799
+ decision: "deny",
46800
+ evaluated: "deny",
46801
+ source: "bundle",
46802
+ reason_code: INTERJECTION_OPEN,
46803
+ reason: held.question
46804
+ };
46805
+ delete refused2.rule;
46806
+ delete refused2.rules;
46807
+ return refused2;
46808
+ }
46383
46809
  function isBound(clause, remote) {
46384
46810
  const bound = new Set(clause.bound_remote_digests);
46385
46811
  return bound.has(remote.remote_digest) || bound.has(remote.remote_digest_folded);
46386
46812
  }
46387
- function interjectionQuestion(clause) {
46813
+ function interjectionQuestion(clause, harness) {
46388
46814
  const minutes = Math.max(1, Math.round(clause.timeout_ms / 6e4));
46389
46815
  const slug = clause.workspace_slug;
46390
- return `Oxagen is holding this session before its first model call. It started in a repository that no workspace in your organization has bound, and skills are on for workspace ${slug}. Should Oxagen link this repository to ${slug}, or create a new workspace for it with skills off? A person with access answers on this run's page in Oxagen. With no answer in ${minutes} minutes, the session goes on without skills.`;
46816
+ const hold = showsRefusedPrompt(harness) ? `Oxagen is holding this session before its first model call. ` : `Oxagen is refusing this session's tool calls until a person answers. `;
46817
+ return hold + `It started in a repository that no workspace in your organization has bound, and skills are on for workspace ${slug}. Should Oxagen link this repository to ${slug}, or create a new workspace for it with skills off? A person with access answers on this run's page in Oxagen. With no answer in ${minutes} minutes, the session goes on without skills.`;
46391
46818
  }
46392
46819
  function proposedSlug(name) {
46393
46820
  const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+/, "").slice(0, SLUG_MAX).replace(/-+$/, "");
46394
46821
  return slug.length >= SLUG_MIN && SLUG_PATTERN.test(slug) ? slug : null;
46395
46822
  }
46396
- function raiseInterjection(record, clause, remote, now, fields) {
46823
+ function prepareInterjection(record, clause, remote, now) {
46397
46824
  const key = ulid(now);
46398
- const question = interjectionQuestion(clause);
46825
+ const question = interjectionQuestion(clause, record.harness);
46399
46826
  const expiresAt = toProtocolTimestamp(now + clause.timeout_ms);
46400
46827
  const name = remote.name !== void 0 && remote.name.length <= NAME_MAX2 ? remote.name : null;
46401
46828
  const unknown2 = repoUnknownBodySchema.safeParse({
@@ -46432,21 +46859,28 @@ function raiseInterjection(record, clause, remote, now, fields) {
46432
46859
  ]
46433
46860
  });
46434
46861
  if (!unknown2.success || !interject.success) return void 0;
46862
+ return {
46863
+ unknown: unknown2.data,
46864
+ interject: interject.data,
46865
+ held: {
46866
+ key,
46867
+ question,
46868
+ expiresAt,
46869
+ workspaceSlug: clause.workspace_slug,
46870
+ configVersion: clause.config_version
46871
+ }
46872
+ };
46873
+ }
46874
+ function sealInterjection(record, pending, fields) {
46435
46875
  const events = [
46436
- record.recorder.sealCollectorEvent("repo.unknown", unknown2.data, fields),
46876
+ record.recorder.sealCollectorEvent("repo.unknown", pending.unknown, fields),
46437
46877
  record.recorder.sealCollectorEvent(
46438
46878
  "control.interject",
46439
- interject.data,
46879
+ pending.interject,
46440
46880
  fields
46441
46881
  )
46442
46882
  ];
46443
- record.control.interjection = {
46444
- key,
46445
- question,
46446
- expiresAt,
46447
- workspaceSlug: clause.workspace_slug,
46448
- configVersion: clause.config_version
46449
- };
46883
+ record.control.interjection = { ...pending.held };
46450
46884
  return events;
46451
46885
  }
46452
46886
  function expireInterjection(record, now, fields) {
@@ -46533,7 +46967,7 @@ function settle(record, held, answer, payload, fields) {
46533
46967
  record.control.interjection = void 0;
46534
46968
  return events;
46535
46969
  }
46536
- var SLUG_PATTERN, SLUG_MIN, SLUG_MAX, NAME_MAX2, INTERJECTION_TIMED_OUT_TEXT;
46970
+ var SLUG_PATTERN, SLUG_MIN, SLUG_MAX, NAME_MAX2, INTERJECTION_OPEN, INTERJECTION_TIMED_OUT_TEXT;
46537
46971
  var init_interjection2 = __esm({
46538
46972
  "../../packages/tacho/src/collector/interjection.ts"() {
46539
46973
  "use strict";
@@ -46544,6 +46978,7 @@ var init_interjection2 = __esm({
46544
46978
  SLUG_MIN = 2;
46545
46979
  SLUG_MAX = 40;
46546
46980
  NAME_MAX2 = 120;
46981
+ INTERJECTION_OPEN = "interjection_open";
46547
46982
  INTERJECTION_TIMED_OUT_TEXT = "Oxagen asked a person whether to bind this repository to a workspace, and nobody answered in time. This session goes on without skills.";
46548
46983
  }
46549
46984
  });
@@ -46809,21 +47244,23 @@ function promptBlock(view, record) {
46809
47244
  const block = operatorBlock(view, record);
46810
47245
  if (block !== void 0) return block;
46811
47246
  const held = record.control.interjection;
46812
- if (held === void 0) return void 0;
46813
- return { code: "interjection_open", reason: held.question, source: "bundle" };
47247
+ if (held === void 0 || !showsRefusedPrompt(record.harness))
47248
+ return void 0;
47249
+ return { code: INTERJECTION_OPEN, reason: held.question, source: "bundle" };
46814
47250
  }
46815
- async function askAboutRepository(record, view, deps, replay, fields) {
46816
- if (record.control.repoChecked === true) return [];
47251
+ async function repositoryQuestion(record, view, deps, replay, boundary) {
47252
+ if (record.control.repoChecked === true || questionBoundary(record.harness) !== boundary)
47253
+ return void 0;
46817
47254
  record.control.repoChecked = true;
46818
47255
  const clause = view.verified ? view.bundle.unbound_repo : void 0;
46819
- if (clause === void 0 || replay !== void 0 || !showsRefusedPrompt(record.harness) || record.recorder.hasModelCall || record.cwd === void 0 || deps.repositoryRemote === void 0)
46820
- return [];
47256
+ if (clause === void 0 || replay !== void 0 || record.recorder.hasModelCall || record.cwd === void 0 || deps.repositoryRemote === void 0)
47257
+ return void 0;
46821
47258
  const remote = await readRepository(
46822
47259
  recallView(record),
46823
47260
  deps.repositoryRemote
46824
47261
  );
46825
- if (remote === void 0 || isBound(clause, remote)) return [];
46826
- return raiseInterjection(record, clause, remote, deps.now(), fields) ?? [];
47262
+ if (remote === void 0 || isBound(clause, remote)) return void 0;
47263
+ return prepareInterjection(record, clause, remote, deps.now());
46827
47264
  }
46828
47265
  function invocationToolUseId(raw, input, record) {
46829
47266
  const derived = input.tool_use_id;
@@ -47274,12 +47711,19 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
47274
47711
  switch (input.hook_event_name) {
47275
47712
  case "SessionStart": {
47276
47713
  const block = operatorBlock(view, record);
47714
+ const question = input.agent_id === void 0 ? await repositoryQuestion(record, view, deps, replay, "SessionStart") : void 0;
47715
+ const questionText = question?.held.question;
47277
47716
  const steering = block === void 0 && replay === void 0 ? await runSteeringBundle(record, deps) ?? view.bundle : view.bundle;
47278
47717
  const context = steering.context.system;
47279
47718
  if (block === void 0 && replay === void 0)
47280
47719
  await placeSessionSkills(view, record, deps, env);
47281
- const messages = block === void 0 && replay === void 0 && input.agent_id === void 0 ? drainMessages(record, deps, events, context?.length ?? 0) : [];
47282
- const additional = block === void 0 ? [context ?? "", ...messages.map((m) => m.text)].filter((s2) => s2.length > 0).join(CONTEXT_JOINER) : "";
47720
+ const messages = block === void 0 && replay === void 0 && input.agent_id === void 0 ? drainMessages(
47721
+ record,
47722
+ deps,
47723
+ events,
47724
+ (context?.length ?? 0) + (questionText?.length ?? 0)
47725
+ ) : [];
47726
+ const additional = block === void 0 ? [questionText ?? "", context ?? "", ...messages.map((m) => m.text)].filter((s2) => s2.length > 0).join(CONTEXT_JOINER) : "";
47283
47727
  const delivered = replay === void 0 ? void 0 : readDeliveredContext(replay.deliveredContext);
47284
47728
  const deliveryAttrs = replay === void 0 ? {
47285
47729
  // A blocked start hands the agent nothing, so it names no text.
@@ -47306,6 +47750,13 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
47306
47750
  })
47307
47751
  )
47308
47752
  );
47753
+ if (question !== void 0)
47754
+ events.push(
47755
+ ...sealInterjection(record, question, {
47756
+ hook_event_name: "SessionStart",
47757
+ attrs: replayed
47758
+ })
47759
+ );
47309
47760
  if (block !== void 0) {
47310
47761
  events.push(
47311
47762
  record.recorder.sealCollectorEvent(
@@ -47355,14 +47806,15 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
47355
47806
  };
47356
47807
  const timedOut = replay === void 0 ? expireInterjection(record, deps.now(), questionFields) : void 0;
47357
47808
  if (timedOut !== void 0) events.push(...timedOut);
47358
- const asked = await askAboutRepository(
47809
+ const asked = await repositoryQuestion(
47359
47810
  record,
47360
47811
  view,
47361
47812
  deps,
47362
47813
  replay,
47363
- questionFields
47814
+ "UserPromptSubmit"
47364
47815
  );
47365
- events.push(...asked);
47816
+ if (asked !== void 0)
47817
+ events.push(...sealInterjection(record, asked, questionFields));
47366
47818
  const block = promptBlock(view, record);
47367
47819
  const notice = block === void 0 && timedOut !== void 0 ? INTERJECTION_TIMED_OUT_TEXT : void 0;
47368
47820
  const messages = block === void 0 && replay === void 0 && input.agent_id === void 0 && deliversMessages(record.harness, input.hook_event_name) ? drainMessages(record, deps, events, notice?.length ?? 0) : [];
@@ -47440,12 +47892,17 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
47440
47892
  const toolInput = input.tool_input;
47441
47893
  noteToolCall(record, toolName3, toolInput);
47442
47894
  noteRecallHints(record, input.tool_name, toolInput);
47895
+ const timedOut = replay === void 0 ? expireInterjection(record, deps.now(), {
47896
+ hook_event_name: "PreToolUse",
47897
+ attrs: replayed
47898
+ }) : void 0;
47899
+ if (timedOut !== void 0) events.push(...timedOut);
47443
47900
  const decided = await decideToolCall(view, record, input, deps, replay, {
47444
47901
  toolName: toolName3,
47445
47902
  toolInput
47446
47903
  });
47447
47904
  const currentView = decided.view;
47448
- let evaluation = decided.evaluation;
47905
+ let evaluation = replay === void 0 ? holdToolCall(decided.evaluation, record) : decided.evaluation;
47449
47906
  if (replay === void 0 && evaluation.decision === "deny" && evaluation.reason_code === "session_paused" && record.control.pauseEffect === void 0)
47450
47907
  record.control.pauseEffect = "refused";
47451
47908
  const refusal = evaluation.decision === "deny" ? void 0 : refusesForSteer(record.harness, record.control.messages);
@@ -47471,7 +47928,7 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
47471
47928
  };
47472
47929
  }
47473
47930
  }
47474
- if (evaluation.decision === "deny" && evaluation.source !== "human")
47931
+ if (evaluation.decision === "deny" && evaluation.source !== "human" && evaluation.reason_code !== INTERJECTION_OPEN)
47475
47932
  notePolicyDenial(record, toolName3);
47476
47933
  const facts = policyFacts2(evaluation, currentView);
47477
47934
  const attrs2 = policyAttrs(evaluation, replay);
@@ -48182,6 +48639,14 @@ function expiredAt(command2, now) {
48182
48639
  }
48183
48640
  async function applyCommands(commands, deps) {
48184
48641
  const now = deps.now();
48642
+ if (newCommands(commands, deps).fresh.some(
48643
+ (command2) => command2.session_uuid === null && command2.command === "refresh_bundle" && !expiredAt(command2, now)
48644
+ ))
48645
+ await deps.refreshBundle();
48646
+ const seal2 = () => sealNewCommands(commands, deps, now);
48647
+ return deps.exclusive !== void 0 ? deps.exclusive(seal2) : seal2();
48648
+ }
48649
+ function newCommands(commands, deps) {
48185
48650
  const fresh = [];
48186
48651
  const repeated = [];
48187
48652
  for (const command2 of commands) {
@@ -48189,10 +48654,10 @@ async function applyCommands(commands, deps) {
48189
48654
  repeated.push(command2.id);
48190
48655
  else fresh.push(command2);
48191
48656
  }
48192
- if (fresh.some(
48193
- (command2) => command2.session_uuid === null && command2.command === "refresh_bundle" && !expiredAt(command2, now)
48194
- ))
48195
- await deps.refreshBundle();
48657
+ return { fresh, repeated };
48658
+ }
48659
+ function sealNewCommands(commands, deps, now) {
48660
+ const { fresh, repeated } = newCommands(commands, deps);
48196
48661
  const result2 = sealCommands(fresh, deps, now);
48197
48662
  for (const ack of result2.acknowledgements) deps.handled?.remember(ack);
48198
48663
  for (const id2 of repeated) {
@@ -53461,6 +53926,19 @@ import {
53461
53926
  } from "node:http";
53462
53927
  import { Agent as HttpsAgent, request as httpsRequest } from "node:https";
53463
53928
  import { brotliDecompress, gunzip, inflate, zstdDecompress } from "node:zlib";
53929
+ async function landedWithin(landing, ms) {
53930
+ let timer;
53931
+ try {
53932
+ return await Promise.race([
53933
+ landing.then(() => true),
53934
+ new Promise((resolve26) => {
53935
+ timer = setTimeout(() => resolve26(false), ms);
53936
+ })
53937
+ ]);
53938
+ } finally {
53939
+ if (timer !== void 0) clearTimeout(timer);
53940
+ }
53941
+ }
53464
53942
  function streamsAnthropicMessages(route, body2) {
53465
53943
  return route.api === "anthropic.messages" && body2?.["stream"] === true;
53466
53944
  }
@@ -53651,6 +54129,7 @@ function createModelProxy(deps) {
53651
54129
  const maxRequestBytes = deps.maxRequestBytes ?? DEFAULT_MAX_REQUEST_BYTES;
53652
54130
  const upstreamIdleMs = deps.upstreamIdleMs ?? DEFAULT_UPSTREAM_IDLE_MS;
53653
54131
  const hookTimeoutMs = deps.beforeForwardTimeoutMs ?? DEFAULT_BEFORE_FORWARD_TIMEOUT_MS;
54132
+ const refusalWaitMs = deps.refusalFrameWaitMs ?? DEFAULT_REFUSAL_FRAME_WAIT_MS;
53654
54133
  const httpAgent = new HttpAgent({ keepAlive: true, maxSockets: Infinity });
53655
54134
  const httpsAgent = new HttpsAgent({ keepAlive: true, maxSockets: Infinity });
53656
54135
  const inFlight = /* @__PURE__ */ new Map();
@@ -53776,10 +54255,10 @@ function createModelProxy(deps) {
53776
54255
  if (known !== void 0 && !known.sealed && !known.pendingTerminal)
53777
54256
  return { record: known, how };
53778
54257
  if (known !== void 0) {
53779
- const open6 = deps.registry.list().find(
54258
+ const open7 = deps.registry.list().find(
53780
54259
  (record2) => record2.harnessSessionId === id2 && !record2.sealed && !record2.pendingTerminal
53781
54260
  );
53782
- if (open6 !== void 0) return { record: open6, how };
54261
+ if (open7 !== void 0) return { record: open7, how };
53783
54262
  return { how: "session_closed" };
53784
54263
  }
53785
54264
  const { record } = deps.registry.ensure(id2, { harness, ambient: true });
@@ -54006,59 +54485,75 @@ function createModelProxy(deps) {
54006
54485
  found = { how: "unattributed" };
54007
54486
  }
54008
54487
  return {
54488
+ ...found.record !== void 0 ? { record: found.record } : {},
54009
54489
  recorder: found.record?.recorder ?? deps.hostRecorder(),
54010
54490
  how: found.how,
54011
54491
  attrs: attrsFor(route, found.how)
54012
54492
  };
54013
54493
  }
54494
+ function landingChain(record, how) {
54495
+ if (record === void 0) return { chain: deps.hostRecorder(), attrs: {} };
54496
+ const owner = deps.registry.byUuid(record.recorder.sessionUuid);
54497
+ if (owner !== void 0 && !owner.sealed && owner.pendingTerminal !== true)
54498
+ return { chain: owner.recorder, attrs: {} };
54499
+ return {
54500
+ chain: deps.hostRecorder(),
54501
+ attrs: {
54502
+ "oxagen.correlation": "session_closed",
54503
+ "oxagen.session_correlation": how,
54504
+ "oxagen.session_uuid": record.recorder.sessionUuid
54505
+ }
54506
+ };
54507
+ }
54014
54508
  function sealNotForwarded(req, route, attempt, reason3, status2) {
54015
54509
  if (attempt.done) return;
54016
54510
  attempt.done = true;
54017
54511
  if (route.api === "other") return;
54018
- let recorder;
54019
- let mark;
54512
+ let session;
54513
+ const lost = (error) => {
54514
+ deps.log(
54515
+ `model proxy: sealing the frame of a call it did not forward (${reason3}) failed: ${error instanceof Error ? error.message : String(error)} session=${session ?? "unattributed"}`
54516
+ );
54517
+ };
54020
54518
  try {
54021
54519
  const attribution2 = attempt.attribution ?? attributeByHeaders(req, route);
54022
- const chain = attribution2.recorder;
54023
- recorder = chain;
54024
- mark = chain.markChain();
54520
+ session = attribution2.recorder.sessionUuid;
54025
54521
  const at2 = deps.now();
54026
- deps.record([
54027
- chain.sealCollectorEvent(
54028
- "error",
54029
- {
54030
- provider: route.provider,
54031
- ...attempt.model !== void 0 ? { model: attempt.model.slice(0, 512) } : {},
54032
- ...status2 !== void 0 ? { api_status_code: status2 } : {},
54033
- api_error_class: reason3,
54034
- api_duration_ms: Math.max(0, at2 - attempt.startedAt)
54035
- },
54036
- {
54037
- ts: toProtocolTimestamp(at2),
54038
- fidelity: "proxy",
54039
- attrs: {
54040
- ...attribution2.attrs,
54041
- [NOT_FORWARDED_ATTR]: reason3,
54042
- "oxagen.provider": route.provider,
54043
- "oxagen.request_bytes_read": String(attempt.bytesRead),
54044
- ...attempt.request !== void 0 ? { "oxagen.request_digest": digestBytes(attempt.request) } : {}
54045
- }
54046
- }
54047
- )
54048
- ]);
54522
+ const body2 = {
54523
+ provider: route.provider,
54524
+ ...attempt.model !== void 0 ? { model: attempt.model.slice(0, 512) } : {},
54525
+ ...status2 !== void 0 ? { api_status_code: status2 } : {},
54526
+ api_error_class: reason3,
54527
+ api_duration_ms: Math.max(0, at2 - attempt.startedAt)
54528
+ };
54529
+ const attrs2 = {
54530
+ ...attribution2.attrs,
54531
+ [NOT_FORWARDED_ATTR]: reason3,
54532
+ "oxagen.provider": route.provider,
54533
+ "oxagen.request_bytes_read": String(attempt.bytesRead),
54534
+ ...attempt.request !== void 0 ? { "oxagen.request_digest": digestBytes(attempt.request) } : {}
54535
+ };
54536
+ const land = () => {
54537
+ const landing = landingChain(attribution2.record, attribution2.how);
54538
+ recordOnChain(
54539
+ landing.chain,
54540
+ (chain) => [
54541
+ chain.sealCollectorEvent("error", body2, {
54542
+ ts: toProtocolTimestamp(at2),
54543
+ fidelity: "proxy",
54544
+ attrs: { ...attrs2, ...landing.attrs }
54545
+ })
54546
+ ],
54547
+ deps.record
54548
+ );
54549
+ };
54550
+ if (attribution2.record === void 0) land();
54551
+ else
54552
+ void onSessionQueue(deps.exclusive, attribution2.record, land).catch(
54553
+ lost
54554
+ );
54049
54555
  } catch (error) {
54050
- if (recorder !== void 0 && mark !== void 0) {
54051
- try {
54052
- recorder.rollbackChain(mark);
54053
- } catch (rollbackError) {
54054
- deps.log(
54055
- `model proxy: rolling the chain back after a failed frame failed too: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)} session=${recorder.sessionUuid}`
54056
- );
54057
- }
54058
- }
54059
- deps.log(
54060
- `model proxy: sealing the frame of a call it did not forward (${reason3}) failed: ${error instanceof Error ? error.message : String(error)} session=${recorder?.sessionUuid ?? "unattributed"}`
54061
- );
54556
+ lost(error);
54062
54557
  }
54063
54558
  }
54064
54559
  function sendProviderError(res, route, status2, code, message, retryable = false) {
@@ -54347,29 +54842,6 @@ function createModelProxy(deps) {
54347
54842
  }
54348
54843
  callsObserved += 1;
54349
54844
  observed.set(sessionKey, (observed.get(sessionKey) ?? 0) + 1);
54350
- const closed2 = record.sealed || record.pendingTerminal === true;
54351
- const requestText = payload.body.toString("utf8");
54352
- const memory = new RequestPrefixMemory();
54353
- if (payload.priorShape !== void 0 && !closed2)
54354
- memory.remember("parent", {
54355
- text: "",
54356
- fullDigest: payload.priorShape.requestDigest,
54357
- fullBytes: 0,
54358
- storedBytes: 0,
54359
- prior: void 0,
54360
- shape: payload.priorShape,
54361
- priorPayload: void 0
54362
- });
54363
- const fold = memory.fold("parent", requestText);
54364
- const capped = cappedExchange(
54365
- fold.storedBytes > TACHO_MAX_BODY_BYTES ? void 0 : fold.text,
54366
- {
54367
- text: answer.responseText,
54368
- tooLarge: answer.responseTooLarge,
54369
- bytes: answer.responseBytes
54370
- }
54371
- );
54372
- const chain = closed2 ? deps.hostRecorder() : record.recorder;
54373
54845
  const callBody2 = llmCallBody({
54374
54846
  provider: "anthropic",
54375
54847
  model,
@@ -54385,10 +54857,33 @@ function createModelProxy(deps) {
54385
54857
  failed: answer.errorClass ?? (answer.status >= 400 ? `http_${answer.status}` : void 0),
54386
54858
  requestId: answer.requestId
54387
54859
  });
54388
- const mark = chain.markChain();
54389
- try {
54390
- deps.record(
54391
- [
54860
+ const land = () => {
54861
+ const owner = deps.registry.byUuid(record.recorder.sessionUuid);
54862
+ const closed2 = owner === void 0 || owner.sealed || owner.pendingTerminal === true;
54863
+ const requestText = payload.body.toString("utf8");
54864
+ const memory = new RequestPrefixMemory();
54865
+ if (payload.priorShape !== void 0 && !closed2)
54866
+ memory.remember("parent", {
54867
+ text: "",
54868
+ fullDigest: payload.priorShape.requestDigest,
54869
+ fullBytes: 0,
54870
+ storedBytes: 0,
54871
+ prior: void 0,
54872
+ shape: payload.priorShape,
54873
+ priorPayload: void 0
54874
+ });
54875
+ const fold = memory.fold("parent", requestText);
54876
+ const capped = cappedExchange(
54877
+ fold.storedBytes > TACHO_MAX_BODY_BYTES ? void 0 : fold.text,
54878
+ {
54879
+ text: answer.responseText,
54880
+ tooLarge: answer.responseTooLarge,
54881
+ bytes: answer.responseBytes
54882
+ }
54883
+ );
54884
+ recordOnChain(
54885
+ closed2 ? deps.hostRecorder() : owner.recorder,
54886
+ (chain) => [
54392
54887
  chain.sealCollectorEvent("llm_call", callBody2, {
54393
54888
  ts: toProtocolTimestamp(settledAt),
54394
54889
  fidelity: "proxy",
@@ -54427,20 +54922,16 @@ function createModelProxy(deps) {
54427
54922
  }
54428
54923
  })
54429
54924
  ],
54430
- chain.takeBodies()
54925
+ deps.record
54431
54926
  );
54432
- } catch (error) {
54433
- try {
54434
- chain.rollbackChain(mark);
54435
- } catch (rollbackError) {
54927
+ };
54928
+ void onSessionQueue(deps.exclusive, record, land).catch(
54929
+ (error) => {
54436
54930
  deps.log(
54437
- `model proxy: rolling the chain back after a failed keep-alive frame failed too: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)} session=${sessionKey}`
54931
+ `model proxy: sealing a cache keep-alive's frame failed: ${error instanceof Error ? error.message : String(error)} session=${sessionKey}`
54438
54932
  );
54439
54933
  }
54440
- deps.log(
54441
- `model proxy: sealing a cache keep-alive's frame failed: ${error instanceof Error ? error.message : String(error)} session=${sessionKey}`
54442
- );
54443
- }
54934
+ );
54444
54935
  return usage2.cacheReadTokens ?? 0;
54445
54936
  }
54446
54937
  async function forward(req, res, route, admitted, attempt) {
@@ -54481,7 +54972,12 @@ function createModelProxy(deps) {
54481
54972
  const sessionKey = record?.recorder.sessionUuid ?? HOST_KEY;
54482
54973
  const credential = resolveCredential(req, route);
54483
54974
  const attrs2 = attrsFor(route, how, credential);
54484
- attempt.attribution = { recorder, how, attrs: attrs2 };
54975
+ attempt.attribution = {
54976
+ ...record !== void 0 ? { record } : {},
54977
+ recorder,
54978
+ how,
54979
+ attrs: attrs2
54980
+ };
54485
54981
  const metered = route.api !== "other";
54486
54982
  const { model: askedModel, ambiguous: modelAmbiguous } = modelOf(
54487
54983
  readable,
@@ -54527,44 +55023,58 @@ function createModelProxy(deps) {
54527
55023
  if (refusal !== void 0) {
54528
55024
  refused2 += 1;
54529
55025
  const view = deps.policy();
54530
- const mark = recorder.markChain();
54531
- try {
54532
- deps.record([
54533
- recorder.sealCollectorEvent(
54534
- "policy_decision",
54535
- {
54536
- policy_decision: "deny",
54537
- policy_source: refusal.source,
54538
- policy_reason_code: refusal.code,
54539
- policy_reason_digest: digestText(refusal.message),
54540
- bundle_version: view.bundle.version,
54541
- bundle_mode: view.bundle.mode
54542
- },
54543
- {
55026
+ const decision = {
55027
+ policy_decision: "deny",
55028
+ policy_source: refusal.source,
55029
+ policy_reason_code: refusal.code,
55030
+ policy_reason_digest: digestText(refusal.message),
55031
+ bundle_version: view.bundle.version,
55032
+ bundle_mode: view.bundle.mode
55033
+ };
55034
+ const decisionAttrs = {
55035
+ ...attrs2,
55036
+ "oxagen.refused": "model_call",
55037
+ "oxagen.provider": route.provider,
55038
+ "oxagen.request_digest": digestBytes(body2),
55039
+ // The model the refusal was about, when the proxy could read one. A
55040
+ // `model_not_permitted` frame that does not name the model leaves the
55041
+ // operator guessing which entry to add.
55042
+ ...askedModel !== void 0 ? { "oxagen.model": askedModel.slice(0, 512) } : {},
55043
+ ...modelAmbiguous ? { "oxagen.model_ambiguous": "true" } : {},
55044
+ ...record !== void 0 ? {
55045
+ "oxagen.session_spend_usd_micros": String(spendFor(sessionKey))
55046
+ } : {},
55047
+ ..."attrs" in refusal ? refusal.attrs : {}
55048
+ };
55049
+ const land = () => {
55050
+ const landing = landingChain(record, how);
55051
+ recordOnChain(
55052
+ landing.chain,
55053
+ (chain) => [
55054
+ chain.sealCollectorEvent("policy_decision", decision, {
54544
55055
  fidelity: "proxy",
54545
- attrs: {
54546
- ...attrs2,
54547
- "oxagen.refused": "model_call",
54548
- "oxagen.provider": route.provider,
54549
- "oxagen.request_digest": digestBytes(body2),
54550
- // The model the refusal was about, when the proxy could read
54551
- // one. A `model_not_permitted` frame that does not name the
54552
- // model leaves the operator guessing which entry to add.
54553
- ...askedModel !== void 0 ? { "oxagen.model": askedModel.slice(0, 512) } : {},
54554
- ...modelAmbiguous ? { "oxagen.model_ambiguous": "true" } : {},
54555
- ...record !== void 0 ? {
54556
- "oxagen.session_spend_usd_micros": String(
54557
- spendFor(sessionKey)
54558
- )
54559
- } : {},
54560
- ..."attrs" in refusal ? refusal.attrs : {}
54561
- }
54562
- }
54563
- )
54564
- ]);
54565
- } catch (error) {
54566
- recorder.rollbackChain(mark);
54567
- throw error;
55056
+ attrs: { ...decisionAttrs, ...landing.attrs }
55057
+ })
55058
+ ],
55059
+ deps.record
55060
+ );
55061
+ };
55062
+ if (record === void 0) land();
55063
+ else {
55064
+ let answered = false;
55065
+ const written2 = onSessionQueue(deps.exclusive, record, land);
55066
+ void written2.catch((error) => {
55067
+ if (answered)
55068
+ deps.log(
55069
+ `model proxy: the frame of a refusal already answered was not written: ${error instanceof Error ? error.message : String(error)} session=${sessionKey}`
55070
+ );
55071
+ });
55072
+ if (!await landedWithin(written2, refusalWaitMs)) {
55073
+ answered = true;
55074
+ deps.log(
55075
+ `model proxy: answered a refused call before its frame could land, because the session's queue was busy for ${refusalWaitMs} ms session=${sessionKey}`
55076
+ );
55077
+ }
54568
55078
  }
54569
55079
  attempt.done = true;
54570
55080
  deps.log(
@@ -55245,9 +55755,9 @@ function createModelProxy(deps) {
55245
55755
  callsObservedFor: (sessionUuid2) => observed.get(sessionUuid2) ?? 0,
55246
55756
  keepAliveTick: () => keepAlive.tick(),
55247
55757
  stats: () => {
55248
- let open6 = 0;
55249
- for (const calls of inFlight.values()) open6 += calls.size;
55250
- return { callsObserved, refused: refused2, inFlight: open6 };
55758
+ let open7 = 0;
55759
+ for (const calls of inFlight.values()) open7 += calls.size;
55760
+ return { callsObserved, refused: refused2, inFlight: open7 };
55251
55761
  },
55252
55762
  close: () => {
55253
55763
  clearInterval(keepAliveTimer);
@@ -55262,7 +55772,7 @@ function createModelProxy(deps) {
55262
55772
  }
55263
55773
  };
55264
55774
  }
55265
- var DEFAULT_MAX_REQUEST_BYTES, NOT_FORWARDED_ATTR, DEFAULT_UPSTREAM_IDLE_MS, DEFAULT_BEFORE_FORWARD_TIMEOUT_MS, DEFAULT_UPSTREAM_CONNECT_MS, DEFAULT_PAUSE_HOLD_MS, DEFAULT_PAUSE_STREAM_HOLD_MS, DEFAULT_PAUSE_HOLD_PING_MS, DEFAULT_PAUSE_HOLD_CHECK_MS, MAX_HELD_CALLS_PER_SESSION, PAUSE_HOLD_CODES, ANTHROPIC_PING_EVENT, HELD_STREAM_ERROR_BYTES, FREE_SOCKET_TIMEOUT_MS, KEEP_ALIVE_TIMEOUT_MS, KEEP_ALIVE_MAX_RESPONSE_BYTES, BodyCapture, EXCHANGE_OPEN_BYTES, EXCHANGE_JOIN_BYTES, DECODERS, REQUEST_EFFORT_MAX, CREDENTIAL_MESSAGES;
55775
+ var DEFAULT_MAX_REQUEST_BYTES, NOT_FORWARDED_ATTR, DEFAULT_UPSTREAM_IDLE_MS, DEFAULT_BEFORE_FORWARD_TIMEOUT_MS, DEFAULT_UPSTREAM_CONNECT_MS, DEFAULT_REFUSAL_FRAME_WAIT_MS, DEFAULT_PAUSE_HOLD_MS, DEFAULT_PAUSE_STREAM_HOLD_MS, DEFAULT_PAUSE_HOLD_PING_MS, DEFAULT_PAUSE_HOLD_CHECK_MS, MAX_HELD_CALLS_PER_SESSION, PAUSE_HOLD_CODES, ANTHROPIC_PING_EVENT, HELD_STREAM_ERROR_BYTES, FREE_SOCKET_TIMEOUT_MS, KEEP_ALIVE_TIMEOUT_MS, KEEP_ALIVE_MAX_RESPONSE_BYTES, BodyCapture, EXCHANGE_OPEN_BYTES, EXCHANGE_JOIN_BYTES, DECODERS, REQUEST_EFFORT_MAX, CREDENTIAL_MESSAGES;
55266
55776
  var init_model_proxy = __esm({
55267
55777
  "../../packages/tacho/src/collector/model-proxy.ts"() {
55268
55778
  "use strict";
@@ -55293,6 +55803,7 @@ var init_model_proxy = __esm({
55293
55803
  DEFAULT_UPSTREAM_IDLE_MS = 10 * 6e4;
55294
55804
  DEFAULT_BEFORE_FORWARD_TIMEOUT_MS = 250;
55295
55805
  DEFAULT_UPSTREAM_CONNECT_MS = 3e4;
55806
+ DEFAULT_REFUSAL_FRAME_WAIT_MS = 1e3;
55296
55807
  DEFAULT_PAUSE_HOLD_MS = 12e4;
55297
55808
  DEFAULT_PAUSE_STREAM_HOLD_MS = 12 * 60 * 6e4;
55298
55809
  DEFAULT_PAUSE_HOLD_PING_MS = 15e3;
@@ -55477,7 +55988,7 @@ var init_model_proxy_listener = __esm({
55477
55988
  import {
55478
55989
  createServer as createServer5
55479
55990
  } from "node:http";
55480
- import { chmodSync as chmodSync9, existsSync as existsSync23, statSync as statSync12, unlinkSync as unlinkSync11 } from "node:fs";
55991
+ import { chmodSync as chmodSync9, existsSync as existsSync23, statSync as statSync13, unlinkSync as unlinkSync11 } from "node:fs";
55481
55992
  import { randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
55482
55993
  import { createConnection } from "node:net";
55483
55994
  function isExportFormat(value) {
@@ -55853,7 +56364,7 @@ function createRequestHandler(api, log, options = {}) {
55853
56364
  }
55854
56365
  async function removeStaleSocket(socketPath) {
55855
56366
  if (!existsSync23(socketPath)) return;
55856
- if (!statSync12(socketPath).isSocket()) {
56367
+ if (!statSync13(socketPath).isSocket()) {
55857
56368
  throw new Error(`collector socket path is not a socket: ${socketPath}`);
55858
56369
  }
55859
56370
  const stale = await new Promise((resolve26, reject) => {
@@ -56022,7 +56533,7 @@ var init_server = __esm({
56022
56533
  });
56023
56534
 
56024
56535
  // ../../packages/tacho/src/collector/refused.ts
56025
- import { readdirSync as readdirSync12, readFileSync as readFileSync20, statSync as statSync13 } from "node:fs";
56536
+ import { readdirSync as readdirSync12, readFileSync as readFileSync20, statSync as statSync14 } from "node:fs";
56026
56537
  import { join as join45 } from "node:path";
56027
56538
  function refusalRetryable(refusal) {
56028
56539
  return refusal === "malformed" || refusal === "parked";
@@ -56072,7 +56583,7 @@ function readRefusedEntry(file) {
56072
56583
  let mtimeMs;
56073
56584
  try {
56074
56585
  record = JSON.parse(readFileSync20(file, "utf8"));
56075
- mtimeMs = statSync13(file).mtimeMs;
56586
+ mtimeMs = statSync14(file).mtimeMs;
56076
56587
  } catch {
56077
56588
  return void 0;
56078
56589
  }
@@ -57025,7 +57536,7 @@ import {
57025
57536
  readdirSync as readdirSync13,
57026
57537
  readFileSync as readFileSync22,
57027
57538
  renameSync as renameSync4,
57028
- statSync as statSync14,
57539
+ statSync as statSync15,
57029
57540
  unlinkSync as unlinkSync13
57030
57541
  } from "node:fs";
57031
57542
  import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "node:fs/promises";
@@ -57224,7 +57735,8 @@ async function startDaemon(options) {
57224
57735
  }
57225
57736
  }
57226
57737
  async function initializeDaemon(options, server, port, ready) {
57227
- const paths = options.paths;
57738
+ const paths = withRecordedHarnessFiles(options.paths, options.host);
57739
+ const harnessHomes = recordedHarnessHomes(options.host);
57228
57740
  const now = options.now ?? (() => Date.now());
57229
57741
  const log = options.log;
57230
57742
  const timers = { ...DEFAULT_TIMERS, ...options.timers };
@@ -57374,7 +57886,7 @@ async function initializeDaemon(options, server, port, ready) {
57374
57886
  processStarts: hookProcessStarts,
57375
57887
  adoptedChain: (sessionUuid2) => backfillLedger.chainHead(sessionUuid2)
57376
57888
  });
57377
- const priorStateWrittenAt = existsSync25(paths.daemonState) ? statSync14(paths.daemonState).mtimeMs : void 0;
57889
+ const priorStateWrittenAt = existsSync25(paths.daemonState) ? statSync15(paths.daemonState).mtimeMs : void 0;
57378
57890
  const sealedState = parseSealedState(
57379
57891
  readJsonStateFile(
57380
57892
  paths.daemonSealedState,
@@ -57521,7 +58033,8 @@ async function initializeDaemon(options, server, port, ready) {
57521
58033
  },
57522
58034
  ...options.workOrders?.spawn !== void 0 ? { spawn: options.workOrders.spawn } : {},
57523
58035
  ...options.workOrders?.isAlive !== void 0 ? { isAlive: options.workOrders.isAlive } : {},
57524
- ...options.workOrders?.findCheckout !== void 0 ? { findCheckout: options.workOrders.findCheckout } : {}
58036
+ ...options.workOrders?.findCheckout !== void 0 ? { findCheckout: options.workOrders.findCheckout } : {},
58037
+ ...options.workOrders?.wait !== void 0 ? { wait: options.workOrders.wait } : {}
57525
58038
  });
57526
58039
  void workOrderAutostart.clearDeadMarks();
57527
58040
  function persistState(options2 = {}) {
@@ -57736,25 +58249,24 @@ async function initializeDaemon(options, server, port, ready) {
57736
58249
  });
57737
58250
  }
57738
58251
  async function applyCommandsRecorded(commands, deps) {
57739
- let marks = markCommandChains(commands);
57740
- let result2;
57741
- try {
57742
- result2 = await applyCommands2(commands, {
57743
- ...deps,
57744
- refreshBundle: async () => {
57745
- marks = [];
57746
- await deps.refreshBundle();
57747
- marks = markCommandChains(commands);
58252
+ return applyCommands2(commands, {
58253
+ ...deps,
58254
+ exclusive: (seal2) => queues.host(async () => {
58255
+ if (hostRecord.sealed) throw new Error(STOPPING);
58256
+ const marks = markCommandChains(commands);
58257
+ let result2;
58258
+ try {
58259
+ result2 = seal2();
58260
+ record(result2.events);
58261
+ return result2;
58262
+ } catch (error) {
58263
+ for (const { session, mark } of [...marks].reverse())
58264
+ session.recorder.rollbackChain(mark);
58265
+ result2?.restore();
58266
+ throw error;
57748
58267
  }
57749
- });
57750
- record(result2.events);
57751
- return result2;
57752
- } catch (error) {
57753
- for (const { session, mark } of [...marks].reverse())
57754
- session.recorder.rollbackChain(mark);
57755
- result2?.restore();
57756
- throw error;
57757
- }
58268
+ })
58269
+ });
57758
58270
  }
57759
58271
  let modelBaseUrls = [];
57760
58272
  const spoolFacts = /* @__PURE__ */ new Map();
@@ -57876,6 +58388,31 @@ async function initializeDaemon(options, server, port, ready) {
57876
58388
  }
57877
58389
  }
57878
58390
  function retryOwedBodyPurge() {
58391
+ if (existsSync25(bodyPurgeOwedPath)) void settleOwedBodyPurge("owed");
58392
+ }
58393
+ let bodyPurge;
58394
+ let bodyPurgeAgain = false;
58395
+ let bodyPurgeNarrowing = false;
58396
+ function settleOwedBodyPurge(trigger) {
58397
+ if (trigger === "narrowed") bodyPurgeNarrowing = true;
58398
+ if (bodyPurge !== void 0) {
58399
+ bodyPurgeAgain = true;
58400
+ return bodyPurge;
58401
+ }
58402
+ const run3 = (async () => {
58403
+ do {
58404
+ bodyPurgeAgain = false;
58405
+ const narrowing = bodyPurgeNarrowing;
58406
+ bodyPurgeNarrowing = false;
58407
+ await sweepOwedBodies(narrowing);
58408
+ } while (bodyPurgeAgain && !stopped2);
58409
+ })().finally(() => {
58410
+ if (bodyPurge === run3) bodyPurge = void 0;
58411
+ });
58412
+ bodyPurge = run3;
58413
+ return run3;
58414
+ }
58415
+ async function sweepOwedBodies(narrowing) {
57879
58416
  const owed = owedBodyPurge();
57880
58417
  if (owed === void 0) return;
57881
58418
  let clause = owed.retention;
@@ -57885,16 +58422,28 @@ async function initializeDaemon(options, server, port, ready) {
57885
58422
  clause = inForce.mandate;
57886
58423
  }
57887
58424
  try {
57888
- const purged = wal.purgeBodiesOutsideMandate(clause) + purgePendingEndBodies(clause);
58425
+ const purged = await wal.purgeBodiesOutsideMandateAsync(clause, {
58426
+ signal: stopping.signal
58427
+ }) + purgePendingEndBodies(clause);
57889
58428
  purgeQueuedPrompts(clause);
57890
- clearBodyPurgeOwed();
58429
+ if (!bodyPurgeAgain) clearBodyPurgeOwed();
57891
58430
  log(
57892
- `completed an owed body purge: erased ${purged} queued body(ies) the mandate does not cover`
58431
+ narrowing ? `mandate narrowed (${owed.classes.join(", ")}): erased ${purged} queued body(ies) from the WAL` : `completed an owed body purge: erased ${purged} queued body(ies) the mandate does not cover`
57893
58432
  );
57894
58433
  } catch (error) {
57895
- log(
57896
- `an owed body purge failed again; content remains in the WAL: ${error instanceof Error ? error.message : String(error)}; check free space and permissions under ${paths.wal}. The purge is retried at each bundle confirmation`
57897
- );
58434
+ const reason3 = error instanceof Error ? error.message : String(error);
58435
+ if (stopping.signal.aborted)
58436
+ log(
58437
+ "stop: left an owed body purge unfinished; the next start finishes it"
58438
+ );
58439
+ else if (narrowing)
58440
+ log(
58441
+ `failed to erase bodies the narrowed mandate no longer covers (${owed.classes.join(", ")}); content remains in the WAL: ${reason3}; check free space and permissions under ${paths.wal}. The purge is retried at each bundle confirmation`
58442
+ );
58443
+ else
58444
+ log(
58445
+ `an owed body purge failed again; content remains in the WAL: ${reason3}; check free space and permissions under ${paths.wal}. The purge is retried at each bundle confirmation`
58446
+ );
57898
58447
  }
57899
58448
  }
57900
58449
  function purgeQueuedPrompts(clause) {
@@ -57917,21 +58466,30 @@ async function initializeDaemon(options, server, port, ready) {
57917
58466
  const dropped = HOST_RETENTION_CLASSES.filter(
57918
58467
  (contentClass) => retentionAllows(previous, contentClass) && !retentionAllows(next, contentClass)
57919
58468
  );
57920
- if (dropped.length === 0) return true;
57921
- const recorded = markBodyPurgeOwed(next, dropped);
58469
+ if (dropped.length === 0) return "none";
58470
+ if (markBodyPurgeOwed(next, dropped)) {
58471
+ try {
58472
+ purgePendingEndBodies(next);
58473
+ purgeQueuedPrompts(next);
58474
+ } catch (error) {
58475
+ log(
58476
+ `failed to erase bodies the narrowed mandate no longer covers (${dropped.join(", ")}); content remains on disk: ${error instanceof Error ? error.message : String(error)}`
58477
+ );
58478
+ }
58479
+ return "owed";
58480
+ }
57922
58481
  try {
57923
58482
  const purged = wal.purgeBodiesOutsideMandate(next) + purgePendingEndBodies(next);
57924
58483
  purgeQueuedPrompts(next);
57925
- clearBodyPurgeOwed();
57926
58484
  log(
57927
58485
  `mandate narrowed (${dropped.join(", ")}): erased ${purged} queued body(ies) from the WAL`
57928
58486
  );
57929
- return true;
58487
+ return "swept";
57930
58488
  } catch (error) {
57931
58489
  log(
57932
58490
  `failed to erase bodies the narrowed mandate no longer covers (${dropped.join(", ")}); content remains in the WAL: ${error instanceof Error ? error.message : String(error)}`
57933
58491
  );
57934
- return recorded;
58492
+ return "refused";
57935
58493
  }
57936
58494
  }
57937
58495
  async function fetchBundle() {
@@ -57963,10 +58521,11 @@ async function initializeDaemon(options, server, port, ready) {
57963
58521
  );
57964
58522
  return "refused";
57965
58523
  }
57966
- if (!purgeBodiesNarrowedOut(
58524
+ const narrowing = purgeBodiesNarrowedOut(
57967
58525
  host.bundle.retention,
57968
58526
  response.bundle.retention
57969
- )) {
58527
+ );
58528
+ if (narrowing === "refused") {
57970
58529
  log(
57971
58530
  "refusing to cache a narrowed bundle this host can neither enforce on disk nor remember owing; the old etag stands so the next poll retries"
57972
58531
  );
@@ -57979,6 +58538,7 @@ async function initializeDaemon(options, server, port, ready) {
57979
58538
  bundleVerified = true;
57980
58539
  mandateConfirmedAt = now();
57981
58540
  log(`bundle ${response.bundle.version} (${response.bundle.etag}) cached`);
58541
+ if (narrowing === "owed") void settleOwedBodyPurge("narrowed");
57982
58542
  return "cached";
57983
58543
  } catch (error) {
57984
58544
  log(
@@ -58341,7 +58901,8 @@ async function initializeDaemon(options, server, port, ready) {
58341
58901
  lastIngestAt ?? -Infinity,
58342
58902
  lastCommandPollAt ?? -Infinity
58343
58903
  );
58344
- if (pendingAcks.length === 0 && now() - lastEnvelopeAt < timers.commandsPollMs) {
58904
+ const pollEveryMs = registry2.live().some((session) => session !== hostRecord) ? timers.commandsPollMs : timers.commandsPollMs * IDLE_COMMAND_POLL_FACTOR;
58905
+ if (pendingAcks.length === 0 && now() - lastEnvelopeAt < pollEveryMs) {
58345
58906
  return;
58346
58907
  }
58347
58908
  if (now() < commandPollNextAttemptAt) return;
@@ -58742,6 +59303,8 @@ async function initializeDaemon(options, server, port, ready) {
58742
59303
  restoreEveryQueue(queued);
58743
59304
  throw error;
58744
59305
  }
59306
+ const workOrder = workOrderIdOf(envelope2.env?.[WORK_ORDER_ENV]);
59307
+ if (workOrder !== void 0) workOrderAutostart.noteSession(workOrder);
58745
59308
  if (pending !== void 0) {
58746
59309
  const state = registry2.state();
58747
59310
  const key = sessionMapKey(harnessSessionId, {
@@ -59244,7 +59807,8 @@ async function initializeDaemon(options, server, port, ready) {
59244
59807
  const state = await readModelBaseUrlState({
59245
59808
  home: options.home ?? homedir8(),
59246
59809
  port: modelProxyPortFor(host),
59247
- stellaHome: dirname26(options.paths.stellaToml),
59810
+ stellaHome: dirname26(paths.stellaToml),
59811
+ ...harnessHomes,
59248
59812
  harnesses: ["claude-code", "codex", "stella"]
59249
59813
  });
59250
59814
  const next = {};
@@ -59807,7 +60371,7 @@ async function initializeDaemon(options, server, port, ready) {
59807
60371
  if (!name.endsWith(".json")) continue;
59808
60372
  const file = join47(paths.quarantine, name);
59809
60373
  try {
59810
- const refusedAt = readRefusedEntry(file)?.refusedAt ?? statSync14(file).mtimeMs;
60374
+ const refusedAt = readRefusedEntry(file)?.refusedAt ?? statSync15(file).mtimeMs;
59811
60375
  if (at2 - refusedAt < retainMs) continue;
59812
60376
  unlinkSync13(file);
59813
60377
  removed += 1;
@@ -60080,7 +60644,11 @@ async function initializeDaemon(options, server, port, ready) {
60080
60644
  else if (typeof refreshed === "object")
60081
60645
  log(`ChatGPT login refresh: ${refreshed.failed}`);
60082
60646
  if (refreshed !== "none")
60083
- restateCodexLastRefresh(options.home ?? homedir8(), now());
60647
+ restateCodexLastRefresh(
60648
+ options.home ?? homedir8(),
60649
+ now(),
60650
+ harnessHomes
60651
+ );
60084
60652
  } catch (error) {
60085
60653
  log(
60086
60654
  `ChatGPT login refresh: ${error instanceof Error ? error.message : String(error)}`
@@ -60093,6 +60661,7 @@ async function initializeDaemon(options, server, port, ready) {
60093
60661
  await renewStaticToken(harness, {
60094
60662
  home: options.home ?? homedir8(),
60095
60663
  stellaHome: dirname26(paths.stellaToml),
60664
+ ...harnessHomes,
60096
60665
  host,
60097
60666
  runTokenKeyPath: paths.runTokenKey,
60098
60667
  now,
@@ -60222,6 +60791,9 @@ async function initializeDaemon(options, server, port, ready) {
60222
60791
  drainSpool: () => queues.host(() => drainSpool()),
60223
60792
  queues,
60224
60793
  refreshBundle: () => refreshBundle({ force: true }),
60794
+ bodyPurgeIdle: async () => {
60795
+ while (bodyPurge !== void 0) await bodyPurge;
60796
+ },
60225
60797
  workOrdersIdle: () => workOrderAutostart.idle(),
60226
60798
  stop: async () => {
60227
60799
  if (stopped2) return;
@@ -60240,7 +60812,8 @@ async function initializeDaemon(options, server, port, ready) {
60240
60812
  }
60241
60813
  const [gitSettled, memorySettled] = await Promise.all([
60242
60814
  settleWithin(gitLane, timers.stopLaneMs),
60243
- settleWithin(memoryScan, timers.stopLaneMs)
60815
+ settleWithin(memoryScan, timers.stopLaneMs),
60816
+ settleWithin(bodyPurge, timers.stopLaneMs)
60244
60817
  ]);
60245
60818
  if (!gitSettled)
60246
60819
  log(
@@ -60281,7 +60854,7 @@ async function initializeDaemon(options, server, port, ready) {
60281
60854
  }
60282
60855
  };
60283
60856
  }
60284
- var DEFAULT_TIMERS, STATIC_TOKEN_RENEWAL_CHECK_MS, CHATGPT_LOGIN_CHECK_MS, MEMORY_SCAN_MS, CLOUD_GATEWAY_ORIGIN, CLOUD_GATEWAY_BASE_PATH, QUEUED_PROMPT_CLASS, TRACE_DRAIN_TIMEOUT_MS, RECORDER_EVENTS_KEPT, owedBodyPurgeSchema, objectSchema, spoolFileSchema, otlpResourceListSchema, otlpPayloadSchema, defaultExecAsync, STOPPING, STOPPED_EXEC, STATE_WRITE_DEFER_MS;
60857
+ var IDLE_COMMAND_POLL_FACTOR, DEFAULT_TIMERS, STATIC_TOKEN_RENEWAL_CHECK_MS, CHATGPT_LOGIN_CHECK_MS, MEMORY_SCAN_MS, CLOUD_GATEWAY_ORIGIN, CLOUD_GATEWAY_BASE_PATH, QUEUED_PROMPT_CLASS, TRACE_DRAIN_TIMEOUT_MS, RECORDER_EVENTS_KEPT, owedBodyPurgeSchema, objectSchema, spoolFileSchema, otlpResourceListSchema, otlpPayloadSchema, defaultExecAsync, STOPPING, STOPPED_EXEC, STATE_WRITE_DEFER_MS;
60285
60858
  var init_daemon = __esm({
60286
60859
  "../../packages/tacho/src/collector/daemon.ts"() {
60287
60860
  "use strict";
@@ -60357,6 +60930,7 @@ var init_daemon = __esm({
60357
60930
  init_refused();
60358
60931
  init_session_heads();
60359
60932
  init_transcript_tailer();
60933
+ IDLE_COMMAND_POLL_FACTOR = 8;
60360
60934
  DEFAULT_TIMERS = {
60361
60935
  shipMs: 5e3,
60362
60936
  bundleRefreshMs: 5 * 6e4,
@@ -60650,7 +61224,7 @@ import {
60650
61224
  lstatSync as lstatSync8,
60651
61225
  mkdirSync as mkdirSync7,
60652
61226
  openSync as openSync8,
60653
- readSync as readSync4,
61227
+ readSync as readSync5,
60654
61228
  realpathSync as realpathSync6,
60655
61229
  writeFileSync as writeFileSync4
60656
61230
  } from "node:fs";
@@ -60696,11 +61270,11 @@ function readBounded(root, relative11, limit = ARP_MAX_BLOB_BYTES) {
60696
61270
  const bytes = Buffer.alloc(stat7.size);
60697
61271
  let offset = 0;
60698
61272
  while (offset < bytes.length) {
60699
- const count12 = readSync4(fd2, bytes, offset, bytes.length - offset, offset);
61273
+ const count12 = readSync5(fd2, bytes, offset, bytes.length - offset, offset);
60700
61274
  if (!count12) arpFail(`The bundle file ${relative11} changed while it was read.`);
60701
61275
  offset += count12;
60702
61276
  }
60703
- if (readSync4(fd2, Buffer.alloc(1), 0, 1, offset) !== 0)
61277
+ if (readSync5(fd2, Buffer.alloc(1), 0, 1, offset) !== 0)
60704
61278
  arpFail(`The bundle file ${relative11} changed while it was read.`);
60705
61279
  return bytes;
60706
61280
  } finally {
@@ -60883,13 +61457,13 @@ function verifySettledEvidence(events) {
60883
61457
  }
60884
61458
  }
60885
61459
  if (tools.size || children.size || approvals.size) {
60886
- const open6 = [
61460
+ const open7 = [
60887
61461
  ...[...tools].map((id2) => `tool ${id2}`),
60888
61462
  ...[...approvals].map((id2) => `approval ${id2}`),
60889
61463
  ...[...children].map((id2) => `child work ${id2}`)
60890
61464
  ];
60891
61465
  arpFail(
60892
- `The source has unsettled tools, approvals, or child work at its boundary: ${open6.join(", ")}.`
61466
+ `The source has unsettled tools, approvals, or child work at its boundary: ${open7.join(", ")}.`
60893
61467
  );
60894
61468
  }
60895
61469
  const present = (value) => value !== void 0 && value !== null && value !== false && !(Array.isArray(value) && value.length === 0);
@@ -61621,7 +62195,7 @@ import {
61621
62195
  lstatSync as lstatSync9,
61622
62196
  mkdirSync as mkdirSync8,
61623
62197
  openSync as openSync9,
61624
- readSync as readSync5,
62198
+ readSync as readSync6,
61625
62199
  realpathSync as realpathSync7,
61626
62200
  rmSync as rmSync9,
61627
62201
  writeFileSync as writeFileSync5
@@ -61647,14 +62221,14 @@ function readRegular(label2, path2, limit) {
61647
62221
  const bytes = Buffer.alloc(before.size);
61648
62222
  let offset = 0;
61649
62223
  while (offset < bytes.length) {
61650
- const count12 = readSync5(fd2, bytes, offset, bytes.length - offset, offset);
62224
+ const count12 = readSync6(fd2, bytes, offset, bytes.length - offset, offset);
61651
62225
  if (count12 === 0)
61652
62226
  arpFail(
61653
62227
  `${label2} ${path2} changed while it was read. Stop its writer and capture again.`
61654
62228
  );
61655
62229
  offset += count12;
61656
62230
  }
61657
- if (readSync5(fd2, Buffer.alloc(1), 0, 1, offset) !== 0)
62231
+ if (readSync6(fd2, Buffer.alloc(1), 0, 1, offset) !== 0)
61658
62232
  arpFail(
61659
62233
  `${label2} ${path2} grew while it was read. Stop its writer and capture again.`
61660
62234
  );
@@ -62672,7 +63246,7 @@ var init_package = __esm({
62672
63246
  "package.json"() {
62673
63247
  package_default = {
62674
63248
  name: "@oxagen/cli",
62675
- version: "2.1.5-60",
63249
+ version: "2.1.5-69",
62676
63250
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
62677
63251
  type: "module",
62678
63252
  main: "./dist/index.js",
@@ -62768,7 +63342,7 @@ var init_atomic_write = __esm({
62768
63342
  // src/lib/config.ts
62769
63343
  import { homedir as homedir9 } from "os";
62770
63344
  import { join as join51 } from "path";
62771
- import { readFileSync as readFileSync25, mkdirSync as mkdirSync9, existsSync as existsSync27, statSync as statSync15 } from "fs";
63345
+ import { readFileSync as readFileSync25, mkdirSync as mkdirSync9, existsSync as existsSync27, statSync as statSync16 } from "fs";
62772
63346
  function getConfigDir() {
62773
63347
  return CONFIG_DIR;
62774
63348
  }
@@ -62798,7 +63372,7 @@ function parseConfig(raw) {
62798
63372
  function readConfig() {
62799
63373
  let identity;
62800
63374
  try {
62801
- const info = statSync15(CONFIG_FILE);
63375
+ const info = statSync16(CONFIG_FILE);
62802
63376
  identity = `${info.ino}:${info.size}:${info.mtimeMs}`;
62803
63377
  } catch {
62804
63378
  cached2 = void 0;
@@ -81663,14 +82237,17 @@ var init_digest_shared = __esm({
81663
82237
  });
81664
82238
 
81665
82239
  // ../../packages/oxagen/src/contracts/run.shared.ts
82240
+ var LATEST_ACTIVITY_MESSAGES, LATEST_ACTIVITY_TEXT_MAX;
81666
82241
  var init_run_shared = __esm({
81667
82242
  "../../packages/oxagen/src/contracts/run.shared.ts"() {
81668
82243
  "use strict";
82244
+ LATEST_ACTIVITY_MESSAGES = 5;
82245
+ LATEST_ACTIVITY_TEXT_MAX = 300;
81669
82246
  }
81670
82247
  });
81671
82248
 
81672
82249
  // ../../packages/oxagen/src/contracts/run.list.ts
81673
- var runPublicIdSchema, frameSeqSchema, runStatusSchema, runOutcomeSchema, runCostSchema, replayGradeSchema, generatedRunSummarySchema, editedRunSummarySchema, runSummarySchema, operatorKindSchema, RUN_OPERATOR_ROLES, runOperatorRoleSchema, runModelSchema, runMachineSnapshotSchema, runMachineSchema, COMMAND_BLOCKS, HOST_POLL_WINDOW_MS, RUN_PULL_REQUEST_MAX, runPullRequestSchema, runDiffSchema, runAttributionSchema, RUN_PULL_REQUEST_FILTERS, RUN_PAUSE_STATES, runPauseSchema, RUN_REPLAY_FILTERS, RUN_SORT_KEYS, RUN_LIST_TOTAL_BOUND, RUN_LABEL_MAX, runItemSchema, runList;
82250
+ var runPublicIdSchema, frameSeqSchema, runStatusSchema, runOutcomeSchema, runCostSchema, replayGradeSchema, generatedRunSummarySchema, editedRunSummarySchema, runSummarySchema, latestActivitySchema, operatorKindSchema, RUN_OPERATOR_ROLES, runOperatorRoleSchema, runModelSchema, runMachineSnapshotSchema, runMachineSchema, COMMAND_BLOCKS, HOST_POLL_WINDOW_MS, RUN_PULL_REQUEST_MAX, runPullRequestSchema, runDiffSchema, runAttributionSchema, RUN_PULL_REQUEST_FILTERS, RUN_PAUSE_STATES, runPauseSchema, RUN_REPLAY_FILTERS, RUN_SORT_KEYS, RUN_LIST_TOTAL_BOUND, RUN_LABEL_MAX, runItemSchema, runList;
81674
82251
  var init_run_list = __esm({
81675
82252
  "../../packages/oxagen/src/contracts/run.list.ts"() {
81676
82253
  "use strict";
@@ -81681,6 +82258,7 @@ var init_run_list = __esm({
81681
82258
  init_run_enrichment();
81682
82259
  init_run_fit();
81683
82260
  init_digest_shared();
82261
+ init_run_shared();
81684
82262
  init_spend_shared();
81685
82263
  init_run_shared();
81686
82264
  runPublicIdSchema = external_exports.string().regex(/^(arun|tse)_[0-9a-z]+$/, "a run public id (arun_\u2026 or tse_\u2026)");
@@ -81715,6 +82293,18 @@ var init_run_list = __esm({
81715
82293
  editedRunSummarySchema,
81716
82294
  generatedRunSummarySchema
81717
82295
  ]);
82296
+ latestActivitySchema = external_exports.object({
82297
+ /** RFC 3339; when the newest message was recorded. */
82298
+ at: external_exports.string().datetime(),
82299
+ messages: external_exports.array(
82300
+ external_exports.object({
82301
+ role: external_exports.enum(["person", "agent"]),
82302
+ text: external_exports.string().max(LATEST_ACTIVITY_TEXT_MAX),
82303
+ /** RFC 3339. */
82304
+ at: external_exports.string().datetime()
82305
+ }).strict()
82306
+ ).min(1).max(LATEST_ACTIVITY_MESSAGES)
82307
+ }).strict();
81718
82308
  operatorKindSchema = external_exports.enum(["human", "agent", "service"]);
81719
82309
  RUN_OPERATOR_ROLES = [
81720
82310
  "owner",
@@ -82132,6 +82722,11 @@ var init_run_list = __esm({
82132
82722
  * one. With automatic accounts turned off, only a person's is read.
82133
82723
  */
82134
82724
  summary: runSummarySchema.nullable(),
82725
+ /**
82726
+ * The last few messages of the run's transcript, newest last. Absent
82727
+ * until the first read of its tail (#6109).
82728
+ */
82729
+ latestActivity: latestActivitySchema.optional(),
82135
82730
  /**
82136
82731
  * The pull requests the run's frames name, earliest first. Absent when
82137
82732
  * they were not read: the session's frames could not be read (the page
@@ -87486,7 +88081,7 @@ __export(verify_exports, {
87486
88081
  readBundle: () => readBundle,
87487
88082
  verifyBundle: () => verifyBundle3
87488
88083
  });
87489
- import { existsSync as existsSync30, readFileSync as readFileSync27, statSync as statSync16 } from "node:fs";
88084
+ import { existsSync as existsSync30, readFileSync as readFileSync27, statSync as statSync17 } from "node:fs";
87490
88085
  import { join as join61 } from "node:path";
87491
88086
  function readDirectory(dir) {
87492
88087
  const read2 = (name) => {
@@ -87537,7 +88132,7 @@ function readBundle(path2) {
87537
88132
  if (!existsSync30(path2)) {
87538
88133
  throw new BundleReadError(`${path2} does not exist.`);
87539
88134
  }
87540
- return statSync16(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
88135
+ return statSync17(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
87541
88136
  }
87542
88137
  function plural3(count12, one, many) {
87543
88138
  return `${count12} ${count12 === 1 ? one : many}`;
@@ -91236,7 +91831,7 @@ var init_upstream_tool = __esm({
91236
91831
  });
91237
91832
 
91238
91833
  // ../../packages/mcp-studio/src/contract/lock.ts
91239
- var LOCK_BYTES_MAX, serverVersionSchema, lockPackageSchema, pypiLockFileSchema, fileOnlyForPypi, registryLockPackageSchema, remoteLockSourceSchema, urlOrPackage, transportOnlyWithUrl, launchOnlyWithPackage, registryLockSourceSchema, localLockSourceSchema, mcpLockSourceSchema, DEFINITION_LABELS, fromFitsType, definitionLockSourceSchema, versionSchema, definitionHashSchema, upstreamHashSchema, mcpLockedToolSchema, definitionLockedToolSchema, mcpLockSchema, definitionLockSchema, mcpToolsLockSchema;
91834
+ var LOCK_BYTES_MAX, PACKAGE_NAME_MAX, PACKAGE_VERSION_MAX, LAUNCH_ARG_MAX, LAUNCH_ARGS_MAX, serverVersionSchema, lockPackageSchema, pypiLockFileSchema, fileOnlyForPypi, registryLockPackageSchema, remoteLockSourceSchema, urlOrPackage, transportOnlyWithUrl, launchOnlyWithPackage, registryLockSourceSchema, localLockSourceSchema, mcpLockSourceSchema, DEFINITION_LABELS, fromFitsType, definitionLockSourceSchema, versionSchema, definitionHashSchema, upstreamHashSchema, mcpLockedToolSchema, definitionLockedToolSchema, mcpLockSchema, definitionLockSchema, mcpToolsLockSchema;
91240
91835
  var init_lock = __esm({
91241
91836
  "../../packages/mcp-studio/src/contract/lock.ts"() {
91242
91837
  "use strict";
@@ -91249,14 +91844,18 @@ var init_lock = __esm({
91249
91844
  init_primitives();
91250
91845
  init_server2();
91251
91846
  LOCK_BYTES_MAX = 5 * 1024 * 1024;
91847
+ PACKAGE_NAME_MAX = 214;
91848
+ PACKAGE_VERSION_MAX = 64;
91849
+ LAUNCH_ARG_MAX = 4096;
91850
+ LAUNCH_ARGS_MAX = 1024;
91252
91851
  serverVersionSchema = external_exports.string().min(1).max(64).optional().describe(
91253
91852
  "The version the server reported in initialize, when it reported one."
91254
91853
  );
91255
91854
  lockPackageSchema = external_exports.object({
91256
- name: external_exports.string().min(1).max(214).describe(
91855
+ name: external_exports.string().min(1).max(PACKAGE_NAME_MAX).describe(
91257
91856
  "The package or binary: @modelcontextprotocol/server-filesystem."
91258
91857
  ),
91259
- version: external_exports.string().min(1).max(64),
91858
+ version: external_exports.string().min(1).max(PACKAGE_VERSION_MAX),
91260
91859
  digest: sha256Schema2.describe("SHA-256 of the package or binary.")
91261
91860
  }).strict();
91262
91861
  pypiLockFileSchema = external_exports.object({
@@ -91359,7 +91958,7 @@ var init_lock = __esm({
91359
91958
  command: external_exports.string().min(1).max(1024).optional().describe(
91360
91959
  "For a package: the command the local gateway runs, npx, uvx, docker, or dnx."
91361
91960
  ),
91362
- args: external_exports.array(external_exports.string().max(4096)).max(1024).optional().describe(
91961
+ args: external_exports.array(external_exports.string().max(LAUNCH_ARG_MAX)).max(LAUNCH_ARGS_MAX).optional().describe(
91363
91962
  "For a package: the command's arguments. ${NAME} stays for the local gateway to fill from the machine, and $$ writes one $."
91364
91963
  ),
91365
91964
  server_version: serverVersionSchema
@@ -92018,6 +92617,9 @@ function keyProblems(all, given, type) {
92018
92617
  }
92019
92618
  return problems;
92020
92619
  }
92620
+ function fromPackage(texts) {
92621
+ return texts.map((text5) => ({ text: text5, field: "source.registry_type" }));
92622
+ }
92021
92623
  function words(slot, given, problems) {
92022
92624
  const { argument, label: label2, key } = slot;
92023
92625
  const field = key === void 0 ? "source.registry_type" : `source.arguments.${key}`;
@@ -92057,7 +92659,38 @@ function words(slot, given, problems) {
92057
92659
  }
92058
92660
  return [];
92059
92661
  }
92060
- return argument.type === "named" ? [literal2(argument.name), value] : [value];
92662
+ const valueWord = { text: value, field };
92663
+ return argument.type === "named" ? [...fromPackage([literal2(argument.name)]), valueWord] : [valueWord];
92664
+ }
92665
+ function lockLimitProblems(type, pkg, version2, launch) {
92666
+ const problems = [];
92667
+ if (pkg.identifier.length > PACKAGE_NAME_MAX) {
92668
+ problems.push({
92669
+ field: "source.registry_type",
92670
+ message: `the ${type} package's identifier is ${pkg.identifier.length} characters long, and the lock holds at most ${PACKAGE_NAME_MAX}`
92671
+ });
92672
+ }
92673
+ if (version2.length > PACKAGE_VERSION_MAX) {
92674
+ problems.push({
92675
+ field: "source.registry_type",
92676
+ message: `the ${type} package's version is ${version2.length} characters long, and the lock holds at most ${PACKAGE_VERSION_MAX}`
92677
+ });
92678
+ }
92679
+ launch.forEach(({ text: text5, field }, index) => {
92680
+ if (text5.length > LAUNCH_ARG_MAX) {
92681
+ problems.push({
92682
+ field,
92683
+ message: `args[${index}] is ${text5.length} characters long once each $ is written $$, and the lock holds at most ${LAUNCH_ARG_MAX}`
92684
+ });
92685
+ }
92686
+ });
92687
+ if (launch.length > LAUNCH_ARGS_MAX) {
92688
+ problems.push({
92689
+ field: "source.registry_type",
92690
+ message: `the ${type} package's launch has ${launch.length} args, and the lock holds at most ${LAUNCH_ARGS_MAX}`
92691
+ });
92692
+ }
92693
+ return problems;
92061
92694
  }
92062
92695
  function registryLaunch({
92063
92696
  source,
@@ -92123,18 +92756,22 @@ function registryLaunch({
92123
92756
  const reference = pinned !== void 0 ? ["--from", literal2(pinned.url), literal2(pkg.identifier)] : [
92124
92757
  `${literal2(pkg.identifier)}@${runner.pin === "digest" ? digest6 : literal2(version2)}`
92125
92758
  ];
92126
- const args = [
92127
- ...runner.flags,
92128
- ...type === "oci" ? env.flatMap((name) => ["-e", name]) : [],
92759
+ const launch = [
92760
+ ...fromPackage(runner.flags),
92761
+ ...type === "oci" ? env.flatMap((name) => [
92762
+ ...fromPackage(["-e"]),
92763
+ { text: name, field: "source.env" }
92764
+ ]) : [],
92129
92765
  ...runtime.flatMap((slot) => words(slot, given, problems)),
92130
- ...reference,
92766
+ ...fromPackage(reference),
92131
92767
  ...packaged.flatMap((slot) => words(slot, given, problems))
92132
92768
  ];
92769
+ problems.push(...lockLimitProblems(type, pkg, version2, launch));
92133
92770
  if (problems.length > 0) return { ok: false, problems };
92134
92771
  return {
92135
92772
  ok: true,
92136
92773
  command: runner.command,
92137
- args,
92774
+ args: launch.map((word) => word.text),
92138
92775
  package: {
92139
92776
  name: pkg.identifier,
92140
92777
  version: version2,
@@ -92148,6 +92785,7 @@ var REGISTRY_RUNNERS2, ONE_VARIABLE;
92148
92785
  var init_registry_launch = __esm({
92149
92786
  "../../packages/mcp-studio/src/model/registry-launch.ts"() {
92150
92787
  "use strict";
92788
+ init_lock();
92151
92789
  REGISTRY_RUNNERS2 = {
92152
92790
  npm: { command: "npx", flags: ["--yes"], pin: "version" },
92153
92791
  pypi: { command: "uvx", flags: [], pin: "file" },
@@ -106293,15 +106931,15 @@ var init_print = __esm({
106293
106931
  // ── Extensions ─────────────────────────────────────────────────────────────
106294
106932
  /** `extend` blocks, one for each run of extensions of the same message. */
106295
106933
  extensions(fields, scope, path2) {
106296
- let open6;
106934
+ let open7;
106297
106935
  fields.forEach((field, index) => {
106298
106936
  const where = `the extension ${scope === "" ? "" : `${scope}.`}${field.name}`;
106299
106937
  if (field.extendee === "")
106300
106938
  this.refuse(`${where} names no message to extend`);
106301
106939
  if (isFieldSet(field, FieldDescriptorProtoSchema.field.oneofIndex))
106302
106940
  this.refuse(`${where} is in a oneof`);
106303
- if (field.extendee !== open6) {
106304
- if (open6 !== void 0) this.close();
106941
+ if (field.extendee !== open7) {
106942
+ if (open7 !== void 0) this.close();
106305
106943
  const extendee = this.typeName(
106306
106944
  field.extendee,
106307
106945
  scope,
@@ -106311,7 +106949,7 @@ var init_print = __esm({
106311
106949
  this.gap();
106312
106950
  this.line(`extend ${extendee} {`);
106313
106951
  this.depth += 1;
106314
- open6 = field.extendee;
106952
+ open7 = field.extendee;
106315
106953
  }
106316
106954
  this.comment([...path2, index]);
106317
106955
  this.line(
@@ -106324,7 +106962,7 @@ var init_print = __esm({
106324
106962
  )
106325
106963
  );
106326
106964
  });
106327
- if (open6 !== void 0) this.close();
106965
+ if (open7 !== void 0) this.close();
106328
106966
  }
106329
106967
  // ── Options ────────────────────────────────────────────────────────────────
106330
106968
  /**
@@ -106820,7 +107458,7 @@ function importOrder(sources, loaded) {
106820
107458
  const done = /* @__PURE__ */ new Set();
106821
107459
  const stack = [];
106822
107460
  const onStack = /* @__PURE__ */ new Set();
106823
- const open6 = (name, importer) => {
107461
+ const open7 = (name, importer) => {
106824
107462
  const entry2 = load(name, importer, sources);
106825
107463
  loaded.set(name, entry2);
106826
107464
  const imports = entry2.kind === "source" ? entry2.parsed.header.imports.map((each) => each.path) : entry2.proto.dependency;
@@ -106829,7 +107467,7 @@ function importOrder(sources, loaded) {
106829
107467
  };
106830
107468
  for (const root of [...sources.keys()].sort()) {
106831
107469
  if (done.has(root)) continue;
106832
- open6(root, void 0);
107470
+ open7(root, void 0);
106833
107471
  for (let top = stack.at(-1); top !== void 0; top = stack.at(-1)) {
106834
107472
  const next = top.imports[top.next];
106835
107473
  top.next += 1;
@@ -106842,7 +107480,7 @@ function importOrder(sources, loaded) {
106842
107480
  const at2 = stack.findIndex((frame) => frame.name === next);
106843
107481
  throw cycle([...stack.slice(at2).map((frame) => frame.name), next]);
106844
107482
  } else if (!done.has(next)) {
106845
- open6(next, top.name);
107483
+ open7(next, top.name);
106846
107484
  }
106847
107485
  }
106848
107486
  }
@@ -107329,8 +107967,8 @@ var init_json_schema4 = __esm({
107329
107967
  description: `Cut: messages nest past ${DEPTH_MAX} levels here.`
107330
107968
  };
107331
107969
  }
107332
- const open6 = this.active.get(name) ?? 0;
107333
- if (open6 >= RECURSION_DEPTH) {
107970
+ const open7 = this.active.get(name) ?? 0;
107971
+ if (open7 >= RECURSION_DEPTH) {
107334
107972
  this.note(
107335
107973
  `Import cut the recursive message ${name} at depth ${RECURSION_DEPTH}.`
107336
107974
  );
@@ -107348,12 +107986,12 @@ var init_json_schema4 = __esm({
107348
107986
  description: `Cut: this tool's schemas passed ${count8(TOOL_NODES_SOFT_MAX)} nodes.`
107349
107987
  };
107350
107988
  }
107351
- this.active.set(name, open6 + 1);
107989
+ this.active.set(name, open7 + 1);
107352
107990
  try {
107353
107991
  return this.fieldsOf(desc, direction, depth);
107354
107992
  } finally {
107355
- if (open6 === 0) this.active.delete(name);
107356
- else this.active.set(name, open6);
107993
+ if (open7 === 0) this.active.delete(name);
107994
+ else this.active.set(name, open7);
107357
107995
  }
107358
107996
  }
107359
107997
  fieldsOf(desc, direction, depth) {
@@ -107869,7 +108507,7 @@ function selectOutput(outputSchema, select) {
107869
108507
  return trimObject(outputSchema, selectTree(select));
107870
108508
  }
107871
108509
  function selectionProblem(selection) {
107872
- const open6 = [];
108510
+ const open7 = [];
107873
108511
  let quoted3 = false;
107874
108512
  for (let index = 0; index < selection.length; index += 1) {
107875
108513
  const char = selection[index];
@@ -107879,15 +108517,15 @@ function selectionProblem(selection) {
107879
108517
  continue;
107880
108518
  }
107881
108519
  if (char === '"') quoted3 = true;
107882
- else if (char === "{" || char === "(") open6.push(char);
108520
+ else if (char === "{" || char === "(") open7.push(char);
107883
108521
  else if (char === "}" || char === ")") {
107884
108522
  const expected = char === "}" ? "{" : "(";
107885
- if (open6.pop() !== expected)
108523
+ if (open7.pop() !== expected)
107886
108524
  return `selection has an unmatched ${char} at character ${index + 1}.`;
107887
108525
  }
107888
108526
  }
107889
108527
  if (quoted3) return "selection has a string with no closing quote.";
107890
- const unclosed = open6.pop();
108528
+ const unclosed = open7.pop();
107891
108529
  if (unclosed !== void 0) return `selection has an unclosed ${unclosed}.`;
107892
108530
  if (!/[_A-Za-z]/.test(selection)) return "selection names no field.";
107893
108531
  return void 0;
@@ -109670,8 +110308,7 @@ function locationWithoutQuery(location) {
109670
110308
  url = new URL(location);
109671
110309
  } catch {
109672
110310
  const cut = location.search(/[?#]/);
109673
- const head2 = cut === -1 ? location : location.slice(0, cut);
109674
- return head2.replace(/^\/\/[^/]*@/, "//");
110311
+ return withoutUserinfo(cut === -1 ? location : location.slice(0, cut));
109675
110312
  }
109676
110313
  url.username = "";
109677
110314
  url.password = "";
@@ -109679,6 +110316,13 @@ function locationWithoutQuery(location) {
109679
110316
  url.hash = "";
109680
110317
  return url.href;
109681
110318
  }
110319
+ function withoutUserinfo(head2) {
110320
+ const match = UNPARSED_USERINFO.exec(head2);
110321
+ if (match === null) return head2;
110322
+ const prefix = match[1] ?? "";
110323
+ const rest = head2.slice(match[0].length);
110324
+ return rest === "" || /^[\\/]/.test(rest) ? `${prefix}@${rest}` : `${prefix}${rest}`;
110325
+ }
109682
110326
  function recordedResponseHeaders(headers) {
109683
110327
  const out = /* @__PURE__ */ new Map();
109684
110328
  for (const [name, raw] of headers) {
@@ -109712,12 +110356,13 @@ function recordHttpResponse(response, bytes) {
109712
110356
  ...body2 === void 0 ? {} : { body: body2 }
109713
110357
  };
109714
110358
  }
109715
- var REFUSED_RESPONSE_HEADERS;
110359
+ var UNPARSED_USERINFO, REFUSED_RESPONSE_HEADERS;
109716
110360
  var init_exchange = __esm({
109717
110361
  "../../packages/mcp-studio/src/execute/exchange.ts"() {
109718
110362
  "use strict";
109719
110363
  init_tests_files();
109720
110364
  init_body();
110365
+ UNPARSED_USERINFO = /^([A-Za-z][A-Za-z0-9+.-]*:[\\/]*|[\\/]{2})[^/\\]*@/;
109721
110366
  REFUSED_RESPONSE_HEADERS = new Set(
109722
110367
  CREDENTIAL_RESPONSE_HEADERS
109723
110368
  );
@@ -110227,14 +110872,17 @@ async function notifyInitialized(session) {
110227
110872
  });
110228
110873
  return read2.ok ? { ok: true, value: void 0 } : read2;
110229
110874
  }
110230
- async function callTool2(session) {
110231
- const { template, args } = session.call;
110232
- const sent = await post(session, {
110875
+ function toolsCallMessage(template, args) {
110876
+ return {
110233
110877
  jsonrpc: "2.0",
110234
110878
  id: CALL_ID,
110235
110879
  method: "tools/call",
110236
110880
  params: { name: template.tool, arguments: args }
110237
- });
110881
+ };
110882
+ }
110883
+ async function callTool2(session) {
110884
+ const { template, args } = session.call;
110885
+ const sent = await post(session, toolsCallMessage(template, args));
110238
110886
  if (!sent.ok) return { attempt: sent, exchange: void 0 };
110239
110887
  const response = sent.value;
110240
110888
  if (response.status < 200 || response.status >= 300) {
@@ -111150,7 +111798,7 @@ async function attemptCall(call2, context, deadline, settings) {
111150
111798
  const controller = new AbortController();
111151
111799
  const clock2 = new Clock(deadline, context.signal, controller);
111152
111800
  try {
111153
- const opening = open4(context.transport, {
111801
+ const opening = open5(context.transport, {
111154
111802
  ...call2.request,
111155
111803
  deadline_ms: Math.max(1, deadline - Date.now()),
111156
111804
  signal: controller.signal
@@ -111170,7 +111818,7 @@ async function attemptCall(call2, context, deadline, settings) {
111170
111818
  clock2.dispose();
111171
111819
  }
111172
111820
  }
111173
- async function open4(transport, request3) {
111821
+ async function open5(transport, request3) {
111174
111822
  return transport.grpc(request3);
111175
111823
  }
111176
111824
  async function read(response, call2, clock2, settings) {
@@ -130575,6 +131223,19 @@ var init_transport2 = __esm({
130575
131223
  }
130576
131224
  });
130577
131225
 
131226
+ // ../../packages/mcp-studio/src/execute/request-size.ts
131227
+ var init_request_size = __esm({
131228
+ "../../packages/mcp-studio/src/execute/request-size.ts"() {
131229
+ "use strict";
131230
+ init_body();
131231
+ init_graphql2();
131232
+ init_http();
131233
+ init_mcp();
131234
+ init_shape_input();
131235
+ init_util4();
131236
+ }
131237
+ });
131238
+
130578
131239
  // ../../packages/mcp-studio/src/execute/index.ts
130579
131240
  var init_execute = __esm({
130580
131241
  "../../packages/mcp-studio/src/execute/index.ts"() {
@@ -130591,6 +131252,7 @@ var init_execute = __esm({
130591
131252
  init_http();
130592
131253
  init_mcp();
130593
131254
  init_senders();
131255
+ init_request_size();
130594
131256
  init_validate();
130595
131257
  init_body();
130596
131258
  init_http_call();
@@ -133294,7 +133956,7 @@ import {
133294
133956
  mkdirSync as mkdirSync13,
133295
133957
  readFileSync as readFileSync30,
133296
133958
  readlinkSync as readlinkSync4,
133297
- statSync as statSync17
133959
+ statSync as statSync18
133298
133960
  } from "node:fs";
133299
133961
  import { dirname as dirname36, isAbsolute as isAbsolute18, join as join64, relative as relative9, resolve as resolve25, sep as sep12 } from "node:path";
133300
133962
  function git5(cwd, args, input) {
@@ -133407,14 +134069,14 @@ function chooseBase(root, ref2) {
133407
134069
  }
133408
134070
  function isFile(path2) {
133409
134071
  try {
133410
- return statSync17(path2).isFile();
134072
+ return statSync18(path2).isFile();
133411
134073
  } catch {
133412
134074
  return false;
133413
134075
  }
133414
134076
  }
133415
134077
  function isDir(path2) {
133416
134078
  try {
133417
- return statSync17(path2).isDirectory();
134079
+ return statSync18(path2).isDirectory();
133418
134080
  } catch {
133419
134081
  return false;
133420
134082
  }
@@ -136706,16 +137368,16 @@ function assignmentValue(bytes, assignment, nameStart, nameEnd) {
136706
137368
  if (!assignment.names.some((k) => containsLower(name, k))) return null;
136707
137369
  while (isBlank2(bytes[p])) p += 1;
136708
137370
  if (p >= n2) return null;
136709
- const open6 = bytes[p];
137371
+ const open7 = bytes[p];
136710
137372
  let start;
136711
137373
  let end;
136712
137374
  let quoted3;
136713
- if (open6 === QUOTE || open6 === APOSTROPHE || open6 === BACKTICK) {
137375
+ if (open7 === QUOTE || open7 === APOSTROPHE || open7 === BACKTICK) {
136714
137376
  start = p + 1;
136715
137377
  end = start;
136716
- while (end < n2 && bytes[end] !== open6 && bytes[end] !== NEWLINE2)
137378
+ while (end < n2 && bytes[end] !== open7 && bytes[end] !== NEWLINE2)
136717
137379
  end += bytes[end] === BACKSLASH ? 2 : 1;
136718
- if (end >= n2 || bytes[end] !== open6) return null;
137380
+ if (end >= n2 || bytes[end] !== open7) return null;
136719
137381
  quoted3 = true;
136720
137382
  } else {
136721
137383
  start = p;
@@ -137891,7 +138553,7 @@ __export(logs_exports, {
137891
138553
  handleLogs: () => handleLogs
137892
138554
  });
137893
138555
  import { watch } from "node:fs";
137894
- import { open as open5, stat as stat6 } from "node:fs/promises";
138556
+ import { open as open6, stat as stat6 } from "node:fs/promises";
137895
138557
  function formatEntry(entry2) {
137896
138558
  const head2 = `${entry2.ts} ${entry2.category.padEnd(11)} ${entry2.event}`;
137897
138559
  if (entry2.data === void 0) return head2;
@@ -137922,7 +138584,7 @@ async function logSize(file) {
137922
138584
  async function readAppended(file, offset) {
137923
138585
  let handle;
137924
138586
  try {
137925
- handle = await open5(file, "r");
138587
+ handle = await open6(file, "r");
137926
138588
  } catch {
137927
138589
  return { entries: [], offset: 0 };
137928
138590
  }