zelari-code 2.5.0 → 2.6.0

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.
@@ -3288,10 +3288,10 @@ function mergeDefs(...defs) {
3288
3288
  function cloneDef(schema) {
3289
3289
  return mergeDefs(schema._zod.def);
3290
3290
  }
3291
- function getElementAtPath(obj, path64) {
3292
- if (!path64)
3291
+ function getElementAtPath(obj, path65) {
3292
+ if (!path65)
3293
3293
  return obj;
3294
- return path64.reduce((acc, key) => acc?.[key], obj);
3294
+ return path65.reduce((acc, key) => acc?.[key], obj);
3295
3295
  }
3296
3296
  function promiseAllObject(promisesObj) {
3297
3297
  const keys = Object.keys(promisesObj);
@@ -3619,11 +3619,11 @@ function explicitlyAborted(x, startIndex = 0) {
3619
3619
  }
3620
3620
  return false;
3621
3621
  }
3622
- function prefixIssues(path64, issues) {
3622
+ function prefixIssues(path65, issues) {
3623
3623
  return issues.map((iss) => {
3624
3624
  var _a3;
3625
3625
  (_a3 = iss).path ?? (_a3.path = []);
3626
- iss.path.unshift(path64);
3626
+ iss.path.unshift(path65);
3627
3627
  return iss;
3628
3628
  });
3629
3629
  }
@@ -3841,16 +3841,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
3841
3841
  }
3842
3842
  function formatError(error51, mapper = (issue2) => issue2.message) {
3843
3843
  const fieldErrors = { _errors: [] };
3844
- const processError = (error52, path64 = []) => {
3844
+ const processError = (error52, path65 = []) => {
3845
3845
  for (const issue2 of error52.issues) {
3846
3846
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3847
- issue2.errors.map((issues) => processError({ issues }, [...path64, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path65, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path65, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path65, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path64, ...issue2.path];
3853
+ const fullpath = [...path65, ...issue2.path];
3854
3854
  if (fullpath.length === 0) {
3855
3855
  fieldErrors._errors.push(mapper(issue2));
3856
3856
  } else {
@@ -3877,17 +3877,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
3877
3877
  }
3878
3878
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
3879
3879
  const result = { errors: [] };
3880
- const processError = (error52, path64 = []) => {
3880
+ const processError = (error52, path65 = []) => {
3881
3881
  var _a3, _b;
3882
3882
  for (const issue2 of error52.issues) {
3883
3883
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3884
- issue2.errors.map((issues) => processError({ issues }, [...path64, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path65, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path65, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path65, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path64, ...issue2.path];
3890
+ const fullpath = [...path65, ...issue2.path];
3891
3891
  if (fullpath.length === 0) {
3892
3892
  result.errors.push(mapper(issue2));
3893
3893
  continue;
@@ -3919,8 +3919,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
3919
3919
  }
3920
3920
  function toDotPath(_path) {
3921
3921
  const segs = [];
3922
- const path64 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path64) {
3922
+ const path65 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
+ for (const seg of path65) {
3924
3924
  if (typeof seg === "number")
3925
3925
  segs.push(`[${seg}]`);
3926
3926
  else if (typeof seg === "symbol")
@@ -17423,13 +17423,13 @@ function resolveRef(ref, ctx) {
17423
17423
  if (!ref.startsWith("#")) {
17424
17424
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17425
17425
  }
17426
- const path64 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path64.length === 0) {
17426
+ const path65 = ref.slice(1).split("/").filter(Boolean);
17427
+ if (path65.length === 0) {
17428
17428
  return ctx.rootSchema;
17429
17429
  }
17430
17430
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17431
- if (path64[0] === defsKey) {
17432
- const key = path64[1];
17431
+ if (path65[0] === defsKey) {
17432
+ const key = path65[1];
17433
17433
  if (!key || !ctx.defs[key]) {
17434
17434
  throw new Error(`Reference not found: ${ref}`);
17435
17435
  }
@@ -19765,11 +19765,11 @@ var init_tools = __esm({
19765
19765
  if (!ctx.addDocument)
19766
19766
  return "Knowledge vault tool not available.";
19767
19767
  const title = args["title"] || "New Document";
19768
- const path64 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19768
+ const path65 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19769
19769
  const content = args["content"] || "";
19770
19770
  const tags = args["tags"] || [];
19771
19771
  ctx.addDocument({
19772
- path: path64,
19772
+ path: path65,
19773
19773
  title,
19774
19774
  content,
19775
19775
  format: "markdown",
@@ -19778,7 +19778,7 @@ var init_tools = __esm({
19778
19778
  workspaceId: ctx.workspaceId
19779
19779
  });
19780
19780
  ctx.addActivity("vault", "created document", title);
19781
- return `Document "${title}" created at "${path64}".`;
19781
+ return `Document "${title}" created at "${path65}".`;
19782
19782
  }
19783
19783
  }
19784
19784
  ];
@@ -22177,6 +22177,19 @@ var init_AgentHarness = __esm({
22177
22177
  * via Promise.all (chunked by ZELARI_MAX_PARALLEL_TOOLS, default 6); write/
22178
22178
  * execute tools run one-at-a-time in order.
22179
22179
  */
22180
+ /**
22181
+ * v2.6 Phase 3 resource seam: consult the host-owned pre-dispatch gate.
22182
+ * Degrade-and-stop — a throwing gate NEVER blocks a tool call.
22183
+ */
22184
+ checkToolCallGate(toolName, args) {
22185
+ if (!this.config.toolCallGate)
22186
+ return { allowed: true };
22187
+ try {
22188
+ return this.config.toolCallGate(toolName, args) ?? { allowed: true };
22189
+ } catch {
22190
+ return { allowed: true };
22191
+ }
22192
+ }
22180
22193
  async executePendingTools(pending, maxToolCalls) {
22181
22194
  const out = new Array(pending.length);
22182
22195
  const maxParallel = Math.max(1, Number.parseInt(process.env.ZELARI_MAX_PARALLEL_TOOLS ?? "6", 10) || 6);
@@ -22189,6 +22202,14 @@ var init_AgentHarness = __esm({
22189
22202
  durationMs: 0
22190
22203
  };
22191
22204
  }
22205
+ const gate = this.checkToolCallGate(p3.toolName, p3.args);
22206
+ if (!gate.allowed) {
22207
+ return {
22208
+ content: `[resource-gate] ${gate.reason ?? "denied by resource policy"} Prioritize verification/repair actions (test, typecheck, build, read failures) or finalize honestly.`,
22209
+ isError: true,
22210
+ durationMs: 0
22211
+ };
22212
+ }
22192
22213
  const callKey = hashToolCall(p3.toolName, p3.args);
22193
22214
  const nextCount = (this.toolCallCounts.get(callKey) ?? 0) + 1;
22194
22215
  this.toolCallCounts.set(callKey, nextCount);
@@ -22708,6 +22729,21 @@ ${cached2}`
22708
22729
  });
22709
22730
  this.emit(startEv);
22710
22731
  yield startEv;
22732
+ const gate = this.checkToolCallGate(tt.name, tt.args);
22733
+ if (!gate.allowed) {
22734
+ const denied = `[resource-gate] ${gate.reason ?? "denied by resource policy"} Prioritize verification/repair actions (test, typecheck, build, read failures) or finalize honestly.`;
22735
+ const denyEv = createBrainEvent("tool_execution_end", this.sessionId, {
22736
+ toolCallId,
22737
+ result: denied,
22738
+ isError: true,
22739
+ durationMs: 0
22740
+ });
22741
+ this.emit(denyEv);
22742
+ yield denyEv;
22743
+ turnToolResults.push({ toolCallId, content: denied });
22744
+ executedAny = true;
22745
+ continue;
22746
+ }
22711
22747
  let resultStr = "";
22712
22748
  let isError = false;
22713
22749
  const startMs = Date.now();
@@ -24238,11 +24274,11 @@ var init_synthesisAudit = __esm({
24238
24274
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
24239
24275
  import { join as join2 } from "node:path";
24240
24276
  function loadNfrSpec(zelariRoot) {
24241
- const path64 = join2(zelariRoot, "nfr-spec.json");
24242
- if (!existsSync7(path64))
24277
+ const path65 = join2(zelariRoot, "nfr-spec.json");
24278
+ if (!existsSync7(path65))
24243
24279
  return null;
24244
24280
  try {
24245
- const raw = JSON.parse(readFileSync7(path64, "utf8"));
24281
+ const raw = JSON.parse(readFileSync7(path65, "utf8"));
24246
24282
  if (raw.version !== 1 || !Array.isArray(raw.targets))
24247
24283
  return null;
24248
24284
  return raw;
@@ -26548,9 +26584,9 @@ var init_types4 = __esm({
26548
26584
  import { readFileSync as readFileSync12 } from "node:fs";
26549
26585
  import { join as join8 } from "node:path";
26550
26586
  function readLessonsDeduped(zelariRoot) {
26551
- const path64 = join8(zelariRoot, LESSONS_FILE);
26587
+ const path65 = join8(zelariRoot, LESSONS_FILE);
26552
26588
  try {
26553
- const raw = readFileSync12(path64, "utf8");
26589
+ const raw = readFileSync12(path65, "utf8");
26554
26590
  const byId = /* @__PURE__ */ new Map();
26555
26591
  for (const line of raw.split(/\r?\n/)) {
26556
26592
  if (!line.trim())
@@ -26651,8 +26687,8 @@ function keywordsFrom(check2, signature) {
26651
26687
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
26652
26688
  }
26653
26689
  function writeLesson(zelariRoot, lesson) {
26654
- const path64 = join9(zelariRoot, LESSONS_FILE);
26655
- appendFileSync(path64, `${JSON.stringify(lesson)}
26690
+ const path65 = join9(zelariRoot, LESSONS_FILE);
26691
+ appendFileSync(path65, `${JSON.stringify(lesson)}
26656
26692
  `, "utf8");
26657
26693
  }
26658
26694
  function findSimilar(lessons, signature) {
@@ -27266,9 +27302,9 @@ function findCycle(nodes) {
27266
27302
  if (color.get(start) !== WHITE)
27267
27303
  continue;
27268
27304
  const stack = [[start, 0]];
27269
- const path64 = [];
27305
+ const path65 = [];
27270
27306
  color.set(start, GRAY);
27271
- path64.push(start);
27307
+ path65.push(start);
27272
27308
  while (stack.length > 0) {
27273
27309
  const top = stack[stack.length - 1];
27274
27310
  const [id, idx] = top;
@@ -27281,17 +27317,17 @@ function findCycle(nodes) {
27281
27317
  continue;
27282
27318
  const c = color.get(dep);
27283
27319
  if (c === GRAY) {
27284
- const at = path64.indexOf(dep);
27285
- return [...path64.slice(at), dep];
27320
+ const at = path65.indexOf(dep);
27321
+ return [...path65.slice(at), dep];
27286
27322
  }
27287
27323
  if (c === WHITE) {
27288
27324
  color.set(dep, GRAY);
27289
- path64.push(dep);
27325
+ path65.push(dep);
27290
27326
  stack.push([dep, 0]);
27291
27327
  }
27292
27328
  } else {
27293
27329
  color.set(id, BLACK);
27294
- path64.pop();
27330
+ path65.pop();
27295
27331
  stack.pop();
27296
27332
  }
27297
27333
  }
@@ -28211,8 +28247,8 @@ var init_runner = __esm({
28211
28247
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
28212
28248
  pending: []
28213
28249
  };
28214
- const path64 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
28215
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path64}`);
28250
+ const path65 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
28251
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path65}`);
28216
28252
  return snapshot;
28217
28253
  }
28218
28254
  callLog(msg, data) {
@@ -28300,6 +28336,10 @@ var init_types8 = __esm({
28300
28336
  "session.resumed",
28301
28337
  "session.ended",
28302
28338
  "session.forked",
28339
+ // 2.6 Track A (harness manifest): canonical harness fingerprint recorded
28340
+ // once at session start / manifest change. State-only (never model-surface):
28341
+ // data = {manifest, manifestHash}. Schema review per ADR-0021.
28342
+ "session.harness_manifest",
28303
28343
  "user.message",
28304
28344
  "assistant.message",
28305
28345
  "tool.call",
@@ -28310,6 +28350,11 @@ var init_types8 = __esm({
28310
28350
  "session.compacted",
28311
28351
  "task.created",
28312
28352
  "task.updated",
28353
+ // 2.6 Track A (doc §14): first-class task contract. Append-only,
28354
+ // monotone versioning (task.contract_updated supersedes, never rewrites).
28355
+ // State-only: compaction projects it into CompactionStateSnapshot.
28356
+ "task.contract",
28357
+ "task.contract_updated",
28313
28358
  "kraken.task",
28314
28359
  "council.member",
28315
28360
  "mission.phase",
@@ -28322,6 +28367,12 @@ var init_types8 = __esm({
28322
28367
  // the session-log anchor EvidenceRef.seq points at (command output, fs
28323
28368
  // observation, digest). Not model-surface. Schema review per ADR-0021.
28324
28369
  "verification.evidence",
28370
+ // 2.6 Track B (resource-aware execution, doc §9-§12): host-owned resource
28371
+ // state. `resource.snapshot` is model-surface with LATEST-ONLY projection
28372
+ // (doc §10.2 — see modelSurface.ts); limit/reserve events are state-only.
28373
+ "resource.snapshot",
28374
+ "resource.limit_reached",
28375
+ "resource.reserve_entered",
28325
28376
  "note"
28326
28377
  ];
28327
28378
  SessionEventEnvelopeSchema = external_exports.object({
@@ -28479,9 +28530,13 @@ function buildCompactionStateSnapshot(events, toSeq) {
28479
28530
  ...typeof lastMissionAdvice?.data.recommendation === "string" ? { recommendation: lastMissionAdvice.data.recommendation } : {},
28480
28531
  ...stringsOf(lastMissionAdvice?.data.blockers).length > 0 ? { blockers: stringsOf(lastMissionAdvice?.data.blockers) } : {}
28481
28532
  } : void 0;
28533
+ const latestContractEvent = [...scoped].reverse().find((event) => event.kind === "task.contract" || event.kind === "task.contract_updated");
28534
+ const contractRaw = latestContractEvent && typeof latestContractEvent.data.contract === "object" ? latestContractEvent.data.contract : void 0;
28535
+ const contractConstraints = Array.isArray(contractRaw?.constraints) ? contractRaw.constraints.filter((c) => c.source === "user" && c.required === true && typeof c.text === "string").map((c) => String(c.text)) : void 0;
28536
+ const contractCriteria = Array.isArray(contractRaw?.acceptanceCriteria) ? contractRaw.acceptanceCriteria.filter((c) => typeof c.id === "string").map((c) => ({ id: String(c.id), required: c.required === true })) : void 0;
28482
28537
  return {
28483
28538
  version: 1,
28484
- activeCriteria,
28539
+ activeCriteria: contractCriteria ?? activeCriteria,
28485
28540
  unresolvedIssues,
28486
28541
  ...latestVerification ? {
28487
28542
  latestVerification: {
@@ -28492,7 +28547,7 @@ function buildCompactionStateSnapshot(events, toSeq) {
28492
28547
  } : {},
28493
28548
  retainedEvidenceRefs: evidenceFromResults(results),
28494
28549
  affectedFiles: [...affectedFiles].slice(0, 64),
28495
- userConstraints: [...userConstraints].slice(-12),
28550
+ userConstraints: contractConstraints ?? [...userConstraints].slice(-12),
28496
28551
  ...missionState ? { missionState } : {}
28497
28552
  };
28498
28553
  }
@@ -28630,13 +28685,45 @@ var init_compaction = __esm({
28630
28685
  function isModelSurfaceEvent(event) {
28631
28686
  return MODEL_SURFACE_KINDS.has(event.kind);
28632
28687
  }
28688
+ function latestSurfaceSeqByKind(events) {
28689
+ const latest = /* @__PURE__ */ new Map();
28690
+ for (const e of events) {
28691
+ if (LATEST_ONLY_SURFACE_KINDS.has(e.kind) && !isSeqShadowed(e.seq, coveringCompactions(events))) {
28692
+ latest.set(e.kind, e.seq);
28693
+ }
28694
+ }
28695
+ return latest;
28696
+ }
28633
28697
  function asString(value) {
28634
28698
  return typeof value === "string" ? value : void 0;
28635
28699
  }
28700
+ function asNumber(value) {
28701
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
28702
+ }
28703
+ function formatResourceSnapshot(data) {
28704
+ const used = asNumber(data.toolCallsUsed) ?? 0;
28705
+ const remaining = asNumber(data.toolCallsRemaining) ?? 0;
28706
+ const lines = [
28707
+ "RESOURCE STATUS",
28708
+ `Tool calls: ${used} / ${used + remaining}`,
28709
+ `Remaining: ${remaining}`
28710
+ ];
28711
+ const wall = asNumber(data.wallMsRemaining);
28712
+ if (wall !== void 0)
28713
+ lines.push(`Wall clock remaining: ${Math.max(0, Math.round(wall / 1e3))}s`);
28714
+ lines.push(`Verification reserve: ${asNumber(data.verificationReserve) ?? 0}`);
28715
+ lines.push(`Repair reserve: ${asNumber(data.repairReserve) ?? 0}`);
28716
+ if (typeof data.stage === "string")
28717
+ lines.push(`Stage: ${data.stage}`);
28718
+ if (typeof data.pressure === "string")
28719
+ lines.push(`Pressure: ${data.pressure}`);
28720
+ return lines.join("\n");
28721
+ }
28636
28722
  function deriveMessages(events, options = {}) {
28637
28723
  const coverings = coveringCompactions(events);
28638
28724
  const orderedCoverings = [...coverings].sort((a, b) => a.fromSeq - b.fromSeq || a.seq - b.seq);
28639
28725
  const compactBySeq = new Map(coverings.map((c) => [c.seq, c]));
28726
+ const latestOfKind = latestSurfaceSeqByKind(events);
28640
28727
  const messages = [];
28641
28728
  let nextCheckpoint = 0;
28642
28729
  const pushCheckpoint = (compact) => {
@@ -28663,6 +28750,8 @@ function deriveMessages(events, options = {}) {
28663
28750
  continue;
28664
28751
  if (!isModelSurfaceEvent(e))
28665
28752
  continue;
28753
+ if (LATEST_ONLY_SURFACE_KINDS.has(e.kind) && latestOfKind.get(e.kind) !== e.seq)
28754
+ continue;
28666
28755
  const d = e.data;
28667
28756
  switch (e.kind) {
28668
28757
  case "user.message":
@@ -28703,6 +28792,13 @@ function deriveMessages(events, options = {}) {
28703
28792
  seq: e.seq
28704
28793
  });
28705
28794
  break;
28795
+ case "resource.snapshot":
28796
+ messages.push({
28797
+ role: "system",
28798
+ content: formatResourceSnapshot(d),
28799
+ seq: e.seq
28800
+ });
28801
+ break;
28706
28802
  }
28707
28803
  }
28708
28804
  pushDueCheckpoints(Number.POSITIVE_INFINITY);
@@ -28729,7 +28825,7 @@ function pairToolCalls(events) {
28729
28825
  }
28730
28826
  return ordered;
28731
28827
  }
28732
- var MODEL_SURFACE_KINDS;
28828
+ var MODEL_SURFACE_KINDS, LATEST_ONLY_SURFACE_KINDS;
28733
28829
  var init_modelSurface = __esm({
28734
28830
  "packages/core/dist/session/modelSurface.js"() {
28735
28831
  "use strict";
@@ -28739,8 +28835,11 @@ var init_modelSurface = __esm({
28739
28835
  "assistant.message",
28740
28836
  "tool.call",
28741
28837
  "tool.result",
28742
- "session.compacted"
28838
+ "session.compacted",
28839
+ // 2.6: budget awareness for the model (latest-only projection below).
28840
+ "resource.snapshot"
28743
28841
  ]);
28842
+ LATEST_ONLY_SURFACE_KINDS = /* @__PURE__ */ new Set(["resource.snapshot"]);
28744
28843
  }
28745
28844
  });
28746
28845
 
@@ -29493,6 +29592,60 @@ function pushCompactionViolations(events, knownSeq, pairs, violations) {
29493
29592
  }
29494
29593
  }
29495
29594
  }
29595
+ function validateResourceAndContractEvents(events) {
29596
+ const violations = [];
29597
+ let lastToolCallsUsed = -1;
29598
+ for (const e of events) {
29599
+ if (e.kind === "resource.snapshot") {
29600
+ const used = e.data.toolCallsUsed;
29601
+ const remaining = e.data.toolCallsRemaining;
29602
+ if (typeof used !== "number" || typeof remaining !== "number" || used < 0 || remaining < 0) {
29603
+ violations.push({ code: "RESOURCE_SNAPSHOT_INVALID", seq: e.seq, message: "snapshot must carry non-negative toolCallsUsed/Remaining" });
29604
+ continue;
29605
+ }
29606
+ if (used < lastToolCallsUsed) {
29607
+ violations.push({ code: "RESOURCE_USED_MONOTONIC", seq: e.seq, message: `toolCallsUsed went ${lastToolCallsUsed} -> ${used}` });
29608
+ }
29609
+ lastToolCallsUsed = used;
29610
+ const limit = typeof e.data.toolCallsLimit === "number" ? e.data.toolCallsLimit : used + remaining;
29611
+ if (used + remaining !== limit) {
29612
+ violations.push({ code: "RESOURCE_REMAINING_COHERENT", seq: e.seq, message: `used(${used}) + remaining(${remaining}) != limit(${limit})` });
29613
+ }
29614
+ for (const key of ["verificationReserve", "repairReserve"]) {
29615
+ const v = e.data[key];
29616
+ if (typeof v === "number" && v < 0) {
29617
+ violations.push({ code: "RESERVE_NEGATIVE", seq: e.seq, message: `${key} is negative (${v})` });
29618
+ }
29619
+ }
29620
+ }
29621
+ if (e.kind === "session.harness_manifest") {
29622
+ if (typeof e.data.manifestHash !== "string" || !e.data.manifest) {
29623
+ violations.push({ code: "MANIFEST_PAYLOAD_INVALID", seq: e.seq, message: "session.harness_manifest needs {manifest, manifestHash}" });
29624
+ }
29625
+ }
29626
+ }
29627
+ let lastVersion = 0;
29628
+ for (const e of events) {
29629
+ if (e.kind !== "task.contract" && e.kind !== "task.contract_updated")
29630
+ continue;
29631
+ const contract = e.data.contract;
29632
+ const version2 = contract && typeof contract === "object" ? contract.version : void 0;
29633
+ if (typeof version2 !== "number" || version2 < 1) {
29634
+ violations.push({ code: "TASK_CONTRACT_VERSION_MONOTONIC", seq: e.seq, message: "contract payload must carry a positive version" });
29635
+ continue;
29636
+ }
29637
+ if (version2 <= lastVersion) {
29638
+ violations.push({ code: "TASK_CONTRACT_VERSION_MONOTONIC", seq: e.seq, message: `version ${version2} did not increase past ${lastVersion}` });
29639
+ }
29640
+ lastVersion = version2;
29641
+ const source = contract && typeof contract === "object" ? contract.source : void 0;
29642
+ const userSeq = source && typeof source === "object" ? source.userSeq : void 0;
29643
+ if (typeof userSeq !== "number" || userSeq < 1) {
29644
+ violations.push({ code: "TASK_CONTRACT_SOURCE_INVALID", seq: e.seq, message: "contract.source.userSeq must be a positive event seq" });
29645
+ }
29646
+ }
29647
+ return violations;
29648
+ }
29496
29649
  var init_invariants = __esm({
29497
29650
  "packages/core/dist/session/invariants.js"() {
29498
29651
  "use strict";
@@ -29500,6 +29653,122 @@ var init_invariants = __esm({
29500
29653
  }
29501
29654
  });
29502
29655
 
29656
+ // packages/core/dist/session/taskContract.js
29657
+ function applyTaskContractUpdate(contract, update) {
29658
+ if (update.goal !== void 0 && update.goal !== contract.goal) {
29659
+ if (update.nextUserSeq === void 0) {
29660
+ throw new TaskContractConflictError("agent-derived update may not rewrite the goal");
29661
+ }
29662
+ }
29663
+ const constraints = contract.constraints.filter((c) => {
29664
+ if (!update.removeConstraintIds?.includes(c.id))
29665
+ return true;
29666
+ if (c.source === "user" && c.required) {
29667
+ throw new TaskContractConflictError(`required user constraint "${c.id}" cannot be removed`);
29668
+ }
29669
+ return false;
29670
+ });
29671
+ const criteria = contract.acceptanceCriteria.filter((c) => {
29672
+ if (!update.removeCriterionIds?.includes(c.id))
29673
+ return true;
29674
+ if (c.source === "user" && c.required) {
29675
+ throw new TaskContractConflictError(`required user criterion "${c.id}" cannot be removed`);
29676
+ }
29677
+ return false;
29678
+ });
29679
+ for (const add of update.addConstraints ?? []) {
29680
+ if (constraints.some((c) => c.id === add.id) || (update.addConstraints ?? []).filter((x) => x.id === add.id).length > 1) {
29681
+ throw new TaskContractConflictError(`duplicate constraint id "${add.id}"`);
29682
+ }
29683
+ }
29684
+ for (const add of update.addCriteria ?? []) {
29685
+ if (criteria.some((c) => c.id === add.id) || (update.addCriteria ?? []).filter((x) => x.id === add.id).length > 1) {
29686
+ throw new TaskContractConflictError(`duplicate criterion id "${add.id}"`);
29687
+ }
29688
+ }
29689
+ return TaskContractSchema.parse({
29690
+ version: contract.version + 1,
29691
+ goal: update.goal ?? contract.goal,
29692
+ constraints: [...constraints, ...update.addConstraints ?? []],
29693
+ acceptanceCriteria: [...criteria, ...update.addCriteria ?? []],
29694
+ source: {
29695
+ userSeq: update.nextUserSeq ?? contract.source.userSeq
29696
+ }
29697
+ });
29698
+ }
29699
+ function contractToCompactionFields(contract) {
29700
+ return {
29701
+ goal: contract.goal,
29702
+ userConstraints: contract.constraints.filter((c) => c.source === "user" && c.required).map((c) => c.text),
29703
+ activeCriteria: contract.acceptanceCriteria.map((c) => ({ id: c.id, required: c.required }))
29704
+ };
29705
+ }
29706
+ function deriveInitialContract(userSeq, text) {
29707
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
29708
+ const stripped = lines.map((l) => l.replace(/^[-*]\s+/, ""));
29709
+ const constraintLines = stripped.filter((l) => /^(do not|don't|never|no |non |without changing|keep)/i.test(l));
29710
+ const criteriaLines = stripped.filter((l) => /^\[?\s*[x ]?\s*\]?\s*/.test(l) === false ? /^(acceptance|verify|test)[:\s]/i.test(l) : true).map((l) => l.replace(/^\[?\s*[x ]?\s*\]?\s*/, "").replace(/^(acceptance|verify|test)[:\s]+/i, ""));
29711
+ return TaskContractSchema.parse({
29712
+ version: 1,
29713
+ goal: lines[0] ?? text.slice(0, 200),
29714
+ constraints: constraintLines.map((t, i) => ({
29715
+ id: `uc-${i + 1}`,
29716
+ text: t,
29717
+ source: "user",
29718
+ required: true
29719
+ })),
29720
+ acceptanceCriteria: criteriaLines.map((t, i) => ({
29721
+ id: `ac-${i + 1}`,
29722
+ text: t,
29723
+ source: "user",
29724
+ required: true
29725
+ })),
29726
+ source: { userSeq }
29727
+ });
29728
+ }
29729
+ var TaskConstraintSchema, TaskCriterionSchema, TaskContractSchema, TaskContractConflictError;
29730
+ var init_taskContract = __esm({
29731
+ "packages/core/dist/session/taskContract.js"() {
29732
+ "use strict";
29733
+ init_zod();
29734
+ TaskConstraintSchema = external_exports.object({
29735
+ id: external_exports.string().min(1),
29736
+ text: external_exports.string().min(1),
29737
+ source: external_exports.enum(["user", "agent-derived"]),
29738
+ required: external_exports.boolean()
29739
+ });
29740
+ TaskCriterionSchema = external_exports.object({
29741
+ id: external_exports.string().min(1),
29742
+ text: external_exports.string().min(1),
29743
+ source: external_exports.enum(["user", "agent-derived"]),
29744
+ required: external_exports.boolean(),
29745
+ verificationHint: external_exports.object({
29746
+ kind: external_exports.enum(["command", "tool", "semantic", "manual"]),
29747
+ value: external_exports.string().optional()
29748
+ }).optional()
29749
+ });
29750
+ TaskContractSchema = external_exports.object({
29751
+ version: external_exports.number().int().positive(),
29752
+ goal: external_exports.string().min(1),
29753
+ constraints: external_exports.array(TaskConstraintSchema),
29754
+ acceptanceCriteria: external_exports.array(TaskCriterionSchema),
29755
+ source: external_exports.object({
29756
+ /** Seq of the user.message the contract was extracted from. */
29757
+ userSeq: external_exports.number().int().positive()
29758
+ })
29759
+ });
29760
+ TaskContractConflictError = class extends Error {
29761
+ reason;
29762
+ code = "TASK_CONTRACT_CONFLICT";
29763
+ constructor(reason) {
29764
+ super(`task contract update rejected: ${reason}`);
29765
+ this.reason = reason;
29766
+ this.name = "TaskContractConflictError";
29767
+ }
29768
+ };
29769
+ }
29770
+ });
29771
+
29503
29772
  // packages/core/dist/session/index.js
29504
29773
  var init_session = __esm({
29505
29774
  "packages/core/dist/session/index.js"() {
@@ -29515,6 +29784,7 @@ var init_session = __esm({
29515
29784
  init_exportSession();
29516
29785
  init_invariants();
29517
29786
  init_recovery();
29787
+ init_taskContract();
29518
29788
  }
29519
29789
  });
29520
29790
 
@@ -29850,11 +30120,15 @@ function toolManifestHash(tools) {
29850
30120
  const manifest = [...tools].sort().join(",");
29851
30121
  return createHash3("sha256").update(manifest).digest("hex");
29852
30122
  }
30123
+ function profileHash(profile) {
30124
+ return createHash3("sha256").update(stableStringify(profile)).digest("hex");
30125
+ }
29853
30126
  var ProfileSchema, MINIMAL_TOOLS, MINIMAL_V1, KRAKEN_V1, COUNCIL_V1, MISSION_V1, BUILT_IN_PROFILES, UnknownProfileError;
29854
30127
  var init_profiles = __esm({
29855
30128
  "packages/core/dist/runtime/profiles.js"() {
29856
30129
  "use strict";
29857
30130
  init_zod();
30131
+ init_requestSnapshot();
29858
30132
  ProfileSchema = external_exports.object({
29859
30133
  /** `<name>/v<N>` — immutable once published. */
29860
30134
  id: external_exports.string().regex(/^[a-z0-9-]+\/v\d+$/, "profile id must be <name>/v<N>"),
@@ -29926,6 +30200,99 @@ var init_profiles = __esm({
29926
30200
  }
29927
30201
  });
29928
30202
 
30203
+ // packages/core/dist/runtime/harnessManifest.js
30204
+ function hashHarnessManifest(manifest) {
30205
+ return sha256Hex(stableStringify(manifest));
30206
+ }
30207
+ function harnessInputHash(value) {
30208
+ return sha256Hex(stableStringify(value));
30209
+ }
30210
+ function collectPaths2(base, a, b, out, depth = 0) {
30211
+ if (depth > 6)
30212
+ return;
30213
+ if (stableStringify(a) === stableStringify(b))
30214
+ return;
30215
+ const objA = a && typeof a === "object" && !Array.isArray(a) ? a : null;
30216
+ const objB = b && typeof b === "object" && !Array.isArray(b) ? b : null;
30217
+ if (objA && objB) {
30218
+ for (const key of [.../* @__PURE__ */ new Set([...Object.keys(objA), ...Object.keys(objB)])].sort()) {
30219
+ collectPaths2(base ? `${base}.${key}` : key, objA[key], objB[key], out, depth + 1);
30220
+ }
30221
+ return;
30222
+ }
30223
+ out.push(base);
30224
+ }
30225
+ function diffHarnessManifest(oldManifest, newManifest) {
30226
+ const changed = [];
30227
+ collectPaths2("", oldManifest, newManifest, changed);
30228
+ return { changed: changed.sort() };
30229
+ }
30230
+ function classifyHarnessChanges(diff) {
30231
+ const byField = {};
30232
+ for (const field of diff.changed) {
30233
+ const hit = FIELD_CLASSES.find((m) => field === m.prefix || field.startsWith(m.prefix + "."));
30234
+ byField[field] = hit ? hit.cls : "cosmetic";
30235
+ }
30236
+ const order = { behavioral: 3, structural: 2, cosmetic: 1 };
30237
+ const overall = Object.values(byField).reduce((acc, cls) => order[cls] > order[acc] ? cls : acc, "cosmetic");
30238
+ return { overall, byField };
30239
+ }
30240
+ var HARNESS_MANIFEST_SCHEMA_VERSION, HarnessPromptsSchema, HarnessManifestV1Schema, FIELD_CLASSES;
30241
+ var init_harnessManifest = __esm({
30242
+ "packages/core/dist/runtime/harnessManifest.js"() {
30243
+ "use strict";
30244
+ init_zod();
30245
+ init_requestSnapshot();
30246
+ HARNESS_MANIFEST_SCHEMA_VERSION = 1;
30247
+ HarnessPromptsSchema = external_exports.object({
30248
+ kraken: external_exports.string().min(1).optional(),
30249
+ gauntlet: external_exports.string().min(1).optional(),
30250
+ council: external_exports.string().min(1).optional(),
30251
+ mission: external_exports.string().min(1).optional()
30252
+ });
30253
+ HarnessManifestV1Schema = external_exports.object({
30254
+ schemaVersion: external_exports.literal(HARNESS_MANIFEST_SCHEMA_VERSION),
30255
+ profile: external_exports.object({
30256
+ id: external_exports.string().min(1),
30257
+ /** WorkPhase of the session that recorded the manifest. */
30258
+ phase: external_exports.enum(["plan", "build"]),
30259
+ /** Hash of the full profile (see profileHash in profiles.ts). */
30260
+ hash: external_exports.string().min(1)
30261
+ }),
30262
+ prompts: HarnessPromptsSchema,
30263
+ capabilities: external_exports.object({
30264
+ toolManifestHash: external_exports.string().min(1),
30265
+ skillManifestHash: external_exports.string().min(1)
30266
+ }),
30267
+ policies: external_exports.object({
30268
+ routingHash: external_exports.string().min(1),
30269
+ verificationHash: external_exports.string().min(1),
30270
+ completionPolicyHash: external_exports.string().min(1),
30271
+ compactionHash: external_exports.string().min(1),
30272
+ /** Behavioural since 2.6 Track B: budget policy shapes execution. */
30273
+ resourcePolicyHash: external_exports.string().min(1)
30274
+ }),
30275
+ runtime: external_exports.object({
30276
+ coreVersion: external_exports.string().min(1),
30277
+ cliVersion: external_exports.string().min(1)
30278
+ })
30279
+ });
30280
+ FIELD_CLASSES = [
30281
+ { prefix: "prompts", cls: "behavioral" },
30282
+ { prefix: "capabilities.toolManifestHash", cls: "behavioral" },
30283
+ { prefix: "capabilities.skillManifestHash", cls: "behavioral" },
30284
+ { prefix: "policies.routingHash", cls: "behavioral" },
30285
+ { prefix: "policies.verificationHash", cls: "behavioral" },
30286
+ { prefix: "policies.completionPolicyHash", cls: "structural" },
30287
+ { prefix: "policies.compactionHash", cls: "behavioral" },
30288
+ { prefix: "policies.resourcePolicyHash", cls: "behavioral" },
30289
+ { prefix: "profile", cls: "structural" },
30290
+ { prefix: "runtime", cls: "structural" },
30291
+ { prefix: "schemaVersion", cls: "structural" }
30292
+ ];
30293
+ }
30294
+ });
30295
+
29929
30296
  // packages/core/dist/experimental.js
29930
30297
  function isExperimentalEnabled(flag, env = process.env) {
29931
30298
  const raw = env.ZELARI_EXPERIMENTAL;
@@ -30008,6 +30375,155 @@ var init_executionContext = __esm({
30008
30375
  }
30009
30376
  });
30010
30377
 
30378
+ // packages/core/dist/runtime/resourcePolicy.js
30379
+ import { createHash as createHash4 } from "node:crypto";
30380
+ function defaultResourcePolicy(profileId) {
30381
+ return PROFILE_RESOURCE_POLICIES[profileId] ?? PROFILE_RESOURCE_POLICIES["kraken/v1"];
30382
+ }
30383
+ function resourcePolicyHash(policy) {
30384
+ return createHash4("sha256").update(stableStringify(ResourcePolicySchema.parse(policy))).digest("hex");
30385
+ }
30386
+ var BudgetPressureSchema, ResourceStageSchema, PressureThresholdsSchema, DEFAULT_PRESSURE_THRESHOLDS, ResourcePolicySchema, PROFILE_RESOURCE_POLICIES;
30387
+ var init_resourcePolicy = __esm({
30388
+ "packages/core/dist/runtime/resourcePolicy.js"() {
30389
+ "use strict";
30390
+ init_zod();
30391
+ init_requestSnapshot();
30392
+ BudgetPressureSchema = external_exports.enum(["ample", "normal", "constrained", "critical"]);
30393
+ ResourceStageSchema = external_exports.enum(["explore", "implement", "verify", "repair"]);
30394
+ PressureThresholdsSchema = external_exports.object({
30395
+ /** remaining/usable >= ample → ample. */
30396
+ ample: external_exports.number().min(0).max(1),
30397
+ /** remaining/usable >= constrained → normal; below → constrained. */
30398
+ constrained: external_exports.number().min(0).max(1),
30399
+ /** remaining/usable < critical → critical. */
30400
+ critical: external_exports.number().min(0).max(1)
30401
+ });
30402
+ DEFAULT_PRESSURE_THRESHOLDS = Object.freeze({
30403
+ ample: 0.5,
30404
+ constrained: 0.25,
30405
+ critical: 0.1
30406
+ });
30407
+ ResourcePolicySchema = external_exports.object({
30408
+ maxToolCalls: external_exports.number().int().positive(),
30409
+ reserve: external_exports.object({
30410
+ /** Protected: tool budget the non-verification loop may not consume (§11.4). */
30411
+ verification: external_exports.number().int().min(0),
30412
+ /** Advisory-only by default (§11.4). */
30413
+ repair: external_exports.number().int().min(0)
30414
+ }),
30415
+ wallClockMs: external_exports.number().int().positive().optional(),
30416
+ /** Soft token ceiling (advisory telemetry, not enforced). */
30417
+ softMaxTokens: external_exports.number().int().positive().optional(),
30418
+ pressure: PressureThresholdsSchema.default(DEFAULT_PRESSURE_THRESHOLDS)
30419
+ });
30420
+ PROFILE_RESOURCE_POLICIES = Object.freeze({
30421
+ "minimal/v1": {
30422
+ maxToolCalls: 25,
30423
+ reserve: { verification: 4, repair: 3 },
30424
+ pressure: DEFAULT_PRESSURE_THRESHOLDS
30425
+ },
30426
+ "kraken/v1": {
30427
+ maxToolCalls: 40,
30428
+ reserve: { verification: 6, repair: 4 },
30429
+ wallClockMs: 9e5,
30430
+ pressure: DEFAULT_PRESSURE_THRESHOLDS
30431
+ },
30432
+ "council/v1": {
30433
+ maxToolCalls: 30,
30434
+ reserve: { verification: 4, repair: 2 },
30435
+ pressure: DEFAULT_PRESSURE_THRESHOLDS
30436
+ },
30437
+ "mission/v1": {
30438
+ maxToolCalls: 60,
30439
+ reserve: { verification: 8, repair: 6 },
30440
+ wallClockMs: 18e5,
30441
+ pressure: DEFAULT_PRESSURE_THRESHOLDS
30442
+ }
30443
+ });
30444
+ }
30445
+ });
30446
+
30447
+ // packages/core/dist/runtime/resourceBudget.js
30448
+ function computeBudget(policy, usage, stage = "explore") {
30449
+ const used = Math.max(0, Math.min(usage.toolCallsUsed, policy.maxToolCalls));
30450
+ return {
30451
+ toolCalls: {
30452
+ limit: policy.maxToolCalls,
30453
+ used,
30454
+ remaining: policy.maxToolCalls - used
30455
+ },
30456
+ wallTime: {
30457
+ limitMs: policy.wallClockMs,
30458
+ elapsedMs: Math.max(0, usage.elapsedMs ?? 0),
30459
+ remainingMs: policy.wallClockMs === void 0 ? void 0 : Math.max(0, policy.wallClockMs - (usage.elapsedMs ?? 0))
30460
+ },
30461
+ tokens: usage.tokensUsed === void 0 && policy.softMaxTokens === void 0 ? void 0 : {
30462
+ softLimit: policy.softMaxTokens,
30463
+ used: usage.tokensUsed ?? 0,
30464
+ remaining: policy.softMaxTokens === void 0 ? void 0 : Math.max(0, policy.softMaxTokens - (usage.tokensUsed ?? 0))
30465
+ },
30466
+ reserve: {
30467
+ verification: Math.max(0, policy.reserve.verification),
30468
+ repair: Math.max(0, policy.reserve.repair)
30469
+ },
30470
+ stage
30471
+ };
30472
+ }
30473
+ function usageFromLedger(entries) {
30474
+ const seen = /* @__PURE__ */ new Set();
30475
+ let toolCallsUsed = 0;
30476
+ let wallMs = 0;
30477
+ let tokensUsed = 0;
30478
+ for (const e of entries) {
30479
+ if (seen.has(e.seq))
30480
+ continue;
30481
+ seen.add(e.seq);
30482
+ toolCallsUsed += e.delta.toolCalls ?? 0;
30483
+ wallMs += e.delta.wallMs ?? 0;
30484
+ tokensUsed += e.delta.tokens ?? 0;
30485
+ }
30486
+ return { toolCallsUsed: Math.max(0, toolCallsUsed), wallMs: Math.max(0, wallMs), tokensUsed: Math.max(0, tokensUsed) };
30487
+ }
30488
+ function budgetPressure(budget, policy) {
30489
+ const usable = Math.max(0, budget.toolCalls.remaining - budget.reserve.verification);
30490
+ const capacity = Math.max(1, budget.toolCalls.limit - budget.reserve.verification);
30491
+ const ratio = usable / capacity;
30492
+ const t = policy.pressure;
30493
+ if (ratio >= t.ample)
30494
+ return "ample";
30495
+ if (ratio >= t.constrained)
30496
+ return "normal";
30497
+ if (ratio >= t.critical)
30498
+ return "constrained";
30499
+ return "critical";
30500
+ }
30501
+ function isVerificationReserveProtected(budget) {
30502
+ return budget.toolCalls.remaining <= budget.reserve.verification;
30503
+ }
30504
+ function canSpendOutsideReserve(budget, calls) {
30505
+ return budget.toolCalls.remaining - budget.reserve.verification >= calls;
30506
+ }
30507
+ function ledgerDeltaFor(reason, calls = 1) {
30508
+ switch (reason) {
30509
+ case "tool-call":
30510
+ return { toolCalls: calls };
30511
+ case "model-turn":
30512
+ case "verification":
30513
+ case "repair":
30514
+ return { toolCalls: calls };
30515
+ case "timeout":
30516
+ return {};
30517
+ case "reservation":
30518
+ return {};
30519
+ }
30520
+ }
30521
+ var init_resourceBudget = __esm({
30522
+ "packages/core/dist/runtime/resourceBudget.js"() {
30523
+ "use strict";
30524
+ }
30525
+ });
30526
+
30011
30527
  // packages/core/dist/runtime/index.js
30012
30528
  var init_runtime2 = __esm({
30013
30529
  "packages/core/dist/runtime/index.js"() {
@@ -30017,7 +30533,10 @@ var init_runtime2 = __esm({
30017
30533
  init_memoryProviders();
30018
30534
  init_worktreeWorkspace();
30019
30535
  init_profiles();
30536
+ init_harnessManifest();
30020
30537
  init_executionContext();
30538
+ init_resourcePolicy();
30539
+ init_resourceBudget();
30021
30540
  }
30022
30541
  });
30023
30542
 
@@ -30194,9 +30713,9 @@ var init_scopeDiscipline = __esm({
30194
30713
  });
30195
30714
 
30196
30715
  // packages/core/dist/verification/engine.js
30197
- import { createHash as createHash4 } from "node:crypto";
30716
+ import { createHash as createHash5 } from "node:crypto";
30198
30717
  function defaultSha256(input) {
30199
- return createHash4("sha256").update(input).digest("hex");
30718
+ return createHash5("sha256").update(input).digest("hex");
30200
30719
  }
30201
30720
  function tail(text, max = 400) {
30202
30721
  return text.length > max ? `\u2026${text.slice(text.length - max)}` : text;
@@ -30358,12 +30877,12 @@ var init_engine = __esm({
30358
30877
  * content digest) and the returned ref carries the event seq when the
30359
30878
  * emitter resolved one.
30360
30879
  */
30361
- async fsEvidence(observation, path64, sha256, content, extra = {}) {
30880
+ async fsEvidence(observation, path65, sha256, content, extra = {}) {
30362
30881
  const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
30363
- const seq = await this.emitEvidence({ observation, path: path64, ...extra, ...digest ? { digest } : {} });
30882
+ const seq = await this.emitEvidence({ observation, path: path65, ...extra, ...digest ? { digest } : {} });
30364
30883
  return {
30365
30884
  tier: "fs-observation",
30366
- ref: path64,
30885
+ ref: path65,
30367
30886
  capturedAt: Date.now(),
30368
30887
  ...digest ? { digest } : {},
30369
30888
  ...seq !== void 0 ? { seq } : {}
@@ -30682,6 +31201,23 @@ function verificationCostRatio(verificationMs, totalMs) {
30682
31201
  return null;
30683
31202
  return verificationMs / totalMs;
30684
31203
  }
31204
+ function costPerVerifiedSolve(samples) {
31205
+ const solved = samples.filter((s) => s.verified === true);
31206
+ const totalCostUsd = samples.reduce((sum, s) => sum + s.modelCostUsd, 0);
31207
+ const totalWallMs = samples.reduce((sum, s) => sum + s.wallMs, 0);
31208
+ const totalToolCalls = samples.reduce((sum, s) => sum + s.toolCalls, 0);
31209
+ const n = solved.length;
31210
+ return {
31211
+ verifiedSolves: n,
31212
+ totalCostUsd: round(totalCostUsd),
31213
+ costPerVerifiedSolve: n > 0 ? round(totalCostUsd / n) : null,
31214
+ wallMsPerVerifiedSolve: n > 0 ? Math.round(totalWallMs / n) : null,
31215
+ toolCallsPerVerifiedSolve: n > 0 ? round(totalToolCalls / n) : null
31216
+ };
31217
+ }
31218
+ function round(v) {
31219
+ return Math.round(v * 1e6) / 1e6;
31220
+ }
30685
31221
  var init_metrics = __esm({
30686
31222
  "packages/core/dist/verification/metrics.js"() {
30687
31223
  "use strict";
@@ -30890,6 +31426,44 @@ var init_verifier = __esm({
30890
31426
  }
30891
31427
  });
30892
31428
 
31429
+ // packages/core/dist/verification/resourceReserveGate.js
31430
+ function evaluateResourceReserveGate(input) {
31431
+ const { evaluation, budget } = input;
31432
+ if (evaluation.verdict === "PASS") {
31433
+ return {
31434
+ verdict: "PASS",
31435
+ deterministicVerdict: "PASS",
31436
+ resourceExhausted: false,
31437
+ evidenceAffordable: true,
31438
+ summary: evaluation.summary
31439
+ };
31440
+ }
31441
+ const spendable = Math.max(0, budget.toolCalls.remaining - budget.reserve.verification);
31442
+ const affordable = spendable + budget.reserve.verification > 0 && budget.toolCalls.remaining > 0;
31443
+ if (affordable) {
31444
+ return {
31445
+ verdict: evaluation.verdict,
31446
+ deterministicVerdict: evaluation.verdict,
31447
+ resourceExhausted: false,
31448
+ evidenceAffordable: true,
31449
+ summary: evaluation.summary
31450
+ };
31451
+ }
31452
+ const summary = `${evaluation.summary} \xB7 resource-exhausted: ${budget.toolCalls.used}/${budget.toolCalls.limit} tool calls used, no budget left for required evidence \u2014 BLOCKED, not a false done`;
31453
+ return {
31454
+ verdict: "BLOCKED",
31455
+ deterministicVerdict: evaluation.verdict,
31456
+ resourceExhausted: true,
31457
+ evidenceAffordable: false,
31458
+ summary
31459
+ };
31460
+ }
31461
+ var init_resourceReserveGate = __esm({
31462
+ "packages/core/dist/verification/resourceReserveGate.js"() {
31463
+ "use strict";
31464
+ }
31465
+ });
31466
+
30893
31467
  // packages/core/dist/verification/index.js
30894
31468
  var init_verification2 = __esm({
30895
31469
  "packages/core/dist/verification/index.js"() {
@@ -30902,6 +31476,7 @@ var init_verification2 = __esm({
30902
31476
  init_metrics();
30903
31477
  init_verifier();
30904
31478
  init_scopeDiscipline();
31479
+ init_resourceReserveGate();
30905
31480
  }
30906
31481
  });
30907
31482
 
@@ -31060,6 +31635,7 @@ __export(dist_exports, {
31060
31635
  BENNETTS_RAZOR_SHORT: () => BENNETTS_RAZOR_SHORT,
31061
31636
  BUILT_IN_PROFILES: () => BUILT_IN_PROFILES,
31062
31637
  BonConfigSchema: () => BonConfigSchema,
31638
+ BudgetPressureSchema: () => BudgetPressureSchema,
31063
31639
  CLARIFICATION_PROTOCOL_MODULE: () => CLARIFICATION_PROTOCOL_MODULE,
31064
31640
  CODING_CATEGORY: () => CODING_CATEGORY,
31065
31641
  CODING_PRACTICES_MODULE: () => CODING_PRACTICES_MODULE,
@@ -31074,6 +31650,7 @@ __export(dist_exports, {
31074
31650
  DEFAULT_MAX_TENTACLES: () => DEFAULT_MAX_TENTACLES,
31075
31651
  DEFAULT_NFR_SPEC: () => DEFAULT_NFR_SPEC,
31076
31652
  DEFAULT_PLAN_TIMEOUT_MS: () => DEFAULT_PLAN_TIMEOUT_MS,
31653
+ DEFAULT_PRESSURE_THRESHOLDS: () => DEFAULT_PRESSURE_THRESHOLDS,
31077
31654
  DEFAULT_VERIFIER_CONFIG: () => DEFAULT_VERIFIER_CONFIG,
31078
31655
  DEGRADED_RUN_BANNER: () => DEGRADED_RUN_BANNER,
31079
31656
  DESIGN_PHASE_MODE_BANNER: () => DESIGN_PHASE_MODE_BANNER,
@@ -31090,6 +31667,9 @@ __export(dist_exports, {
31090
31667
  FileAbsentCheckSchema: () => FileAbsentCheckSchema,
31091
31668
  FileContainsCheckSchema: () => FileContainsCheckSchema,
31092
31669
  FileExistsCheckSchema: () => FileExistsCheckSchema,
31670
+ HARNESS_MANIFEST_SCHEMA_VERSION: () => HARNESS_MANIFEST_SCHEMA_VERSION,
31671
+ HarnessManifestV1Schema: () => HarnessManifestV1Schema,
31672
+ HarnessPromptsSchema: () => HarnessPromptsSchema,
31093
31673
  IMPLEMENTATION_ADVISOR_BANNER: () => IMPLEMENTATION_ADVISOR_BANNER,
31094
31674
  IMPLEMENTATION_IMPLEMENTER_BANNER: () => IMPLEMENTATION_IMPLEMENTER_BANNER,
31095
31675
  IMPLEMENTATION_MODE_BANNER: () => IMPLEMENTATION_MODE_BANNER,
@@ -31099,6 +31679,7 @@ __export(dist_exports, {
31099
31679
  KRAKEN_SELECTION_PLAYBOOK_MODULE: () => KRAKEN_SELECTION_PLAYBOOK_MODULE,
31100
31680
  KRAKEN_V1: () => KRAKEN_V1,
31101
31681
  LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
31682
+ LATEST_ONLY_SURFACE_KINDS: () => LATEST_ONLY_SURFACE_KINDS,
31102
31683
  LAYOUT_MOTION_PROPS: () => LAYOUT_MOTION_PROPS,
31103
31684
  LESSONS_FILE: () => LESSONS_FILE,
31104
31685
  LifecycleHookRunner: () => LifecycleHookRunner,
@@ -31123,12 +31704,16 @@ __export(dist_exports, {
31123
31704
  NoneCheckSchema: () => NoneCheckSchema,
31124
31705
  NoopSubagentProvider: () => NoopSubagentProvider,
31125
31706
  OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
31707
+ PROFILE_RESOURCE_POLICIES: () => PROFILE_RESOURCE_POLICIES,
31126
31708
  PROMPT_MODULES: () => PROMPT_MODULES,
31127
31709
  PROPRIETARY_REFUSAL_TEXT: () => PROPRIETARY_REFUSAL_TEXT,
31128
31710
  PROPRIETARY_SECRECY_MARKER: () => PROPRIETARY_SECRECY_MARKER,
31129
31711
  PROPRIETARY_SECRECY_MODULE: () => PROPRIETARY_SECRECY_MODULE,
31130
31712
  PlanError: () => PlanError,
31713
+ PressureThresholdsSchema: () => PressureThresholdsSchema,
31131
31714
  ProfileSchema: () => ProfileSchema,
31715
+ ResourcePolicySchema: () => ResourcePolicySchema,
31716
+ ResourceStageSchema: () => ResourceStageSchema,
31132
31717
  SESSION_EVENT_KINDS: () => SESSION_EVENT_KINDS,
31133
31718
  SESSION_EXPORT_FORMAT: () => SESSION_EXPORT_FORMAT,
31134
31719
  SESSION_EXPORT_VERSION: () => SESSION_EXPORT_VERSION,
@@ -31152,6 +31737,10 @@ __export(dist_exports, {
31152
31737
  TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
31153
31738
  TOOL_USE_PROTOCOL_DIRECTIVE: () => TOOL_USE_PROTOCOL_DIRECTIVE,
31154
31739
  TURN_COMPLETION_MODULE: () => TURN_COMPLETION_MODULE,
31740
+ TaskConstraintSchema: () => TaskConstraintSchema,
31741
+ TaskContractConflictError: () => TaskContractConflictError,
31742
+ TaskContractSchema: () => TaskContractSchema,
31743
+ TaskCriterionSchema: () => TaskCriterionSchema,
31155
31744
  UnknownMemberError: () => UnknownMemberError,
31156
31745
  UnknownProfileError: () => UnknownProfileError,
31157
31746
  VAULT_TOOL_DEFINITIONS: () => VAULT_TOOL_DEFINITIONS,
@@ -31173,8 +31762,10 @@ __export(dist_exports, {
31173
31762
  applyInlineJsAutofix: () => applyInlineJsAutofix,
31174
31763
  applyMotionAutofix: () => applyMotionAutofix,
31175
31764
  applyRetryIfMissing: () => applyRetryIfMissing,
31765
+ applyTaskContractUpdate: () => applyTaskContractUpdate,
31176
31766
  auditDegradedBanner: () => auditDegradedBanner,
31177
31767
  auditSynthesisTiers: () => auditSynthesisTiers,
31768
+ budgetPressure: () => budgetPressure,
31178
31769
  buildCompactionStateSnapshot: () => buildCompactionStateSnapshot,
31179
31770
  buildCouncilCompletion: () => buildCouncilCompletion,
31180
31771
  buildCustomParameters: () => buildCustomParameters,
@@ -31192,12 +31783,14 @@ __export(dist_exports, {
31192
31783
  buildSystemPrompt: () => buildSystemPrompt,
31193
31784
  buildSystemPromptSplit: () => buildSystemPromptSplit,
31194
31785
  canRunParallel: () => canRunParallel,
31786
+ canSpendOutsideReserve: () => canSpendOutsideReserve,
31195
31787
  canonicalTools: () => canonicalTools,
31196
31788
  captureFailure: () => captureFailure,
31197
31789
  checkImplementationCompletion: () => checkImplementationCompletion,
31198
31790
  checkImplementationDelivery: () => checkImplementationDelivery,
31199
31791
  checkMemberToolEmissionSets: () => checkMemberToolEmissionSets,
31200
31792
  checkMemberToolEmissions: () => checkMemberToolEmissions,
31793
+ classifyHarnessChanges: () => classifyHarnessChanges,
31201
31794
  classifyInterruptedTools: () => classifyInterruptedTools,
31202
31795
  classifyMission: () => classifyMission,
31203
31796
  classifyTaskScope: () => classifyTaskScope,
@@ -31209,7 +31802,10 @@ __export(dist_exports, {
31209
31802
  compareReplayPrefix: () => compareReplayPrefix,
31210
31803
  computeAgentSkills: () => computeAgentSkills,
31211
31804
  computeAgentTools: () => computeAgentTools,
31805
+ computeBudget: () => computeBudget,
31212
31806
  computeFalseDoneRate: () => computeFalseDoneRate,
31807
+ contractToCompactionFields: () => contractToCompactionFields,
31808
+ costPerVerifiedSolve: () => costPerVerifiedSolve,
31213
31809
  councilModeBanner: () => councilModeBanner,
31214
31810
  councilTierFromSize: () => councilTierFromSize,
31215
31811
  countByStatus: () => countByStatus,
@@ -31221,6 +31817,8 @@ __export(dist_exports, {
31221
31817
  createGraph: () => createGraph,
31222
31818
  createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
31223
31819
  defaultPersonaParse: () => defaultPersonaParse,
31820
+ defaultResourcePolicy: () => defaultResourcePolicy,
31821
+ deriveInitialContract: () => deriveInitialContract,
31224
31822
  deriveMessages: () => deriveMessages,
31225
31823
  deriveMissionState: () => deriveMissionState,
31226
31824
  derivedToAgentMessages: () => derivedToAgentMessages,
@@ -31228,11 +31826,13 @@ __export(dist_exports, {
31228
31826
  detectAssistantTextLoopWindow: () => detectAssistantTextLoopWindow,
31229
31827
  detectDegradedRun: () => detectDegradedRun,
31230
31828
  detectResponseLanguage: () => detectResponseLanguage,
31829
+ diffHarnessManifest: () => diffHarnessManifest,
31231
31830
  disjointScopeSets: () => disjointScopeSets,
31232
31831
  emptyContextGrowthStats: () => emptyContextGrowthStats,
31233
31832
  enforceDesignPhaseToolEmissions: () => enforceDesignPhaseToolEmissions,
31234
31833
  evaluateCompletion: () => evaluateCompletion,
31235
31834
  evaluateMissionContinuation: () => evaluateMissionContinuation,
31835
+ evaluateResourceReserveGate: () => evaluateResourceReserveGate,
31236
31836
  executeTool: () => executeTool,
31237
31837
  exportSession: () => exportSession,
31238
31838
  exportSessionJson: () => exportSessionJson,
@@ -31249,6 +31849,7 @@ __export(dist_exports, {
31249
31849
  forkSession: () => forkSession,
31250
31850
  formatCompactionStateSnapshot: () => formatCompactionStateSnapshot,
31251
31851
  formatLessonsForContext: () => formatLessonsForContext,
31852
+ formatResourceSnapshot: () => formatResourceSnapshot,
31252
31853
  getAgent: () => getAgent,
31253
31854
  getAllTools: () => getAllTools,
31254
31855
  getAvailableTools: () => getAvailableTools,
@@ -31265,7 +31866,9 @@ __export(dist_exports, {
31265
31866
  getSkillMetadata: () => getSkillMetadata,
31266
31867
  getSkillsByCategory: () => getSkillsByCategory,
31267
31868
  getToolDescriptions: () => getToolDescriptions,
31869
+ harnessInputHash: () => harnessInputHash,
31268
31870
  hasInteractiveClarification: () => hasInteractiveClarification,
31871
+ hashHarnessManifest: () => hashHarnessManifest,
31269
31872
  hashToolCall: () => hashToolCall,
31270
31873
  hookMatches: () => hookMatches,
31271
31874
  interruptedEventData: () => interruptedEventData,
@@ -31299,10 +31902,13 @@ __export(dist_exports, {
31299
31902
  isSettled: () => isSettled,
31300
31903
  isStatusTheaterUnit: () => isStatusTheaterUnit,
31301
31904
  isValidTool: () => isValidTool,
31905
+ isVerificationReserveProtected: () => isVerificationReserveProtected,
31302
31906
  isVerifyToolCheckSkipped: () => isVerifyToolCheckSkipped,
31303
31907
  jaccardSimilarity: () => jaccardSimilarity,
31304
31908
  jsonBytes: () => jsonBytes,
31305
31909
  lastVerificationRun: () => lastVerificationRun,
31910
+ latestSurfaceSeqByKind: () => latestSurfaceSeqByKind,
31911
+ ledgerDeltaFor: () => ledgerDeltaFor,
31306
31912
  lineageOf: () => lineageOf,
31307
31913
  lintSynthesisHonesty: () => lintSynthesisHonesty,
31308
31914
  listCodingSkills: () => listCodingSkills,
@@ -31331,6 +31937,7 @@ __export(dist_exports, {
31331
31937
  parseVerifyVerdict: () => parseVerifyVerdict,
31332
31938
  pathsOverlap: () => pathsOverlap,
31333
31939
  pickWeakest: () => pickWeakest,
31940
+ profileHash: () => profileHash,
31334
31941
  promoteMember: () => promoteMember,
31335
31942
  rankByWeakness: () => rankByWeakness,
31336
31943
  readLessonsDeduped: () => readLessonsDeduped,
@@ -31357,6 +31964,7 @@ __export(dist_exports, {
31357
31964
  resolveSessionsDir: () => resolveSessionsDir,
31358
31965
  resolveSkillDependencies: () => resolveSkillDependencies,
31359
31966
  resolveVerifyRetryTool: () => resolveVerifyRetryTool,
31967
+ resourcePolicyHash: () => resourcePolicyHash,
31360
31968
  restrictImplementationWrites: () => restrictImplementationWrites,
31361
31969
  resumeSession: () => resumeSession,
31362
31970
  retrySafetyForSideEffect: () => retrySafetyForSideEffect,
@@ -31394,9 +32002,11 @@ __export(dist_exports, {
31394
32002
  topoLevels: () => topoLevels,
31395
32003
  unregisterCustomTool: () => unregisterCustomTool,
31396
32004
  unregisterSkill: () => unregisterSkill,
32005
+ usageFromLedger: () => usageFromLedger,
31397
32006
  utf8Bytes: () => utf8Bytes,
31398
32007
  validateCodingSkillRequires: () => validateCodingSkillRequires,
31399
32008
  validateGraph: () => validateGraph,
32009
+ validateResourceAndContractEvents: () => validateResourceAndContractEvents,
31400
32010
  validateSessionTrace: () => validateSessionTrace,
31401
32011
  verificationCostRatio: () => verificationCostRatio,
31402
32012
  verifiedSolveRate: () => verifiedSolveRate,
@@ -31695,6 +32305,189 @@ var init_sessionManager = __esm({
31695
32305
  }
31696
32306
  });
31697
32307
 
32308
+ // src/cli/budget/resourceLedger.ts
32309
+ function rebuildLedgerFromEvents(events) {
32310
+ const ledger = new ResourceLedger();
32311
+ for (const e of events) {
32312
+ if (e.kind === "tool.call") {
32313
+ ledger.record("tool-call", 1);
32314
+ } else if (e.kind === "verification.run") {
32315
+ ledger.record("verification", 0);
32316
+ }
32317
+ }
32318
+ return ledger;
32319
+ }
32320
+ var ResourceLedger;
32321
+ var init_resourceLedger = __esm({
32322
+ "src/cli/budget/resourceLedger.ts"() {
32323
+ "use strict";
32324
+ init_dist();
32325
+ init_dist();
32326
+ ResourceLedger = class _ResourceLedger {
32327
+ entries = [];
32328
+ nextSeq = 1;
32329
+ static fromEntries(entries) {
32330
+ const ledger = new _ResourceLedger();
32331
+ ledger.entries = [...entries];
32332
+ ledger.nextSeq = entries.reduce((m, e) => Math.max(m, e.seq), 0) + 1;
32333
+ return ledger;
32334
+ }
32335
+ /** Record one spend. Returns the entry (host call sites only). */
32336
+ record(reason, calls = 1) {
32337
+ const entry = { seq: this.nextSeq++, reason, delta: ledgerDeltaFor(reason, calls) };
32338
+ this.entries.push(entry);
32339
+ return entry;
32340
+ }
32341
+ snapshot() {
32342
+ return [...this.entries];
32343
+ }
32344
+ /** Replace state (BudgetRuntime resume path); host call sites only. */
32345
+ resetTo(entries) {
32346
+ this.entries = [...entries];
32347
+ this.nextSeq = entries.reduce((m, e) => Math.max(m, e.seq), 0) + 1;
32348
+ }
32349
+ usage() {
32350
+ return usageFromLedger(this.entries);
32351
+ }
32352
+ /** Current budget projection under the given policy. Pure over the ledger. */
32353
+ budget(policy, stage = "implement") {
32354
+ const usage = this.usage();
32355
+ return computeBudget(policy, { toolCallsUsed: usage.toolCallsUsed, elapsedMs: usage.wallMs, tokensUsed: usage.tokensUsed }, stage);
32356
+ }
32357
+ pressure(policy) {
32358
+ return budgetPressure(this.budget(policy), policy);
32359
+ }
32360
+ };
32361
+ }
32362
+ });
32363
+
32364
+ // src/cli/budget/resourceSnapshot.ts
32365
+ function buildResourceSnapshot(budget, policy) {
32366
+ return {
32367
+ toolCallsLimit: budget.toolCalls.limit,
32368
+ toolCallsUsed: budget.toolCalls.used,
32369
+ toolCallsRemaining: budget.toolCalls.remaining,
32370
+ ...budget.wallTime.remainingMs !== void 0 ? { wallMsRemaining: budget.wallTime.remainingMs } : {},
32371
+ verificationReserve: budget.reserve.verification,
32372
+ repairReserve: budget.reserve.repair,
32373
+ stage: budget.stage,
32374
+ pressure: budgetPressure(budget, policy),
32375
+ reserveProtected: isVerificationReserveProtected(budget)
32376
+ };
32377
+ }
32378
+ function shouldEmitSnapshot(previous, next) {
32379
+ if (!previous) return true;
32380
+ if (previous.stage !== next.stage) return true;
32381
+ if (previous.pressure !== next.pressure) return true;
32382
+ if (previous.reserveProtected !== next.reserveProtected) return true;
32383
+ if (previous.toolCallsUsed !== next.toolCallsUsed) return true;
32384
+ return false;
32385
+ }
32386
+ var init_resourceSnapshot = __esm({
32387
+ "src/cli/budget/resourceSnapshot.ts"() {
32388
+ "use strict";
32389
+ init_dist();
32390
+ }
32391
+ });
32392
+
32393
+ // src/cli/budget/budgetRuntime.ts
32394
+ function resolveResourceEnforcement(env = process.env) {
32395
+ return env.ZELARI_RESOURCE_ENFORCEMENT === "protected" ? "protected" : "advisory";
32396
+ }
32397
+ var DEFAULT_ESSENTIAL_TOOLS, ADVISORY_NOTICE, PROTECTED_DENIAL, BudgetRuntime;
32398
+ var init_budgetRuntime = __esm({
32399
+ "src/cli/budget/budgetRuntime.ts"() {
32400
+ "use strict";
32401
+ init_dist();
32402
+ init_resourceLedger();
32403
+ init_resourceSnapshot();
32404
+ DEFAULT_ESSENTIAL_TOOLS = [
32405
+ "bash",
32406
+ "read_file",
32407
+ "edit_file",
32408
+ "write_file",
32409
+ "apply_diff",
32410
+ "grep_content",
32411
+ "list_files",
32412
+ "show_diff"
32413
+ ];
32414
+ ADVISORY_NOTICE = "Resource advisory: verification reserve reached. Prioritize test/typecheck/build/diff and targeted repair; avoid broad exploration or delegation.";
32415
+ PROTECTED_DENIAL = "Resource protected: remaining tool calls are reserved for verification and targeted repair. Run the required checks (test/typecheck/build), read the failure, apply a minimal fix, retest \u2014 or report BLOCKED with the evidence you have.";
32416
+ BudgetRuntime = class {
32417
+ policy;
32418
+ enforcement;
32419
+ ledger;
32420
+ essential;
32421
+ stage;
32422
+ lastEmitted;
32423
+ constructor(profileId, opts = {}) {
32424
+ this.policy = opts.policy ?? defaultResourcePolicy(profileId);
32425
+ this.enforcement = opts.enforcement ?? "advisory";
32426
+ this.essential = new Set(opts.essentialTools ?? DEFAULT_ESSENTIAL_TOOLS);
32427
+ this.stage = opts.stage ?? "implement";
32428
+ this.ledger = new ResourceLedger();
32429
+ }
32430
+ /**
32431
+ * Count one tool call; returns the snapshot to emit when §10.4 says so
32432
+ * (first sight, stage/pressure change, reserve crossing, any usage delta).
32433
+ */
32434
+ noteToolCall() {
32435
+ this.ledger.record("tool-call");
32436
+ return this.emitIfDue();
32437
+ }
32438
+ /** §10.4 verification start: stage change (and a zero-cost ledger mark). */
32439
+ noteVerificationStart() {
32440
+ this.ledger.record("verification", 0);
32441
+ return this.setStage("verify");
32442
+ }
32443
+ /** §10.4 repair start. */
32444
+ noteRepairStart() {
32445
+ return this.setStage("repair");
32446
+ }
32447
+ /** Stage transition; emits when the stage actually changes. */
32448
+ setStage(stage) {
32449
+ if (stage === this.stage) return null;
32450
+ this.stage = stage;
32451
+ return this.emitIfDue();
32452
+ }
32453
+ /** Current projection without emitting. */
32454
+ current() {
32455
+ return buildResourceSnapshot(this.ledger.budget(this.policy, this.stage), this.policy);
32456
+ }
32457
+ /** §11.3 gate — advisory mode never blocks; protected mode guards the zone. */
32458
+ gateToolCall(toolName) {
32459
+ const snapshot = this.current();
32460
+ if (!snapshot.reserveProtected) return { allowed: true, advisory: false, snapshot };
32461
+ if (this.enforcement === "advisory") {
32462
+ return { allowed: true, advisory: true, reason: ADVISORY_NOTICE, snapshot };
32463
+ }
32464
+ if (this.essential.has(toolName)) {
32465
+ return { allowed: true, advisory: true, reason: ADVISORY_NOTICE, snapshot };
32466
+ }
32467
+ return { allowed: false, advisory: false, reason: PROTECTED_DENIAL, snapshot };
32468
+ }
32469
+ /** Resume path (§9.5): rebuild usage from the session log, no emission. */
32470
+ adoptLedgerFromEvents(events) {
32471
+ const rebuilt = rebuildLedgerFromEvents(events);
32472
+ this.ledger.resetTo(rebuilt.snapshot());
32473
+ this.lastEmitted = void 0;
32474
+ }
32475
+ /** Latest emitted snapshot (what the model surface shows), if any. */
32476
+ latestEmitted() {
32477
+ return this.lastEmitted;
32478
+ }
32479
+ emitIfDue() {
32480
+ const next = this.current();
32481
+ if (shouldEmitSnapshot(this.lastEmitted, next)) {
32482
+ this.lastEmitted = next;
32483
+ return next;
32484
+ }
32485
+ return null;
32486
+ }
32487
+ };
32488
+ }
32489
+ });
32490
+
31698
32491
  // src/cli/sessionSpine.ts
31699
32492
  import path21 from "node:path";
31700
32493
  function spineEnabled() {
@@ -31776,6 +32569,14 @@ function mapBrainEventToSpine(ev) {
31776
32569
  }
31777
32570
  async function wrapSessionWriter(inner, sessionId2, options = {}) {
31778
32571
  const spine = await SessionSpineMirror.adopt(sessionId2, options);
32572
+ if (spine.status === "active") {
32573
+ const profile = options.extraStarted?.profile;
32574
+ spine.attachBudgetRuntime(
32575
+ new BudgetRuntime(typeof profile === "string" ? profile : "kraken/v1", {
32576
+ enforcement: resolveResourceEnforcement()
32577
+ })
32578
+ );
32579
+ }
31779
32580
  return new SpineMirroringWriter(inner, spine.status === "active" ? spine : null);
31780
32581
  }
31781
32582
  var MAX_STREAM_BUFFERS, SessionSpineMirror, SpineMirroringWriter;
@@ -31785,6 +32586,7 @@ var init_sessionSpine = __esm({
31785
32586
  init_session();
31786
32587
  init_session();
31787
32588
  init_verification2();
32589
+ init_budgetRuntime();
31788
32590
  MAX_STREAM_BUFFERS = 32;
31789
32591
  SessionSpineMirror = class _SessionSpineMirror {
31790
32592
  constructor(sessionId2, options) {
@@ -31796,6 +32598,10 @@ var init_sessionSpine = __esm({
31796
32598
  chain = Promise.resolve(null);
31797
32599
  streamBuffers = /* @__PURE__ */ new Map();
31798
32600
  warned = false;
32601
+ /** Host-owned budget runtime (attached via attachBudgetRuntime). */
32602
+ budgetRuntime = null;
32603
+ /** 2.6 Track A: set once a task.contract has been seeded (or the log had one). */
32604
+ contractSeeded = false;
31799
32605
  status = "disabled";
31800
32606
  /** Seq the log continued from when adopting an existing session. */
31801
32607
  resumedFromSeq;
@@ -31812,6 +32618,9 @@ var init_sessionSpine = __esm({
31812
32618
  const sessionDir = path21.join(mirror.sessionsDir, sessionId2);
31813
32619
  const report = await readSessionLog(path21.join(sessionDir, "events.jsonl"));
31814
32620
  const existed = report.events.length > 0 || report.issues.length > 0;
32621
+ if (report.events.some((e) => e.kind === "task.contract" || e.kind === "user.message")) {
32622
+ mirror.contractSeeded = true;
32623
+ }
31815
32624
  const lastSeq = report.events[report.events.length - 1]?.seq ?? 0;
31816
32625
  mirror.writer = await SessionLogWriter.open(sessionDir, sessionId2, lastSeq + 1, {
31817
32626
  now: options.now
@@ -31828,6 +32637,16 @@ var init_sessionSpine = __esm({
31828
32637
  ...options.extraStarted
31829
32638
  }
31830
32639
  });
32640
+ if (options.harnessManifest) {
32641
+ await mirror.append({
32642
+ kind: "session.harness_manifest",
32643
+ actor: ACTOR_SYSTEM,
32644
+ data: {
32645
+ manifest: options.harnessManifest.manifest,
32646
+ manifestHash: options.harnessManifest.manifestHash
32647
+ }
32648
+ });
32649
+ }
31831
32650
  mirror.status = "active";
31832
32651
  } catch (err) {
31833
32652
  if (err instanceof SessionLogLockedError) {
@@ -31842,7 +32661,8 @@ var init_sessionSpine = __esm({
31842
32661
  }
31843
32662
  /** Log the user prompt — the P1 gap the 1.x log never closed. */
31844
32663
  userMessage(text) {
31845
- void this.append({ kind: "user.message", actor: ACTOR_USER, data: { text } });
32664
+ const seqP = this.append({ kind: "user.message", actor: ACTOR_USER, data: { text } });
32665
+ void seqP;
31846
32666
  }
31847
32667
  /**
31848
32668
  * Log an assistant message outside the streaming path — legacy
@@ -31857,6 +32677,45 @@ var init_sessionSpine = __esm({
31857
32677
  });
31858
32678
  }
31859
32679
  /** Await all pending appends (import → derive read-back needs this). */
32680
+ /**
32681
+ * Record the canonical harness manifest (2.6 Track A, doc section 6.5): one
32682
+ * state-only event at session start / manifest change. Degrade-and-stop
32683
+ * like every other mirror method - the spine never breaks the loop.
32684
+ */
32685
+ harnessManifest(manifest, manifestHash) {
32686
+ void this.append({
32687
+ kind: "session.harness_manifest",
32688
+ actor: ACTOR_SYSTEM,
32689
+ data: { manifest, manifestHash }
32690
+ });
32691
+ }
32692
+ /**
32693
+ * Attach the host-owned budget runtime (2.6 Track B, Phase 2/3). From here
32694
+ * on every `tool.call` that lands on the spine is counted and — at §10.4
32695
+ * frequency — a `resource.snapshot` event is appended right after it.
32696
+ * Degrade-and-stop discipline applies: budget wiring never breaks a turn.
32697
+ */
32698
+ attachBudgetRuntime(runtime) {
32699
+ this.budgetRuntime = runtime;
32700
+ }
32701
+ /** Latest emitted resource snapshot (the model-visible one), or null. */
32702
+ latestResourceSnapshot() {
32703
+ return this.budgetRuntime?.latestEmitted() ?? null;
32704
+ }
32705
+ /**
32706
+ * §11.3 pre-dispatch gate for hosts that enforce the protected zone
32707
+ * (Phase 3): delegates to the attached runtime, never throws. Null when
32708
+ * no runtime is attached (hosts treat as "no budget info, allow").
32709
+ */
32710
+ gateResourceToolCall(toolName) {
32711
+ const gate = this.budgetRuntime?.gateToolCall(toolName);
32712
+ if (!gate) return null;
32713
+ return { allowed: gate.allowed, ...gate.reason ? { reason: gate.reason } : {} };
32714
+ }
32715
+ /** Count a landed tool.call; returns the snapshot due (§10.4), if any. */
32716
+ onToolCallBudget() {
32717
+ return this.budgetRuntime?.noteToolCall() ?? null;
32718
+ }
31860
32719
  async flush() {
31861
32720
  await this.chain;
31862
32721
  }
@@ -31961,7 +32820,31 @@ var init_sessionSpine = __esm({
31961
32820
  }
31962
32821
  append(input) {
31963
32822
  if (!this.writer || this.status === "closed") return Promise.resolve(null);
31964
- this.chain = this.chain.then(() => this.writer.append(input)).then((envelope) => envelope.seq).catch((err) => {
32823
+ const dueSnapshot = input.kind === "tool.call" ? this.onToolCallBudget() : null;
32824
+ const dueContract = input.kind === "user.message" && process.env.ZELARI_TASK_CONTRACT === "1" && !this.contractSeeded ? (this.contractSeeded = true, input.data?.text) : null;
32825
+ let seq = this.chain.then(() => this.writer.append(input)).then((envelope) => envelope.seq);
32826
+ if (dueSnapshot) {
32827
+ seq = seq.then(
32828
+ (s) => this.writer.append({ kind: "resource.snapshot", actor: ACTOR_SYSTEM, data: { ...dueSnapshot } }).then(() => s)
32829
+ );
32830
+ }
32831
+ if (dueContract) {
32832
+ seq = seq.then(async (s) => {
32833
+ try {
32834
+ const contract = deriveInitialContract(s ?? 1, dueContract);
32835
+ if (contract) {
32836
+ await this.writer.append({
32837
+ kind: "task.contract",
32838
+ actor: ACTOR_SYSTEM,
32839
+ data: { contract, kind: "task.contract" }
32840
+ });
32841
+ }
32842
+ } catch {
32843
+ }
32844
+ return s;
32845
+ });
32846
+ }
32847
+ this.chain = seq.catch((err) => {
31965
32848
  this.status = "degraded";
31966
32849
  this.writer = null;
31967
32850
  this.warnOnce(err);
@@ -33576,7 +34459,7 @@ var init_resolveStream = __esm({
33576
34459
  });
33577
34460
 
33578
34461
  // packages/core/dist/core/tools/toolOutputSpill.js
33579
- import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
34462
+ import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
33580
34463
  import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync11 } from "node:fs";
33581
34464
  import { homedir as homedir3, tmpdir } from "node:os";
33582
34465
  import { join as join11 } from "node:path";
@@ -33606,14 +34489,14 @@ function spillToolOutput(fullText, meta3) {
33606
34489
  if (!existsSync13(dir)) {
33607
34490
  mkdirSync7(dir, { recursive: true });
33608
34491
  }
33609
- const hash3 = createHash5("sha256").update(fullText).digest("hex").slice(0, 12);
34492
+ const hash3 = createHash6("sha256").update(fullText).digest("hex").slice(0, 12);
33610
34493
  const stamp = Date.now().toString(36);
33611
34494
  const rnd = randomBytes2(3).toString("hex");
33612
34495
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
33613
34496
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
33614
- const path64 = join11(dir, file2);
33615
- writeFileSync11(path64, fullText, "utf8");
33616
- return path64;
34497
+ const path65 = join11(dir, file2);
34498
+ writeFileSync11(path65, fullText, "utf8");
34499
+ return path65;
33617
34500
  } catch {
33618
34501
  return null;
33619
34502
  }
@@ -33659,10 +34542,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
33659
34542
  ${tail2}`;
33660
34543
  }
33661
34544
  if (doSpill) {
33662
- const path64 = spillToolOutput(text, { toolName: opts.toolName });
33663
- if (path64) {
34545
+ const path65 = spillToolOutput(text, { toolName: opts.toolName });
34546
+ if (path65) {
33664
34547
  const spillNote = `
33665
- \u2026 [full output spilled to: ${path64} \u2014 re-read with read_file if you need the complete text] \u2026`;
34548
+ \u2026 [full output spilled to: ${path65} \u2014 re-read with read_file if you need the complete text] \u2026`;
33666
34549
  if (preview.includes("] \u2026\n")) {
33667
34550
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
33668
34551
  `);
@@ -36078,7 +36961,7 @@ import {
36078
36961
  } from "node:fs";
36079
36962
  import { join as join13, basename } from "node:path";
36080
36963
  import { homedir as homedir5 } from "node:os";
36081
- import { createHash as createHash6 } from "node:crypto";
36964
+ import { createHash as createHash7 } from "node:crypto";
36082
36965
  function resolveWorkspaceRoot(projectRoot = process.cwd()) {
36083
36966
  const candidates = [
36084
36967
  join13(projectRoot, ".zelari"),
@@ -36094,7 +36977,7 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
36094
36977
  return candidates[0];
36095
36978
  }
36096
36979
  function hashProject(projectPath) {
36097
- return createHash6("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
36980
+ return createHash7("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
36098
36981
  }
36099
36982
  function isWritableDir(dir) {
36100
36983
  try {
@@ -36408,28 +37291,28 @@ var init_storage = __esm({
36408
37291
  VALID_SCALARS = /^(true|false|null|~)$/i;
36409
37292
  Storage = class {
36410
37293
  /** Read a Markdown file with frontmatter. Throws if not found. */
36411
- read(path64) {
36412
- if (!existsSync19(path64)) {
36413
- throw new Error(`File not found: ${path64}`);
37294
+ read(path65) {
37295
+ if (!existsSync19(path65)) {
37296
+ throw new Error(`File not found: ${path65}`);
36414
37297
  }
36415
- const md = readFileSync16(path64, "utf8");
37298
+ const md = readFileSync16(path65, "utf8");
36416
37299
  return parseFrontmatter(md);
36417
37300
  }
36418
37301
  /** Read a Markdown file; returns null if not found. */
36419
- readIfExists(path64) {
36420
- if (!existsSync19(path64)) return null;
36421
- return this.read(path64);
37302
+ readIfExists(path65) {
37303
+ if (!existsSync19(path65)) return null;
37304
+ return this.read(path65);
36422
37305
  }
36423
37306
  /**
36424
37307
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
36425
37308
  * The meta object is serialized as YAML frontmatter; body as Markdown.
36426
37309
  */
36427
- write(path64, meta3, body) {
36428
- mkdirSync11(dirname2(path64), { recursive: true });
36429
- const tmp = path64 + ".tmp-" + process.pid;
37310
+ write(path65, meta3, body) {
37311
+ mkdirSync11(dirname2(path65), { recursive: true });
37312
+ const tmp = path65 + ".tmp-" + process.pid;
36430
37313
  const md = serializeFrontmatter(meta3, body);
36431
37314
  writeFileSync13(tmp, md, "utf8");
36432
- renameSync2(tmp, path64);
37315
+ renameSync2(tmp, path65);
36433
37316
  }
36434
37317
  /** List all .md files in a directory (non-recursive). */
36435
37318
  listMarkdown(dir) {
@@ -36491,8 +37374,8 @@ function nextPlanTaskId(store6) {
36491
37374
  return `t${store6.counter}`;
36492
37375
  }
36493
37376
  function writePlanTaskArtifact(rootDir, task) {
36494
- const path64 = join15(rootDir, "plan-tasks", `${task.id}.md`);
36495
- mkdirSync12(dirname3(path64), { recursive: true });
37377
+ const path65 = join15(rootDir, "plan-tasks", `${task.id}.md`);
37378
+ mkdirSync12(dirname3(path65), { recursive: true });
36496
37379
  const meta3 = {
36497
37380
  kind: "task",
36498
37381
  id: task.id,
@@ -36513,7 +37396,7 @@ function writePlanTaskArtifact(rootDir, task) {
36513
37396
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
36514
37397
  ""
36515
37398
  ].filter((l) => l !== null).join("\n");
36516
- new Storage().write(path64, meta3, body);
37399
+ new Storage().write(path65, meta3, body);
36517
37400
  }
36518
37401
  function loadHandle(rootDir) {
36519
37402
  const jsonPath = join15(rootDir, "plan.json");
@@ -36912,7 +37795,7 @@ var init_inspectTypecheckSafety = __esm({
36912
37795
 
36913
37796
  // src/cli/tools/inspectCommand.ts
36914
37797
  import { spawn as spawn7 } from "node:child_process";
36915
- import { createHash as createHash7 } from "node:crypto";
37798
+ import { createHash as createHash8 } from "node:crypto";
36916
37799
  import { existsSync as existsSync21, promises as fs17 } from "node:fs";
36917
37800
  import os8 from "node:os";
36918
37801
  import path30 from "node:path";
@@ -36999,7 +37882,7 @@ function buildInspectCommand(op, ctx) {
36999
37882
  }
37000
37883
  case "typecheck": {
37001
37884
  const project = path30.resolve(ctx.cwd, op.project ?? "tsconfig.json");
37002
- const hash3 = createHash7("sha256").update(project).digest("hex").slice(0, 16);
37885
+ const hash3 = createHash8("sha256").update(project).digest("hex").slice(0, 16);
37003
37886
  const tsBuildInfoFile = path30.join(os8.tmpdir(), "zelari-inspect", `${hash3}.tsbuildinfo`);
37004
37887
  return {
37005
37888
  ok: true,
@@ -38438,9 +39321,9 @@ var init_store2 = __esm({
38438
39321
  import { promises as fs18, existsSync as existsSync22, readFileSync as readFileSync19 } from "node:fs";
38439
39322
  import { homedir as homedir6 } from "node:os";
38440
39323
  import path33 from "node:path";
38441
- import { createHash as createHash8 } from "node:crypto";
39324
+ import { createHash as createHash9 } from "node:crypto";
38442
39325
  function getIndexPath(root) {
38443
- const hash3 = createHash8("sha1").update(path33.resolve(root)).digest("hex").slice(0, 16);
39326
+ const hash3 = createHash9("sha1").update(path33.resolve(root)).digest("hex").slice(0, 16);
38444
39327
  return process.env.ZELARI_SEMANTIC_FILE ?? path33.join(homedir6(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
38445
39328
  }
38446
39329
  async function collectSourceFiles(root, maxFiles = 1500) {
@@ -39071,21 +39954,21 @@ function normalizeAuth(auth) {
39071
39954
  return "agent";
39072
39955
  }
39073
39956
  function readSecrets() {
39074
- const path64 = getSshSecretsPath();
39075
- if (!existsSync23(path64)) return {};
39957
+ const path65 = getSshSecretsPath();
39958
+ if (!existsSync23(path65)) return {};
39076
39959
  try {
39077
- return JSON.parse(readFileSync20(path64, "utf8"));
39960
+ return JSON.parse(readFileSync20(path65, "utf8"));
39078
39961
  } catch {
39079
39962
  return {};
39080
39963
  }
39081
39964
  }
39082
39965
  function writeSecrets(data) {
39083
- const path64 = getSshSecretsPath();
39084
- mkdirSync13(dirname4(path64), { recursive: true });
39085
- writeFileSync15(path64, `${JSON.stringify(data, null, 2)}
39966
+ const path65 = getSshSecretsPath();
39967
+ mkdirSync13(dirname4(path65), { recursive: true });
39968
+ writeFileSync15(path65, `${JSON.stringify(data, null, 2)}
39086
39969
  `, "utf8");
39087
39970
  try {
39088
- chmodSync(path64, 384);
39971
+ chmodSync(path65, 384);
39089
39972
  } catch {
39090
39973
  }
39091
39974
  }
@@ -39114,10 +39997,10 @@ function deleteSshPassword(id) {
39114
39997
  writeSecrets({ passwords });
39115
39998
  }
39116
39999
  function readStore2() {
39117
- const path64 = getSshTargetsPath();
39118
- if (!existsSync23(path64)) return [];
40000
+ const path65 = getSshTargetsPath();
40001
+ if (!existsSync23(path65)) return [];
39119
40002
  try {
39120
- const parsed = JSON.parse(readFileSync20(path64, "utf8"));
40003
+ const parsed = JSON.parse(readFileSync20(path65, "utf8"));
39121
40004
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
39122
40005
  return list.filter(
39123
40006
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -39132,11 +40015,11 @@ function readStore2() {
39132
40015
  }
39133
40016
  }
39134
40017
  function writeStore2(targets) {
39135
- const path64 = getSshTargetsPath();
39136
- mkdirSync13(dirname4(path64), { recursive: true });
40018
+ const path65 = getSshTargetsPath();
40019
+ mkdirSync13(dirname4(path65), { recursive: true });
39137
40020
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
39138
40021
  writeFileSync15(
39139
- path64,
40022
+ path65,
39140
40023
  `${JSON.stringify({ targets: clean }, null, 2)}
39141
40024
  `,
39142
40025
  "utf8"
@@ -39382,11 +40265,11 @@ function formatSshTargetsForPrompt() {
39382
40265
  ];
39383
40266
  for (const t of targets) {
39384
40267
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
39385
- const path64 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
40268
+ const path65 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
39386
40269
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
39387
40270
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
39388
40271
  lines.push(
39389
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path64}${tags}${allow}`
40272
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path65}${tags}${allow}`
39390
40273
  );
39391
40274
  }
39392
40275
  return lines.join("\n");
@@ -40064,7 +40947,7 @@ var init_lifecycleHooks = __esm({
40064
40947
  });
40065
40948
 
40066
40949
  // src/cli/toolResultCache.ts
40067
- import { createHash as createHash9 } from "node:crypto";
40950
+ import { createHash as createHash10 } from "node:crypto";
40068
40951
  import { promises as fs20 } from "node:fs";
40069
40952
  import path39 from "node:path";
40070
40953
  function isToolCacheEnabled() {
@@ -40077,7 +40960,7 @@ function resolveToolCacheTtlMs() {
40077
40960
  return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
40078
40961
  }
40079
40962
  function hashKey(parts) {
40080
- return createHash9("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
40963
+ return createHash10("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
40081
40964
  }
40082
40965
  function resultBytes(result) {
40083
40966
  try {
@@ -40832,7 +41715,7 @@ var init_toolRegistry = __esm({
40832
41715
  });
40833
41716
 
40834
41717
  // src/cli/state/fileStateStore.ts
40835
- import { createHash as createHash11, randomUUID as randomUUID2 } from "node:crypto";
41718
+ import { createHash as createHash12, randomUUID as randomUUID2 } from "node:crypto";
40836
41719
  import { promises as fs21 } from "node:fs";
40837
41720
  import * as path41 from "node:path";
40838
41721
  function shortId() {
@@ -40884,7 +41767,7 @@ async function getStateStore(projectRoot, env = process.env) {
40884
41767
  }
40885
41768
  }
40886
41769
  function hashStablePrompt(stable) {
40887
- return createHash11("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
41770
+ return createHash12("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
40888
41771
  }
40889
41772
  var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
40890
41773
  var init_fileStateStore = __esm({
@@ -41188,7 +42071,7 @@ function extractiveHistorySummary(dropped, opts) {
41188
42071
  if (m.toolCalls) {
41189
42072
  for (const tc of m.toolCalls) {
41190
42073
  tools.set(tc.name, (tools.get(tc.name) ?? 0) + 1);
41191
- collectPaths2(tc.args, files);
42074
+ collectPaths3(tc.args, files);
41192
42075
  }
41193
42076
  }
41194
42077
  } else if (m.role === "tool") {
@@ -41248,7 +42131,7 @@ function oneLine(s, max) {
41248
42131
  if (t.length <= max) return t;
41249
42132
  return `${t.slice(0, max - 1)}\u2026`;
41250
42133
  }
41251
- function collectPaths2(args, out) {
42134
+ function collectPaths3(args, out) {
41252
42135
  if (!args || typeof args !== "object") return;
41253
42136
  const obj = args;
41254
42137
  for (const key of ["path", "file", "filepath", "filePath", "target", "cwd"]) {
@@ -42241,6 +43124,7 @@ __export(headlessSpine_exports, {
42241
43124
  seedHeadlessModelHistory: () => seedHeadlessModelHistory,
42242
43125
  sessionStartedEvent: () => sessionStartedEvent
42243
43126
  });
43127
+ import path42 from "node:path";
42244
43128
  function sessionStartedEvent(handle) {
42245
43129
  return {
42246
43130
  type: "session_started",
@@ -42272,6 +43156,14 @@ async function openHeadlessSpine(opts) {
42272
43156
  };
42273
43157
  const spine = await SessionSpineMirror.adopt(opts.sessionId, mirrorOpts);
42274
43158
  if (spine.status === "active") {
43159
+ const budget = new BudgetRuntime(profileId, { enforcement: resolveResourceEnforcement() });
43160
+ if (spine.resumedFromSeq !== void 0 && spine.resumedFromSeq > 0) {
43161
+ const prior = await readSessionLog(
43162
+ path42.join(spine.sessionsDir, opts.sessionId, "events.jsonl")
43163
+ ).catch(() => null);
43164
+ if (prior) budget.adoptLedgerFromEvents(prior.events);
43165
+ }
43166
+ spine.attachBudgetRuntime(budget);
42275
43167
  spine.note("headless.profile", { profile: profileId, mode: opts.mode ?? "kraken" });
42276
43168
  }
42277
43169
  return {
@@ -42286,6 +43178,9 @@ async function openHeadlessSpine(opts) {
42286
43178
  userMessage(text) {
42287
43179
  spine.userMessage(text);
42288
43180
  },
43181
+ gateResourceToolCall(toolName) {
43182
+ return spine.gateResourceToolCall(toolName);
43183
+ },
42289
43184
  verificationRun(payload) {
42290
43185
  spine.verificationRun(payload);
42291
43186
  },
@@ -42402,8 +43297,10 @@ var init_headlessSpine = __esm({
42402
43297
  init_dist();
42403
43298
  init_session();
42404
43299
  init_mission2();
43300
+ init_session();
42405
43301
  init_runtime2();
42406
43302
  init_sessionSpine();
43303
+ init_budgetRuntime();
42407
43304
  init_headless();
42408
43305
  }
42409
43306
  });
@@ -43329,8 +44226,8 @@ function readPlan(ctx) {
43329
44226
  } catch {
43330
44227
  }
43331
44228
  }
43332
- const path64 = workspaceFile(ctx.rootDir, "plan");
43333
- const doc = ctx.storage.readIfExists(path64);
44229
+ const path65 = workspaceFile(ctx.rootDir, "plan");
44230
+ const doc = ctx.storage.readIfExists(path65);
43334
44231
  if (!doc) return { phases: [], tasks: [], milestones: [] };
43335
44232
  const meta3 = doc.meta;
43336
44233
  return {
@@ -43508,7 +44405,7 @@ function addMilestoneRecord(ctx, summary, input) {
43508
44405
  dueDate: input.dueDate,
43509
44406
  targetVersion: version2
43510
44407
  });
43511
- const path64 = join25(ctx.rootDir, "milestones", `${id}.md`);
44408
+ const path65 = join25(ctx.rootDir, "milestones", `${id}.md`);
43512
44409
  const meta3 = {
43513
44410
  kind: "milestone",
43514
44411
  id,
@@ -43525,7 +44422,7 @@ function addMilestoneRecord(ctx, summary, input) {
43525
44422
  `Target version: ${version2}`,
43526
44423
  ""
43527
44424
  ].join("\n");
43528
- ctx.storage.write(path64, meta3, body);
44425
+ ctx.storage.write(path65, meta3, body);
43529
44426
  return { id, created: true };
43530
44427
  }
43531
44428
  function readPlanSummary(ctx) {
@@ -43729,7 +44626,7 @@ function addIdeaStub(ctx) {
43729
44626
  const tags = args["tags"] ?? [];
43730
44627
  const category = args["category"] ?? "General";
43731
44628
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
43732
- const path64 = workspaceArtifact(ctx.rootDir, "decisions", id);
44629
+ const path65 = workspaceArtifact(ctx.rootDir, "decisions", id);
43733
44630
  const meta3 = {
43734
44631
  kind: "adr",
43735
44632
  status: "proposed",
@@ -43755,7 +44652,7 @@ function addIdeaStub(ctx) {
43755
44652
  ...consequences.map((c) => `- ${c}`),
43756
44653
  ""
43757
44654
  ].join("\n");
43758
- ctx.storage.write(path64, meta3, body);
44655
+ ctx.storage.write(path65, meta3, body);
43759
44656
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
43760
44657
  });
43761
44658
  }
@@ -43837,14 +44734,14 @@ function createDocumentStub(ctx) {
43837
44734
  ctx.storage.write(risksPath, riskMeta, content);
43838
44735
  return `Document "${title}" created at risks.md (workspace root).`;
43839
44736
  }
43840
- const path64 = workspaceArtifact(ctx.rootDir, "docs", slug);
44737
+ const path65 = workspaceArtifact(ctx.rootDir, "docs", slug);
43841
44738
  const meta3 = {
43842
44739
  kind: "doc",
43843
44740
  id: slug,
43844
44741
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
43845
44742
  tags
43846
44743
  };
43847
- ctx.storage.write(path64, meta3, content);
44744
+ ctx.storage.write(path65, meta3, content);
43848
44745
  return `Document "${title}" created at docs/${slug}.md.`;
43849
44746
  });
43850
44747
  }
@@ -44065,15 +44962,15 @@ __export(updater_exports, {
44065
44962
  import { createRequire as createRequire2 } from "node:module";
44066
44963
  import { spawn as spawn12 } from "node:child_process";
44067
44964
  import { existsSync as existsSync32 } from "node:fs";
44068
- import path42 from "node:path";
44965
+ import path43 from "node:path";
44069
44966
  import { fileURLToPath } from "node:url";
44070
44967
  function resolveBundledNpmCli(execPath = process.execPath) {
44071
- const dir = path42.dirname(execPath);
44968
+ const dir = path43.dirname(execPath);
44072
44969
  const candidates = [
44073
44970
  // Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
44074
- path42.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
44971
+ path43.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
44075
44972
  // POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
44076
- path42.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
44973
+ path43.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
44077
44974
  ];
44078
44975
  for (const candidate of candidates) {
44079
44976
  try {
@@ -44090,7 +44987,7 @@ function looksLikeBrokenShim(exitCode, output) {
44090
44987
  }
44091
44988
  function getCurrentVersion() {
44092
44989
  try {
44093
- const pkgPath = path42.resolve(__dirname2, "..", "..", "package.json");
44990
+ const pkgPath = path43.resolve(__dirname2, "..", "..", "package.json");
44094
44991
  const pkg = require2(pkgPath);
44095
44992
  return pkg.version;
44096
44993
  } catch {
@@ -44213,7 +45110,7 @@ var init_updater = __esm({
44213
45110
  "use strict";
44214
45111
  init_cmdline();
44215
45112
  require2 = createRequire2(import.meta.url);
44216
- __dirname2 = path42.dirname(fileURLToPath(import.meta.url));
45113
+ __dirname2 = path43.dirname(fileURLToPath(import.meta.url));
44217
45114
  REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
44218
45115
  }
44219
45116
  });
@@ -44401,10 +45298,10 @@ function getUserMcpPath() {
44401
45298
  function getProjectMcpPath(projectRoot) {
44402
45299
  return join26(projectRoot, ".zelari", "mcp.json");
44403
45300
  }
44404
- function readFile3(path64) {
44405
- if (!existsSync33(path64)) return {};
45301
+ function readFile3(path65) {
45302
+ if (!existsSync33(path65)) return {};
44406
45303
  try {
44407
- const parsed = JSON.parse(readFileSync28(path64, "utf8"));
45304
+ const parsed = JSON.parse(readFileSync28(path65, "utf8"));
44408
45305
  const out = {};
44409
45306
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
44410
45307
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -44420,10 +45317,10 @@ function readFile3(path64) {
44420
45317
  return {};
44421
45318
  }
44422
45319
  }
44423
- function writeFile(path64, servers) {
44424
- mkdirSync16(dirname7(path64), { recursive: true });
45320
+ function writeFile(path65, servers) {
45321
+ mkdirSync16(dirname7(path65), { recursive: true });
44425
45322
  const body = { mcpServers: servers };
44426
- writeFileSync18(path64, `${JSON.stringify(body, null, 2)}
45323
+ writeFileSync18(path65, `${JSON.stringify(body, null, 2)}
44427
45324
  `, "utf8");
44428
45325
  }
44429
45326
  function listMcpServers(projectRoot) {
@@ -44456,9 +45353,9 @@ function upsertMcpServer(opts) {
44456
45353
  if (!opts.config.command?.trim()) {
44457
45354
  return { ok: false, error: "command is required" };
44458
45355
  }
44459
- let path64;
45356
+ let path65;
44460
45357
  if (opts.scope === "user") {
44461
- path64 = getUserMcpPath();
45358
+ path65 = getUserMcpPath();
44462
45359
  } else {
44463
45360
  const root = opts.projectRoot?.trim();
44464
45361
  if (!root) {
@@ -44467,30 +45364,30 @@ function upsertMcpServer(opts) {
44467
45364
  error: "projectRoot required for project scope (Open Folder first)"
44468
45365
  };
44469
45366
  }
44470
- path64 = getProjectMcpPath(root);
45367
+ path65 = getProjectMcpPath(root);
44471
45368
  }
44472
- const current = readFile3(path64);
45369
+ const current = readFile3(path65);
44473
45370
  current[name] = {
44474
45371
  command: opts.config.command.trim(),
44475
45372
  args: opts.config.args,
44476
45373
  env: opts.config.env,
44477
45374
  enabled: opts.config.enabled !== false
44478
45375
  };
44479
- writeFile(path64, current);
44480
- return { ok: true, path: path64 };
45376
+ writeFile(path65, current);
45377
+ return { ok: true, path: path65 };
44481
45378
  }
44482
45379
  function removeMcpServer(opts) {
44483
- const path64 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
44484
- if (!path64) {
45380
+ const path65 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
45381
+ if (!path65) {
44485
45382
  return { ok: false, error: "projectRoot required for project scope" };
44486
45383
  }
44487
- const current = readFile3(path64);
45384
+ const current = readFile3(path65);
44488
45385
  if (!(opts.name in current)) {
44489
- return { ok: false, error: `Server "${opts.name}" not found in ${path64}` };
45386
+ return { ok: false, error: `Server "${opts.name}" not found in ${path65}` };
44490
45387
  }
44491
45388
  delete current[opts.name];
44492
- writeFile(path64, current);
44493
- return { ok: true, path: path64 };
45389
+ writeFile(path65, current);
45390
+ return { ok: true, path: path65 };
44494
45391
  }
44495
45392
  var init_mcpConfigIo = __esm({
44496
45393
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -44887,14 +45784,14 @@ __export(agentsMd_exports, {
44887
45784
  updateAgentsMd: () => updateAgentsMd
44888
45785
  });
44889
45786
  import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync19 } from "node:fs";
44890
- import { createHash as createHash12 } from "node:crypto";
45787
+ import { createHash as createHash13 } from "node:crypto";
44891
45788
  import { join as join28 } from "node:path";
44892
45789
  import { readFile as readFile4 } from "node:fs/promises";
44893
45790
  async function readPackageJson2(projectRoot) {
44894
- const path64 = join28(projectRoot, "package.json");
44895
- if (!existsSync35(path64)) return null;
45791
+ const path65 = join28(projectRoot, "package.json");
45792
+ if (!existsSync35(path65)) return null;
44896
45793
  try {
44897
- return JSON.parse(await readFile4(path64, "utf8"));
45794
+ return JSON.parse(await readFile4(path65, "utf8"));
44898
45795
  } catch {
44899
45796
  return null;
44900
45797
  }
@@ -44976,9 +45873,9 @@ async function genBuild(ctx) {
44976
45873
  ].join("\n");
44977
45874
  }
44978
45875
  async function genOpenQuestions(ctx) {
44979
- const path64 = join28(ctx.rootDir, "risks.md");
44980
- if (!existsSync35(path64)) return "_No open questions._";
44981
- const content = readFileSync30(path64, "utf8");
45876
+ const path65 = join28(ctx.rootDir, "risks.md");
45877
+ if (!existsSync35(path65)) return "_No open questions._";
45878
+ const content = readFileSync30(path65, "utf8");
44982
45879
  const lines = content.split("\n");
44983
45880
  const questions = [];
44984
45881
  let currentTitle = "";
@@ -45102,7 +45999,7 @@ async function updateAgentsMd(ctx, projectRoot) {
45102
45999
  return { changed: true, sections: changedSections };
45103
46000
  }
45104
46001
  function hash2(s) {
45105
- return createHash12("sha256").update(s).digest("hex").slice(0, 16);
46002
+ return createHash13("sha256").update(s).digest("hex").slice(0, 16);
45106
46003
  }
45107
46004
  var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
45108
46005
  var init_agentsMd = __esm({
@@ -45252,9 +46149,9 @@ function versionKey(value) {
45252
46149
  function firstString2(v) {
45253
46150
  return typeof v === "string" && v.trim().length > 0 ? v : null;
45254
46151
  }
45255
- function readFileSyncSafe(path64) {
46152
+ function readFileSyncSafe(path65) {
45256
46153
  try {
45257
- return readFileSync31(path64, "utf8");
46154
+ return readFileSync31(path65, "utf8");
45258
46155
  } catch {
45259
46156
  return null;
45260
46157
  }
@@ -45717,8 +46614,8 @@ async function runPostCouncilHook(ctx, options) {
45717
46614
  sources: scope.sources
45718
46615
  } : void 0
45719
46616
  });
45720
- const path64 = writeCouncilCompletion(ctx.rootDir, completion);
45721
- completionHook = { ran: true, path: path64, completion };
46617
+ const path65 = writeCouncilCompletion(ctx.rootDir, completion);
46618
+ completionHook = { ran: true, path: path65, completion };
45722
46619
  } catch (err) {
45723
46620
  completionHook = {
45724
46621
  ran: true,
@@ -45763,7 +46660,7 @@ import {
45763
46660
  writeFileSync as writeFileSync21,
45764
46661
  mkdirSync as mkdirSync17
45765
46662
  } from "node:fs";
45766
- import path43 from "node:path";
46663
+ import path44 from "node:path";
45767
46664
  import os10 from "node:os";
45768
46665
  var FeedbackStore;
45769
46666
  var init_councilFeedback = __esm({
@@ -45774,7 +46671,7 @@ var init_councilFeedback = __esm({
45774
46671
  now;
45775
46672
  entries = [];
45776
46673
  constructor(options = {}) {
45777
- this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path43.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
46674
+ this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path44.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
45778
46675
  this.now = options.now ?? Date.now;
45779
46676
  this.load();
45780
46677
  }
@@ -45880,7 +46777,7 @@ var init_councilFeedback = __esm({
45880
46777
  }
45881
46778
  }
45882
46779
  save() {
45883
- mkdirSync17(path43.dirname(this.file), { recursive: true });
46780
+ mkdirSync17(path44.dirname(this.file), { recursive: true });
45884
46781
  writeFileSync21(
45885
46782
  this.file,
45886
46783
  JSON.stringify({ entries: this.entries }, null, 2),
@@ -45948,7 +46845,7 @@ import { execFile as execFile3 } from "node:child_process";
45948
46845
  import { promisify as promisify2 } from "node:util";
45949
46846
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
45950
46847
  import { tmpdir as tmpdir2 } from "node:os";
45951
- import path44 from "node:path";
46848
+ import path45 from "node:path";
45952
46849
  import { randomUUID as randomUUID3 } from "node:crypto";
45953
46850
  async function git3(cwd, args, env) {
45954
46851
  const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
@@ -45968,8 +46865,8 @@ async function isGitRepo(cwd) {
45968
46865
  return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
45969
46866
  }
45970
46867
  async function withTempIndex(fn) {
45971
- const dir = mkdtempSync(path44.join(tmpdir2(), "zelari-ckpt-"));
45972
- const indexFile = path44.join(dir, "index");
46868
+ const dir = mkdtempSync(path45.join(tmpdir2(), "zelari-ckpt-"));
46869
+ const indexFile = path45.join(dir, "index");
45973
46870
  try {
45974
46871
  return await fn(indexFile);
45975
46872
  } finally {
@@ -46060,7 +46957,7 @@ async function restoreCheckpoint(cwd, id) {
46060
46957
  const deleted = [];
46061
46958
  for (const rel2 of added) {
46062
46959
  try {
46063
- rmSync2(path44.join(cwd, rel2), { force: true });
46960
+ rmSync2(path45.join(cwd, rel2), { force: true });
46064
46961
  deleted.push(rel2);
46065
46962
  } catch {
46066
46963
  }
@@ -46161,7 +47058,7 @@ __export(fileBackend_exports, {
46161
47058
  });
46162
47059
  import { randomUUID as randomUUID4 } from "node:crypto";
46163
47060
  import { promises as fs23 } from "node:fs";
46164
- import * as path45 from "node:path";
47061
+ import * as path46 from "node:path";
46165
47062
  function tokenize(text) {
46166
47063
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
46167
47064
  }
@@ -46205,8 +47102,8 @@ var init_fileBackend = __esm({
46205
47102
  logPath = "";
46206
47103
  memoryDir = "";
46207
47104
  async init(projectRoot) {
46208
- this.memoryDir = path45.join(projectRoot, ".zelari", "memory");
46209
- this.logPath = path45.join(this.memoryDir, "log.jsonl");
47105
+ this.memoryDir = path46.join(projectRoot, ".zelari", "memory");
47106
+ this.logPath = path46.join(this.memoryDir, "log.jsonl");
46210
47107
  await fs23.mkdir(this.memoryDir, { recursive: true });
46211
47108
  }
46212
47109
  async add(content, metadata = {}, graph) {
@@ -46279,12 +47176,12 @@ var init_fileBackend = __esm({
46279
47176
 
46280
47177
  // src/cli/traceStore.ts
46281
47178
  import { promises as fs24 } from "node:fs";
46282
- import * as path46 from "node:path";
47179
+ import * as path47 from "node:path";
46283
47180
  function traceDir(projectRoot) {
46284
- return path46.join(projectRoot, ".zelari", "trace");
47181
+ return path47.join(projectRoot, ".zelari", "trace");
46285
47182
  }
46286
47183
  function tracePath(projectRoot, missionId) {
46287
- return path46.join(traceDir(projectRoot), `${missionId}.json`);
47184
+ return path47.join(traceDir(projectRoot), `${missionId}.json`);
46288
47185
  }
46289
47186
  async function saveTrace(projectRoot, missionId, entries) {
46290
47187
  const dir = traceDir(projectRoot);
@@ -46319,7 +47216,7 @@ __export(zelariMission_exports, {
46319
47216
  });
46320
47217
  import { randomUUID as randomUUID5 } from "node:crypto";
46321
47218
  import { promises as fs25 } from "node:fs";
46322
- import * as path47 from "node:path";
47219
+ import * as path48 from "node:path";
46323
47220
  function resolveMaxIterations(env = process.env) {
46324
47221
  const raw = env.ZELARI_MISSION_MAX_ITER;
46325
47222
  const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
@@ -46347,10 +47244,10 @@ function isMissionAutoStart(env = process.env) {
46347
47244
  return env.ZELARI_MISSION_AUTO === "1";
46348
47245
  }
46349
47246
  async function writeMissionState(projectRoot, state3) {
46350
- const dir = path47.join(projectRoot, ".zelari");
47247
+ const dir = path48.join(projectRoot, ".zelari");
46351
47248
  await fs25.mkdir(dir, { recursive: true });
46352
47249
  await fs25.writeFile(
46353
- path47.join(dir, "mission-state.json"),
47250
+ path48.join(dir, "mission-state.json"),
46354
47251
  JSON.stringify(state3, null, 2) + "\n",
46355
47252
  "utf8"
46356
47253
  );
@@ -46972,7 +47869,7 @@ function safeSocketPath(socketPath) {
46972
47869
  return socketPath.trim();
46973
47870
  }
46974
47871
  function startPermissionBroker(socketPath, handlers, opts) {
46975
- const path64 = safeSocketPath(socketPath);
47872
+ const path65 = safeSocketPath(socketPath);
46976
47873
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
46977
47874
  const sockets = /* @__PURE__ */ new Set();
46978
47875
  const server = createServer2((socket) => {
@@ -47072,10 +47969,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
47072
47969
  return new Promise((resolve3, reject) => {
47073
47970
  const onError = (err) => reject(err);
47074
47971
  server.once("error", onError);
47075
- server.listen(path64, () => {
47972
+ server.listen(path65, () => {
47076
47973
  server.removeListener("error", onError);
47077
47974
  resolve3({
47078
- socketPath: path64,
47975
+ socketPath: path65,
47079
47976
  stop: () => new Promise((res) => {
47080
47977
  for (const s of sockets) s.destroy();
47081
47978
  sockets.clear();
@@ -47086,7 +47983,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
47086
47983
  if (done) return;
47087
47984
  done = true;
47088
47985
  if (process.platform !== "win32") {
47089
- unlink(path64, () => res());
47986
+ unlink(path65, () => res());
47090
47987
  } else {
47091
47988
  res();
47092
47989
  }
@@ -47099,11 +47996,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
47099
47996
  });
47100
47997
  }
47101
47998
  function requestBrokerAsk(socketPath, ask, opts) {
47102
- const path64 = safeSocketPath(socketPath);
47999
+ const path65 = safeSocketPath(socketPath);
47103
48000
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
47104
48001
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
47105
48002
  return new Promise((resolve3, reject) => {
47106
- const socket = connect(path64);
48003
+ const socket = connect(path65);
47107
48004
  let buffer = "";
47108
48005
  let settled = false;
47109
48006
  const settle = (fn) => {
@@ -47118,7 +48015,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
47118
48015
  settle(
47119
48016
  () => reject(
47120
48017
  new Error(
47121
- `permission broker unavailable at "${path64}" (connect timed out after ${connectTimeoutMs}ms)`
48018
+ `permission broker unavailable at "${path65}" (connect timed out after ${connectTimeoutMs}ms)`
47122
48019
  )
47123
48020
  )
47124
48021
  );
@@ -47851,9 +48748,9 @@ __export(graphMemory_exports, {
47851
48748
  toGraphSnapshot: () => toGraphSnapshot
47852
48749
  });
47853
48750
  import { promises as fs26 } from "node:fs";
47854
- import path49 from "node:path";
48751
+ import path50 from "node:path";
47855
48752
  function snapshotPath(cwd) {
47856
- return path49.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
48753
+ return path50.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
47857
48754
  }
47858
48755
  function toGraphSnapshot(graph, opts) {
47859
48756
  const unresolved = (opts.unresolvedFindings ?? []).map((u) => ({
@@ -47880,7 +48777,7 @@ async function saveGraphSnapshot(cwd, snapshot) {
47880
48777
  try {
47881
48778
  await fs26.access(cwd);
47882
48779
  const file2 = snapshotPath(cwd);
47883
- await fs26.mkdir(path49.dirname(file2), { recursive: true });
48780
+ await fs26.mkdir(path50.dirname(file2), { recursive: true });
47884
48781
  await fs26.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
47885
48782
  } catch {
47886
48783
  }
@@ -47947,7 +48844,7 @@ var SNAPSHOT_DIR, SNAPSHOT_FILE, MAX_SNAPSHOT_FINDINGS_CHARS;
47947
48844
  var init_graphMemory = __esm({
47948
48845
  "src/cli/kraken/graphMemory.ts"() {
47949
48846
  "use strict";
47950
- SNAPSHOT_DIR = path49.join(".zelari", "kraken");
48847
+ SNAPSHOT_DIR = path50.join(".zelari", "kraken");
47951
48848
  SNAPSHOT_FILE = "last-graph.json";
47952
48849
  MAX_SNAPSHOT_FINDINGS_CHARS = 400;
47953
48850
  }
@@ -47963,14 +48860,14 @@ var init_tentacle = __esm({
47963
48860
 
47964
48861
  // src/cli/kraken/workbench.ts
47965
48862
  import { promises as fs27 } from "node:fs";
47966
- import path50 from "node:path";
48863
+ import path51 from "node:path";
47967
48864
  function isWorkbenchEnabled(env = process.env) {
47968
48865
  const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
47969
48866
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
47970
48867
  return true;
47971
48868
  }
47972
48869
  function workbenchPath(cwd, graphId) {
47973
- return path50.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
48870
+ return path51.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
47974
48871
  }
47975
48872
  function countByStatus2(nodes) {
47976
48873
  const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
@@ -48133,7 +49030,7 @@ var init_workbench = __esm({
48133
49030
  if (!this.enabled) return null;
48134
49031
  if (!this.dirty && this.lastWrite) return this.lastWrite;
48135
49032
  const out = workbenchPath(this.cwd, this.graphId);
48136
- await fs27.mkdir(path50.dirname(out), { recursive: true });
49033
+ await fs27.mkdir(path51.dirname(out), { recursive: true });
48137
49034
  const body = this.render();
48138
49035
  const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
48139
49036
  await fs27.writeFile(tmp, body, "utf8");
@@ -48330,7 +49227,7 @@ __export(executor_exports, {
48330
49227
  thoroughnessForKind: () => thoroughnessForKind
48331
49228
  });
48332
49229
  import { existsSync as existsSync40 } from "node:fs";
48333
- import path51 from "node:path";
49230
+ import path52 from "node:path";
48334
49231
  function resolveMaxParallel(env = process.env) {
48335
49232
  const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
48336
49233
  if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
@@ -48388,7 +49285,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
48388
49285
  }
48389
49286
  function defaultChecksExists(cwd) {
48390
49287
  try {
48391
- return existsSync40(path51.join(cwd, ".zelari", "world", "checks.json"));
49288
+ return existsSync40(path52.join(cwd, ".zelari", "world", "checks.json"));
48392
49289
  } catch {
48393
49290
  return false;
48394
49291
  }
@@ -49184,8 +50081,8 @@ ${upstream}` : node.prompt,
49184
50081
  * nodes that already exist, and the rewiring moves an existing edge forward
49185
50082
  * along the chain rather than back into it.
49186
50083
  */
49187
- spawnReworkPair(graph, writer, verify, findings, root, round) {
49188
- const reworkId = `rework-${root}-${round}`;
50084
+ spawnReworkPair(graph, writer, verify, findings, root, round2) {
50085
+ const reworkId = `rework-${root}-${round2}`;
49189
50086
  const reworkNode = {
49190
50087
  id: reworkId,
49191
50088
  kind: "fix",
@@ -49212,7 +50109,7 @@ ${findings || "(the reviewer reported FAIL without detail)"}`,
49212
50109
  const reVerifyNode = {
49213
50110
  id: reVerifyId,
49214
50111
  kind: "verify",
49215
- label: `verify: ${writer.label} (rework ${round})`,
50112
+ label: `verify: ${writer.label} (rework ${round2})`,
49216
50113
  prompt: verify.prompt,
49217
50114
  deps: [reworkId],
49218
50115
  status: "pending",
@@ -49230,7 +50127,7 @@ ${findings || "(the reviewer reported FAIL without detail)"}`,
49230
50127
  this.radio("node_fix", {
49231
50128
  description: reworkNode.label,
49232
50129
  agent: "fix",
49233
- detail: `verify FAIL on "${writer.label}" \u2014 rework round ${round}/${this.maxReviewRounds}`,
50130
+ detail: `verify FAIL on "${writer.label}" \u2014 rework round ${round2}/${this.maxReviewRounds}`,
49234
50131
  ok: false
49235
50132
  });
49236
50133
  }
@@ -49727,10 +50624,10 @@ var init_prereqChecks = __esm({
49727
50624
 
49728
50625
  // src/cli/plugins/prefs.ts
49729
50626
  import { existsSync as existsSync42, readFileSync as readFileSync35, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
49730
- import path54 from "node:path";
50627
+ import path55 from "node:path";
49731
50628
  import os11 from "node:os";
49732
50629
  function getPluginPrefsPath() {
49733
- return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path54.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
50630
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path55.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
49734
50631
  }
49735
50632
  function getPluginPrefs() {
49736
50633
  const file2 = getPluginPrefsPath();
@@ -49751,7 +50648,7 @@ function getPluginPrefs() {
49751
50648
  }
49752
50649
  function writePluginPrefs(prefs) {
49753
50650
  const file2 = getPluginPrefsPath();
49754
- mkdirSync18(path54.dirname(file2), { recursive: true });
50651
+ mkdirSync18(path55.dirname(file2), { recursive: true });
49755
50652
  writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
49756
50653
  encoding: "utf-8",
49757
50654
  mode: 384
@@ -49788,7 +50685,7 @@ __export(registry_exports, {
49788
50685
  isBinaryOnPath: () => isBinaryOnPath
49789
50686
  });
49790
50687
  import { existsSync as existsSync43 } from "node:fs";
49791
- import path55 from "node:path";
50688
+ import path56 from "node:path";
49792
50689
  function detectLocalBin(bin) {
49793
50690
  return (cwd) => {
49794
50691
  try {
@@ -49806,7 +50703,7 @@ function isBinaryOnPath(bin, opts = {}) {
49806
50703
  const platform = opts.platform ?? process.platform;
49807
50704
  const exists = opts.exists ?? existsSync43;
49808
50705
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
49809
- const pathMod = platform === "win32" ? path55.win32 : path55.posix;
50706
+ const pathMod = platform === "win32" ? path56.win32 : path56.posix;
49810
50707
  const sep2 = platform === "win32" ? ";" : ":";
49811
50708
  const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
49812
50709
  const candidates = [bin];
@@ -50753,6 +51650,7 @@ __export(policy_exports, {
50753
51650
  DEFAULT_MAX_ROUNDS: () => DEFAULT_MAX_ROUNDS,
50754
51651
  DEFAULT_WALL_MS: () => DEFAULT_WALL_MS,
50755
51652
  GAUNTLET_PARENT_BLOCKED_TOOLS: () => GAUNTLET_PARENT_BLOCKED_TOOLS,
51653
+ budgetAwareGauntletGate: () => budgetAwareGauntletGate,
50756
51654
  isGauntletFlagOn: () => isGauntletFlagOn,
50757
51655
  resolveGauntletCaps: () => resolveGauntletCaps,
50758
51656
  shouldRunGauntletHostLoop: () => shouldRunGauntletHostLoop
@@ -50791,6 +51689,12 @@ function shouldRunGauntletHostLoop(opts) {
50791
51689
  const phase2 = opts.phase ?? "build";
50792
51690
  return phase2 !== "plan";
50793
51691
  }
51692
+ function budgetAwareGauntletGate(input) {
51693
+ if (input.verdict === "PASS") return "proceed";
51694
+ if (input.toolCallsRemaining <= input.verificationReserve) return "finalize-verify";
51695
+ if (input.toolCallsRemaining <= 0) return "hold";
51696
+ return "proceed";
51697
+ }
50794
51698
  var DEFAULT_MAX_PIECES, DEFAULT_MAX_ROUNDS, DEFAULT_MAX_PARALLEL2, DEFAULT_WALL_MS, GAUNTLET_PARENT_BLOCKED_TOOLS;
50795
51699
  var init_policy = __esm({
50796
51700
  "src/cli/gauntlet/policy.ts"() {
@@ -51169,9 +52073,9 @@ function builderUserPrompt(piece, gap, briefing) {
51169
52073
  parts.push("", "Return: files touched, what changed, residual risks.");
51170
52074
  return parts.join("\n");
51171
52075
  }
51172
- function criticUserPrompt(piece, round, rand) {
52076
+ function criticUserPrompt(piece, round2, rand) {
51173
52077
  const parts = [
51174
- `Round ${round}. Inspect the current tree against this piece.`,
52078
+ `Round ${round2}. Inspect the current tree against this piece.`,
51175
52079
  "Do not edit files. Do not take the builder's word \u2014 open files / run checks.",
51176
52080
  "",
51177
52081
  `## Piece: ${piece.label}`,
@@ -51285,9 +52189,9 @@ async function runGauntletLoop(args) {
51285
52189
  let winner;
51286
52190
  let rounds = 0;
51287
52191
  const i = Math.max(0, indexOf(piece));
51288
- for (let round = 1; round <= caps.maxRounds; round++) {
52192
+ for (let round2 = 1; round2 <= caps.maxRounds; round2++) {
51289
52193
  if (stop()) break;
51290
- rounds = round;
52194
+ rounds = round2;
51291
52195
  const phase2 = gap ? "repairing" : "building";
51292
52196
  emitProgress({
51293
52197
  phase: phase2,
@@ -51295,13 +52199,13 @@ async function runGauntletLoop(args) {
51295
52199
  pieceLabel: piece.label,
51296
52200
  pieceIndex: i,
51297
52201
  pieceCount: pieces.length,
51298
- round,
52202
+ round: round2,
51299
52203
  maxRounds: caps.maxRounds
51300
52204
  });
51301
52205
  const built = await deps.runBuilder({
51302
52206
  piece,
51303
52207
  prompt: builderUserPrompt(piece, gap, deps.briefing),
51304
- round
52208
+ round: round2
51305
52209
  });
51306
52210
  if (stop()) break;
51307
52211
  emitProgress({
@@ -51310,14 +52214,14 @@ async function runGauntletLoop(args) {
51310
52214
  pieceLabel: piece.label,
51311
52215
  pieceIndex: i,
51312
52216
  pieceCount: pieces.length,
51313
- round,
52217
+ round: round2,
51314
52218
  maxRounds: caps.maxRounds
51315
52219
  });
51316
52220
  const criticized = await deps.runCritic({
51317
52221
  piece,
51318
- prompt: criticUserPrompt(piece, round),
52222
+ prompt: criticUserPrompt(piece, round2),
51319
52223
  systemPrompt: GAUNTLET_CRITIC_SYSTEM,
51320
- round
52224
+ round: round2
51321
52225
  });
51322
52226
  last = parseGauntletVerdict(criticized.result, {
51323
52227
  toolTraceCount: criticized.toolTraceCount ?? 0,
@@ -51331,7 +52235,7 @@ async function runGauntletLoop(args) {
51331
52235
  pieceLabel: piece.label,
51332
52236
  pieceIndex: i,
51333
52237
  pieceCount: pieces.length,
51334
- round,
52238
+ round: round2,
51335
52239
  maxRounds: caps.maxRounds,
51336
52240
  verdict: last.kind,
51337
52241
  gap: last.gap,
@@ -51500,10 +52404,10 @@ async function runHeadlessGauntlet(opts, provider, model) {
51500
52404
  emit,
51501
52405
  briefing: workspace.slice(0, 1200),
51502
52406
  note: (text, data) => spine.note(text, data),
51503
- runBuilder: async ({ piece, prompt, round }) => {
52407
+ runBuilder: async ({ piece, prompt, round: round2 }) => {
51504
52408
  emit({
51505
52409
  type: "log",
51506
- message: `[gauntlet] ${piece.id} round ${round}/${caps.maxRounds} builder`
52410
+ message: `[gauntlet] ${piece.id} round ${round2}/${caps.maxRounds} builder`
51507
52411
  });
51508
52412
  const tent = await runTentacle({
51509
52413
  deps,
@@ -51513,7 +52417,7 @@ async function runHeadlessGauntlet(opts, provider, model) {
51513
52417
  sessionId: sessionId2,
51514
52418
  signal: abort.signal,
51515
52419
  args: {
51516
- description: `gauntlet-builder ${piece.id} r${round}`,
52420
+ description: `gauntlet-builder ${piece.id} r${round2}`,
51517
52421
  prompt,
51518
52422
  ...piece.scope ? { scope: piece.scope } : {},
51519
52423
  ...piece.acceptance.length > 0 ? { acceptance: piece.acceptance } : {}
@@ -51526,10 +52430,10 @@ async function runHeadlessGauntlet(opts, provider, model) {
51526
52430
  toolTraceCount: tent.toolTrace?.length ?? 0
51527
52431
  };
51528
52432
  },
51529
- runCritic: async ({ piece, prompt, systemPrompt, round }) => {
52433
+ runCritic: async ({ piece, prompt, systemPrompt, round: round2 }) => {
51530
52434
  emit({
51531
52435
  type: "log",
51532
- message: `[gauntlet] ${piece.id} round ${round}/${caps.maxRounds} critic`
52436
+ message: `[gauntlet] ${piece.id} round ${round2}/${caps.maxRounds} critic`
51533
52437
  });
51534
52438
  const tent = await runTentacle({
51535
52439
  deps,
@@ -51540,7 +52444,7 @@ async function runHeadlessGauntlet(opts, provider, model) {
51540
52444
  signal: abort.signal,
51541
52445
  systemPromptOverride: systemPrompt,
51542
52446
  args: {
51543
- description: `gauntlet-critic ${piece.id} r${round}`,
52447
+ description: `gauntlet-critic ${piece.id} r${round2}`,
51544
52448
  prompt,
51545
52449
  ...piece.scope ? { scope: piece.scope } : {},
51546
52450
  ...piece.acceptance.length > 0 ? { acceptance: piece.acceptance } : {}
@@ -51611,9 +52515,9 @@ __export(triggerLock_exports, {
51611
52515
  releaseLock: () => releaseLock
51612
52516
  });
51613
52517
  import { promises as fs35 } from "node:fs";
51614
- import * as path60 from "node:path";
52518
+ import * as path61 from "node:path";
51615
52519
  function lockPath(projectRoot) {
51616
- return path60.join(projectRoot, ".zelari", "trigger.lock");
52520
+ return path61.join(projectRoot, ".zelari", "trigger.lock");
51617
52521
  }
51618
52522
  function isPidAlive(pid) {
51619
52523
  try {
@@ -51626,7 +52530,7 @@ function isPidAlive(pid) {
51626
52530
  }
51627
52531
  async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
51628
52532
  const lp = lockPath(projectRoot);
51629
- const dir = path60.dirname(lp);
52533
+ const dir = path61.dirname(lp);
51630
52534
  await fs35.mkdir(dir, { recursive: true });
51631
52535
  try {
51632
52536
  const raw = await fs35.readFile(lp, "utf8");
@@ -52220,7 +53124,7 @@ function upsertSkill(opts) {
52220
53124
  }
52221
53125
  dir = getProjectSkillsDir(root);
52222
53126
  }
52223
- const path64 = skillFilePath(dir, name);
53127
+ const path65 = skillFilePath(dir, name);
52224
53128
  const content = serializeSkillMd({
52225
53129
  name,
52226
53130
  description,
@@ -52229,13 +53133,13 @@ function upsertSkill(opts) {
52229
53133
  tools: opts.tools,
52230
53134
  cost: opts.cost
52231
53135
  });
52232
- const parsed = parseSkillMd(content, path64);
53136
+ const parsed = parseSkillMd(content, path65);
52233
53137
  if (!parsed) {
52234
53138
  return { ok: false, error: "Generated SKILL.md failed validation" };
52235
53139
  }
52236
- mkdirSync21(dirname10(path64), { recursive: true });
52237
- writeFileSync24(path64, content, "utf8");
52238
- return { ok: true, path: path64 };
53140
+ mkdirSync21(dirname10(path65), { recursive: true });
53141
+ writeFileSync24(path65, content, "utf8");
53142
+ return { ok: true, path: path65 };
52239
53143
  }
52240
53144
  function removeSkill(opts) {
52241
53145
  const name = opts.name.trim().toLowerCase();
@@ -52253,8 +53157,8 @@ function removeSkill(opts) {
52253
53157
  dir = getProjectSkillsDir(root);
52254
53158
  }
52255
53159
  const skillDir = join37(dir, name);
52256
- const path64 = skillFilePath(dir, name);
52257
- if (!existsSync48(path64) && !existsSync48(skillDir)) {
53160
+ const path65 = skillFilePath(dir, name);
53161
+ if (!existsSync48(path65) && !existsSync48(skillDir)) {
52258
53162
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
52259
53163
  }
52260
53164
  try {
@@ -52265,7 +53169,7 @@ function removeSkill(opts) {
52265
53169
  error: err instanceof Error ? err.message : String(err)
52266
53170
  };
52267
53171
  }
52268
- return { ok: true, path: path64 };
53172
+ return { ok: true, path: path65 };
52269
53173
  }
52270
53174
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
52271
53175
  var init_skillConfigIo = __esm({
@@ -52566,7 +53470,7 @@ import {
52566
53470
  } from "node:fs";
52567
53471
  import { join as join38 } from "node:path";
52568
53472
  import { homedir as homedir13 } from "node:os";
52569
- import { createHash as createHash13, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
53473
+ import { createHash as createHash14, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
52570
53474
  function getZelariHome() {
52571
53475
  return join38(homedir13(), ".zelari-code");
52572
53476
  }
@@ -52583,12 +53487,12 @@ function ensureHome() {
52583
53487
  }
52584
53488
  }
52585
53489
  function loadCompanionConfig() {
52586
- const path64 = getCompanionConfigPath();
52587
- if (!existsSync49(path64)) {
53490
+ const path65 = getCompanionConfigPath();
53491
+ if (!existsSync49(path65)) {
52588
53492
  return { projects: [] };
52589
53493
  }
52590
53494
  try {
52591
- const raw = JSON.parse(readFileSync39(path64, "utf8"));
53495
+ const raw = JSON.parse(readFileSync39(path65, "utf8"));
52592
53496
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
52593
53497
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
52594
53498
  ).map((p3) => ({
@@ -52626,24 +53530,24 @@ function loadOrCreateToken(explicit) {
52626
53530
  return { token: explicit.trim(), created: false };
52627
53531
  }
52628
53532
  ensureHome();
52629
- const path64 = getCompanionTokenPath();
52630
- if (existsSync49(path64)) {
52631
- const t = readFileSync39(path64, "utf8").trim();
53533
+ const path65 = getCompanionTokenPath();
53534
+ if (existsSync49(path65)) {
53535
+ const t = readFileSync39(path65, "utf8").trim();
52632
53536
  if (t) return { token: t, created: false };
52633
53537
  }
52634
53538
  const token = randomBytes5(24).toString("base64url");
52635
- writeFileSync25(path64, token + "\n", "utf8");
53539
+ writeFileSync25(path65, token + "\n", "utf8");
52636
53540
  try {
52637
53541
  const fs37 = __require("node:fs");
52638
- fs37.chmodSync?.(path64, 384);
53542
+ fs37.chmodSync?.(path65, 384);
52639
53543
  } catch {
52640
53544
  }
52641
53545
  return { token, created: true };
52642
53546
  }
52643
53547
  function tokenMatches(expected, provided) {
52644
53548
  if (!provided) return false;
52645
- const a = createHash13("sha256").update(expected).digest();
52646
- const b = createHash13("sha256").update(provided).digest();
53549
+ const a = createHash14("sha256").update(expected).digest();
53550
+ const b = createHash14("sha256").update(provided).digest();
52647
53551
  try {
52648
53552
  return timingSafeEqual(a, b);
52649
53553
  } catch {
@@ -52660,17 +53564,17 @@ function mergeProjects(cfg, extraPaths) {
52660
53564
  byId.set(p3.id, p3);
52661
53565
  }
52662
53566
  for (const raw of extraPaths) {
52663
- const path64 = raw.trim();
52664
- if (!path64) continue;
52665
- let id = slugFromPath(path64);
53567
+ const path65 = raw.trim();
53568
+ if (!path65) continue;
53569
+ let id = slugFromPath(path65);
52666
53570
  let n = 2;
52667
- while (byId.has(id) && byId.get(id).path !== path64) {
52668
- id = `${slugFromPath(path64)}-${n++}`;
53571
+ while (byId.has(id) && byId.get(id).path !== path65) {
53572
+ id = `${slugFromPath(path65)}-${n++}`;
52669
53573
  }
52670
53574
  byId.set(id, {
52671
53575
  id,
52672
- name: slugFromPath(path64),
52673
- path: path64
53576
+ name: slugFromPath(path65),
53577
+ path: path65
52674
53578
  });
52675
53579
  }
52676
53580
  return [...byId.values()];
@@ -53047,9 +53951,9 @@ async function runCompanionServe(opts = {}) {
53047
53951
  return;
53048
53952
  }
53049
53953
  const url2 = parseUrl(req);
53050
- const path64 = url2.pathname.replace(/\/+$/, "") || "/";
53954
+ const path65 = url2.pathname.replace(/\/+$/, "") || "/";
53051
53955
  try {
53052
- if (req.method === "GET" && (path64 === "/health" || path64 === "/v1/health")) {
53956
+ if (req.method === "GET" && (path65 === "/health" || path65 === "/v1/health")) {
53053
53957
  sendJson2(res, 200, {
53054
53958
  ok: true,
53055
53959
  service: "zelari-companion",
@@ -53061,18 +53965,18 @@ async function runCompanionServe(opts = {}) {
53061
53965
  });
53062
53966
  return;
53063
53967
  }
53064
- if (path64.startsWith("/v1")) {
53968
+ if (path65.startsWith("/v1")) {
53065
53969
  if (!tokenMatches(token, getBearer(req))) {
53066
53970
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
53067
53971
  return;
53068
53972
  }
53069
53973
  }
53070
- if (req.method === "GET" && path64 === "/v1/config") {
53974
+ if (req.method === "GET" && path65 === "/v1/config") {
53071
53975
  const snap = buildDesktopConfigSnapshot();
53072
53976
  sendJson2(res, 200, { ok: true, ...snap });
53073
53977
  return;
53074
53978
  }
53075
- if (req.method === "GET" && path64 === "/v1/projects") {
53979
+ if (req.method === "GET" && path65 === "/v1/projects") {
53076
53980
  sendJson2(res, 200, {
53077
53981
  ok: true,
53078
53982
  projects: projects.map((p3) => ({
@@ -53083,7 +53987,7 @@ async function runCompanionServe(opts = {}) {
53083
53987
  });
53084
53988
  return;
53085
53989
  }
53086
- if (req.method === "GET" && path64 === "/v1/runs") {
53990
+ if (req.method === "GET" && path65 === "/v1/runs") {
53087
53991
  sendJson2(res, 200, {
53088
53992
  ok: true,
53089
53993
  active: runs.getActive(),
@@ -53101,7 +54005,7 @@ async function runCompanionServe(opts = {}) {
53101
54005
  });
53102
54006
  return;
53103
54007
  }
53104
- if (req.method === "POST" && path64 === "/v1/runs") {
54008
+ if (req.method === "POST" && path65 === "/v1/runs") {
53105
54009
  const raw = await readBody(req);
53106
54010
  let body = {};
53107
54011
  try {
@@ -53148,7 +54052,7 @@ async function runCompanionServe(opts = {}) {
53148
54052
  });
53149
54053
  return;
53150
54054
  }
53151
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path64);
54055
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path65);
53152
54056
  if (req.method === "GET" && eventsMatch) {
53153
54057
  const runId = eventsMatch[1];
53154
54058
  const run = runs.getRun(runId);
@@ -53213,7 +54117,7 @@ async function runCompanionServe(opts = {}) {
53213
54117
  }, 500);
53214
54118
  return;
53215
54119
  }
53216
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path64);
54120
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path65);
53217
54121
  if (req.method === "POST" && cancelMatch) {
53218
54122
  const runId = cancelMatch[1];
53219
54123
  const result = runs.cancel(runId);
@@ -53366,11 +54270,11 @@ import { execSync as execSync2 } from "node:child_process";
53366
54270
  import { existsSync as existsSync51, readFileSync as readFileSync40, readlinkSync, statSync as statSync10 } from "node:fs";
53367
54271
  import { createRequire as createRequire3 } from "node:module";
53368
54272
  import { fileURLToPath as fileURLToPath2 } from "node:url";
53369
- import path62 from "node:path";
54273
+ import path63 from "node:path";
53370
54274
  function findPackageRoot(start) {
53371
54275
  let dir = start;
53372
54276
  for (let i = 0; i < 6; i += 1) {
53373
- const candidate = path62.join(dir, "package.json");
54277
+ const candidate = path63.join(dir, "package.json");
53374
54278
  if (existsSync51(candidate)) {
53375
54279
  try {
53376
54280
  const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
@@ -53378,11 +54282,11 @@ function findPackageRoot(start) {
53378
54282
  } catch {
53379
54283
  }
53380
54284
  }
53381
- const parent = path62.dirname(dir);
54285
+ const parent = path63.dirname(dir);
53382
54286
  if (parent === dir) break;
53383
54287
  dir = parent;
53384
54288
  }
53385
- return path62.resolve(__dirname3, "..", "..", "..");
54289
+ return path63.resolve(__dirname3, "..", "..", "..");
53386
54290
  }
53387
54291
  function tryExec(cmd) {
53388
54292
  try {
@@ -53396,7 +54300,7 @@ function tryExec(cmd) {
53396
54300
  }
53397
54301
  function readPackageJson3() {
53398
54302
  try {
53399
- const pkgPath = path62.join(packageRoot, "package.json");
54303
+ const pkgPath = path63.join(packageRoot, "package.json");
53400
54304
  return JSON.parse(readFileSync40(pkgPath, "utf8"));
53401
54305
  } catch {
53402
54306
  return null;
@@ -53412,7 +54316,7 @@ function checkShim(pkgName) {
53412
54316
  }
53413
54317
  const isWin = process.platform === "win32";
53414
54318
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
53415
- const shimPath = path62.join(prefix, shimName);
54319
+ const shimPath = path63.join(prefix, shimName);
53416
54320
  if (!existsSync51(shimPath)) {
53417
54321
  return FAIL(
53418
54322
  `shim not found at ${shimPath}
@@ -53440,8 +54344,8 @@ function checkShim(pkgName) {
53440
54344
  fix: npm install -g ${pkgName}@latest --force`
53441
54345
  );
53442
54346
  }
53443
- const resolved = path62.resolve(path62.dirname(shimPath), target);
53444
- const expected = path62.join(
54347
+ const resolved = path63.resolve(path63.dirname(shimPath), target);
54348
+ const expected = path63.join(
53445
54349
  prefix,
53446
54350
  "node_modules",
53447
54351
  pkgName,
@@ -53480,7 +54384,7 @@ function checkNode(pkg) {
53480
54384
  return OK(`node ${raw}`);
53481
54385
  }
53482
54386
  function checkBundle() {
53483
- const bundle = path62.join(packageRoot, "dist", "cli", "main.bundled.js");
54387
+ const bundle = path63.join(packageRoot, "dist", "cli", "main.bundled.js");
53484
54388
  if (!existsSync51(bundle)) {
53485
54389
  return FAIL(
53486
54390
  `dist/cli/main.bundled.js missing at ${bundle}
@@ -53501,7 +54405,7 @@ function checkRuntimeDeps() {
53501
54405
  const missing = [];
53502
54406
  for (const dep of required2) {
53503
54407
  try {
53504
- const localReq = createRequire3(path62.join(packageRoot, "package.json"));
54408
+ const localReq = createRequire3(path63.join(packageRoot, "package.json"));
53505
54409
  localReq.resolve(dep);
53506
54410
  } catch {
53507
54411
  missing.push(dep);
@@ -53701,7 +54605,7 @@ var init_doctor = __esm({
53701
54605
  init_metrics2();
53702
54606
  init_contextGrowthSummary();
53703
54607
  require3 = createRequire3(import.meta.url);
53704
- __dirname3 = path62.dirname(fileURLToPath2(import.meta.url));
54608
+ __dirname3 = path63.dirname(fileURLToPath2(import.meta.url));
53705
54609
  packageRoot = findPackageRoot(__dirname3);
53706
54610
  OK = (message) => ({
53707
54611
  ok: true,
@@ -53885,15 +54789,15 @@ __export(inspect_exports, {
53885
54789
  collectInspectReport: () => collectInspectReport,
53886
54790
  runInspect: () => runInspect
53887
54791
  });
53888
- import path63 from "node:path";
54792
+ import path64 from "node:path";
53889
54793
  import { existsSync as existsSync52, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
53890
54794
  import { homedir as homedir14 } from "node:os";
53891
54795
  async function collectInspectReport(cwd = process.cwd()) {
53892
54796
  ensureBuiltinSkillsLoadedSync();
53893
54797
  const snap = listSkillsSnapshot(cwd);
53894
54798
  const mcp = listMcpServers(cwd);
53895
- const userMcpPath = path63.join(homedir14(), ".zelari-code", "mcp.json");
53896
- const projectMcpPath = path63.join(cwd, ".zelari", "mcp.json");
54799
+ const userMcpPath = path64.join(homedir14(), ".zelari-code", "mcp.json");
54800
+ const projectMcpPath = path64.join(cwd, ".zelari", "mcp.json");
53897
54801
  const globalHooks = globalHooksDir();
53898
54802
  const projectHooks = projectHooksDir(cwd);
53899
54803
  const projectTrusted = isFolderTrusted(cwd);
@@ -53921,9 +54825,9 @@ async function collectInspectReport(cwd = process.cwd()) {
53921
54825
  configSources: [
53922
54826
  { path: userMcpPath, exists: existsSync52(userMcpPath) },
53923
54827
  { path: projectMcpPath, exists: existsSync52(projectMcpPath) },
53924
- { path: path63.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync52(path63.join(homedir14(), ".zelari-code", "provider.json")) },
53925
- { path: path63.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync52(path63.join(cwd, ".zelari", "AGENTS.md")) },
53926
- { path: path63.join(cwd, "AGENTS.md"), exists: existsSync52(path63.join(cwd, "AGENTS.md")) }
54828
+ { path: path64.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync52(path64.join(homedir14(), ".zelari-code", "provider.json")) },
54829
+ { path: path64.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync52(path64.join(cwd, ".zelari", "AGENTS.md")) },
54830
+ { path: path64.join(cwd, "AGENTS.md"), exists: existsSync52(path64.join(cwd, "AGENTS.md")) }
53927
54831
  ],
53928
54832
  skills: {
53929
54833
  total: snap.skills.length,
@@ -53963,8 +54867,8 @@ function listJsonFiles(dir) {
53963
54867
  }
53964
54868
  function findAgentsMd(cwd) {
53965
54869
  const candidates = [
53966
- path63.join(cwd, "AGENTS.md"),
53967
- path63.join(cwd, ".zelari", "AGENTS.md")
54870
+ path64.join(cwd, "AGENTS.md"),
54871
+ path64.join(cwd, ".zelari", "AGENTS.md")
53968
54872
  ];
53969
54873
  const found = [];
53970
54874
  for (const c of candidates) {
@@ -57239,7 +58143,7 @@ init_completionGate();
57239
58143
  // src/cli/kraken/verificationBridge.ts
57240
58144
  init_candidateRegistry();
57241
58145
  init_completionGate();
57242
- import { createHash as createHash10 } from "node:crypto";
58146
+ import { createHash as createHash11 } from "node:crypto";
57243
58147
 
57244
58148
  // src/cli/kraken/nativeVerification.ts
57245
58149
  init_runtime2();
@@ -57366,7 +58270,7 @@ function krakenResultsToContract(requiredChecks, results, now = Date.now()) {
57366
58270
  return { criteria, results: verifications };
57367
58271
  }
57368
58272
  function sha256Hex2(input) {
57369
- return createHash10("sha256").update(input).digest("hex");
58273
+ return createHash11("sha256").update(input).digest("hex");
57370
58274
  }
57371
58275
  function matchNoteToToolTrace(note, trace) {
57372
58276
  const n = normalize5(note);
@@ -58013,10 +58917,14 @@ function compactEventPayload(budget, stateSnapshot, telemetry) {
58013
58917
 
58014
58918
  // src/cli/budget/modelContextBuilder.ts
58015
58919
  init_headlessSpine();
58920
+ init_session();
58016
58921
  function messageWasRecompacted(message, history2) {
58017
58922
  if (message.compactedFromSeq === void 0) return false;
58018
58923
  return !history2.some((candidate) => candidate.seq !== void 0 && candidate.seq === message.seq);
58019
58924
  }
58925
+ function resourceStatusMessage(payload) {
58926
+ return { role: "system", content: formatResourceSnapshot(payload) };
58927
+ }
58020
58928
  async function sessionHistory(session) {
58021
58929
  if (!session || session.status !== "active") return null;
58022
58930
  const derived = await session.derivedPriorTurns();
@@ -58083,6 +58991,9 @@ async function buildModelContext(input) {
58083
58991
  };
58084
58992
  input.onCompactionMetric?.(compactionMetrics);
58085
58993
  }
58994
+ if (input.resourceSnapshot) {
58995
+ history2 = [...history2, resourceStatusMessage(input.resourceSnapshot)];
58996
+ }
58086
58997
  if (requestSurface) {
58087
58998
  const measured = measureRequest({
58088
58999
  systemMessages: requestSurface.systemMessages,
@@ -58287,6 +59198,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
58287
59198
  const modelContext = await buildModelContext({
58288
59199
  fallbackHistory: historyForModel,
58289
59200
  session: writerRef.current?.spine ?? null,
59201
+ resourceSnapshot: writerRef.current?.spine?.latestResourceSnapshot() ?? null,
58290
59202
  phase: workPhase,
58291
59203
  model: getActiveModel(),
58292
59204
  provider: envConfig?.providerId ?? (localCli || "local"),
@@ -58512,6 +59424,9 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
58512
59424
  })),
58513
59425
  toolRegistry,
58514
59426
  providerStream,
59427
+ // 2.6 Phase 3: host-owned pre-dispatch resource gate via the spine
59428
+ // mirror (doc section 11.3). Degrade-and-stop (null gate = allow).
59429
+ toolCallGate: (name) => writerRef.current?.spine?.gateResourceToolCall(name) ?? { allowed: true },
58515
59430
  cwd,
58516
59431
  maxToolCallsPerTurn,
58517
59432
  maxToolLoopIterations,
@@ -60583,7 +61498,7 @@ init_sessionManager();
60583
61498
  // src/cli/gitOps.ts
60584
61499
  import { execFile as execFile4 } from "node:child_process";
60585
61500
  import { promisify as promisify3 } from "node:util";
60586
- import path48 from "node:path";
61501
+ import path49 from "node:path";
60587
61502
  var execFileAsync3 = promisify3(execFile4);
60588
61503
  async function git4(cwd, args) {
60589
61504
  try {
@@ -60629,7 +61544,7 @@ async function undoWorkingChanges(opts = {}) {
60629
61544
  };
60630
61545
  }
60631
61546
  function defaultProjectRoot() {
60632
- return path48.resolve(__dirname, "..", "..", "..");
61547
+ return path49.resolve(__dirname, "..", "..", "..");
60633
61548
  }
60634
61549
 
60635
61550
  // src/cli/slashHandlers/git.ts
@@ -61081,7 +61996,7 @@ import { promises as fs29 } from "node:fs";
61081
61996
  init_zod();
61082
61997
  init_taskTool();
61083
61998
  import { promises as fs28 } from "node:fs";
61084
- import path52 from "node:path";
61999
+ import path53 from "node:path";
61085
62000
  import { randomBytes as randomBytes4 } from "node:crypto";
61086
62001
  var CsvFanoutArgsSchema = external_exports.object({
61087
62002
  csv_path: external_exports.string().min(1),
@@ -61175,8 +62090,8 @@ function resolveMaxConcurrency(env = process.env) {
61175
62090
  }
61176
62091
  async function runCsvFanout(args, deps, opts) {
61177
62092
  const start = Date.now();
61178
- const absCsv = path52.isAbsolute(args.csv_path) ? args.csv_path : path52.join(opts.parentCwd, args.csv_path);
61179
- const absOut = path52.isAbsolute(args.output_csv_path) ? args.output_csv_path : path52.join(opts.parentCwd, args.output_csv_path);
62093
+ const absCsv = path53.isAbsolute(args.csv_path) ? args.csv_path : path53.join(opts.parentCwd, args.csv_path);
62094
+ const absOut = path53.isAbsolute(args.output_csv_path) ? args.output_csv_path : path53.join(opts.parentCwd, args.output_csv_path);
61180
62095
  const { headers: headers2, rows } = await readCsv(absCsv);
61181
62096
  if (headers2.length === 0) {
61182
62097
  throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
@@ -61232,7 +62147,7 @@ async function runCsvFanout(args, deps, opts) {
61232
62147
  errored += 1;
61233
62148
  errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
61234
62149
  }
61235
- await fs28.mkdir(path52.dirname(absOut), { recursive: true });
62150
+ await fs28.mkdir(path53.dirname(absOut), { recursive: true });
61236
62151
  await queueWrite(serializeCsv(outHeaders, outputRecords));
61237
62152
  }
61238
62153
  }
@@ -61427,7 +62342,7 @@ function splitArgs(s) {
61427
62342
  // src/cli/slashHandlers/krakenWorkbench.ts
61428
62343
  init_messageHelpers();
61429
62344
  import { promises as fs30 } from "node:fs";
61430
- import path53 from "node:path";
62345
+ import path54 from "node:path";
61431
62346
 
61432
62347
  // src/cli/kraken/workbenchView.ts
61433
62348
  var EMPTY = {
@@ -61544,14 +62459,14 @@ function formatWorkbenchForTerminal(p3) {
61544
62459
 
61545
62460
  // src/cli/slashHandlers/krakenWorkbench.ts
61546
62461
  async function handleKrakenWorkbench(ctx) {
61547
- const dir = path53.join(ctx.cwd, ".zelari", "radio");
62462
+ const dir = path54.join(ctx.cwd, ".zelari", "radio");
61548
62463
  let latest = null;
61549
62464
  let latestMtime = 0;
61550
62465
  try {
61551
62466
  const files = await fs30.readdir(dir);
61552
62467
  for (const f of files) {
61553
62468
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
61554
- const full = path53.join(dir, f);
62469
+ const full = path54.join(dir, f);
61555
62470
  const stat = await fs30.stat(full);
61556
62471
  if (stat.mtimeMs > latestMtime) {
61557
62472
  latestMtime = stat.mtimeMs;
@@ -61568,10 +62483,10 @@ async function handleKrakenWorkbench(ctx) {
61568
62483
  const parsed = parseWorkbench(content);
61569
62484
  const rendered = formatWorkbenchForTerminal(parsed);
61570
62485
  if (!rendered.trim()) {
61571
- appendSystem(ctx.setMessages, `[kraken workbench] ${path53.basename(latest)}: (no nodes / no events yet)`);
62486
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path54.basename(latest)}: (no nodes / no events yet)`);
61572
62487
  return;
61573
62488
  }
61574
- appendSystem(ctx.setMessages, `[kraken workbench] ${path53.basename(latest)}:
62489
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path54.basename(latest)}:
61575
62490
  ${rendered}`);
61576
62491
  }
61577
62492
 
@@ -61878,15 +62793,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
61878
62793
  // src/cli/slashHandlers/promoteMember.ts
61879
62794
  init_messageHelpers();
61880
62795
  import { promises as fs31 } from "node:fs";
61881
- import path56 from "node:path";
62796
+ import path57 from "node:path";
61882
62797
  import os12 from "node:os";
61883
62798
  async function handlePromoteMember(ctx, memberId) {
61884
62799
  try {
61885
62800
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
61886
62801
  const { skill, markdown } = promoteMember2(memberId);
61887
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path56.join(os12.homedir(), ".tmp", "zelari-code", "skills");
62802
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path57.join(os12.homedir(), ".tmp", "zelari-code", "skills");
61888
62803
  await fs31.mkdir(skillDir, { recursive: true });
61889
- const filePath = path56.join(skillDir, `${skill.id}.md`);
62804
+ const filePath = path57.join(skillDir, `${skill.id}.md`);
61890
62805
  await fs31.writeFile(filePath, markdown, "utf8");
61891
62806
  appendSystem(
61892
62807
  ctx.setMessages,
@@ -61904,24 +62819,24 @@ async function handlePromoteMember(ctx, memberId) {
61904
62819
 
61905
62820
  // src/cli/branchManager.ts
61906
62821
  import { promises as fs32, existsSync as existsSync44, readFileSync as readFileSync36, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
61907
- import path57 from "node:path";
62822
+ import path58 from "node:path";
61908
62823
  import os13 from "node:os";
61909
62824
  var META_FILENAME = "meta.json";
61910
62825
  var SESSIONS_SUBDIR = "sessions";
61911
62826
  function getBranchesBaseDir() {
61912
- return process.env.ANATHEMA_BRANCHES_DIR ?? path57.join(os13.homedir(), ".tmp", "zelari-code", "branches");
62827
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path58.join(os13.homedir(), ".tmp", "zelari-code", "branches");
61913
62828
  }
61914
62829
  function getSessionsBaseDir() {
61915
- return process.env.ANATHEMA_SESSIONS_DIR ?? path57.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
62830
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path58.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
61916
62831
  }
61917
62832
  function branchPathFor(name, baseDir) {
61918
- return path57.join(baseDir, name);
62833
+ return path58.join(baseDir, name);
61919
62834
  }
61920
62835
  function metaPathFor(name, baseDir) {
61921
- return path57.join(baseDir, name, META_FILENAME);
62836
+ return path58.join(baseDir, name, META_FILENAME);
61922
62837
  }
61923
62838
  function sessionsPathFor(name, baseDir) {
61924
- return path57.join(baseDir, name, SESSIONS_SUBDIR);
62839
+ return path58.join(baseDir, name, SESSIONS_SUBDIR);
61925
62840
  }
61926
62841
  function readBranchMeta(name, baseDir) {
61927
62842
  const metaPath = metaPathFor(name, baseDir);
@@ -61946,7 +62861,7 @@ function readBranchMeta(name, baseDir) {
61946
62861
  }
61947
62862
  function writeBranchMeta(name, baseDir, meta3) {
61948
62863
  const metaPath = metaPathFor(name, baseDir);
61949
- mkdirSync19(path57.dirname(metaPath), { recursive: true });
62864
+ mkdirSync19(path58.dirname(metaPath), { recursive: true });
61950
62865
  writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
61951
62866
  }
61952
62867
  async function countSessions(name, baseDir) {
@@ -61997,14 +62912,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
61997
62912
  if (branchExists(name, baseDir)) {
61998
62913
  throw new BranchAlreadyExistsError(name);
61999
62914
  }
62000
- const sourcePath = path57.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
62915
+ const sourcePath = path58.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
62001
62916
  if (!existsSync44(sourcePath)) {
62002
62917
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
62003
62918
  }
62004
62919
  const branchPath = branchPathFor(name, baseDir);
62005
62920
  const branchSessionsPath = sessionsPathFor(name, baseDir);
62006
62921
  mkdirSync19(branchSessionsPath, { recursive: true });
62007
- const destPath = path57.join(branchSessionsPath, `${fromSessionId}.jsonl`);
62922
+ const destPath = path58.join(branchSessionsPath, `${fromSessionId}.jsonl`);
62008
62923
  await fs32.copyFile(sourcePath, destPath);
62009
62924
  const meta3 = {
62010
62925
  name,
@@ -62108,14 +63023,14 @@ async function handleBranchCheckout(ctx, branchName) {
62108
63023
  // src/cli/slashHandlers/workspace.ts
62109
63024
  init_messageHelpers();
62110
63025
  import { promises as fs33 } from "node:fs";
62111
- import path58 from "node:path";
63026
+ import path59 from "node:path";
62112
63027
  async function handleWorkspaceShow(ctx, what) {
62113
63028
  try {
62114
- const zelari = path58.join(process.cwd(), ".zelari");
63029
+ const zelari = path59.join(process.cwd(), ".zelari");
62115
63030
  let content;
62116
63031
  switch (what) {
62117
63032
  case "plan": {
62118
- const planPath = path58.join(zelari, "plan.md");
63033
+ const planPath = path59.join(zelari, "plan.md");
62119
63034
  try {
62120
63035
  content = await fs33.readFile(planPath, "utf-8");
62121
63036
  } catch {
@@ -62124,7 +63039,7 @@ async function handleWorkspaceShow(ctx, what) {
62124
63039
  break;
62125
63040
  }
62126
63041
  case "decisions": {
62127
- const decisionsDir = path58.join(zelari, "decisions");
63042
+ const decisionsDir = path59.join(zelari, "decisions");
62128
63043
  try {
62129
63044
  const files = (await fs33.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
62130
63045
  if (files.length === 0) {
@@ -62134,7 +63049,7 @@ async function handleWorkspaceShow(ctx, what) {
62134
63049
  `];
62135
63050
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
62136
63051
  for (const f of files) {
62137
- const raw = await fs33.readFile(path58.join(decisionsDir, f), "utf-8");
63052
+ const raw = await fs33.readFile(path59.join(decisionsDir, f), "utf-8");
62138
63053
  const { meta: meta3, body } = parseFrontmatter2(raw);
62139
63054
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
62140
63055
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -62147,7 +63062,7 @@ async function handleWorkspaceShow(ctx, what) {
62147
63062
  break;
62148
63063
  }
62149
63064
  case "risks": {
62150
- const risksPath = path58.join(zelari, "risks.md");
63065
+ const risksPath = path59.join(zelari, "risks.md");
62151
63066
  try {
62152
63067
  content = await fs33.readFile(risksPath, "utf-8");
62153
63068
  } catch {
@@ -62156,7 +63071,7 @@ async function handleWorkspaceShow(ctx, what) {
62156
63071
  break;
62157
63072
  }
62158
63073
  case "agents": {
62159
- const agentsPath = path58.join(process.cwd(), "AGENTS.MD");
63074
+ const agentsPath = path59.join(process.cwd(), "AGENTS.MD");
62160
63075
  try {
62161
63076
  content = await fs33.readFile(agentsPath, "utf-8");
62162
63077
  } catch {
@@ -62165,7 +63080,7 @@ async function handleWorkspaceShow(ctx, what) {
62165
63080
  break;
62166
63081
  }
62167
63082
  case "docs": {
62168
- const docsDir = path58.join(zelari, "docs");
63083
+ const docsDir = path59.join(zelari, "docs");
62169
63084
  try {
62170
63085
  const files = (await fs33.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
62171
63086
  content = files.length ? `# Docs (${files.length})
@@ -62207,7 +63122,7 @@ async function handleWorkspaceReset(ctx, force) {
62207
63122
  return;
62208
63123
  }
62209
63124
  try {
62210
- const target = path58.join(process.cwd(), ".zelari");
63125
+ const target = path59.join(process.cwd(), ".zelari");
62211
63126
  await fs33.rm(target, { recursive: true, force: true });
62212
63127
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
62213
63128
  } catch (err) {
@@ -62219,7 +63134,7 @@ async function handleWorkspaceReset(ctx, force) {
62219
63134
  init_provider2();
62220
63135
 
62221
63136
  // src/cli/slashHandlers/skills.ts
62222
- import path59 from "node:path";
63137
+ import path60 from "node:path";
62223
63138
  import os14 from "node:os";
62224
63139
 
62225
63140
  // src/cli/skillHistory.ts
@@ -62348,7 +63263,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
62348
63263
  });
62349
63264
  }
62350
63265
  async function handleSkillStats(ctx, skillId) {
62351
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path59.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
63266
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path60.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
62352
63267
  try {
62353
63268
  const records = await readSkillHistory(historyFile);
62354
63269
  const stats = getSkillStats(records, skillId);
@@ -62364,7 +63279,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
62364
63279
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
62365
63280
  return;
62366
63281
  }
62367
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path59.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
63282
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path60.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
62368
63283
  try {
62369
63284
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
62370
63285
  appendSystem(ctx.setMessages, formatted);
@@ -63908,7 +64823,7 @@ function createStreamScrubber2() {
63908
64823
  init_taskTool();
63909
64824
  init_sessionTodos();
63910
64825
  import { promises as fs36 } from "node:fs";
63911
- import path61 from "node:path";
64826
+ import path62 from "node:path";
63912
64827
  import { randomUUID as randomUUID6 } from "node:crypto";
63913
64828
 
63914
64829
  // src/cli/kraken/verifierLifecycle.ts
@@ -64165,7 +65080,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
64165
65080
  try {
64166
65081
  let preflightGraph;
64167
65082
  if (opts.runPlan && opts.runPlan.trim() !== "") {
64168
- const planPath = path61.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
65083
+ const planPath = path62.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
64169
65084
  log(`loading pre-flight plan: ${planPath}`);
64170
65085
  let raw;
64171
65086
  try {
@@ -64205,8 +65120,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
64205
65120
  log(formatKrakenGraphAscii2(graph));
64206
65121
  if (opts.planOnly) {
64207
65122
  const planId = randomUUID6();
64208
- const planDir = path61.join(cwd, ".zelari", "radio");
64209
- const planPath = path61.join(planDir, `plan-${planId}.json`);
65123
+ const planDir = path62.join(cwd, ".zelari", "radio");
65124
+ const planPath = path62.join(planDir, `plan-${planId}.json`);
64210
65125
  await fs36.mkdir(planDir, { recursive: true });
64211
65126
  await fs36.writeFile(
64212
65127
  planPath,
@@ -64485,6 +65400,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
64485
65400
  const modelContext = await buildModelContext({
64486
65401
  fallbackHistory: seededHistory.history,
64487
65402
  session: spine.spine,
65403
+ resourceSnapshot: spine.spine.latestResourceSnapshot(),
64488
65404
  phase: opts.phase ?? "build",
64489
65405
  model,
64490
65406
  provider,
@@ -64524,6 +65440,10 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
64524
65440
  tools,
64525
65441
  toolRegistry,
64526
65442
  providerStream,
65443
+ // 2.6 Phase 3: host-owned pre-dispatch resource gate (doc section 11.3).
65444
+ // Advisory by default; ZELARI_RESOURCE_ENFORCEMENT=protected enables the
65445
+ // protected verification reserve. Degrade-and-stop (null gate = allow).
65446
+ toolCallGate: (name) => spine.gateResourceToolCall(name) ?? { allowed: true },
64527
65447
  maxToolLoopIterations: maxToolLoop
64528
65448
  });
64529
65449
  let finalReason = "completed";
@@ -64765,7 +65685,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
64765
65685
  if (json2) {
64766
65686
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
64767
65687
  else {
64768
- await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
65688
+ await fs36.mkdir(path62.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
64769
65689
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
64770
65690
  }
64771
65691
  }
@@ -64949,7 +65869,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
64949
65869
  if (json2) {
64950
65870
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
64951
65871
  else {
64952
- await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
65872
+ await fs36.mkdir(path62.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
64953
65873
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
64954
65874
  }
64955
65875
  }
@@ -65313,7 +66233,7 @@ ${ragContext}` : slicePrompt;
65313
66233
  if (json2) {
65314
66234
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
65315
66235
  else {
65316
- await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
66236
+ await fs36.mkdir(path62.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
65317
66237
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
65318
66238
  }
65319
66239
  }
@@ -65515,8 +66435,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
65515
66435
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
65516
66436
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
65517
66437
  try {
65518
- const path64 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
65519
- name = path64 && /^[a-z0-9]/.test(path64) ? path64 : "imported-skill";
66438
+ const path65 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
66439
+ name = path65 && /^[a-z0-9]/.test(path65) ? path65 : "imported-skill";
65520
66440
  } catch {
65521
66441
  name = "imported-skill";
65522
66442
  }