zelari-code 2.9.2 → 2.11.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.
@@ -276,8 +276,8 @@ function formatDuration(ms) {
276
276
  const rs = s % 60;
277
277
  if (m < 60) return rs === 0 ? `${m}m${sign}` : `${m}m ${rs}s${sign}`;
278
278
  const h = Math.floor(m / 60);
279
- const rm = m % 60;
280
- if (h < 24) return rm === 0 ? `${h}h${sign}` : `${h}h ${rm}m${sign}`;
279
+ const rm2 = m % 60;
280
+ if (h < 24) return rm2 === 0 ? `${h}h${sign}` : `${h}h ${rm2}m${sign}`;
281
281
  const d = Math.floor(h / 24);
282
282
  const rh = h % 24;
283
283
  return rh === 0 ? `${d}d${sign}` : `${d}d ${rh}h${sign}`;
@@ -1627,12 +1627,12 @@ var init_keyStore = __esm({
1627
1627
 
1628
1628
  // src/cli/thinkingCapability.ts
1629
1629
  function thinkingCapabilityFor(id3, model) {
1630
- const base = PROVIDER_THINKING_CAPABILITY[id3] ?? {};
1630
+ const base2 = PROVIDER_THINKING_CAPABILITY[id3] ?? {};
1631
1631
  const efforts = effortLevelsFor(id3, model);
1632
1632
  const budget = supportsBudget(id3, model);
1633
1633
  return {
1634
- ...base,
1635
- effort: efforts.length > 0 || Boolean(base.effort),
1634
+ ...base2,
1635
+ effort: efforts.length > 0 || Boolean(base2.effort),
1636
1636
  budget,
1637
1637
  efforts: efforts.length > 0 ? efforts : void 0
1638
1638
  };
@@ -18536,8 +18536,8 @@ var init_shellResolver = __esm({
18536
18536
  // packages/core/dist/core/tools/builtin/shell.js
18537
18537
  import { spawn } from "node:child_process";
18538
18538
  import { dirname } from "node:path";
18539
- function withNodeDirOnPath(base) {
18540
- const env = { ...base };
18539
+ function withNodeDirOnPath(base2) {
18540
+ const env = { ...base2 };
18541
18541
  try {
18542
18542
  const nodeDir = dirname(process.execPath);
18543
18543
  if (!nodeDir)
@@ -18840,8 +18840,8 @@ async function searchFile(absPath, relPath, regex, contextLines, remainingSlots)
18840
18840
  }
18841
18841
  async function isDirectory(p3) {
18842
18842
  try {
18843
- const stat = await fs6.stat(p3);
18844
- return stat.isDirectory();
18843
+ const stat2 = await fs6.stat(p3);
18844
+ return stat2.isDirectory();
18845
18845
  } catch {
18846
18846
  return false;
18847
18847
  }
@@ -21207,8 +21207,8 @@ function formatTodoStatusSummary(list = todos) {
21207
21207
  (t) => t.status === "completed" || t.status === "cancelled"
21208
21208
  ).length;
21209
21209
  const active = list.filter((t) => t.status === "in_progress").length;
21210
- const base = `todos ${done}/${list.length}`;
21211
- return active > 0 ? `${base} \xB7 ${active} active` : base;
21210
+ const base2 = `todos ${done}/${list.length}`;
21211
+ return active > 0 ? `${base2} \xB7 ${active} active` : base2;
21212
21212
  }
21213
21213
  var todos;
21214
21214
  var init_sessionTodos = __esm({
@@ -21622,11 +21622,11 @@ function createRoutedRequestSnapshot(params) {
21622
21622
  };
21623
21623
  }
21624
21624
  function compareReplayPrefix(snapshot, messages) {
21625
- const base = snapshot.conversation;
21626
- const n = Math.min(base.length, messages.length);
21625
+ const base2 = snapshot.conversation;
21626
+ const n = Math.min(base2.length, messages.length);
21627
21627
  let matching = 0;
21628
21628
  for (let i = 0; i < n; i++) {
21629
- if (stableStringify(base[i]) !== stableStringify(messages[i])) {
21629
+ if (stableStringify(base2[i]) !== stableStringify(messages[i])) {
21630
21630
  return { exact: false, matchingMessages: matching, mismatchIndex: i };
21631
21631
  }
21632
21632
  matching++;
@@ -21835,6 +21835,1156 @@ var init_textLoopDetect = __esm({
21835
21835
  }
21836
21836
  });
21837
21837
 
21838
+ // packages/core/dist/runtime/observers/types.js
21839
+ var CONTINUE;
21840
+ var init_types2 = __esm({
21841
+ "packages/core/dist/runtime/observers/types.js"() {
21842
+ "use strict";
21843
+ CONTINUE = { action: "continue" };
21844
+ }
21845
+ });
21846
+
21847
+ // packages/core/dist/runtime/observers/resolve.js
21848
+ function resolveInterventions(results) {
21849
+ if (results.length === 0)
21850
+ return CONTINUE;
21851
+ let best = results[0];
21852
+ let bestRank = ACTION_RANK[best.action];
21853
+ for (let i = 1; i < results.length; i += 1) {
21854
+ const rank = ACTION_RANK[results[i].action];
21855
+ if (rank > bestRank) {
21856
+ best = results[i];
21857
+ bestRank = rank;
21858
+ }
21859
+ }
21860
+ return best;
21861
+ }
21862
+ var ACTION_RANK;
21863
+ var init_resolve = __esm({
21864
+ "packages/core/dist/runtime/observers/resolve.js"() {
21865
+ "use strict";
21866
+ init_types2();
21867
+ ACTION_RANK = {
21868
+ deny_tool: 6,
21869
+ stop: 5,
21870
+ retry: 4,
21871
+ replace: 3,
21872
+ inject: 2,
21873
+ continue: 1
21874
+ };
21875
+ }
21876
+ });
21877
+
21878
+ // packages/core/dist/runtime/guards/RepetitionGuard.js
21879
+ import { createHash as createHash3 } from "node:crypto";
21880
+ function toolCallFingerprint(tool, args) {
21881
+ const argsHash = createHash3("sha256").update(stableStringify(args ?? null)).digest("hex");
21882
+ return { tool, argsHash };
21883
+ }
21884
+ var REASSESS_MESSAGE, RepetitionGuard;
21885
+ var init_RepetitionGuard = __esm({
21886
+ "packages/core/dist/runtime/guards/RepetitionGuard.js"() {
21887
+ "use strict";
21888
+ init_requestSnapshot();
21889
+ init_types2();
21890
+ REASSESS_MESSAGE = [
21891
+ "The same tool call has produced no new progress multiple times.",
21892
+ "Reassess the current hypothesis before repeating it again."
21893
+ ].join("\n");
21894
+ RepetitionGuard = class {
21895
+ counts = /* @__PURE__ */ new Map();
21896
+ warnAfter;
21897
+ stopAfter;
21898
+ constructor(config2 = {}) {
21899
+ this.warnAfter = config2.warnAfter ?? 2;
21900
+ this.stopAfter = config2.stopAfter ?? 5;
21901
+ }
21902
+ async onToolCall(event) {
21903
+ const fingerprint = toolCallFingerprint(event.toolName, event.args);
21904
+ const key = `${fingerprint.tool}\0${fingerprint.argsHash}`;
21905
+ const count = (this.counts.get(key) ?? 0) + 1;
21906
+ this.counts.set(key, count);
21907
+ if (count >= this.stopAfter) {
21908
+ return {
21909
+ action: "stop",
21910
+ reason: `repeated tool call "${fingerprint.tool}" ${count} times without new progress`,
21911
+ code: "repeated_tool"
21912
+ };
21913
+ }
21914
+ if (count >= this.warnAfter) {
21915
+ return {
21916
+ action: "inject",
21917
+ message: { role: "user", kind: "runtime-warning", content: REASSESS_MESSAGE }
21918
+ };
21919
+ }
21920
+ return CONTINUE;
21921
+ }
21922
+ reset() {
21923
+ this.counts.clear();
21924
+ }
21925
+ };
21926
+ }
21927
+ });
21928
+
21929
+ // packages/core/dist/runtime/guards/FailureSignatureGuard.js
21930
+ import { createHash as createHash4 } from "node:crypto";
21931
+ function stripAnsi(text) {
21932
+ return text.replace(/\u001b\[[0-9;]*[A-Za-z]/g, "");
21933
+ }
21934
+ function normalizeFailureTail(text, tailChars = 2e3) {
21935
+ const tail2 = stripAnsi(text).slice(-tailChars);
21936
+ const lines = tail2.split(/\r?\n/).map((line) => line.replace(/\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?/g, "<ts>").replace(/\b\d{1,2}:\d{2}:\d{2}(\.\d+)?\b/g, "<time>").replace(/\b\d+(\.\d+)?\s*(ms|s|sec|secs|seconds|min|mins|minutes)\b/gi, "<dur>").replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, "<uuid>").replace(/\b(?:pid|process)\s*[:=]?\s*\d+\b/gi, "<pid>").replace(/(?:\/tmp\/|\/var\/tmp\/|\b[A-Za-z]:\\Users\\[^\\]*\\AppData\\Local\\Temp\\)[^\s"']*/gi, "<tmp>"));
21937
+ const meaningful = lines.filter((line) => {
21938
+ const trimmed = line.trim();
21939
+ if (!trimmed)
21940
+ return false;
21941
+ if (/^\s*[✓✔●○◐◑◌]/.test(line))
21942
+ return false;
21943
+ if (/^\s*[|/\\-–—]\s*$/.test(line))
21944
+ return false;
21945
+ if (/^\s*\d+\s*\/\s*\d+\s*$/.test(line))
21946
+ return false;
21947
+ return true;
21948
+ });
21949
+ return meaningful.join("\n");
21950
+ }
21951
+ function extractFailure(result) {
21952
+ if (typeof result === "string")
21953
+ return { text: result };
21954
+ if (result && typeof result === "object") {
21955
+ const r = result;
21956
+ const parts = [r.stdout, r.stderr, r.output, r.content, r.message].filter((p3) => typeof p3 === "string");
21957
+ if (typeof r.exitCode === "number" || parts.length > 0) {
21958
+ return { exitCode: r.exitCode, text: parts.join("\n") };
21959
+ }
21960
+ return { text: stableStringify(result).slice(0, 4e3) };
21961
+ }
21962
+ return void 0;
21963
+ }
21964
+ var REASSESS_MESSAGE2, MAX_PENDING, FailureSignatureGuard;
21965
+ var init_FailureSignatureGuard = __esm({
21966
+ "packages/core/dist/runtime/guards/FailureSignatureGuard.js"() {
21967
+ "use strict";
21968
+ init_requestSnapshot();
21969
+ init_types2();
21970
+ init_RepetitionGuard();
21971
+ REASSESS_MESSAGE2 = [
21972
+ "The same failure signature has persisted across multiple attempts.",
21973
+ "Do not repeat the previous edit strategy. Re-evaluate the root cause,",
21974
+ "inspect upstream state, or delegate a fresh verification/exploration task."
21975
+ ].join("\n");
21976
+ MAX_PENDING = 256;
21977
+ FailureSignatureGuard = class {
21978
+ counts = /* @__PURE__ */ new Map();
21979
+ pendingArgs = /* @__PURE__ */ new Map();
21980
+ warnAfter;
21981
+ stopAfter;
21982
+ tailChars;
21983
+ constructor(config2 = {}) {
21984
+ this.warnAfter = config2.warnAfter ?? 2;
21985
+ this.stopAfter = config2.stopAfter ?? 5;
21986
+ this.tailChars = config2.tailChars ?? 2e3;
21987
+ }
21988
+ /** Track per-call command hashes so onToolResult can bind args → result. */
21989
+ async onToolCall(event) {
21990
+ const { argsHash } = toolCallFingerprint(event.toolName, event.args);
21991
+ if (this.pendingArgs.size >= MAX_PENDING) {
21992
+ const oldest = this.pendingArgs.keys().next().value;
21993
+ if (oldest !== void 0)
21994
+ this.pendingArgs.delete(oldest);
21995
+ }
21996
+ this.pendingArgs.set(event.toolCallId, `${event.toolName}\0${argsHash}`);
21997
+ return CONTINUE;
21998
+ }
21999
+ async onToolResult(event) {
22000
+ const commandHash = this.pendingArgs.get(event.toolCallId) ?? event.toolName;
22001
+ this.pendingArgs.delete(event.toolCallId);
22002
+ const failure = extractFailure(event.result);
22003
+ if (!failure || !failure.text.trim())
22004
+ return CONTINUE;
22005
+ const failed = failure.exitCode !== void 0 ? failure.exitCode !== 0 : event.ok === false;
22006
+ if (!failed) {
22007
+ for (const key2 of this.counts.keys()) {
22008
+ if (key2.startsWith(`${commandHash}\0`))
22009
+ this.counts.delete(key2);
22010
+ }
22011
+ return CONTINUE;
22012
+ }
22013
+ const tailHash = createHash4("sha256").update(normalizeFailureTail(failure.text, this.tailChars)).digest("hex");
22014
+ const key = `${commandHash}\0${failure.exitCode ?? "x"}\0${tailHash}`;
22015
+ const count = (this.counts.get(key) ?? 0) + 1;
22016
+ this.counts.set(key, count);
22017
+ if (count >= this.stopAfter) {
22018
+ return {
22019
+ action: "stop",
22020
+ reason: `same failure signature (exit ${failure.exitCode ?? "n/a"}) recurred ${count} times after edits`,
22021
+ code: "repeated_failure"
22022
+ };
22023
+ }
22024
+ if (count >= this.warnAfter) {
22025
+ return {
22026
+ action: "inject",
22027
+ message: { role: "user", kind: "runtime-warning", content: REASSESS_MESSAGE2 }
22028
+ };
22029
+ }
22030
+ return CONTINUE;
22031
+ }
22032
+ reset() {
22033
+ this.counts.clear();
22034
+ this.pendingArgs.clear();
22035
+ }
22036
+ };
22037
+ }
22038
+ });
22039
+
22040
+ // packages/core/dist/runtime/guards/DuplicateSearchGuard.js
22041
+ function isSearchTool(tool) {
22042
+ return SEARCH_TOOLS.has(tool);
22043
+ }
22044
+ function normalizeQuery(text) {
22045
+ return text.toLowerCase().replace(/[.,;:!?()"'`—–…]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 512);
22046
+ }
22047
+ function queryJaccard(a, b) {
22048
+ const ta = new Set(a.split(" ").filter(Boolean));
22049
+ const tb = new Set(b.split(" ").filter(Boolean));
22050
+ if (ta.size === 0 && tb.size === 0)
22051
+ return 1;
22052
+ if (ta.size === 0 || tb.size === 0)
22053
+ return 0;
22054
+ let intersection2 = 0;
22055
+ for (const token of ta)
22056
+ if (tb.has(token))
22057
+ intersection2++;
22058
+ const union2 = ta.size + tb.size - intersection2;
22059
+ return union2 === 0 ? 0 : intersection2 / union2;
22060
+ }
22061
+ function extractSearchQuery(toolName, args) {
22062
+ if (!isSearchTool(toolName))
22063
+ return void 0;
22064
+ const a = args ?? {};
22065
+ switch (toolName) {
22066
+ case "grep_content": {
22067
+ if (typeof a.pattern !== "string" || a.pattern.trim() === "")
22068
+ return void 0;
22069
+ const include = Array.isArray(a.include) ? a.include.join(",") : typeof a.include === "string" ? a.include : "";
22070
+ return {
22071
+ scope: [
22072
+ typeof a.path === "string" ? a.path : "",
22073
+ include
22074
+ ].join("\0"),
22075
+ query: normalizeQuery(a.pattern)
22076
+ };
22077
+ }
22078
+ case "semantic_search":
22079
+ case "web_search": {
22080
+ if (typeof a.query !== "string" || a.query.trim() === "")
22081
+ return void 0;
22082
+ return { scope: "", query: normalizeQuery(a.query) };
22083
+ }
22084
+ case "list_files": {
22085
+ if (typeof a.path !== "string" || a.path.trim() === "")
22086
+ return void 0;
22087
+ return { scope: String(a.maxDepth ?? ""), query: normalizeQuery(a.path) };
22088
+ }
22089
+ default:
22090
+ return void 0;
22091
+ }
22092
+ }
22093
+ var SEARCH_TOOLS, REFINE_MESSAGE, DuplicateSearchGuard;
22094
+ var init_DuplicateSearchGuard = __esm({
22095
+ "packages/core/dist/runtime/guards/DuplicateSearchGuard.js"() {
22096
+ "use strict";
22097
+ init_types2();
22098
+ SEARCH_TOOLS = /* @__PURE__ */ new Set([
22099
+ "grep_content",
22100
+ "semantic_search",
22101
+ "web_search",
22102
+ "list_files"
22103
+ ]);
22104
+ REFINE_MESSAGE = [
22105
+ "Search queries are being repeated with only cosmetic differences in the same scope.",
22106
+ "Reuse the results already in context, or change the query/scope meaningfully."
22107
+ ].join("\n");
22108
+ DuplicateSearchGuard = class {
22109
+ buckets = /* @__PURE__ */ new Map();
22110
+ warnAfter;
22111
+ stopAfter;
22112
+ similarityThreshold;
22113
+ window;
22114
+ maxBuckets;
22115
+ constructor(config2 = {}) {
22116
+ this.warnAfter = config2.warnAfter ?? 2;
22117
+ this.stopAfter = config2.stopAfter ?? 5;
22118
+ this.similarityThreshold = config2.similarityThreshold ?? 0.8;
22119
+ this.window = config2.window ?? 8;
22120
+ this.maxBuckets = config2.maxBuckets ?? 256;
22121
+ }
22122
+ async onToolCall(event) {
22123
+ const ref = extractSearchQuery(event.toolName, event.args);
22124
+ if (!ref)
22125
+ return CONTINUE;
22126
+ const key = `${event.toolName}\0${ref.scope}`;
22127
+ let bucket = this.buckets.get(key);
22128
+ if (!bucket) {
22129
+ bucket = { recent: [], duplicateCount: 0 };
22130
+ this.buckets.set(key, bucket);
22131
+ this.evictIfNeeded();
22132
+ }
22133
+ let best = 0;
22134
+ for (const previous of bucket.recent) {
22135
+ best = Math.max(best, queryJaccard(previous, ref.query));
22136
+ }
22137
+ bucket.duplicateCount = best >= this.similarityThreshold ? bucket.duplicateCount + 1 : 1;
22138
+ bucket.recent.push(ref.query);
22139
+ if (bucket.recent.length > this.window)
22140
+ bucket.recent.shift();
22141
+ if (bucket.duplicateCount >= this.stopAfter) {
22142
+ return {
22143
+ action: "stop",
22144
+ reason: `near-duplicate search queries for "${event.toolName}" ${bucket.duplicateCount} times in the same scope`,
22145
+ code: "duplicate_search"
22146
+ };
22147
+ }
22148
+ if (bucket.duplicateCount >= this.warnAfter) {
22149
+ return {
22150
+ action: "inject",
22151
+ message: { role: "user", kind: "runtime-warning", content: REFINE_MESSAGE }
22152
+ };
22153
+ }
22154
+ return CONTINUE;
22155
+ }
22156
+ reset() {
22157
+ this.buckets.clear();
22158
+ }
22159
+ evictIfNeeded() {
22160
+ while (this.buckets.size > this.maxBuckets) {
22161
+ const oldest = this.buckets.keys().next().value;
22162
+ if (oldest === void 0)
22163
+ break;
22164
+ this.buckets.delete(oldest);
22165
+ }
22166
+ }
22167
+ };
22168
+ }
22169
+ });
22170
+
22171
+ // packages/core/dist/runtime/guards/NoProgressGuard.js
22172
+ var FILE_WRITING_TOOLS, REASSESS_MESSAGE3, NoProgressGuard;
22173
+ var init_NoProgressGuard = __esm({
22174
+ "packages/core/dist/runtime/guards/NoProgressGuard.js"() {
22175
+ "use strict";
22176
+ init_types2();
22177
+ init_RepetitionGuard();
22178
+ FILE_WRITING_TOOLS = /* @__PURE__ */ new Set([
22179
+ "write_file",
22180
+ "edit_file",
22181
+ "apply_diff"
22182
+ ]);
22183
+ REASSESS_MESSAGE3 = [
22184
+ "The run has made no measurable progress for several turns.",
22185
+ "Change strategy: inspect different files, delegate exploration, or revise the hypothesis."
22186
+ ].join("\n");
22187
+ NoProgressGuard = class {
22188
+ seenFingerprints = /* @__PURE__ */ new Set();
22189
+ soft;
22190
+ hard;
22191
+ stallTurns = 0;
22192
+ turnToolCalls = 0;
22193
+ turnWrites = 0;
22194
+ turnNewFingerprints = 0;
22195
+ constructor(config2 = {}) {
22196
+ this.soft = config2.softStallTurns ?? 2;
22197
+ this.hard = config2.hardStallTurns ?? 5;
22198
+ }
22199
+ async onToolCall(event) {
22200
+ this.turnToolCalls += 1;
22201
+ if (FILE_WRITING_TOOLS.has(event.toolName))
22202
+ this.turnWrites += 1;
22203
+ const fingerprint = toolCallFingerprint(event.toolName, event.args);
22204
+ const key = `${fingerprint.tool}\0${fingerprint.argsHash}`;
22205
+ if (!this.seenFingerprints.has(key)) {
22206
+ this.seenFingerprints.add(key);
22207
+ this.turnNewFingerprints += 1;
22208
+ }
22209
+ return CONTINUE;
22210
+ }
22211
+ async onTurnEnd(_event) {
22212
+ if (this.turnToolCalls === 0) {
22213
+ return CONTINUE;
22214
+ }
22215
+ const productive = this.turnWrites > 0 || this.turnNewFingerprints > 0;
22216
+ this.stallTurns = productive ? 0 : this.stallTurns + 1;
22217
+ this.turnToolCalls = 0;
22218
+ this.turnWrites = 0;
22219
+ this.turnNewFingerprints = 0;
22220
+ if (this.stallTurns >= this.hard) {
22221
+ return {
22222
+ action: "stop",
22223
+ reason: `no measurable progress for ${this.stallTurns} consecutive turns`,
22224
+ code: "no_progress"
22225
+ };
22226
+ }
22227
+ if (this.stallTurns >= this.soft) {
22228
+ return {
22229
+ action: "inject",
22230
+ message: { role: "user", kind: "runtime-warning", content: REASSESS_MESSAGE3 }
22231
+ };
22232
+ }
22233
+ return CONTINUE;
22234
+ }
22235
+ reset() {
22236
+ this.seenFingerprints.clear();
22237
+ this.stallTurns = 0;
22238
+ this.turnToolCalls = 0;
22239
+ this.turnWrites = 0;
22240
+ this.turnNewFingerprints = 0;
22241
+ }
22242
+ };
22243
+ }
22244
+ });
22245
+
22246
+ // packages/core/dist/runtime/guards/ReasoningWatchdog.js
22247
+ function envPositiveInt(name, fallback) {
22248
+ const raw = process.env[name]?.trim();
22249
+ if (!raw)
22250
+ return fallback;
22251
+ const parsed = Number.parseInt(raw, 10);
22252
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
22253
+ }
22254
+ var DEFAULT_FIRST_TOKEN_WARN_MS, DEFAULT_STREAM_IDLE_WARN_MS, MAX_COMPLETED, ReasoningWatchdog;
22255
+ var init_ReasoningWatchdog = __esm({
22256
+ "packages/core/dist/runtime/guards/ReasoningWatchdog.js"() {
22257
+ "use strict";
22258
+ init_types2();
22259
+ DEFAULT_FIRST_TOKEN_WARN_MS = 6e4;
22260
+ DEFAULT_STREAM_IDLE_WARN_MS = 3e4;
22261
+ MAX_COMPLETED = 100;
22262
+ ReasoningWatchdog = class {
22263
+ firstTokenWarnMs;
22264
+ streamIdleWarnMs;
22265
+ now;
22266
+ current = null;
22267
+ lastDeltaAt;
22268
+ completed = [];
22269
+ warnings = [];
22270
+ constructor(config2 = {}, now = Date.now) {
22271
+ this.firstTokenWarnMs = config2.firstTokenWarnMs ?? envPositiveInt("ZELARI_MODEL_FIRST_TOKEN_WARN_MS", DEFAULT_FIRST_TOKEN_WARN_MS);
22272
+ this.streamIdleWarnMs = config2.streamIdleWarnMs ?? envPositiveInt("ZELARI_MODEL_STREAM_IDLE_WARN_MS", DEFAULT_STREAM_IDLE_WARN_MS);
22273
+ this.now = now;
22274
+ }
22275
+ async onModelAttempt(event) {
22276
+ this.finalizeCurrent();
22277
+ this.current = {
22278
+ attemptId: event.id,
22279
+ startedAt: this.now(),
22280
+ deltas: 0,
22281
+ warnedFirstToken: false,
22282
+ warnedIdle: false
22283
+ };
22284
+ this.lastDeltaAt = void 0;
22285
+ return CONTINUE;
22286
+ }
22287
+ async onModelDelta(event) {
22288
+ const attempt = this.current;
22289
+ if (!attempt)
22290
+ return CONTINUE;
22291
+ const at = this.now();
22292
+ attempt.deltas += 1;
22293
+ if (attempt.firstTokenAt === void 0) {
22294
+ attempt.firstTokenAt = at;
22295
+ const ttft = at - attempt.startedAt;
22296
+ attempt.timeToFirstTokenMs = ttft;
22297
+ if (!attempt.warnedFirstToken && ttft > this.firstTokenWarnMs) {
22298
+ attempt.warnedFirstToken = true;
22299
+ this.warn("time_to_first_token", attempt, ttft, this.firstTokenWarnMs);
22300
+ }
22301
+ } else {
22302
+ const gap = at - (this.lastDeltaAt ?? attempt.firstTokenAt);
22303
+ if (gap > (attempt.maxStreamIdleMs ?? 0))
22304
+ attempt.maxStreamIdleMs = gap;
22305
+ if (!attempt.warnedIdle && gap > this.streamIdleWarnMs) {
22306
+ attempt.warnedIdle = true;
22307
+ this.warn("stream_idle", attempt, gap, this.streamIdleWarnMs);
22308
+ }
22309
+ }
22310
+ this.lastDeltaAt = at;
22311
+ return CONTINUE;
22312
+ }
22313
+ async onModelResponse(_event) {
22314
+ this.finalizeCurrent();
22315
+ return CONTINUE;
22316
+ }
22317
+ async onRunEnd(_event) {
22318
+ this.finalizeCurrent();
22319
+ return CONTINUE;
22320
+ }
22321
+ async onCancelled(_event) {
22322
+ this.finalizeCurrent();
22323
+ return CONTINUE;
22324
+ }
22325
+ /** Completed attempt metrics, oldest first (bounded). */
22326
+ getAttemptMetrics() {
22327
+ return this.completed.map((attempt) => ({ ...attempt }));
22328
+ }
22329
+ getWarnings() {
22330
+ return this.warnings.map((warning) => ({ ...warning }));
22331
+ }
22332
+ reset() {
22333
+ this.current = null;
22334
+ this.lastDeltaAt = void 0;
22335
+ this.completed.length = 0;
22336
+ this.warnings.length = 0;
22337
+ }
22338
+ finalizeCurrent() {
22339
+ const attempt = this.current;
22340
+ if (!attempt)
22341
+ return;
22342
+ attempt.endedAt = this.now();
22343
+ if (attempt.firstTokenAt !== void 0) {
22344
+ attempt.generationDurationMs = attempt.endedAt - attempt.startedAt;
22345
+ }
22346
+ this.completed.push({ ...attempt });
22347
+ if (this.completed.length > MAX_COMPLETED)
22348
+ this.completed.shift();
22349
+ this.current = null;
22350
+ this.lastDeltaAt = void 0;
22351
+ }
22352
+ warn(metric, attempt, valueMs, thresholdMs) {
22353
+ this.warnings.push({
22354
+ code: "provider_idle",
22355
+ metric,
22356
+ attemptId: attempt.attemptId,
22357
+ valueMs,
22358
+ thresholdMs,
22359
+ message: metric === "time_to_first_token" ? `Provider first token took ${valueMs}ms (threshold ${thresholdMs}ms).` : `Provider stream idle for ${valueMs}ms between deltas (threshold ${thresholdMs}ms).`,
22360
+ ts: this.now()
22361
+ });
22362
+ }
22363
+ };
22364
+ }
22365
+ });
22366
+
22367
+ // packages/core/dist/runtime/observers/TraceObserver.js
22368
+ var DEFAULT_CAPACITY, TraceObserver;
22369
+ var init_TraceObserver = __esm({
22370
+ "packages/core/dist/runtime/observers/TraceObserver.js"() {
22371
+ "use strict";
22372
+ init_types2();
22373
+ DEFAULT_CAPACITY = 500;
22374
+ TraceObserver = class {
22375
+ entries = [];
22376
+ sink;
22377
+ capacity;
22378
+ constructor(options = {}) {
22379
+ this.sink = options.sink;
22380
+ this.capacity = Math.max(1, options.capacity ?? DEFAULT_CAPACITY);
22381
+ }
22382
+ async onRunStart(event) {
22383
+ return this.record("onRunStart", event);
22384
+ }
22385
+ async onModelAttempt(event) {
22386
+ return this.record("onModelAttempt", event);
22387
+ }
22388
+ async onModelDelta(event) {
22389
+ return this.record("onModelDelta", event);
22390
+ }
22391
+ async onModelResponse(event) {
22392
+ return this.record("onModelResponse", event);
22393
+ }
22394
+ async onToolCall(event) {
22395
+ return this.record("onToolCall", event);
22396
+ }
22397
+ async onToolResult(event) {
22398
+ return this.record("onToolResult", event);
22399
+ }
22400
+ async onTurnEnd(event) {
22401
+ return this.record("onTurnEnd", event);
22402
+ }
22403
+ async onRunEnd(event) {
22404
+ return this.record("onRunEnd", event);
22405
+ }
22406
+ async onCancelled(event) {
22407
+ return this.record("onCancelled", event);
22408
+ }
22409
+ /** Recorded entries, oldest first. */
22410
+ getEntries() {
22411
+ return [...this.entries];
22412
+ }
22413
+ get size() {
22414
+ return this.entries.length;
22415
+ }
22416
+ clear() {
22417
+ this.entries.length = 0;
22418
+ }
22419
+ record(hook, event) {
22420
+ const entry = {
22421
+ hook,
22422
+ eventId: event.id,
22423
+ ts: event.ts,
22424
+ agentId: event.identity.agentId,
22425
+ role: event.identity.role,
22426
+ turn: event.turn
22427
+ };
22428
+ this.entries.push(entry);
22429
+ if (this.entries.length > this.capacity)
22430
+ this.entries.shift();
22431
+ try {
22432
+ this.sink?.(entry);
22433
+ } catch {
22434
+ }
22435
+ return CONTINUE;
22436
+ }
22437
+ };
22438
+ }
22439
+ });
22440
+
22441
+ // packages/core/dist/runtime/observers/MetricsObserver.js
22442
+ var MetricsObserver;
22443
+ var init_MetricsObserver = __esm({
22444
+ "packages/core/dist/runtime/observers/MetricsObserver.js"() {
22445
+ "use strict";
22446
+ init_types2();
22447
+ MetricsObserver = class {
22448
+ runsStarted = 0;
22449
+ modelAttempts = 0;
22450
+ modelDeltas = 0;
22451
+ modelResponses = 0;
22452
+ toolCalls = 0;
22453
+ toolResults = 0;
22454
+ toolFailures = 0;
22455
+ turnsEnded = 0;
22456
+ runsEnded = 0;
22457
+ cancelled = 0;
22458
+ firstEventTs;
22459
+ lastEventTs;
22460
+ async onRunStart(event) {
22461
+ this.tick(event.ts);
22462
+ this.runsStarted += 1;
22463
+ return CONTINUE;
22464
+ }
22465
+ async onModelAttempt(event) {
22466
+ this.tick(event.ts);
22467
+ this.modelAttempts += 1;
22468
+ return CONTINUE;
22469
+ }
22470
+ async onModelDelta(event) {
22471
+ this.tick(event.ts);
22472
+ this.modelDeltas += 1;
22473
+ return CONTINUE;
22474
+ }
22475
+ async onModelResponse(event) {
22476
+ this.tick(event.ts);
22477
+ this.modelResponses += 1;
22478
+ return CONTINUE;
22479
+ }
22480
+ async onToolCall(event) {
22481
+ this.tick(event.ts);
22482
+ this.toolCalls += 1;
22483
+ return CONTINUE;
22484
+ }
22485
+ async onToolResult(event) {
22486
+ this.tick(event.ts);
22487
+ this.toolResults += 1;
22488
+ if (!event.ok)
22489
+ this.toolFailures += 1;
22490
+ return CONTINUE;
22491
+ }
22492
+ async onTurnEnd(event) {
22493
+ this.tick(event.ts);
22494
+ this.turnsEnded += 1;
22495
+ return CONTINUE;
22496
+ }
22497
+ async onRunEnd(event) {
22498
+ this.tick(event.ts);
22499
+ this.runsEnded += 1;
22500
+ return CONTINUE;
22501
+ }
22502
+ async onCancelled(event) {
22503
+ this.tick(event.ts);
22504
+ this.cancelled += 1;
22505
+ return CONTINUE;
22506
+ }
22507
+ snapshot() {
22508
+ return {
22509
+ runsStarted: this.runsStarted,
22510
+ modelAttempts: this.modelAttempts,
22511
+ modelDeltas: this.modelDeltas,
22512
+ modelResponses: this.modelResponses,
22513
+ toolCalls: this.toolCalls,
22514
+ toolResults: this.toolResults,
22515
+ toolFailures: this.toolFailures,
22516
+ turnsEnded: this.turnsEnded,
22517
+ runsEnded: this.runsEnded,
22518
+ cancelled: this.cancelled,
22519
+ firstEventTs: this.firstEventTs,
22520
+ lastEventTs: this.lastEventTs
22521
+ };
22522
+ }
22523
+ reset() {
22524
+ this.runsStarted = 0;
22525
+ this.modelAttempts = 0;
22526
+ this.modelDeltas = 0;
22527
+ this.modelResponses = 0;
22528
+ this.toolCalls = 0;
22529
+ this.toolResults = 0;
22530
+ this.toolFailures = 0;
22531
+ this.turnsEnded = 0;
22532
+ this.runsEnded = 0;
22533
+ this.cancelled = 0;
22534
+ this.firstEventTs = void 0;
22535
+ this.lastEventTs = void 0;
22536
+ }
22537
+ tick(ts) {
22538
+ if (this.firstEventTs === void 0 || ts < this.firstEventTs) {
22539
+ this.firstEventTs = ts;
22540
+ }
22541
+ if (this.lastEventTs === void 0 || ts > this.lastEventTs) {
22542
+ this.lastEventTs = ts;
22543
+ }
22544
+ }
22545
+ };
22546
+ }
22547
+ });
22548
+
22549
+ // packages/core/dist/runtime/recorder/Redactor.js
22550
+ function redactString(input) {
22551
+ let out = input;
22552
+ for (const { re, replacement } of VALUE_PATTERNS) {
22553
+ out = out.replace(re, replacement);
22554
+ }
22555
+ return out;
22556
+ }
22557
+ function redactRuntimePayload(value) {
22558
+ if (value === null || value === void 0)
22559
+ return value;
22560
+ if (typeof value === "string")
22561
+ return redactString(value);
22562
+ if (typeof value === "number" || typeof value === "boolean")
22563
+ return value;
22564
+ if (Array.isArray(value))
22565
+ return value.map((item) => redactRuntimePayload(item));
22566
+ if (typeof value === "object") {
22567
+ const out = {};
22568
+ for (const [key, val] of Object.entries(value)) {
22569
+ out[key] = SECRET_KEY_RE.test(key) ? REDACTED : redactRuntimePayload(val);
22570
+ }
22571
+ return out;
22572
+ }
22573
+ return REDACTED;
22574
+ }
22575
+ var REDACTED, SECRET_KEY_RE, VALUE_PATTERNS;
22576
+ var init_Redactor = __esm({
22577
+ "packages/core/dist/runtime/recorder/Redactor.js"() {
22578
+ "use strict";
22579
+ REDACTED = "[REDACTED]";
22580
+ SECRET_KEY_RE = /(pass(word|wd)?|secret|token|api[-_]?key|auth(orization)?|credential|private[-_]?key|access[-_]?key)/i;
22581
+ VALUE_PATTERNS = [
22582
+ // "Authorization: <credential>" (with optional bearer/Basic scheme prefix) — whole value.
22583
+ // Value class excludes [ ] so the [REDACTED] marker can never be re-matched.
22584
+ {
22585
+ re: /\b(authorization\s*[:=]\s*)(?:bearer|basic|token)\s+[^\s,;"'}\]\[]+/gi,
22586
+ replacement: "$1[REDACTED]"
22587
+ },
22588
+ {
22589
+ re: /\b(authorization\s*[:=]\s*)[^\s,;"'}\]\[]+/gi,
22590
+ replacement: "$1[REDACTED]"
22591
+ },
22592
+ // Bare bearer schemes anywhere ("bearer <token>", any casing)
22593
+ { re: /\bbearer\s+[A-Za-z0-9\-._~+/]+=*/gi, replacement: "bearer [REDACTED]" },
22594
+ // OpenAI-style keys (sk-, sk-proj-)
22595
+ { re: /\bsk-(proj-)?[A-Za-z0-9_-]{8,}\b/g, replacement: "sk-[REDACTED]" },
22596
+ // GitHub tokens
22597
+ {
22598
+ re: /\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g,
22599
+ replacement: "[REDACTED]"
22600
+ },
22601
+ { re: /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g, replacement: "[REDACTED]" },
22602
+ // Slack
22603
+ { re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, replacement: "[REDACTED]" },
22604
+ // AWS access keys
22605
+ { re: /\bAKIA[0-9A-Z]{16}\b/g, replacement: "[REDACTED]" },
22606
+ // Google OAuth
22607
+ { re: /\bya29\.[A-Za-z0-9\-_.]+/g, replacement: "[REDACTED]" },
22608
+ // Secret-looking assignments, UPPER_SNAKE or lower: FOO_PASSWORD=x, password: x
22609
+ {
22610
+ re: /\b([A-Za-z0-9_]*(?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key)[A-Za-z0-9_]*)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s,;}&\]]+)/gi,
22611
+ replacement: "$1=[REDACTED]"
22612
+ }
22613
+ ];
22614
+ }
22615
+ });
22616
+
22617
+ // packages/core/dist/runtime/recorder/RunRecorder.js
22618
+ import { appendFile, mkdir, writeFile } from "node:fs/promises";
22619
+ import { join } from "node:path";
22620
+ import { randomBytes as randomBytes2 } from "node:crypto";
22621
+ function newRunId(now = Date.now) {
22622
+ return `run_${now().toString(36)}_${randomBytes2(4).toString("hex")}`;
22623
+ }
22624
+ function runRecordEnabled() {
22625
+ const raw = process.env.ZELARI_RUN_RECORD?.trim().toLowerCase();
22626
+ return raw === "1" || raw === "true" || raw === "on";
22627
+ }
22628
+ function sanitizeAgentId(agentId) {
22629
+ const safe = agentId.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 64);
22630
+ return safe.length > 0 ? safe : "agent";
22631
+ }
22632
+ var RunRecorder;
22633
+ var init_RunRecorder = __esm({
22634
+ "packages/core/dist/runtime/recorder/RunRecorder.js"() {
22635
+ "use strict";
22636
+ init_Redactor();
22637
+ RunRecorder = class {
22638
+ runId;
22639
+ runDir;
22640
+ opts;
22641
+ now;
22642
+ chain = Promise.resolve();
22643
+ startedAt = 0;
22644
+ started = false;
22645
+ finalized = false;
22646
+ models;
22647
+ counters = { modelCalls: 0, toolCalls: 0, toolFailures: 0, turns: 0 };
22648
+ constructor(options) {
22649
+ this.opts = options;
22650
+ this.now = options.now ?? Date.now;
22651
+ this.runId = options.runId ?? newRunId(this.now);
22652
+ this.runDir = join(options.runsDir, this.runId);
22653
+ this.models = { ...options.models ?? {} };
22654
+ }
22655
+ /** Create the run dir and write the initial manifest. Idempotent. */
22656
+ start() {
22657
+ if (this.started)
22658
+ return;
22659
+ this.started = true;
22660
+ this.startedAt = this.now();
22661
+ this.enqueue(async () => {
22662
+ await mkdir(this.runDir, { recursive: true });
22663
+ await mkdir(join(this.runDir, "agents"), { recursive: true });
22664
+ await writeFile(join(this.runDir, "manifest.json"), JSON.stringify(this.buildManifest("running"), null, 2));
22665
+ });
22666
+ }
22667
+ /** Append one redacted event to trace.jsonl. */
22668
+ record(event) {
22669
+ if (!this.started)
22670
+ this.start();
22671
+ this.enqueue(() => appendFile(join(this.runDir, "trace.jsonl"), `${JSON.stringify(redactRuntimePayload(event))}
22672
+ `));
22673
+ }
22674
+ /** Append one redacted event to the per-agent stream. */
22675
+ recordAgent(agentId, event) {
22676
+ if (!this.started)
22677
+ this.start();
22678
+ this.enqueue(() => appendFile(join(this.runDir, "agents", `${sanitizeAgentId(agentId)}.jsonl`), `${JSON.stringify(redactRuntimePayload(event))}
22679
+ `));
22680
+ }
22681
+ /** Track which model served which role, for the final manifest. */
22682
+ noteModel(role, model) {
22683
+ if (role && model)
22684
+ this.models[role] = model;
22685
+ }
22686
+ bumpModelCall() {
22687
+ this.counters.modelCalls += 1;
22688
+ }
22689
+ bumpToolCall() {
22690
+ this.counters.toolCalls += 1;
22691
+ }
22692
+ bumpToolFailure() {
22693
+ this.counters.toolFailures += 1;
22694
+ }
22695
+ bumpTurn() {
22696
+ this.counters.turns += 1;
22697
+ }
22698
+ getMetrics() {
22699
+ return {
22700
+ durationMs: this.finalized || this.startedAt > 0 ? Math.max(0, this.now() - this.startedAt) : 0,
22701
+ ...this.counters
22702
+ };
22703
+ }
22704
+ /** Write final manifest + metrics.json. Idempotent. */
22705
+ finalize(status) {
22706
+ if (this.finalized)
22707
+ return;
22708
+ if (!this.started)
22709
+ this.start();
22710
+ this.finalized = true;
22711
+ const endedAt = this.now();
22712
+ this.enqueue(async () => {
22713
+ await writeFile(join(this.runDir, "manifest.json"), JSON.stringify(this.buildManifest(status, endedAt), null, 2));
22714
+ await writeFile(join(this.runDir, "metrics.json"), JSON.stringify(this.getMetrics(), null, 2));
22715
+ });
22716
+ }
22717
+ /** Await pending writes (tests / graceful shutdown). */
22718
+ async flush() {
22719
+ await this.chain;
22720
+ }
22721
+ buildManifest(status, endedAt) {
22722
+ return {
22723
+ version: 1,
22724
+ runId: this.runId,
22725
+ sessionId: this.opts.sessionId,
22726
+ mode: this.opts.mode ?? "kraken",
22727
+ phase: this.opts.phase ?? "build",
22728
+ startedAt: this.startedAt,
22729
+ ...endedAt !== void 0 ? { endedAt } : {},
22730
+ status,
22731
+ cwd: this.opts.cwd ?? process.cwd(),
22732
+ models: this.models
22733
+ };
22734
+ }
22735
+ /**
22736
+ * Serial best-effort write queue: preserves trace ordering and swallows
22737
+ * IO errors (§102 — recorder failure must not block the run).
22738
+ */
22739
+ enqueue(write) {
22740
+ this.chain = this.chain.then(write).catch(() => {
22741
+ });
22742
+ }
22743
+ };
22744
+ }
22745
+ });
22746
+
22747
+ // packages/core/dist/runtime/recorder/recorderObserver.js
22748
+ function base(event) {
22749
+ return {
22750
+ id: event.id,
22751
+ ts: event.ts,
22752
+ turn: event.turn,
22753
+ agentId: event.identity.agentId,
22754
+ role: event.identity.role
22755
+ };
22756
+ }
22757
+ function createRecorderObserver(recorder) {
22758
+ return {
22759
+ onRunStart(event) {
22760
+ recorder.noteModel(event.identity.role, event.identity.model);
22761
+ recorder.start();
22762
+ recorder.record({ type: "run_start", ...base(event) });
22763
+ return CONTINUE;
22764
+ },
22765
+ onModelAttempt(event) {
22766
+ recorder.bumpModelCall();
22767
+ recorder.record({ type: "model_attempt", ...base(event) });
22768
+ return CONTINUE;
22769
+ },
22770
+ onModelResponse(event) {
22771
+ recorder.record({ type: "model_response", ...base(event) });
22772
+ return CONTINUE;
22773
+ },
22774
+ onToolCall(event) {
22775
+ recorder.bumpToolCall();
22776
+ recorder.record({ type: "tool_call", ...base(event), toolCallId: event.toolCallId, tool: event.toolName });
22777
+ return CONTINUE;
22778
+ },
22779
+ onToolResult(event) {
22780
+ if (!event.ok)
22781
+ recorder.bumpToolFailure();
22782
+ const line = { type: "tool_result", ...base(event), toolCallId: event.toolCallId, tool: event.toolName, ok: event.ok };
22783
+ recorder.record(line);
22784
+ recorder.recordAgent(event.identity.agentId, line);
22785
+ return CONTINUE;
22786
+ },
22787
+ onTurnEnd(event) {
22788
+ recorder.bumpTurn();
22789
+ recorder.record({ type: "turn_end", ...base(event) });
22790
+ return CONTINUE;
22791
+ },
22792
+ onRunEnd(event) {
22793
+ recorder.record({ type: "run_end", ...base(event), reason: event.reason });
22794
+ recorder.finalize(event.reason === "completed" ? "completed" : event.reason === "cancelled" ? "cancelled" : "failed");
22795
+ return CONTINUE;
22796
+ },
22797
+ onCancelled(event) {
22798
+ recorder.record({ type: "run_cancelled", ...base(event), reason: event.reason });
22799
+ recorder.finalize("cancelled");
22800
+ return CONTINUE;
22801
+ }
22802
+ };
22803
+ }
22804
+ var init_recorderObserver = __esm({
22805
+ "packages/core/dist/runtime/recorder/recorderObserver.js"() {
22806
+ "use strict";
22807
+ init_types2();
22808
+ }
22809
+ });
22810
+
22811
+ // packages/core/dist/runtime/controls/SteeringObserver.js
22812
+ function renderSteers(steers) {
22813
+ const lines = ["Runtime user steering received during execution:", ""];
22814
+ steers.forEach((steer, index) => {
22815
+ lines.push(`[${index + 1}]`);
22816
+ lines.push(steer.text);
22817
+ lines.push("");
22818
+ });
22819
+ lines.push("Later instructions may supersede earlier ones.");
22820
+ return lines.join("\n");
22821
+ }
22822
+ var SteeringObserver;
22823
+ var init_SteeringObserver = __esm({
22824
+ "packages/core/dist/runtime/controls/SteeringObserver.js"() {
22825
+ "use strict";
22826
+ init_types2();
22827
+ SteeringObserver = class {
22828
+ queue;
22829
+ constructor(queue) {
22830
+ this.queue = queue;
22831
+ }
22832
+ async onTurnEnd(_event) {
22833
+ const steers = this.queue.drainSteers();
22834
+ if (steers.length === 0) {
22835
+ return CONTINUE;
22836
+ }
22837
+ return {
22838
+ action: "inject",
22839
+ message: {
22840
+ role: "user",
22841
+ kind: "runtime-steer",
22842
+ content: renderSteers(steers)
22843
+ }
22844
+ };
22845
+ }
22846
+ };
22847
+ }
22848
+ });
22849
+
22850
+ // packages/core/dist/runtime/observers/ObserverBus.js
22851
+ import { join as join2 } from "node:path";
22852
+ function runtimeObserversEnabled() {
22853
+ const raw = process.env.ZELARI_RUNTIME_OBSERVERS?.trim().toLowerCase();
22854
+ return raw === "1" || raw === "true" || raw === "on";
22855
+ }
22856
+ function buildRuntimeObserverBus(options = {}) {
22857
+ const steering = options.steeringQueue ? [
22858
+ {
22859
+ id: "steering-observer",
22860
+ priority: 20,
22861
+ failureMode: "warn",
22862
+ observer: new SteeringObserver(options.steeringQueue)
22863
+ }
22864
+ ] : [];
22865
+ if (!runtimeObserversEnabled()) {
22866
+ return steering.length > 0 ? new ObserverBus(steering) : void 0;
22867
+ }
22868
+ const descriptors = [
22869
+ ...steering,
22870
+ {
22871
+ id: "repetition-guard",
22872
+ priority: 30,
22873
+ failureMode: "warn",
22874
+ observer: new RepetitionGuard()
22875
+ },
22876
+ {
22877
+ id: "failure-signature-guard",
22878
+ priority: 30,
22879
+ failureMode: "warn",
22880
+ observer: new FailureSignatureGuard()
22881
+ },
22882
+ {
22883
+ id: "duplicate-search-guard",
22884
+ priority: 30,
22885
+ failureMode: "warn",
22886
+ observer: new DuplicateSearchGuard()
22887
+ },
22888
+ {
22889
+ id: "no-progress-guard",
22890
+ priority: 30,
22891
+ failureMode: "warn",
22892
+ observer: new NoProgressGuard()
22893
+ },
22894
+ {
22895
+ id: "reasoning-watchdog",
22896
+ priority: 30,
22897
+ failureMode: "warn",
22898
+ observer: new ReasoningWatchdog()
22899
+ },
22900
+ {
22901
+ id: "trace-observer",
22902
+ priority: 80,
22903
+ failureMode: "warn",
22904
+ observer: new TraceObserver()
22905
+ },
22906
+ {
22907
+ id: "metrics-observer",
22908
+ priority: 90,
22909
+ failureMode: "ignore",
22910
+ observer: new MetricsObserver()
22911
+ }
22912
+ ];
22913
+ if (runRecordEnabled()) {
22914
+ const recorder = new RunRecorder({
22915
+ runsDir: options.runsDir ?? join2(process.cwd(), ".zelari", "runs"),
22916
+ runId: options.runId ?? newRunId()
22917
+ });
22918
+ descriptors.push({
22919
+ id: "run-recorder",
22920
+ priority: 85,
22921
+ failureMode: "warn",
22922
+ observer: createRecorderObserver(recorder)
22923
+ });
22924
+ }
22925
+ return new ObserverBus(descriptors);
22926
+ }
22927
+ var ObserverBus;
22928
+ var init_ObserverBus = __esm({
22929
+ "packages/core/dist/runtime/observers/ObserverBus.js"() {
22930
+ "use strict";
22931
+ init_types2();
22932
+ init_resolve();
22933
+ init_RepetitionGuard();
22934
+ init_FailureSignatureGuard();
22935
+ init_DuplicateSearchGuard();
22936
+ init_NoProgressGuard();
22937
+ init_ReasoningWatchdog();
22938
+ init_TraceObserver();
22939
+ init_MetricsObserver();
22940
+ init_RunRecorder();
22941
+ init_recorderObserver();
22942
+ init_SteeringObserver();
22943
+ ObserverBus = class {
22944
+ descriptors;
22945
+ logger;
22946
+ constructor(descriptors = [], options = {}) {
22947
+ this.descriptors = [...descriptors].sort((a, b) => a.priority - b.priority);
22948
+ this.logger = options.logger ?? (() => {
22949
+ });
22950
+ }
22951
+ get size() {
22952
+ return this.descriptors.length;
22953
+ }
22954
+ async emit(hook, event) {
22955
+ const results = [];
22956
+ for (const descriptor of this.descriptors) {
22957
+ const callback = descriptor.observer[hook];
22958
+ if (typeof callback !== "function")
22959
+ continue;
22960
+ try {
22961
+ const result = await callback.call(descriptor.observer, event);
22962
+ results.push(result ?? CONTINUE);
22963
+ } catch (err) {
22964
+ const message = err instanceof Error ? err.message : String(err);
22965
+ if (descriptor.failureMode === "fail-closed") {
22966
+ results.push({
22967
+ action: "stop",
22968
+ reason: `observer "${descriptor.id}" failed: ${message}`,
22969
+ code: "OBSERVER_FAIL_CLOSED"
22970
+ });
22971
+ } else {
22972
+ if (descriptor.failureMode === "warn") {
22973
+ this.logger(`[observers] ${descriptor.id} (${String(hook)}) failed: ${message}`);
22974
+ }
22975
+ results.push(CONTINUE);
22976
+ }
22977
+ }
22978
+ }
22979
+ return results;
22980
+ }
22981
+ async emitResolved(hook, event) {
22982
+ return resolveInterventions(await this.emit(hook, event));
22983
+ }
22984
+ };
22985
+ }
22986
+ });
22987
+
21838
22988
  // packages/core/dist/core/AgentHarness.js
21839
22989
  function hashToolCall(toolName, args) {
21840
22990
  const canonical = stableStringify2(args);
@@ -22078,6 +23228,7 @@ var init_AgentHarness = __esm({
22078
23228
  init_contextGrowth();
22079
23229
  init_requestSnapshot();
22080
23230
  init_textLoopDetect();
23231
+ init_ObserverBus();
22081
23232
  init_textLoopDetect();
22082
23233
  BUILD_LIVENESS_RECOVERY_PROMPT = "[build-liveness] The requested task requires an on-disk implementation, but no successful project mutation has occurred yet. Continue working. Inspect only as needed, then make the required change with an available mutating tool. Do not merely describe a patch or claim completion.";
22083
23234
  AgentHarness = class {
@@ -22105,6 +23256,14 @@ var init_AgentHarness = __esm({
22105
23256
  * model must change approach instead of soft-replaying forever.
22106
23257
  */
22107
23258
  toolCallCounts = /* @__PURE__ */ new Map();
23259
+ /**
23260
+ * Frontier PHASE 1: observer bus, or `null` when observers are off. Every
23261
+ * hook site guards on `this.observerBus` so the off-path stays identical
23262
+ * to the pre-observer loop.
23263
+ */
23264
+ observerBus;
23265
+ /** Coarse turn counter attached to observer events (per run, 0-based). */
23266
+ observerTurn = 0;
22108
23267
  /**
22109
23268
  * How many times this run re-entered the provider because the model emitted
22110
23269
  * tool calls as a `---TOOLS---` text block (fallback format) instead of native
@@ -22132,6 +23291,7 @@ var init_AgentHarness = __esm({
22132
23291
  const soft = this.maxToolLoopIterations;
22133
23292
  const hardCfg = config2.maxToolLoopHardCap;
22134
23293
  this.maxToolLoopHardCap = typeof hardCfg === "number" && hardCfg > 0 ? Math.max(soft, hardCfg) : Math.max(soft * 3, soft + 60);
23294
+ this.observerBus = config2.observers?.length ? new ObserverBus(config2.observers) : buildRuntimeObserverBus({ steeringQueue: config2.controlQueue }) ?? null;
22135
23295
  }
22136
23296
  /**
22137
23297
  * Snapshot of the live transcript (`this.config.messages`) as mutated
@@ -22227,6 +23387,57 @@ var init_AgentHarness = __esm({
22227
23387
  return { allowed: true };
22228
23388
  }
22229
23389
  }
23390
+ /** Base fields shared by every observer event this harness emits. */
23391
+ observerEventBase(turn) {
23392
+ return {
23393
+ id: crypto.randomUUID(),
23394
+ ts: Date.now(),
23395
+ turn,
23396
+ identity: {
23397
+ runId: this.sessionId,
23398
+ agentId: this.sessionId,
23399
+ role: this.config.memberId ? "council" : "lead",
23400
+ mode: this.config.memberId ? "council" : "kraken",
23401
+ model: this.config.model,
23402
+ provider: this.config.provider
23403
+ }
23404
+ };
23405
+ }
23406
+ /**
23407
+ * Frontier PHASE 1: observer pre-dispatch gate, consulted after the host
23408
+ * resource gate on EVERY registry dispatch (native + text paths).
23409
+ * `deny_tool` blocks the call with a model-visible reason; `stop` is a
23410
+ * cooperative cancel (agent_end reason 'cancelled'); `inject` appends a
23411
+ * user/system message at the next safe boundary (the in-flight provider
23412
+ * request is never mutated). `retry`/`replace` are not yet honored at
23413
+ * this hook and fall through as continue.
23414
+ */
23415
+ async checkObserverToolGate(toolCallId, toolName, args) {
23416
+ const bus = this.observerBus;
23417
+ if (!bus)
23418
+ return { allowed: true };
23419
+ const result = await bus.emitResolved("onToolCall", {
23420
+ ...this.observerEventBase(this.observerTurn),
23421
+ toolCallId,
23422
+ toolName,
23423
+ args
23424
+ });
23425
+ if (result.action === "deny_tool") {
23426
+ return { allowed: false, reason: result.reason };
23427
+ }
23428
+ if (result.action === "stop") {
23429
+ this.cancel();
23430
+ return { allowed: false, reason: result.reason };
23431
+ }
23432
+ if (result.action === "inject") {
23433
+ this.config.messages.push({
23434
+ role: result.message.role === "system" ? "system" : "user",
23435
+ content: result.message.content
23436
+ });
23437
+ return { allowed: true };
23438
+ }
23439
+ return { allowed: true };
23440
+ }
22230
23441
  async executePendingTools(pending, maxToolCalls) {
22231
23442
  const out = new Array(pending.length);
22232
23443
  const maxParallel = Math.max(1, Number.parseInt(process.env.ZELARI_MAX_PARALLEL_TOOLS ?? "6", 10) || 6);
@@ -22247,6 +23458,14 @@ var init_AgentHarness = __esm({
22247
23458
  durationMs: 0
22248
23459
  };
22249
23460
  }
23461
+ const observerGate = await this.checkObserverToolGate(p3.toolCallId, p3.toolName, p3.args);
23462
+ if (!observerGate.allowed) {
23463
+ return {
23464
+ content: `[observers] ${observerGate.reason ?? "denied by observer policy"}`,
23465
+ isError: true,
23466
+ durationMs: 0
23467
+ };
23468
+ }
22250
23469
  const callKey = hashToolCall(p3.toolName, p3.args);
22251
23470
  const nextCount = (this.toolCallCounts.get(callKey) ?? 0) + 1;
22252
23471
  this.toolCallCounts.set(callKey, nextCount);
@@ -22429,6 +23648,12 @@ ${shared2.content}`,
22429
23648
  recoveries: 0
22430
23649
  };
22431
23650
  const memoryWarning = await this.prepareMemoryContext();
23651
+ this.observerTurn = 0;
23652
+ if (this.observerBus) {
23653
+ const start = await this.observerBus.emitResolved("onRunStart", this.observerEventBase(0));
23654
+ if (start.action === "stop")
23655
+ this.cancel();
23656
+ }
22432
23657
  const startEvent = createBrainEvent("agent_start", this.sessionId, {
22433
23658
  model: this.config.model,
22434
23659
  provider: this.config.provider,
@@ -22638,6 +23863,22 @@ ${shared2.content}`,
22638
23863
  });
22639
23864
  this.emit(growthEvent);
22640
23865
  yield growthEvent;
23866
+ if (this.observerBus) {
23867
+ const endReason = hadError ? "error" : this.cancelled ? "cancelled" : "completed";
23868
+ try {
23869
+ if (endReason === "cancelled") {
23870
+ await this.observerBus.emitResolved("onCancelled", {
23871
+ ...this.observerEventBase(this.observerTurn),
23872
+ reason: "cancelled"
23873
+ });
23874
+ }
23875
+ await this.observerBus.emitResolved("onRunEnd", {
23876
+ ...this.observerEventBase(this.observerTurn),
23877
+ reason: endReason
23878
+ });
23879
+ } catch {
23880
+ }
23881
+ }
22641
23882
  const agentEnd = createBrainEvent("agent_end", this.sessionId, {
22642
23883
  reason: hadError ? "error" : this.cancelled ? "cancelled" : "completed",
22643
23884
  durationMs: Date.now() - startTime,
@@ -22922,6 +24163,21 @@ ${cached2}`
22922
24163
  executedAny = true;
22923
24164
  continue;
22924
24165
  }
24166
+ const observerGate = await this.checkObserverToolGate(toolCallId, tt.name, tt.args);
24167
+ if (!observerGate.allowed) {
24168
+ const denied = `[observers] ${observerGate.reason ?? "denied by observer policy"}`;
24169
+ const denyEv = createBrainEvent("tool_execution_end", this.sessionId, {
24170
+ toolCallId,
24171
+ result: denied,
24172
+ isError: true,
24173
+ durationMs: 0
24174
+ });
24175
+ this.emit(denyEv);
24176
+ yield denyEv;
24177
+ turnToolResults.push({ toolCallId, content: denied });
24178
+ executedAny = true;
24179
+ continue;
24180
+ }
22925
24181
  let resultStr = "";
22926
24182
  let isError = false;
22927
24183
  const startMs = Date.now();
@@ -22981,6 +24237,21 @@ ${cached2}`
22981
24237
  yield truncErr;
22982
24238
  finishRef.value = "stop";
22983
24239
  }
24240
+ if (this.observerBus) {
24241
+ this.observerTurn += 1;
24242
+ try {
24243
+ const turnEnd = await this.observerBus.emitResolved("onTurnEnd", this.observerEventBase(this.observerTurn));
24244
+ if (turnEnd.action === "inject") {
24245
+ this.config.messages.push({
24246
+ role: turnEnd.message.role === "system" ? "system" : "user",
24247
+ content: turnEnd.message.content
24248
+ });
24249
+ } else if (turnEnd.action === "stop") {
24250
+ this.cancel();
24251
+ }
24252
+ } catch {
24253
+ }
24254
+ }
22984
24255
  if (turnToolCalls.length > 0 || turnText.length > 0 || turnReasoning.length > 0) {
22985
24256
  this.config.messages.push({
22986
24257
  role: "assistant",
@@ -23384,7 +24655,7 @@ function hookMatches(hook, event, toolName) {
23384
24655
  return true;
23385
24656
  }
23386
24657
  var TOOL_ALIASES;
23387
- var init_types2 = __esm({
24658
+ var init_types3 = __esm({
23388
24659
  "packages/core/dist/core/hooks/types.js"() {
23389
24660
  "use strict";
23390
24661
  TOOL_ALIASES = {
@@ -23429,7 +24700,7 @@ var DEFAULT_TIMEOUT_MS, LifecycleHookRunner;
23429
24700
  var init_lifecycleHookRunner = __esm({
23430
24701
  "packages/core/dist/core/hooks/lifecycleHookRunner.js"() {
23431
24702
  "use strict";
23432
- init_types2();
24703
+ init_types3();
23433
24704
  DEFAULT_TIMEOUT_MS = 5e3;
23434
24705
  LifecycleHookRunner = class {
23435
24706
  hooks = [];
@@ -23634,7 +24905,7 @@ var init_lifecycleHookRunner = __esm({
23634
24905
  var init_hooks = __esm({
23635
24906
  "packages/core/dist/core/hooks/index.js"() {
23636
24907
  "use strict";
23637
- init_types2();
24908
+ init_types3();
23638
24909
  init_lifecycleHookRunner();
23639
24910
  }
23640
24911
  });
@@ -24161,7 +25432,7 @@ var init_parseCssMotion = __esm({
24161
25432
 
24162
25433
  // packages/core/dist/council/verification/citeVerify.js
24163
25434
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
24164
- import { join } from "node:path";
25435
+ import { join as join3 } from "node:path";
24165
25436
  function extractCitations(text) {
24166
25437
  const out = [];
24167
25438
  const seen = /* @__PURE__ */ new Set();
@@ -24183,7 +25454,7 @@ function verifyCitations(projectRoot, synthesisText) {
24183
25454
  return [];
24184
25455
  const results = [];
24185
25456
  for (const cite of extractCitations(synthesisText)) {
24186
- const abs = join(projectRoot, cite.file);
25457
+ const abs = join3(projectRoot, cite.file);
24187
25458
  if (!existsSync6(abs)) {
24188
25459
  results.push({
24189
25460
  id: "synthesis.cite-invalid",
@@ -24461,9 +25732,9 @@ var init_synthesisAudit = __esm({
24461
25732
 
24462
25733
  // packages/core/dist/council/verification/runChecks.js
24463
25734
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
24464
- import { join as join2 } from "node:path";
25735
+ import { join as join4 } from "node:path";
24465
25736
  function loadNfrSpec(zelariRoot) {
24466
- const path72 = join2(zelariRoot, "nfr-spec.json");
25737
+ const path72 = join4(zelariRoot, "nfr-spec.json");
24467
25738
  if (!existsSync7(path72))
24468
25739
  return null;
24469
25740
  try {
@@ -24478,11 +25749,11 @@ function loadNfrSpec(zelariRoot) {
24478
25749
  function resolveTargets(projectRoot, spec) {
24479
25750
  const found = [];
24480
25751
  for (const rel2 of spec.targets) {
24481
- if (existsSync7(join2(projectRoot, rel2))) {
25752
+ if (existsSync7(join4(projectRoot, rel2))) {
24482
25753
  found.push(rel2);
24483
25754
  }
24484
25755
  }
24485
- if (found.length === 0 && existsSync7(join2(projectRoot, "index.html"))) {
25756
+ if (found.length === 0 && existsSync7(join4(projectRoot, "index.html"))) {
24486
25757
  return ["index.html"];
24487
25758
  }
24488
25759
  return found;
@@ -24537,7 +25808,7 @@ function checkDeadCssHooks(html, relFile) {
24537
25808
  return results;
24538
25809
  }
24539
25810
  function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
24540
- const planPath = join2(zelariRoot, "plan.json");
25811
+ const planPath = join4(zelariRoot, "plan.json");
24541
25812
  if (!existsSync7(planPath) || keywords.length === 0)
24542
25813
  return [];
24543
25814
  let plan;
@@ -24548,7 +25819,7 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
24548
25819
  }
24549
25820
  const milestoneText = (plan.milestones ?? []).map((m) => `${m.name ?? ""} ${m.description ?? ""}`).join(" ").toLowerCase();
24550
25821
  const results = [];
24551
- const targetContent = targets.map((t) => readFileSync7(join2(projectRoot, t), "utf8").toLowerCase()).join("\n");
25822
+ const targetContent = targets.map((t) => readFileSync7(join4(projectRoot, t), "utf8").toLowerCase()).join("\n");
24552
25823
  for (const kw of keywords) {
24553
25824
  const low = kw.toLowerCase();
24554
25825
  if (!milestoneText.includes(low))
@@ -24577,11 +25848,11 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
24577
25848
  return results;
24578
25849
  }
24579
25850
  function checkReadmeStale(projectRoot, targets) {
24580
- const readmePath = join2(projectRoot, "README.md");
25851
+ const readmePath = join4(projectRoot, "README.md");
24581
25852
  if (!existsSync7(readmePath) || targets.length === 0)
24582
25853
  return [];
24583
25854
  const readme = readFileSync7(readmePath, "utf8");
24584
- const htmlPath = join2(projectRoot, targets[0]);
25855
+ const htmlPath = join4(projectRoot, targets[0]);
24585
25856
  if (!existsSync7(htmlPath))
24586
25857
  return [];
24587
25858
  const html = readFileSync7(htmlPath, "utf8");
@@ -24622,7 +25893,7 @@ function runImplementationVerification(input) {
24622
25893
  forbidLayoutProps: anim.forbidLayoutProps ?? true
24623
25894
  };
24624
25895
  for (const rel2 of targets) {
24625
- const html = readFileSync7(join2(input.projectRoot, rel2), "utf8");
25896
+ const html = readFileSync7(join4(input.projectRoot, rel2), "utf8");
24626
25897
  for (const v of scanKeyframesViolations(html, scanOpts)) {
24627
25898
  results.push({
24628
25899
  id: "motion.keyframes",
@@ -24678,7 +25949,7 @@ function runImplementationVerification(input) {
24678
25949
  };
24679
25950
  }
24680
25951
  function writeVerificationReport(zelariRoot, report) {
24681
- const outPath = join2(zelariRoot, "verification-report.json");
25952
+ const outPath = join4(zelariRoot, "verification-report.json");
24682
25953
  writeFileSync5(outPath, JSON.stringify(report, null, 2), "utf8");
24683
25954
  return outPath;
24684
25955
  }
@@ -24709,7 +25980,7 @@ var init_runChecks = __esm({
24709
25980
 
24710
25981
  // packages/core/dist/council/verification/microGate.js
24711
25982
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
24712
- import { join as join3 } from "node:path";
25983
+ import { join as join5 } from "node:path";
24713
25984
  function checkDeadHooksInHtml(html) {
24714
25985
  const warnings = [];
24715
25986
  const scripts = [];
@@ -24737,7 +26008,7 @@ function checkDeadHooksInHtml(html) {
24737
26008
  return warnings;
24738
26009
  }
24739
26010
  function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
24740
- const abs = join3(projectRoot, relPath);
26011
+ const abs = join5(projectRoot, relPath);
24741
26012
  if (!existsSync8(abs) || !/\.html?$/i.test(relPath))
24742
26013
  return [];
24743
26014
  const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
@@ -25103,7 +26374,7 @@ var init_implementationDelivery = __esm({
25103
26374
 
25104
26375
  // packages/core/dist/council/verification/inlineJsAutofix.js
25105
26376
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
25106
- import { join as join4 } from "node:path";
26377
+ import { join as join6 } from "node:path";
25107
26378
  function minifyInlineJs(js) {
25108
26379
  let out = js.replace(/\/\*[\s\S]*?\*\//g, "");
25109
26380
  out = out.split("\n").map((line) => line.replace(/\/\/.*$/, "").trimEnd()).filter((line) => line.trim().length > 0).join("\n");
@@ -25146,7 +26417,7 @@ function applyInlineJsAutofix(projectRoot, report) {
25146
26417
  const fixes = [];
25147
26418
  for (const r of fails) {
25148
26419
  const rel2 = r.file ?? "index.html";
25149
- const abs = join4(projectRoot, rel2);
26420
+ const abs = join6(projectRoot, rel2);
25150
26421
  let html;
25151
26422
  try {
25152
26423
  html = readFileSync9(abs, "utf8");
@@ -25183,7 +26454,7 @@ var init_inlineJsAutofix = __esm({
25183
26454
 
25184
26455
  // packages/core/dist/agents/councilApi.js
25185
26456
  import { existsSync as existsSync9 } from "node:fs";
25186
- import { join as join5 } from "node:path";
26457
+ import { join as join7 } from "node:path";
25187
26458
  function extractBalancedJsonObject(s) {
25188
26459
  const start = s.indexOf("{");
25189
26460
  if (start < 0)
@@ -25810,7 +27081,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
25810
27081
  const zelariRoot = `${chairmanProjectRoot}/.zelari`;
25811
27082
  const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
25812
27083
  for (const rel2 of spec.targets) {
25813
- if (!existsSync9(join5(chairmanProjectRoot, rel2)))
27084
+ if (!existsSync9(join7(chairmanProjectRoot, rel2)))
25814
27085
  continue;
25815
27086
  changedTargetFiles.add(rel2);
25816
27087
  for (const w of runChairmanMicroGate({ projectRoot: chairmanProjectRoot, relPath: rel2, zelariRoot })) {
@@ -25830,7 +27101,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
25830
27101
  const zelariRootReplay = `${chairmanProjectRoot}/.zelari`;
25831
27102
  const specReplay = loadNfrSpec(zelariRootReplay) ?? DEFAULT_NFR_SPEC;
25832
27103
  for (const rel2 of specReplay.targets) {
25833
- if (!existsSync9(join5(chairmanProjectRoot, rel2)))
27104
+ if (!existsSync9(join7(chairmanProjectRoot, rel2)))
25834
27105
  continue;
25835
27106
  changedTargetFiles.add(rel2);
25836
27107
  for (const w of runChairmanMicroGate({
@@ -26541,7 +27812,7 @@ var init_missionBrief = __esm({
26541
27812
  });
26542
27813
 
26543
27814
  // packages/core/dist/council/verification/types.js
26544
- var init_types3 = __esm({
27815
+ var init_types4 = __esm({
26545
27816
  "packages/core/dist/council/verification/types.js"() {
26546
27817
  "use strict";
26547
27818
  }
@@ -26549,7 +27820,7 @@ var init_types3 = __esm({
26549
27820
 
26550
27821
  // packages/core/dist/council/verification/motionAutofix.js
26551
27822
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
26552
- import { join as join6 } from "node:path";
27823
+ import { join as join8 } from "node:path";
26553
27824
  function sanitizeTransitionPart(part) {
26554
27825
  const tokens = part.trim().split(/\s+/);
26555
27826
  if (tokens.length === 0)
@@ -26624,7 +27895,7 @@ function applyMotionAutofix(projectRoot, report) {
26624
27895
  forbidLayoutProps: DEFAULT_NFR_SPEC.animation?.forbidLayoutProps ?? true
26625
27896
  };
26626
27897
  for (const rel2 of targets) {
26627
- const abs = join6(projectRoot, rel2);
27898
+ const abs = join8(projectRoot, rel2);
26628
27899
  let html;
26629
27900
  try {
26630
27901
  html = readFileSync10(abs, "utf8");
@@ -26700,7 +27971,7 @@ var init_motionAutofix = __esm({
26700
27971
 
26701
27972
  // packages/core/dist/council/verification/autofix.js
26702
27973
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
26703
- import { join as join7 } from "node:path";
27974
+ import { join as join9 } from "node:path";
26704
27975
  function applyDeterministicAutofix(projectRoot, report) {
26705
27976
  const motion = applyMotionAutofix(projectRoot, report);
26706
27977
  const inlineJs = applyInlineJsAutofix(projectRoot, report);
@@ -26714,7 +27985,7 @@ function applyDeterministicAutofix(projectRoot, report) {
26714
27985
  const m = r.evidence?.match(/classList\.add\(\s*['"]([\w-]+)['"]\s*\)/);
26715
27986
  if (!m || m[1] === "rm")
26716
27987
  continue;
26717
- const abs = join7(projectRoot, rel2);
27988
+ const abs = join9(projectRoot, rel2);
26718
27989
  let html = readFileSync11(abs, "utf8");
26719
27990
  const snippet = m[0];
26720
27991
  if (!html.includes(snippet))
@@ -26745,7 +28016,7 @@ var init_autofix = __esm({
26745
28016
  var init_verification = __esm({
26746
28017
  "packages/core/dist/council/verification/index.js"() {
26747
28018
  "use strict";
26748
- init_types3();
28019
+ init_types4();
26749
28020
  init_runChecks();
26750
28021
  init_honesty();
26751
28022
  init_parseCssMotion();
@@ -26763,7 +28034,7 @@ var init_verification = __esm({
26763
28034
  });
26764
28035
 
26765
28036
  // packages/core/dist/council/lessons/types.js
26766
- var init_types4 = __esm({
28037
+ var init_types5 = __esm({
26767
28038
  "packages/core/dist/council/lessons/types.js"() {
26768
28039
  "use strict";
26769
28040
  }
@@ -26771,9 +28042,9 @@ var init_types4 = __esm({
26771
28042
 
26772
28043
  // packages/core/dist/council/lessons/io.js
26773
28044
  import { readFileSync as readFileSync12 } from "node:fs";
26774
- import { join as join8 } from "node:path";
28045
+ import { join as join10 } from "node:path";
26775
28046
  function readLessonsDeduped(zelariRoot) {
26776
- const path72 = join8(zelariRoot, LESSONS_FILE);
28047
+ const path72 = join10(zelariRoot, LESSONS_FILE);
26777
28048
  try {
26778
28049
  const raw = readFileSync12(path72, "utf8");
26779
28050
  const byId = /* @__PURE__ */ new Map();
@@ -26855,8 +28126,8 @@ function jaccardSimilarity(a, b) {
26855
28126
  return union2 === 0 ? 0 : inter / union2;
26856
28127
  }
26857
28128
  function normalizeForSignature(checkId, message) {
26858
- const base = `${checkId} ${message}`.toLowerCase().replace(/[A-Za-z]:\\[^\s]+/g, "path").replace(/\/[\w./-]+/g, "path").replace(/l\d{1,6}/gi, "line").replace(/\d+/g, "n");
26859
- return [...tokenizeForSignature(base)].sort().join(" ");
28129
+ const base2 = `${checkId} ${message}`.toLowerCase().replace(/[A-Za-z]:\\[^\s]+/g, "path").replace(/\/[\w./-]+/g, "path").replace(/l\d{1,6}/gi, "line").replace(/\d+/g, "n");
28130
+ return [...tokenizeForSignature(base2)].sort().join(" ");
26860
28131
  }
26861
28132
  var init_signatures = __esm({
26862
28133
  "packages/core/dist/council/lessons/signatures.js"() {
@@ -26866,7 +28137,7 @@ var init_signatures = __esm({
26866
28137
 
26867
28138
  // packages/core/dist/council/lessons/recordFailure.js
26868
28139
  import { appendFileSync } from "node:fs";
26869
- import { join as join9 } from "node:path";
28140
+ import { join as join11 } from "node:path";
26870
28141
  function methodologyFor(check2) {
26871
28142
  return METHODOLOGY[check2.id] ?? `When ${check2.id} fails, fix the underlying issue and cite grep/tool evidence before claiming PASS.`;
26872
28143
  }
@@ -26876,7 +28147,7 @@ function keywordsFrom(check2, signature) {
26876
28147
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
26877
28148
  }
26878
28149
  function writeLesson(zelariRoot, lesson) {
26879
- const path72 = join9(zelariRoot, LESSONS_FILE);
28150
+ const path72 = join11(zelariRoot, LESSONS_FILE);
26880
28151
  appendFileSync(path72, `${JSON.stringify(lesson)}
26881
28152
  `, "utf8");
26882
28153
  }
@@ -27016,7 +28287,7 @@ var init_recallLessons = __esm({
27016
28287
  var init_lessons = __esm({
27017
28288
  "packages/core/dist/council/lessons/index.js"() {
27018
28289
  "use strict";
27019
- init_types4();
28290
+ init_types5();
27020
28291
  init_io();
27021
28292
  init_isAnswerLeak();
27022
28293
  init_signatures();
@@ -27026,7 +28297,7 @@ var init_lessons = __esm({
27026
28297
  });
27027
28298
 
27028
28299
  // packages/core/dist/council/completion/types.js
27029
- var init_types5 = __esm({
28300
+ var init_types6 = __esm({
27030
28301
  "packages/core/dist/council/completion/types.js"() {
27031
28302
  "use strict";
27032
28303
  }
@@ -27034,7 +28305,7 @@ var init_types5 = __esm({
27034
28305
 
27035
28306
  // packages/core/dist/council/completion/buildCompletion.js
27036
28307
  import { writeFileSync as writeFileSync9 } from "node:fs";
27037
- import { join as join10 } from "node:path";
28308
+ import { join as join12 } from "node:path";
27038
28309
  function openFailsFromReport(report) {
27039
28310
  if (!report)
27040
28311
  return [];
@@ -27093,7 +28364,7 @@ function buildCouncilCompletion(input) {
27093
28364
  };
27094
28365
  }
27095
28366
  function writeCouncilCompletion(zelariRoot, completion) {
27096
- const outPath = join10(zelariRoot, "completion.json");
28367
+ const outPath = join12(zelariRoot, "completion.json");
27097
28368
  writeFileSync9(outPath, JSON.stringify(completion, null, 2), "utf8");
27098
28369
  return outPath;
27099
28370
  }
@@ -27107,7 +28378,7 @@ var init_buildCompletion = __esm({
27107
28378
  var init_completion2 = __esm({
27108
28379
  "packages/core/dist/council/completion/index.js"() {
27109
28380
  "use strict";
27110
- init_types5();
28381
+ init_types6();
27111
28382
  init_buildCompletion();
27112
28383
  }
27113
28384
  });
@@ -27428,7 +28699,7 @@ var init_council = __esm({
27428
28699
 
27429
28700
  // packages/core/dist/memory/types.js
27430
28701
  var MEMORY_SCHEMA_VERSION, MEMORY_KINDS, MEMORY_STATUSES, MEMORY_RELATIONS, MEMORY_VISIBILITIES;
27431
- var init_types6 = __esm({
28702
+ var init_types7 = __esm({
27432
28703
  "packages/core/dist/memory/types.js"() {
27433
28704
  "use strict";
27434
28705
  MEMORY_SCHEMA_VERSION = 1;
@@ -27479,7 +28750,7 @@ var init_schemas3 = __esm({
27479
28750
  "packages/core/dist/memory/schemas.js"() {
27480
28751
  "use strict";
27481
28752
  init_zod();
27482
- init_types6();
28753
+ init_types7();
27483
28754
  isoDate = external_exports.string().datetime({ offset: true });
27484
28755
  unit = external_exports.number().finite().min(0).max(1);
27485
28756
  metadata = external_exports.record(external_exports.string(), external_exports.unknown()).refine((value) => {
@@ -28455,18 +29726,18 @@ var init_service = __esm({
28455
29726
  };
28456
29727
  }
28457
29728
  async doctor() {
28458
- const base = await (this.backend.doctor?.() ?? Promise.resolve({
29729
+ const base2 = await (this.backend.doctor?.() ?? Promise.resolve({
28459
29730
  ok: true,
28460
29731
  backend: "custom",
28461
29732
  checks: [{ name: "backend", ok: true, detail: "No diagnostic API exposed." }]
28462
29733
  }));
28463
29734
  if (!this.semantic)
28464
- return base;
29735
+ return base2;
28465
29736
  const semantic = await this.semantic.status();
28466
29737
  return {
28467
- ...base,
29738
+ ...base2,
28468
29739
  checks: [
28469
- ...base.checks,
29740
+ ...base2.checks,
28470
29741
  {
28471
29742
  name: "semantic",
28472
29743
  ok: semantic.state !== "degraded",
@@ -28770,7 +30041,7 @@ var init_noop = __esm({
28770
30041
  var init_memory = __esm({
28771
30042
  "packages/core/dist/memory/index.js"() {
28772
30043
  "use strict";
28773
- init_types6();
30044
+ init_types7();
28774
30045
  init_schemas3();
28775
30046
  init_scoring();
28776
30047
  init_policies();
@@ -29287,10 +30558,10 @@ function extractRequirementsBlock(text) {
29287
30558
  }
29288
30559
  }
29289
30560
  function parsePersonaVerdict(text) {
29290
- const base = parseVerifyVerdict(text);
30561
+ const base2 = parseVerifyVerdict(text);
29291
30562
  return {
29292
- verdict: base.verdict,
29293
- findings: base.findings,
30563
+ verdict: base2.verdict,
30564
+ findings: base2.findings,
29294
30565
  requirements: extractRequirementsBlock(text),
29295
30566
  // Bennett's weakness = "how little the reviewer's free text asserts"
29296
30567
  // (arXiv:2301.12987). `weaknessScoreFromText` is the weakness form
@@ -29492,7 +30763,7 @@ var init_personas = __esm({
29492
30763
 
29493
30764
  // packages/core/dist/kraken/runtime/types.js
29494
30765
  var PlanError;
29495
- var init_types7 = __esm({
30766
+ var init_types8 = __esm({
29496
30767
  "packages/core/dist/kraken/runtime/types.js"() {
29497
30768
  "use strict";
29498
30769
  PlanError = class extends Error {
@@ -29590,7 +30861,7 @@ var MAX_BUNDLE_BYTES, FORBIDDEN_TOKENS;
29590
30861
  var init_sandbox = __esm({
29591
30862
  "packages/core/dist/kraken/runtime/sandbox.js"() {
29592
30863
  "use strict";
29593
- init_types7();
30864
+ init_types8();
29594
30865
  MAX_BUNDLE_BYTES = 256 * 1024;
29595
30866
  FORBIDDEN_TOKENS = [
29596
30867
  /\bprocess\b/,
@@ -29653,7 +30924,7 @@ var init_runner = __esm({
29653
30924
  "use strict";
29654
30925
  init_verdict();
29655
30926
  init_personas();
29656
- init_types7();
30927
+ init_types8();
29657
30928
  DEFAULT_MAX_TENTACLES = 200;
29658
30929
  DEFAULT_PLAN_TIMEOUT_MS = 30 * 6e4;
29659
30930
  ScriptRunner = class {
@@ -29837,7 +31108,7 @@ var init_runner = __esm({
29837
31108
  var init_runtime = __esm({
29838
31109
  "packages/core/dist/kraken/runtime/index.js"() {
29839
31110
  "use strict";
29840
- init_types7();
31111
+ init_types8();
29841
31112
  init_sandbox();
29842
31113
  init_runner();
29843
31114
  }
@@ -29858,7 +31129,7 @@ var init_kraken = __esm({
29858
31129
 
29859
31130
  // packages/core/dist/session/types.js
29860
31131
  var SESSION_SCHEMA_VERSION, SessionActorSchema, SESSION_EVENT_KINDS, SessionEventEnvelopeSchema, ACTOR_USER, ACTOR_AGENT, ACTOR_SYSTEM;
29861
- var init_types8 = __esm({
31132
+ var init_types9 = __esm({
29862
31133
  "packages/core/dist/session/types.js"() {
29863
31134
  "use strict";
29864
31135
  init_zod();
@@ -30422,7 +31693,7 @@ var SessionLogLockedError, DEFAULT_STALE_LOCK_MS, SessionLogWriter;
30422
31693
  var init_writer = __esm({
30423
31694
  "packages/core/dist/session/writer.js"() {
30424
31695
  "use strict";
30425
- init_types8();
31696
+ init_types9();
30426
31697
  SessionLogLockedError = class extends Error {
30427
31698
  sessionPath;
30428
31699
  owner;
@@ -30734,7 +32005,7 @@ function buildProjection(events, issues = []) {
30734
32005
  var init_replay = __esm({
30735
32006
  "packages/core/dist/session/replay.js"() {
30736
32007
  "use strict";
30737
- init_types8();
32008
+ init_types9();
30738
32009
  init_modelSurface();
30739
32010
  init_recovery();
30740
32011
  }
@@ -30759,7 +32030,7 @@ var init_store = __esm({
30759
32030
  "use strict";
30760
32031
  init_writer();
30761
32032
  init_replay();
30762
- init_types8();
32033
+ init_types9();
30763
32034
  SessionStore = class _SessionStore {
30764
32035
  baseDir;
30765
32036
  constructor(baseDir) {
@@ -30915,7 +32186,7 @@ async function lineageOf(store6, sessionId2) {
30915
32186
  var init_lineage = __esm({
30916
32187
  "packages/core/dist/session/lineage.js"() {
30917
32188
  "use strict";
30918
- init_types8();
32189
+ init_types9();
30919
32190
  init_replay();
30920
32191
  init_recovery();
30921
32192
  }
@@ -31398,7 +32669,7 @@ var init_taskContract = __esm({
31398
32669
  var init_session = __esm({
31399
32670
  "packages/core/dist/session/index.js"() {
31400
32671
  "use strict";
31401
- init_types8();
32672
+ init_types9();
31402
32673
  init_modelSurface();
31403
32674
  init_compaction();
31404
32675
  init_agentAdapter();
@@ -31734,7 +33005,7 @@ var init_worktreeWorkspace = __esm({
31734
33005
  });
31735
33006
 
31736
33007
  // packages/core/dist/runtime/profiles.js
31737
- import { createHash as createHash3 } from "node:crypto";
33008
+ import { createHash as createHash5 } from "node:crypto";
31738
33009
  function resolveProfile(id3) {
31739
33010
  const profile = BUILT_IN_PROFILES[id3];
31740
33011
  if (!profile)
@@ -31743,10 +33014,10 @@ function resolveProfile(id3) {
31743
33014
  }
31744
33015
  function toolManifestHash(tools) {
31745
33016
  const manifest = [...tools].sort().join(",");
31746
- return createHash3("sha256").update(manifest).digest("hex");
33017
+ return createHash5("sha256").update(manifest).digest("hex");
31747
33018
  }
31748
33019
  function profileHash(profile) {
31749
- return createHash3("sha256").update(stableStringify(profile)).digest("hex");
33020
+ return createHash5("sha256").update(stableStringify(profile)).digest("hex");
31750
33021
  }
31751
33022
  var ProfileSchema, MINIMAL_TOOLS, MINIMAL_V1, KRAKEN_V1, COUNCIL_V1, MISSION_V1, BUILT_IN_PROFILES, UnknownProfileError;
31752
33023
  var init_profiles = __esm({
@@ -31832,7 +33103,7 @@ function hashHarnessManifest(manifest) {
31832
33103
  function harnessInputHash(value) {
31833
33104
  return sha256Hex(stableStringify(value));
31834
33105
  }
31835
- function collectPaths2(base, a, b, out, depth = 0) {
33106
+ function collectPaths2(base2, a, b, out, depth = 0) {
31836
33107
  if (depth > 6)
31837
33108
  return;
31838
33109
  if (stableStringify(a) === stableStringify(b))
@@ -31841,11 +33112,11 @@ function collectPaths2(base, a, b, out, depth = 0) {
31841
33112
  const objB = b && typeof b === "object" && !Array.isArray(b) ? b : null;
31842
33113
  if (objA && objB) {
31843
33114
  for (const key of [.../* @__PURE__ */ new Set([...Object.keys(objA), ...Object.keys(objB)])].sort()) {
31844
- collectPaths2(base ? `${base}.${key}` : key, objA[key], objB[key], out, depth + 1);
33115
+ collectPaths2(base2 ? `${base2}.${key}` : key, objA[key], objB[key], out, depth + 1);
31845
33116
  }
31846
33117
  return;
31847
33118
  }
31848
- out.push(base);
33119
+ out.push(base2);
31849
33120
  }
31850
33121
  function diffHarnessManifest(oldManifest, newManifest) {
31851
33122
  const changed = [];
@@ -32005,12 +33276,12 @@ var init_executionContext = __esm({
32005
33276
  });
32006
33277
 
32007
33278
  // packages/core/dist/runtime/resourcePolicy.js
32008
- import { createHash as createHash4 } from "node:crypto";
33279
+ import { createHash as createHash6 } from "node:crypto";
32009
33280
  function defaultResourcePolicy(profileId) {
32010
33281
  return PROFILE_RESOURCE_POLICIES[profileId] ?? PROFILE_RESOURCE_POLICIES["kraken/v1"];
32011
33282
  }
32012
33283
  function resourcePolicyHash(policy) {
32013
- return createHash4("sha256").update(stableStringify(ResourcePolicySchema.parse(policy))).digest("hex");
33284
+ return createHash6("sha256").update(stableStringify(ResourcePolicySchema.parse(policy))).digest("hex");
32014
33285
  }
32015
33286
  var BudgetPressureSchema, ResourceStageSchema, PressureThresholdsSchema, DEFAULT_PRESSURE_THRESHOLDS, ResourcePolicySchema, PROFILE_RESOURCE_POLICIES;
32016
33287
  var init_resourcePolicy = __esm({
@@ -32155,6 +33426,257 @@ var init_resourceBudget = __esm({
32155
33426
  }
32156
33427
  });
32157
33428
 
33429
+ // packages/core/dist/runtime/observers/composeObservers.js
33430
+ function composeObservers(observers) {
33431
+ const composed = {};
33432
+ for (const hook of HOOKS) {
33433
+ composed[hook] = async (event) => {
33434
+ const results = [];
33435
+ for (const observer of observers) {
33436
+ const callback = observer[hook];
33437
+ if (typeof callback === "function") {
33438
+ results.push(await callback(event) ?? CONTINUE);
33439
+ }
33440
+ }
33441
+ return resolveInterventions(results);
33442
+ };
33443
+ }
33444
+ return composed;
33445
+ }
33446
+ var HOOKS;
33447
+ var init_composeObservers = __esm({
33448
+ "packages/core/dist/runtime/observers/composeObservers.js"() {
33449
+ "use strict";
33450
+ init_types2();
33451
+ init_resolve();
33452
+ HOOKS = [
33453
+ "onRunStart",
33454
+ "onModelAttempt",
33455
+ "onModelDelta",
33456
+ "onModelResponse",
33457
+ "onToolCall",
33458
+ "onToolResult",
33459
+ "onTurnEnd",
33460
+ "onRunEnd",
33461
+ "onCancelled"
33462
+ ];
33463
+ }
33464
+ });
33465
+
33466
+ // packages/core/dist/runtime/observers/index.js
33467
+ var init_observers = __esm({
33468
+ "packages/core/dist/runtime/observers/index.js"() {
33469
+ "use strict";
33470
+ init_types2();
33471
+ init_resolve();
33472
+ init_ObserverBus();
33473
+ init_composeObservers();
33474
+ init_TraceObserver();
33475
+ init_MetricsObserver();
33476
+ }
33477
+ });
33478
+
33479
+ // packages/core/dist/runtime/guards/index.js
33480
+ var init_guards = __esm({
33481
+ "packages/core/dist/runtime/guards/index.js"() {
33482
+ "use strict";
33483
+ init_RepetitionGuard();
33484
+ init_FailureSignatureGuard();
33485
+ init_DuplicateSearchGuard();
33486
+ init_NoProgressGuard();
33487
+ init_ReasoningWatchdog();
33488
+ }
33489
+ });
33490
+
33491
+ // packages/core/dist/runtime/recorder/retention.js
33492
+ import { readdir, readFile, rm, stat } from "node:fs/promises";
33493
+ import { join as join13 } from "node:path";
33494
+ function runRetentionFromEnv() {
33495
+ const parseDays = Number(process.env.ZELARI_RUN_RETENTION_DAYS);
33496
+ const parseMb = Number(process.env.ZELARI_RUN_RETENTION_MAX_MB);
33497
+ return {
33498
+ maxAgeDays: Number.isFinite(parseDays) && parseDays > 0 ? parseDays : DEFAULT_RUN_RETENTION_DAYS,
33499
+ maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
33500
+ };
33501
+ }
33502
+ async function dirSize(path72) {
33503
+ let total = 0;
33504
+ let entries;
33505
+ try {
33506
+ entries = await readdir(path72, { withFileTypes: true });
33507
+ } catch {
33508
+ return 0;
33509
+ }
33510
+ for (const entry of entries) {
33511
+ const child = join13(path72, entry.name);
33512
+ if (entry.isDirectory())
33513
+ total += await dirSize(child);
33514
+ else {
33515
+ try {
33516
+ total += (await stat(child)).size;
33517
+ } catch {
33518
+ }
33519
+ }
33520
+ }
33521
+ return total;
33522
+ }
33523
+ async function enforceRunRetention(runsDir, options = {}) {
33524
+ const now = options.now ?? Date.now;
33525
+ const maxAgeDays = options.maxAgeDays ?? DEFAULT_RUN_RETENTION_DAYS;
33526
+ const maxTotalBytes = options.maxTotalBytes ?? DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024;
33527
+ const result = { deleted: [], freedBytes: 0, kept: 0 };
33528
+ let entries;
33529
+ try {
33530
+ entries = await readdir(runsDir, { withFileTypes: true });
33531
+ } catch {
33532
+ return result;
33533
+ }
33534
+ const infos = [];
33535
+ for (const entry of entries) {
33536
+ if (!entry.isDirectory())
33537
+ continue;
33538
+ const path72 = join13(runsDir, entry.name);
33539
+ let startedAt = 0;
33540
+ let endedAt;
33541
+ let completed = false;
33542
+ try {
33543
+ const manifest = JSON.parse(await readFile(join13(path72, "manifest.json"), "utf8"));
33544
+ startedAt = manifest.startedAt ?? 0;
33545
+ endedAt = manifest.endedAt;
33546
+ completed = Boolean(endedAt) && manifest.status !== "running";
33547
+ } catch {
33548
+ completed = false;
33549
+ }
33550
+ infos.push({ name: entry.name, path: path72, startedAt, endedAt, completed, bytes: await dirSize(path72) });
33551
+ }
33552
+ const remove = async (info) => {
33553
+ await rm(info.path, { recursive: true, force: true });
33554
+ result.deleted.push(info.name);
33555
+ result.freedBytes += info.bytes;
33556
+ };
33557
+ const ageCutoffMs = maxAgeDays * 24 * 60 * 60 * 1e3;
33558
+ const expired = infos.filter((info) => info.completed && info.startedAt > 0 && now() - info.startedAt > ageCutoffMs).sort((a, b) => a.startedAt - b.startedAt);
33559
+ for (const info of expired)
33560
+ await remove(info);
33561
+ const survivors = infos.filter((info) => !result.deleted.includes(info.name));
33562
+ let totalBytes = survivors.reduce((sum, info) => sum + info.bytes, 0);
33563
+ const byOldest = survivors.filter((info) => info.completed).sort((a, b) => a.startedAt - b.startedAt);
33564
+ for (const info of byOldest) {
33565
+ if (totalBytes <= maxTotalBytes)
33566
+ break;
33567
+ totalBytes -= info.bytes;
33568
+ await remove(info);
33569
+ }
33570
+ result.kept = infos.filter((info) => !result.deleted.includes(info.name)).length;
33571
+ return result;
33572
+ }
33573
+ var DEFAULT_RUN_RETENTION_DAYS, DEFAULT_RUN_RETENTION_MAX_MB;
33574
+ var init_retention = __esm({
33575
+ "packages/core/dist/runtime/recorder/retention.js"() {
33576
+ "use strict";
33577
+ DEFAULT_RUN_RETENTION_DAYS = 30;
33578
+ DEFAULT_RUN_RETENTION_MAX_MB = 2048;
33579
+ }
33580
+ });
33581
+
33582
+ // packages/core/dist/runtime/recorder/index.js
33583
+ var init_recorder = __esm({
33584
+ "packages/core/dist/runtime/recorder/index.js"() {
33585
+ "use strict";
33586
+ init_Redactor();
33587
+ init_RunRecorder();
33588
+ init_recorderObserver();
33589
+ init_retention();
33590
+ }
33591
+ });
33592
+
33593
+ // packages/core/dist/runtime/controls/types.js
33594
+ function isSteerControlEvent(event) {
33595
+ return event.type === "steer";
33596
+ }
33597
+ function isFollowUpControlEvent(event) {
33598
+ return event.type === "follow_up";
33599
+ }
33600
+ function isCancelControlEvent(event) {
33601
+ return event.type === "cancel";
33602
+ }
33603
+ var init_types10 = __esm({
33604
+ "packages/core/dist/runtime/controls/types.js"() {
33605
+ "use strict";
33606
+ }
33607
+ });
33608
+
33609
+ // packages/core/dist/runtime/controls/RuntimeControlQueue.js
33610
+ var RuntimeControlQueue;
33611
+ var init_RuntimeControlQueue = __esm({
33612
+ "packages/core/dist/runtime/controls/RuntimeControlQueue.js"() {
33613
+ "use strict";
33614
+ RuntimeControlQueue = class {
33615
+ items = [];
33616
+ /**
33617
+ * Optional drain listener (CLI control bridge, §24): fired synchronously
33618
+ * whenever a drain* method actually removes events, with the exact events
33619
+ * consumed — lets the host emit `control_applied` at the true boundary.
33620
+ */
33621
+ onDrained;
33622
+ enqueue(event) {
33623
+ this.items.push(event);
33624
+ }
33625
+ /** Next event in arrival order, without removing it. */
33626
+ peek() {
33627
+ return this.items[0];
33628
+ }
33629
+ get size() {
33630
+ return this.items.length;
33631
+ }
33632
+ /**
33633
+ * Remove a not-yet-applied control by id (Desktop queue UI, §32: removal
33634
+ * stays possible until `control_applied`). Returns true when removed.
33635
+ */
33636
+ remove(controlId) {
33637
+ const index = this.items.findIndex((event) => event.id === controlId);
33638
+ if (index === -1)
33639
+ return false;
33640
+ this.items.splice(index, 1);
33641
+ return true;
33642
+ }
33643
+ clear() {
33644
+ this.items = [];
33645
+ }
33646
+ /** All pending steers in arrival order; removes them from the queue. */
33647
+ drainSteers() {
33648
+ return this.drainByType("steer");
33649
+ }
33650
+ /** All pending follow-ups in arrival order; removes them from the queue. */
33651
+ drainFollowUps() {
33652
+ return this.drainByType("follow_up");
33653
+ }
33654
+ /** All pending cancels in arrival order; removes them from the queue. */
33655
+ drainCancels() {
33656
+ return this.drainByType("cancel");
33657
+ }
33658
+ drainByType(type) {
33659
+ const matched = this.items.filter((event) => event.type === type);
33660
+ if (matched.length > 0) {
33661
+ this.items = this.items.filter((event) => event.type !== type);
33662
+ this.onDrained?.(matched);
33663
+ }
33664
+ return matched;
33665
+ }
33666
+ };
33667
+ }
33668
+ });
33669
+
33670
+ // packages/core/dist/runtime/controls/index.js
33671
+ var init_controls = __esm({
33672
+ "packages/core/dist/runtime/controls/index.js"() {
33673
+ "use strict";
33674
+ init_types10();
33675
+ init_RuntimeControlQueue();
33676
+ init_SteeringObserver();
33677
+ }
33678
+ });
33679
+
32158
33680
  // packages/core/dist/runtime/index.js
32159
33681
  var init_runtime2 = __esm({
32160
33682
  "packages/core/dist/runtime/index.js"() {
@@ -32168,6 +33690,295 @@ var init_runtime2 = __esm({
32168
33690
  init_executionContext();
32169
33691
  init_resourcePolicy();
32170
33692
  init_resourceBudget();
33693
+ init_observers();
33694
+ init_guards();
33695
+ init_recorder();
33696
+ init_controls();
33697
+ }
33698
+ });
33699
+
33700
+ // packages/core/dist/context/ContextPolicy.js
33701
+ function contextPolicyForRole(role) {
33702
+ return ROLE_POLICIES[role] ?? DEFAULT_CONTEXT_POLICY;
33703
+ }
33704
+ var KRAKEN_LEAD_POLICY, KRAKEN_EXPLORE_POLICY, KRAKEN_GENERAL_POLICY, KRAKEN_VERIFY_POLICY, DEFAULT_CONTEXT_POLICY, ROLE_POLICIES, CONTEXT_POLICY_VERSION;
33705
+ var init_ContextPolicy = __esm({
33706
+ "packages/core/dist/context/ContextPolicy.js"() {
33707
+ "use strict";
33708
+ KRAKEN_LEAD_POLICY = {
33709
+ history: "recent",
33710
+ includeParentSummary: false,
33711
+ includeDurableState: true,
33712
+ includeGraphState: true,
33713
+ includeVerificationState: true,
33714
+ toolResults: "projected"
33715
+ };
33716
+ KRAKEN_EXPLORE_POLICY = {
33717
+ history: "summary",
33718
+ includeParentSummary: true,
33719
+ includeDurableState: true,
33720
+ includeGraphState: false,
33721
+ includeVerificationState: false,
33722
+ toolResults: "projected"
33723
+ };
33724
+ KRAKEN_GENERAL_POLICY = {
33725
+ history: "summary",
33726
+ includeParentSummary: true,
33727
+ includeDurableState: true,
33728
+ includeGraphState: true,
33729
+ includeVerificationState: false,
33730
+ toolResults: "projected"
33731
+ };
33732
+ KRAKEN_VERIFY_POLICY = {
33733
+ history: "summary",
33734
+ includeParentSummary: true,
33735
+ includeDurableState: false,
33736
+ includeGraphState: true,
33737
+ includeVerificationState: true,
33738
+ toolResults: "projected"
33739
+ };
33740
+ DEFAULT_CONTEXT_POLICY = {
33741
+ history: "recent",
33742
+ includeParentSummary: false,
33743
+ includeDurableState: false,
33744
+ includeGraphState: false,
33745
+ includeVerificationState: false,
33746
+ toolResults: "projected"
33747
+ };
33748
+ ROLE_POLICIES = {
33749
+ lead: KRAKEN_LEAD_POLICY,
33750
+ explore: KRAKEN_EXPLORE_POLICY,
33751
+ general: KRAKEN_GENERAL_POLICY,
33752
+ verify: KRAKEN_VERIFY_POLICY,
33753
+ council: DEFAULT_CONTEXT_POLICY,
33754
+ mission: DEFAULT_CONTEXT_POLICY
33755
+ };
33756
+ CONTEXT_POLICY_VERSION = 1;
33757
+ }
33758
+ });
33759
+
33760
+ // packages/core/dist/context/ContextProjector.js
33761
+ function groupTurnUnits(history2) {
33762
+ const units = [];
33763
+ let i = 0;
33764
+ while (i < history2.length) {
33765
+ const msg = history2[i];
33766
+ if (msg.role === "assistant") {
33767
+ const unit2 = [msg];
33768
+ i++;
33769
+ while (i < history2.length && history2[i].role === "tool") {
33770
+ unit2.push(history2[i]);
33771
+ i++;
33772
+ }
33773
+ units.push({ messages: unit2, kind: "assistant" });
33774
+ } else if (msg.role === "tool") {
33775
+ units.push({ messages: [msg], kind: "orphan-tool" });
33776
+ i++;
33777
+ } else {
33778
+ units.push({ messages: [msg], kind: msg.role === "user" ? "user" : "other" });
33779
+ i++;
33780
+ }
33781
+ }
33782
+ return units;
33783
+ }
33784
+ function firstLine(text, max) {
33785
+ const line = text.split("\n", 1)[0] ?? "";
33786
+ return line.length > max ? `${line.slice(0, max)}\u2026` : line;
33787
+ }
33788
+ function renderHistoryDigest(units) {
33789
+ const lines = units.map((unit2) => {
33790
+ const head = unit2.messages[0];
33791
+ if (unit2.kind === "user")
33792
+ return `- user: ${firstLine(head.content, 120)}`;
33793
+ if (unit2.kind === "assistant") {
33794
+ const tools = head.toolCalls?.map((t) => t.name).join(", ");
33795
+ return tools ? `- assistant (tools: ${tools})` : `- assistant: ${firstLine(head.content, 120)}`;
33796
+ }
33797
+ if (unit2.kind === "orphan-tool")
33798
+ return `- tool result ${head.toolCallId ?? "?"}: omitted`;
33799
+ return `- ${head.role}: ${firstLine(head.content, 120)}`;
33800
+ });
33801
+ return `[Context digest \u2014 earlier turns omitted]
33802
+ ${lines.join("\n")}`;
33803
+ }
33804
+ function truncateProjected(content, maxChars) {
33805
+ if (content.length <= maxChars)
33806
+ return content;
33807
+ const markerLen = TRUNCATION_MARKER.length;
33808
+ const budget = Math.max(maxChars - markerLen, HEAD_TAIL_MIN * 2);
33809
+ const head = Math.floor(budget / 2);
33810
+ const tail2 = budget - head;
33811
+ return `${content.slice(0, head)}${TRUNCATION_MARKER}${content.slice(-tail2)}`;
33812
+ }
33813
+ function summarizeToolResult(content) {
33814
+ const line = firstLine(content, 200);
33815
+ return `[tool result \u2014 ${content.length} chars, first line] ${line}`;
33816
+ }
33817
+ function applyToolResultPolicy(messages, policy) {
33818
+ if (policy.toolResults === "full")
33819
+ return { messages, truncated: 0 };
33820
+ const maxChars = policy.maxToolResultChars ?? DEFAULT_MAX_TOOL_RESULT_CHARS;
33821
+ let truncated = 0;
33822
+ const out = messages.map((msg) => {
33823
+ if (msg.role !== "tool")
33824
+ return msg;
33825
+ if (policy.toolResults === "summary-only") {
33826
+ truncated++;
33827
+ return { ...msg, content: summarizeToolResult(msg.content) };
33828
+ }
33829
+ if (msg.content.length > maxChars)
33830
+ truncated++;
33831
+ return { ...msg, content: truncateProjected(msg.content, maxChars) };
33832
+ });
33833
+ return { messages: out, truncated };
33834
+ }
33835
+ function estimateTokens(messages) {
33836
+ let chars = 0;
33837
+ for (const m of messages) {
33838
+ chars += m.content.length;
33839
+ if (m.toolCalls)
33840
+ chars += JSON.stringify(m.toolCalls).length;
33841
+ if (m.reasoningContent)
33842
+ chars += m.reasoningContent.length;
33843
+ }
33844
+ return Math.ceil(chars / 4);
33845
+ }
33846
+ function projectContext(transcript, policy) {
33847
+ const system = transcript.filter((m) => m.role === "system");
33848
+ const body = transcript.filter((m) => m.role !== "system");
33849
+ let lastUserIdx = -1;
33850
+ for (let i = body.length - 1; i >= 0; i--) {
33851
+ if (body[i].role === "user") {
33852
+ lastUserIdx = i;
33853
+ break;
33854
+ }
33855
+ }
33856
+ const history2 = lastUserIdx >= 0 ? body.slice(0, lastUserIdx) : body.slice();
33857
+ const tail2 = lastUserIdx >= 0 ? body.slice(lastUserIdx) : [];
33858
+ const units = groupTurnUnits(history2);
33859
+ const maxTurns = policy.maxHistoryTurns ?? DEFAULT_MAX_HISTORY_TURNS;
33860
+ let selected = [];
33861
+ let digest = false;
33862
+ let includedUnits = 0;
33863
+ if (policy.history === "full") {
33864
+ selected = history2.slice();
33865
+ includedUnits = units.length;
33866
+ } else if (policy.history === "recent") {
33867
+ const keep = units.slice(-maxTurns);
33868
+ includedUnits = keep.length;
33869
+ selected = keep.flatMap((u) => u.messages);
33870
+ } else if (policy.history === "summary") {
33871
+ const recentUnits = units.slice(-2);
33872
+ includedUnits = recentUnits.length;
33873
+ selected = recentUnits.flatMap((u) => u.messages);
33874
+ if (units.length > recentUnits.length) {
33875
+ digest = true;
33876
+ selected.unshift({
33877
+ role: "user",
33878
+ content: renderHistoryDigest(units.slice(0, units.length - recentUnits.length))
33879
+ });
33880
+ }
33881
+ }
33882
+ const includedMessages = selected.length + tail2.length + system.length;
33883
+ const assembled = [...system, ...selected, ...tail2];
33884
+ const { messages, truncated } = applyToolResultPolicy(assembled, policy);
33885
+ return {
33886
+ messages,
33887
+ stats: {
33888
+ estimatedTokens: estimateTokens(messages),
33889
+ includedMessages,
33890
+ omittedMessages: Math.max(0, history2.length - selected.length - (digest ? 1 : 0)),
33891
+ truncatedToolResults: truncated,
33892
+ digest
33893
+ }
33894
+ };
33895
+ }
33896
+ var DEFAULT_MAX_TOOL_RESULT_CHARS, DEFAULT_MAX_HISTORY_TURNS, TRUNCATION_MARKER, HEAD_TAIL_MIN;
33897
+ var init_ContextProjector = __esm({
33898
+ "packages/core/dist/context/ContextProjector.js"() {
33899
+ "use strict";
33900
+ DEFAULT_MAX_TOOL_RESULT_CHARS = 12e3;
33901
+ DEFAULT_MAX_HISTORY_TURNS = 20;
33902
+ TRUNCATION_MARKER = "\n[\u2026tool output truncated by context projection\u2026]\n";
33903
+ HEAD_TAIL_MIN = 200;
33904
+ }
33905
+ });
33906
+
33907
+ // packages/core/dist/context/parentContext.js
33908
+ function renderCompact(m) {
33909
+ if (m.role === "user") {
33910
+ const text2 = m.content.trim();
33911
+ return text2 ? `user \u2014 ${text2}` : null;
33912
+ }
33913
+ if (m.role === "assistant") {
33914
+ const tools = m.toolCalls?.length ? ` (requested: ${m.toolCalls.map((t) => t.name).join(", ")})` : "";
33915
+ const text2 = m.content.trim();
33916
+ return text2 ? `assistant${tools} \u2014 ${text2}` : tools ? `assistant${tools}` : null;
33917
+ }
33918
+ if (m.role === "tool") {
33919
+ const text2 = m.content.trim();
33920
+ return text2 ? `tool result \u2014 ${text2}` : null;
33921
+ }
33922
+ const text = m.content.trim();
33923
+ return text ? `${m.role} \u2014 ${text}` : null;
33924
+ }
33925
+ function parentContextForRole(role, transcript, opts) {
33926
+ const policy = contextPolicyForRole(role);
33927
+ const body = transcript.filter((m) => m.role !== "system");
33928
+ if (!policy.includeParentSummary || body.length === 0)
33929
+ return null;
33930
+ const projected = projectContext([...body], {
33931
+ ...policy,
33932
+ history: "summary",
33933
+ toolResults: "summary-only"
33934
+ });
33935
+ const lines = [];
33936
+ for (const m of projected.messages) {
33937
+ const line = renderCompact(m);
33938
+ if (line)
33939
+ lines.push(line);
33940
+ }
33941
+ if (lines.length === 0)
33942
+ return null;
33943
+ const maxChars = opts?.maxBlockChars ?? DEFAULT_MAX_BLOCK_CHARS;
33944
+ let body_ = lines.join("\n");
33945
+ let truncated = false;
33946
+ if (body_.length > maxChars) {
33947
+ body_ = body_.slice(0, maxChars) + TRUNCATION_TAIL;
33948
+ truncated = true;
33949
+ }
33950
+ const header = truncated ? "[Parent agent context \u2014 projected summary, full transcript not shared]" : "[Parent agent context \u2014 projected summary]";
33951
+ return {
33952
+ block: `---
33953
+ ${header}
33954
+
33955
+ ${body_}
33956
+ ---`,
33957
+ stats: {
33958
+ estimatedTokens: Math.ceil(body_.length / 4),
33959
+ sourceMessages: body.length,
33960
+ digest: projected.stats.digest
33961
+ }
33962
+ };
33963
+ }
33964
+ var DEFAULT_MAX_BLOCK_CHARS, TRUNCATION_TAIL;
33965
+ var init_parentContext = __esm({
33966
+ "packages/core/dist/context/parentContext.js"() {
33967
+ "use strict";
33968
+ init_ContextPolicy();
33969
+ init_ContextProjector();
33970
+ DEFAULT_MAX_BLOCK_CHARS = 6e3;
33971
+ TRUNCATION_TAIL = "\n[\u2026parent context truncated to fit the role budget\u2026]";
33972
+ }
33973
+ });
33974
+
33975
+ // packages/core/dist/context/index.js
33976
+ var init_context3 = __esm({
33977
+ "packages/core/dist/context/index.js"() {
33978
+ "use strict";
33979
+ init_ContextPolicy();
33980
+ init_ContextProjector();
33981
+ init_parentContext();
32171
33982
  }
32172
33983
  });
32173
33984
 
@@ -32178,7 +33989,7 @@ function isEventBackedEvidence(ref) {
32178
33989
  return typeof ref.seq === "number" && ref.seq > 0;
32179
33990
  }
32180
33991
  var CriterionSourceSchema, CommandCheckSchema, FileExistsCheckSchema, FileContainsCheckSchema, FileAbsentCheckSchema, NoneCheckSchema, DeterministicCheckSchema, CriterionSchema, EvidenceTierSchema, EvidenceRefSchema, EVENT_BACKED_EVIDENCE_TIERS, VerificationStatusSchema, VerificationSourceSchema, VerificationResultSchema;
32181
- var init_types9 = __esm({
33992
+ var init_types11 = __esm({
32182
33993
  "packages/core/dist/verification/types.js"() {
32183
33994
  "use strict";
32184
33995
  init_zod();
@@ -32344,9 +34155,9 @@ var init_scopeDiscipline = __esm({
32344
34155
  });
32345
34156
 
32346
34157
  // packages/core/dist/verification/engine.js
32347
- import { createHash as createHash5 } from "node:crypto";
34158
+ import { createHash as createHash7 } from "node:crypto";
32348
34159
  function defaultSha256(input) {
32349
- return createHash5("sha256").update(input).digest("hex");
34160
+ return createHash7("sha256").update(input).digest("hex");
32350
34161
  }
32351
34162
  function tail(text, max = 400) {
32352
34163
  return text.length > max ? `\u2026${text.slice(text.length - max)}` : text;
@@ -32419,13 +34230,13 @@ var init_engine = __esm({
32419
34230
  }
32420
34231
  async evaluateOne(criterion, scope) {
32421
34232
  const started = this.options.now?.() ?? Date.now();
32422
- const base = {
34233
+ const base2 = {
32423
34234
  criterionId: criterion.id,
32424
34235
  source: "deterministic-engine",
32425
34236
  evaluatedAt: started
32426
34237
  };
32427
34238
  const done = (patch) => ({
32428
- ...base,
34239
+ ...base2,
32429
34240
  ...patch,
32430
34241
  durationMs: Math.max(0, (this.options.now?.() ?? Date.now()) - started)
32431
34242
  });
@@ -32649,7 +34460,7 @@ var STRICT_ALL_POLICY, DETERMINISTIC_EVIDENCE_TIERS, STRICT_BUILD_POLICY, strict
32649
34460
  var init_completionPolicy = __esm({
32650
34461
  "packages/core/dist/verification/completionPolicy.js"() {
32651
34462
  "use strict";
32652
- init_types9();
34463
+ init_types11();
32653
34464
  STRICT_ALL_POLICY = { mode: "strict", required: "*" };
32654
34465
  DETERMINISTIC_EVIDENCE_TIERS = [
32655
34466
  "tool-output",
@@ -32922,9 +34733,9 @@ var init_verifier = __esm({
32922
34733
  /** Review a claimed completion. Degrades to a declared discrete fallback. */
32923
34734
  async reviewCompletion(request) {
32924
34735
  const effective = this.effectiveModel(request.session);
32925
- const base = { effectiveModel: effective, usedLogprobs: false };
34736
+ const base2 = { effectiveModel: effective, usedLogprobs: false };
32926
34737
  if (!this.config.enabled) {
32927
- return { ...base, verdict: "unknown", fallback: "discrete", rationale: "verifier disabled" };
34738
+ return { ...base2, verdict: "unknown", fallback: "discrete", rationale: "verifier disabled" };
32928
34739
  }
32929
34740
  let response;
32930
34741
  try {
@@ -32941,7 +34752,7 @@ var init_verifier = __esm({
32941
34752
  });
32942
34753
  } catch (err) {
32943
34754
  const review2 = {
32944
- ...base,
34755
+ ...base2,
32945
34756
  verdict: "unknown",
32946
34757
  fallback: "discrete",
32947
34758
  rationale: `verifier call failed: ${err instanceof Error ? err.message : String(err)}`
@@ -32952,7 +34763,7 @@ var init_verifier = __esm({
32952
34763
  const parsed = ReviewOutputSchema.safeParse(extractJson(response.text));
32953
34764
  if (!parsed.success) {
32954
34765
  const review2 = {
32955
- ...base,
34766
+ ...base2,
32956
34767
  verdict: "unknown",
32957
34768
  fallback: "discrete",
32958
34769
  rationale: "verifier output unparseable \u2014 declared discrete fallback",
@@ -32967,7 +34778,7 @@ var init_verifier = __esm({
32967
34778
  }
32968
34779
  const blockedByDeterministic = request.results.some((r) => r.status === "fail" && parsed.data.verdict === "confirmed");
32969
34780
  const review = {
32970
- ...base,
34781
+ ...base2,
32971
34782
  verdict: blockedByDeterministic ? "unknown" : parsed.data.verdict,
32972
34783
  score: parsed.data.score,
32973
34784
  rationale: blockedByDeterministic ? "cannot confirm while a deterministic check failed \u2014 downgraded to unknown" : parsed.data.rationale,
@@ -33141,7 +34952,7 @@ __export(verification_exports, {
33141
34952
  var init_verification2 = __esm({
33142
34953
  "packages/core/dist/verification/index.js"() {
33143
34954
  "use strict";
33144
- init_types9();
34955
+ init_types11();
33145
34956
  init_engine();
33146
34957
  init_completionPolicy();
33147
34958
  init_sessionEvidence();
@@ -33216,12 +35027,12 @@ function requiredBlockers(progress) {
33216
35027
  }
33217
35028
  function evaluateMissionContinuation(input) {
33218
35029
  const { progress, verifier, budget, userSteer } = input;
33219
- const base = { goalRewrite: false, doneByScore: false };
35030
+ const base2 = { goalRewrite: false, doneByScore: false };
33220
35031
  const trend = verifier ? { tier: verifier.tier, value: verifier.value } : void 0;
33221
35032
  const blockers = requiredBlockers(progress);
33222
35033
  if (userSteer === "stop") {
33223
35034
  return {
33224
- ...base,
35035
+ ...base2,
33225
35036
  recommendation: "wind-down",
33226
35037
  rationale: "user steer: explicit stop \u2014 winding down without claiming done",
33227
35038
  blockers,
@@ -33230,7 +35041,7 @@ function evaluateMissionContinuation(input) {
33230
35041
  }
33231
35042
  if (userSteer === "continue") {
33232
35043
  return {
33233
- ...base,
35044
+ ...base2,
33234
35045
  recommendation: "continue",
33235
35046
  rationale: "user steer: explicit continue \u2014 overrides any trend-derived advice",
33236
35047
  blockers,
@@ -33239,7 +35050,7 @@ function evaluateMissionContinuation(input) {
33239
35050
  }
33240
35051
  if (budget && budget.iterationsUsed >= budget.iterationsMax) {
33241
35052
  return {
33242
- ...base,
35053
+ ...base2,
33243
35054
  recommendation: "hold-for-user",
33244
35055
  rationale: `iteration budget exhausted (${budget.iterationsUsed}/${budget.iterationsMax}) \u2014 operator decides: raise budget or hand off`,
33245
35056
  blockers,
@@ -33249,7 +35060,7 @@ function evaluateMissionContinuation(input) {
33249
35060
  if (blockers.length > 0) {
33250
35061
  const trendNote = verifier ? ` \xB7 verifier trend recorded as context only (tier ${verifier.tier}${verifier.value === null ? "" : `, ${verifier.value.toFixed(2)}`}) \u2014 trend is never authority` : "";
33251
35062
  return {
33252
- ...base,
35063
+ ...base2,
33253
35064
  recommendation: "continue",
33254
35065
  rationale: `required criteria incomplete \u2192 no early-stop${trendNote}`,
33255
35066
  blockers,
@@ -33260,7 +35071,7 @@ function evaluateMissionContinuation(input) {
33260
35071
  const rejected = verifier?.verdict === "rejected" || blended !== null && blended <= REJECTED_BLEND;
33261
35072
  if (rejected) {
33262
35073
  return {
33263
- ...base,
35074
+ ...base2,
33264
35075
  recommendation: "hold-for-user",
33265
35076
  rationale: "deterministic evidence is complete and PASSing, but the verifier flags risk \u2014 attention requested; the deterministic verdict stands untouched",
33266
35077
  blockers: [],
@@ -33269,7 +35080,7 @@ function evaluateMissionContinuation(input) {
33269
35080
  }
33270
35081
  const confirmed = verifier?.verdict === "confirmed" || blended !== null && blended >= CONFIRMED_BLEND;
33271
35082
  return {
33272
- ...base,
35083
+ ...base2,
33273
35084
  recommendation: "wind-down",
33274
35085
  rationale: confirmed ? "required criteria pass with evidence and the verifier confirms \u2014 candidate done (final say: driver + CompletionPolicy)" : "required criteria pass with evidence \u2014 candidate done on deterministic evidence (final say: driver + CompletionPolicy)",
33275
35086
  blockers: [],
@@ -33287,14 +35098,14 @@ var init_continuationPolicy = __esm({
33287
35098
 
33288
35099
  // packages/core/dist/mission/budgetContinuation.js
33289
35100
  function evaluateBudgetContinuation(input) {
33290
- const base = { passByBudget: false };
35101
+ const base2 = { passByBudget: false };
33291
35102
  const { verdict, budget, pressure, latestGapKey, repairHistory } = input;
33292
35103
  if (verdict === "PASS") {
33293
- return { ...base, decision: "complete", rationale: "deterministic PASS \u2014 complete without spending residual budget" };
35104
+ return { ...base2, decision: "complete", rationale: "deterministic PASS \u2014 complete without spending residual budget" };
33294
35105
  }
33295
35106
  if (pressure === "critical" && budget.toolCalls.remaining <= budget.reserve.verification) {
33296
35107
  return {
33297
- ...base,
35108
+ ...base2,
33298
35109
  decision: "hold",
33299
35110
  rationale: `critical pressure (${budget.toolCalls.remaining}/${budget.toolCalls.limit} left, reserve ${budget.reserve.verification} protected) \u2014 hold for operator instead of a doomed repair`
33300
35111
  };
@@ -33303,11 +35114,11 @@ function evaluateBudgetContinuation(input) {
33303
35114
  if (sameGapRepairs >= MAX_SAME_GAP_REPAIRS) {
33304
35115
  const canPivot = budget.toolCalls.remaining - budget.reserve.verification >= 3;
33305
35116
  return canPivot ? {
33306
- ...base,
35117
+ ...base2,
33307
35118
  decision: "pivot",
33308
35119
  rationale: `gap "${latestGapKey}" unchanged after ${sameGapRepairs} repairs \u2014 pivot approach while budget remains`
33309
35120
  } : {
33310
- ...base,
35121
+ ...base2,
33311
35122
  decision: "hold",
33312
35123
  rationale: `gap "${latestGapKey}" unchanged after ${sameGapRepairs} repairs and no headroom to pivot \u2014 hold`
33313
35124
  };
@@ -33315,19 +35126,19 @@ function evaluateBudgetContinuation(input) {
33315
35126
  const repairHeadroom = budget.toolCalls.remaining - budget.reserve.verification;
33316
35127
  if (repairHeadroom > 0) {
33317
35128
  return {
33318
- ...base,
35129
+ ...base2,
33319
35130
  decision: "repair",
33320
35131
  rationale: `${repairHeadroom} spendable tool calls (pressure ${pressure}) \u2014 targeted repair on the known gap`
33321
35132
  };
33322
35133
  }
33323
35134
  if (budget.toolCalls.remaining > 0) {
33324
35135
  return {
33325
- ...base,
35136
+ ...base2,
33326
35137
  decision: "hold",
33327
35138
  rationale: "only the protected verification reserve remains \u2014 verify/finalize or hold, no further repair"
33328
35139
  };
33329
35140
  }
33330
- return { ...base, decision: "hold", rationale: "budget exhausted \u2014 hold (BLOCKED, never a false done)" };
35141
+ return { ...base2, decision: "hold", rationale: "budget exhausted \u2014 hold (BLOCKED, never a false done)" };
33331
35142
  }
33332
35143
  var MAX_SAME_GAP_REPAIRS;
33333
35144
  var init_budgetContinuation = __esm({
@@ -33357,7 +35168,7 @@ var init_version = __esm({
33357
35168
  });
33358
35169
 
33359
35170
  // packages/core/dist/runtime/fingerprints.js
33360
- import { createHash as createHash6 } from "node:crypto";
35171
+ import { createHash as createHash8 } from "node:crypto";
33361
35172
  function toolFingerprintHash(tools) {
33362
35173
  const canonical = [...tools].map((t) => ({
33363
35174
  name: t.name,
@@ -33366,7 +35177,7 @@ function toolFingerprintHash(tools) {
33366
35177
  ...t.outputContractVersion !== void 0 ? { outputContractVersion: t.outputContractVersion } : {},
33367
35178
  ...t.capabilityFlags !== void 0 ? { capabilityFlags: [...t.capabilityFlags].sort() } : {}
33368
35179
  })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
33369
- return createHash6("sha256").update(stableStringify(canonical)).digest("hex");
35180
+ return createHash8("sha256").update(stableStringify(canonical)).digest("hex");
33370
35181
  }
33371
35182
  function skillFingerprintHash(skills) {
33372
35183
  const canonical = [...skills].map((s) => ({
@@ -33374,7 +35185,7 @@ function skillFingerprintHash(skills) {
33374
35185
  ...s.version !== void 0 ? { version: s.version } : {},
33375
35186
  ...s.contentDigest !== void 0 ? { contentDigest: s.contentDigest } : {}
33376
35187
  })).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
33377
- return createHash6("sha256").update(stableStringify(canonical)).digest("hex");
35188
+ return createHash8("sha256").update(stableStringify(canonical)).digest("hex");
33378
35189
  }
33379
35190
  var init_fingerprints = __esm({
33380
35191
  "packages/core/dist/runtime/fingerprints.js"() {
@@ -33405,17 +35216,22 @@ __export(dist_exports, {
33405
35216
  CODING_SKILL_CATALOG: () => CODING_SKILL_CATALOG,
33406
35217
  COLLABORATION_DIRECTIVE: () => COLLABORATION_DIRECTIVE,
33407
35218
  COMPOSITOR_ONLY_PROPS: () => COMPOSITOR_ONLY_PROPS,
35219
+ CONTEXT_POLICY_VERSION: () => CONTEXT_POLICY_VERSION,
35220
+ CONTINUE: () => CONTINUE,
33408
35221
  CORE_VERSION: () => CORE_VERSION,
33409
35222
  COUNCIL_V1: () => COUNCIL_V1,
33410
35223
  CommandCheckSchema: () => CommandCheckSchema,
33411
35224
  CriterionSchema: () => CriterionSchema,
33412
35225
  CriterionSourceSchema: () => CriterionSourceSchema,
35226
+ DEFAULT_CONTEXT_POLICY: () => DEFAULT_CONTEXT_POLICY,
33413
35227
  DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
33414
35228
  DEFAULT_MAX_TENTACLES: () => DEFAULT_MAX_TENTACLES,
33415
35229
  DEFAULT_MEMORY_SCORING_WEIGHTS: () => DEFAULT_MEMORY_SCORING_WEIGHTS,
33416
35230
  DEFAULT_NFR_SPEC: () => DEFAULT_NFR_SPEC,
33417
35231
  DEFAULT_PLAN_TIMEOUT_MS: () => DEFAULT_PLAN_TIMEOUT_MS,
33418
35232
  DEFAULT_PRESSURE_THRESHOLDS: () => DEFAULT_PRESSURE_THRESHOLDS,
35233
+ DEFAULT_RUN_RETENTION_DAYS: () => DEFAULT_RUN_RETENTION_DAYS,
35234
+ DEFAULT_RUN_RETENTION_MAX_MB: () => DEFAULT_RUN_RETENTION_MAX_MB,
33419
35235
  DEFAULT_VERIFIER_CONFIG: () => DEFAULT_VERIFIER_CONFIG,
33420
35236
  DEGRADED_RUN_BANNER: () => DEGRADED_RUN_BANNER,
33421
35237
  DESIGN_PHASE_MODE_BANNER: () => DESIGN_PHASE_MODE_BANNER,
@@ -33426,12 +35242,15 @@ __export(dist_exports, {
33426
35242
  DefaultMemorySanitizer: () => DefaultMemorySanitizer,
33427
35243
  DefaultMemoryService: () => DefaultMemoryService,
33428
35244
  DeterministicCheckSchema: () => DeterministicCheckSchema,
35245
+ DuplicateSearchGuard: () => DuplicateSearchGuard,
33429
35246
  EPHEMERAL_TAIL_EVENT_KINDS: () => EPHEMERAL_TAIL_EVENT_KINDS,
33430
35247
  EVENT_BACKED_EVIDENCE_TIERS: () => EVENT_BACKED_EVIDENCE_TIERS,
33431
35248
  EXPERIMENTAL_FLAGS: () => EXPERIMENTAL_FLAGS,
33432
35249
  EventBus: () => EventBus,
33433
35250
  EvidenceRefSchema: () => EvidenceRefSchema,
33434
35251
  EvidenceTierSchema: () => EvidenceTierSchema,
35252
+ FILE_WRITING_TOOLS: () => FILE_WRITING_TOOLS,
35253
+ FailureSignatureGuard: () => FailureSignatureGuard,
33435
35254
  FileAbsentCheckSchema: () => FileAbsentCheckSchema,
33436
35255
  FileContainsCheckSchema: () => FileContainsCheckSchema,
33437
35256
  FileExistsCheckSchema: () => FileExistsCheckSchema,
@@ -33442,10 +35261,14 @@ __export(dist_exports, {
33442
35261
  IMPLEMENTATION_IMPLEMENTER_BANNER: () => IMPLEMENTATION_IMPLEMENTER_BANNER,
33443
35262
  IMPLEMENTATION_MODE_BANNER: () => IMPLEMENTATION_MODE_BANNER,
33444
35263
  IMPLEMENTATION_WRITE_REQUIREMENTS: () => IMPLEMENTATION_WRITE_REQUIREMENTS,
35264
+ KRAKEN_EXPLORE_POLICY: () => KRAKEN_EXPLORE_POLICY,
35265
+ KRAKEN_GENERAL_POLICY: () => KRAKEN_GENERAL_POLICY,
33445
35266
  KRAKEN_IDENTITY_MODULE: () => KRAKEN_IDENTITY_MODULE,
33446
35267
  KRAKEN_LEAD_PLAYBOOK_MODULE: () => KRAKEN_LEAD_PLAYBOOK_MODULE,
35268
+ KRAKEN_LEAD_POLICY: () => KRAKEN_LEAD_POLICY,
33447
35269
  KRAKEN_SELECTION_PLAYBOOK_MODULE: () => KRAKEN_SELECTION_PLAYBOOK_MODULE,
33448
35270
  KRAKEN_V1: () => KRAKEN_V1,
35271
+ KRAKEN_VERIFY_POLICY: () => KRAKEN_VERIFY_POLICY,
33449
35272
  LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
33450
35273
  LATEST_ONLY_SURFACE_KINDS: () => LATEST_ONLY_SURFACE_KINDS,
33451
35274
  LAYOUT_MOTION_PROPS: () => LAYOUT_MOTION_PROPS,
@@ -33479,17 +35302,20 @@ __export(dist_exports, {
33479
35302
  MemorySourceSchema: () => MemorySourceSchema,
33480
35303
  MemoryStatusSchema: () => MemoryStatusSchema,
33481
35304
  MemoryVisibilitySchema: () => MemoryVisibilitySchema,
35305
+ MetricsObserver: () => MetricsObserver,
33482
35306
  ModelSelectionSchema: () => ModelSelectionSchema,
33483
35307
  NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
33484
35308
  NFR_KEYWORDS: () => NFR_KEYWORDS,
33485
35309
  NON_RETRY_AGENTS: () => NON_RETRY_AGENTS,
33486
35310
  NOOP_SUBAGENT_PROVIDER: () => NOOP_SUBAGENT_PROVIDER,
35311
+ NoProgressGuard: () => NoProgressGuard,
33487
35312
  NodeFsProvider: () => NodeFsProvider,
33488
35313
  NodeShellProvider: () => NodeShellProvider,
33489
35314
  NoneCheckSchema: () => NoneCheckSchema,
33490
35315
  NoopMemoryService: () => NoopMemoryService,
33491
35316
  NoopSubagentProvider: () => NoopSubagentProvider,
33492
35317
  OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
35318
+ ObserverBus: () => ObserverBus,
33493
35319
  PROFILE_RESOURCE_POLICIES: () => PROFILE_RESOURCE_POLICIES,
33494
35320
  PROMPT_MODULES: () => PROMPT_MODULES,
33495
35321
  PROPRIETARY_REFUSAL_TEXT: () => PROPRIETARY_REFUSAL_TEXT,
@@ -33498,8 +35324,14 @@ __export(dist_exports, {
33498
35324
  PlanError: () => PlanError,
33499
35325
  PressureThresholdsSchema: () => PressureThresholdsSchema,
33500
35326
  ProfileSchema: () => ProfileSchema,
35327
+ REDACTED: () => REDACTED,
35328
+ ReasoningWatchdog: () => ReasoningWatchdog,
35329
+ RepetitionGuard: () => RepetitionGuard,
33501
35330
  ResourcePolicySchema: () => ResourcePolicySchema,
33502
35331
  ResourceStageSchema: () => ResourceStageSchema,
35332
+ RunRecorder: () => RunRecorder,
35333
+ RuntimeControlQueue: () => RuntimeControlQueue,
35334
+ SEARCH_TOOLS: () => SEARCH_TOOLS,
33503
35335
  SESSION_EVENT_KINDS: () => SESSION_EVENT_KINDS,
33504
35336
  SESSION_EXPORT_FORMAT: () => SESSION_EXPORT_FORMAT,
33505
35337
  SESSION_EXPORT_VERSION: () => SESSION_EXPORT_VERSION,
@@ -33517,6 +35349,7 @@ __export(dist_exports, {
33517
35349
  SessionLogLockedError: () => SessionLogLockedError,
33518
35350
  SessionLogWriter: () => SessionLogWriter,
33519
35351
  SessionStore: () => SessionStore,
35352
+ SteeringObserver: () => SteeringObserver,
33520
35353
  TERMINAL_STATUSES: () => TERMINAL_STATUSES,
33521
35354
  TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
33522
35355
  TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
@@ -33528,6 +35361,7 @@ __export(dist_exports, {
33528
35361
  TaskContractConflictError: () => TaskContractConflictError,
33529
35362
  TaskContractSchema: () => TaskContractSchema,
33530
35363
  TaskCriterionSchema: () => TaskCriterionSchema,
35364
+ TraceObserver: () => TraceObserver,
33531
35365
  UnknownMemberError: () => UnknownMemberError,
33532
35366
  UnknownProfileError: () => UnknownProfileError,
33533
35367
  VAULT_TOOL_DEFINITIONS: () => VAULT_TOOL_DEFINITIONS,
@@ -33566,6 +35400,7 @@ __export(dist_exports, {
33566
35400
  buildMotionFixPrompt: () => buildMotionFixPrompt,
33567
35401
  buildProjection: () => buildProjection,
33568
35402
  buildRetryPrompt: () => buildRetryPrompt,
35403
+ buildRuntimeObserverBus: () => buildRuntimeObserverBus,
33569
35404
  buildSkillDefinition: () => buildSkillDefinition,
33570
35405
  buildSystemPrompt: () => buildSystemPrompt,
33571
35406
  buildSystemPromptSplit: () => buildSystemPromptSplit,
@@ -33590,10 +35425,12 @@ __export(dist_exports, {
33590
35425
  codingCriteriaPack: () => codingCriteriaPack,
33591
35426
  collapseLoopedAssistantText: () => collapseLoopedAssistantText,
33592
35427
  compareReplayPrefix: () => compareReplayPrefix,
35428
+ composeObservers: () => composeObservers,
33593
35429
  computeAgentSkills: () => computeAgentSkills,
33594
35430
  computeAgentTools: () => computeAgentTools,
33595
35431
  computeBudget: () => computeBudget,
33596
35432
  computeFalseDoneRate: () => computeFalseDoneRate,
35433
+ contextPolicyForRole: () => contextPolicyForRole,
33597
35434
  contractToCompactionFields: () => contractToCompactionFields,
33598
35435
  costPerVerifiedSolve: () => costPerVerifiedSolve,
33599
35436
  councilModeBanner: () => councilModeBanner,
@@ -33605,6 +35442,7 @@ __export(dist_exports, {
33605
35442
  createDefaultSystemPromptConfig: () => createDefaultSystemPromptConfig,
33606
35443
  createExecutionContext: () => createExecutionContext,
33607
35444
  createGraph: () => createGraph,
35445
+ createRecorderObserver: () => createRecorderObserver,
33608
35446
  createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
33609
35447
  defaultPersonaParse: () => defaultPersonaParse,
33610
35448
  defaultResourcePolicy: () => defaultResourcePolicy,
@@ -33620,6 +35458,7 @@ __export(dist_exports, {
33620
35458
  disjointScopeSets: () => disjointScopeSets,
33621
35459
  emptyContextGrowthStats: () => emptyContextGrowthStats,
33622
35460
  enforceDesignPhaseToolEmissions: () => enforceDesignPhaseToolEmissions,
35461
+ enforceRunRetention: () => enforceRunRetention,
33623
35462
  evaluateBudgetContinuation: () => evaluateBudgetContinuation,
33624
35463
  evaluateCompletion: () => evaluateCompletion,
33625
35464
  evaluateMissionContinuation: () => evaluateMissionContinuation,
@@ -33629,6 +35468,7 @@ __export(dist_exports, {
33629
35468
  exportSessionJson: () => exportSessionJson,
33630
35469
  extractCitations: () => extractCitations,
33631
35470
  extractRequirementsBlock: () => extractRequirementsBlock,
35471
+ extractSearchQuery: () => extractSearchQuery,
33632
35472
  extractTaskScope: () => extractTaskScope,
33633
35473
  failedNodeIds: () => failedNodeIds,
33634
35474
  filterByWeakness: () => filterByWeakness,
@@ -33685,15 +35525,19 @@ __export(dist_exports, {
33685
35525
  isBrainToolExecutionEndEvent: () => isBrainToolExecutionEndEvent,
33686
35526
  isBrainToolExecutionStartEvent: () => isBrainToolExecutionStartEvent,
33687
35527
  isBrainToolExecutionUpdateEvent: () => isBrainToolExecutionUpdateEvent,
35528
+ isCancelControlEvent: () => isCancelControlEvent,
33688
35529
  isConverged: () => isConverged,
33689
35530
  isEventBackedEvidence: () => isEventBackedEvidence,
33690
35531
  isExperimentalEnabled: () => isExperimentalEnabled,
35532
+ isFollowUpControlEvent: () => isFollowUpControlEvent,
33691
35533
  isGeneratedPath: () => isGeneratedPath,
33692
35534
  isModelSurfaceEvent: () => isModelSurfaceEvent,
33693
35535
  isReviewerKind: () => isReviewerKind,
35536
+ isSearchTool: () => isSearchTool,
33694
35537
  isSeqShadowed: () => isSeqShadowed,
33695
35538
  isSettled: () => isSettled,
33696
35539
  isStatusTheaterUnit: () => isStatusTheaterUnit,
35540
+ isSteerControlEvent: () => isSteerControlEvent,
33697
35541
  isValidTool: () => isValidTool,
33698
35542
  isVerificationReserveProtected: () => isVerificationReserveProtected,
33699
35543
  isVerifyToolCheckSkipped: () => isVerifyToolCheckSkipped,
@@ -33713,15 +35557,19 @@ __export(dist_exports, {
33713
35557
  memorySimilarity: () => memorySimilarity,
33714
35558
  memoryTokens: () => memoryTokens,
33715
35559
  missionSnapshot: () => missionSnapshot,
35560
+ newRunId: () => newRunId,
35561
+ normalizeFailureTail: () => normalizeFailureTail,
33716
35562
  normalizeForSignature: () => normalizeForSignature,
33717
35563
  normalizeLoopUnit: () => normalizeLoopUnit,
33718
35564
  normalizeMemoryContent: () => normalizeMemoryContent,
35565
+ normalizeQuery: () => normalizeQuery,
33719
35566
  normalizeScopePath: () => normalizeScopePath,
33720
35567
  normalizeTags: () => normalizeTags,
33721
35568
  normalizeTextToolArgs: () => normalizeTextToolArgs,
33722
35569
  normalizeToolName: () => normalizeToolName,
33723
35570
  normalizedMemoryKey: () => normalizedMemoryKey,
33724
35571
  pairToolCalls: () => pairToolCalls,
35572
+ parentContextForRole: () => parentContextForRole,
33725
35573
  parseClarificationRequest: () => parseClarificationRequest,
33726
35574
  parseCompactedEvent: () => parseCompactedEvent,
33727
35575
  parseEvidenceTier: () => parseEvidenceTier,
@@ -33737,7 +35585,9 @@ __export(dist_exports, {
33737
35585
  pathsOverlap: () => pathsOverlap,
33738
35586
  pickWeakest: () => pickWeakest,
33739
35587
  profileHash: () => profileHash,
35588
+ projectContext: () => projectContext,
33740
35589
  promoteMember: () => promoteMember,
35590
+ queryJaccard: () => queryJaccard,
33741
35591
  rankByWeakness: () => rankByWeakness,
33742
35592
  rankMemoryCandidates: () => rankMemoryCandidates,
33743
35593
  readLessonsDeduped: () => readLessonsDeduped,
@@ -33748,14 +35598,19 @@ __export(dist_exports, {
33748
35598
  recordRequest: () => recordRequest,
33749
35599
  recordToolResult: () => recordToolResult,
33750
35600
  recordUsage: () => recordUsage,
35601
+ redactRuntimePayload: () => redactRuntimePayload,
35602
+ redactString: () => redactString,
33751
35603
  registerCodingSkill: () => registerCodingSkill,
33752
35604
  registerCustomTool: () => registerCustomTool,
33753
35605
  registerPersona: () => registerPersona,
33754
35606
  registerSkill: () => registerSkill,
35607
+ renderHistoryDigest: () => renderHistoryDigest,
33755
35608
  renderSkillMarkdown: () => renderSkillMarkdown,
35609
+ renderSteers: () => renderSteers,
33756
35610
  replayChairmanTextTools: () => replayChairmanTextTools,
33757
35611
  resolveAgentSkills: () => resolveAgentSkills,
33758
35612
  resolveCouncilRunMode: () => resolveCouncilRunMode,
35613
+ resolveInterventions: () => resolveInterventions,
33759
35614
  resolveJailed: () => resolveJailed,
33760
35615
  resolveMaxTentacles: () => resolveMaxTentacles,
33761
35616
  resolvePlanTimeoutMs: () => resolvePlanTimeoutMs,
@@ -33776,7 +35631,10 @@ __export(dist_exports, {
33776
35631
  runImplementationVerification: () => runImplementationVerification,
33777
35632
  runInSandbox: () => runInSandbox,
33778
35633
  runMicroVerificationOnFile: () => runMicroVerificationOnFile,
35634
+ runRecordEnabled: () => runRecordEnabled,
35635
+ runRetentionFromEnv: () => runRetentionFromEnv,
33779
35636
  runRetryTurnForMember: () => runRetryTurnForMember,
35637
+ runtimeObserversEnabled: () => runtimeObserversEnabled,
33780
35638
  sanitizeMemoryMetadata: () => sanitizeMemoryMetadata,
33781
35639
  scanForFootguns: () => scanForFootguns,
33782
35640
  scanKeyframesViolations: () => scanKeyframesViolations,
@@ -33795,12 +35653,14 @@ __export(dist_exports, {
33795
35653
  specificityFromAssumptions: () => specificityFromAssumptions,
33796
35654
  stableStringify: () => stableStringify,
33797
35655
  strictBuildGate: () => strictBuildGate,
35656
+ stripAnsi: () => stripAnsi,
33798
35657
  stripClarificationProtocol: () => stripClarificationProtocol,
33799
35658
  swapMembers: () => swapMembers,
33800
35659
  systemMessagesFromSplit: () => systemMessagesFromSplit,
33801
35660
  taskMatchesNfrKeywords: () => taskMatchesNfrKeywords,
33802
35661
  tierAtLeast: () => tierAtLeast,
33803
35662
  tokenizeForSignature: () => tokenizeForSignature,
35663
+ toolCallFingerprint: () => toolCallFingerprint,
33804
35664
  toolFingerprintHash: () => toolFingerprintHash,
33805
35665
  toolManifestHash: () => toolManifestHash,
33806
35666
  toolMatches: () => toolMatches,
@@ -33837,6 +35697,7 @@ var init_dist = __esm({
33837
35697
  init_kraken();
33838
35698
  init_session();
33839
35699
  init_runtime2();
35700
+ init_context3();
33840
35701
  init_verification2();
33841
35702
  init_mission2();
33842
35703
  init_experimental();
@@ -33902,8 +35763,8 @@ function formatKrakenGraphDigest(graph, opts = {}) {
33902
35763
  if (!n) continue;
33903
35764
  const took = durations[id3] !== void 0 ? `, ${formatDuration2(durations[id3])}` : "";
33904
35765
  const detail = n.status === "error" ? n.error : n.result;
33905
- const firstLine2 = (detail ?? "").trim().split("\n")[0] ?? "";
33906
- const body = firstLine2.length > maxChars ? `${firstLine2.slice(0, maxChars)}\u2026` : firstLine2;
35766
+ const firstLine3 = (detail ?? "").trim().split("\n")[0] ?? "";
35767
+ const body = firstLine3.length > maxChars ? `${firstLine3.slice(0, maxChars)}\u2026` : firstLine3;
33907
35768
  lines.push(
33908
35769
  `[${STATUS_ICON[n.status]}] ${n.id} (${n.kind}${took})${body ? ` \u2014 ${body}` : ""}`
33909
35770
  );
@@ -35561,18 +37422,18 @@ var init_observationStore = __esm({
35561
37422
  });
35562
37423
 
35563
37424
  // packages/core/dist/core/tools/toolOutputSpill.js
35564
- import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
37425
+ import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
35565
37426
  import { existsSync as existsSync13, mkdirSync as mkdirSync6, writeFileSync as writeFileSync11 } from "node:fs";
35566
37427
  import { homedir as homedir3, tmpdir } from "node:os";
35567
- import { join as join11 } from "node:path";
37428
+ import { join as join14 } from "node:path";
35568
37429
  function resolveToolOutputDir() {
35569
37430
  const fromEnv = process.env.ZELARI_TOOL_OUTPUT_DIR?.trim();
35570
37431
  if (fromEnv)
35571
37432
  return fromEnv;
35572
37433
  try {
35573
- return join11(homedir3(), ".tmp", "zelari-code", "tool-output");
37434
+ return join14(homedir3(), ".tmp", "zelari-code", "tool-output");
35574
37435
  } catch {
35575
- return join11(tmpdir(), "zelari-code", "tool-output");
37436
+ return join14(tmpdir(), "zelari-code", "tool-output");
35576
37437
  }
35577
37438
  }
35578
37439
  function isToolSpillEnabled() {
@@ -35591,12 +37452,12 @@ function spillToolOutput(fullText, meta3) {
35591
37452
  if (!existsSync13(dir)) {
35592
37453
  mkdirSync6(dir, { recursive: true });
35593
37454
  }
35594
- const hash3 = createHash7("sha256").update(fullText).digest("hex").slice(0, 12);
37455
+ const hash3 = createHash9("sha256").update(fullText).digest("hex").slice(0, 12);
35595
37456
  const stamp = Date.now().toString(36);
35596
- const rnd = randomBytes2(3).toString("hex");
37457
+ const rnd = randomBytes3(3).toString("hex");
35597
37458
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
35598
37459
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
35599
- const path72 = join11(dir, file2);
37460
+ const path72 = join14(dir, file2);
35600
37461
  writeFileSync11(path72, fullText, "utf8");
35601
37462
  return path72;
35602
37463
  } catch {
@@ -36308,7 +38169,7 @@ import { execFile as execFile2 } from "node:child_process";
36308
38169
  import { existsSync as existsSync16, mkdirSync as mkdirSync8, rmSync } from "node:fs";
36309
38170
  import path29 from "node:path";
36310
38171
  import { promisify } from "node:util";
36311
- import { randomBytes as randomBytes3 } from "node:crypto";
38172
+ import { randomBytes as randomBytes4 } from "node:crypto";
36312
38173
  function isKrakenWorktreeEnabled(env = process.env) {
36313
38174
  const v = (env.ZELARI_KRAKEN_WORKTREE ?? "").trim().toLowerCase();
36314
38175
  return v === "1" || v === "true" || v === "yes" || v === "on";
@@ -36351,7 +38212,7 @@ async function createKrakenWorktree(cwd, label) {
36351
38212
  if (!repoRoot) return null;
36352
38213
  const head = await git2(repoRoot, ["rev-parse", "HEAD"]);
36353
38214
  const baseSha = head.ok ? head.stdout.trim() : void 0;
36354
- const id3 = `${Date.now().toString(36)}-${randomBytes3(3).toString("hex")}`;
38215
+ const id3 = `${Date.now().toString(36)}-${randomBytes4(3).toString("hex")}`;
36355
38216
  const slug = (label ?? "task").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 24) || "task";
36356
38217
  const branch = `kraken/${slug}-${id3}`;
36357
38218
  const wtRoot = path29.join(repoRoot, ".zelari", "worktrees");
@@ -36862,6 +38723,7 @@ var init_metrics2 = __esm({
36862
38723
  });
36863
38724
 
36864
38725
  // src/cli/tools/taskTool.ts
38726
+ import { randomUUID as randomUUID2 } from "node:crypto";
36865
38727
  function resetTaskSpawnCount() {
36866
38728
  const g = globalThis;
36867
38729
  g.__zelariTaskSpawnCount = 0;
@@ -36958,6 +38820,9 @@ async function runSubAgent(harness, opts = {}) {
36958
38820
  ...toolTrace.length > 0 ? { toolTrace } : {}
36959
38821
  };
36960
38822
  }
38823
+ if (opts.onEvent && (ev.type === "agent_start" || ev.type === "agent_end" || ev.type === "tool_execution_start" || ev.type === "tool_execution_update" || ev.type === "tool_execution_end")) {
38824
+ opts.onEvent(ev);
38825
+ }
36961
38826
  if (ev.type === "tool_execution_start") {
36962
38827
  pendingTools.set(ev.toolCallId, { tool: ev.toolName, command: toolCommandHint(ev.args) });
36963
38828
  } else if (ev.type === "tool_execution_end") {
@@ -37044,6 +38909,20 @@ async function runTentacle(opts) {
37044
38909
  ...opts.graphId ? { graphId: opts.graphId } : {},
37045
38910
  ...opts.nodeId ? { nodeId: opts.nodeId } : {}
37046
38911
  });
38912
+ const emitActivity = (ev) => {
38913
+ deps.onTentacleEvent?.({ ...ev, id: randomUUID2(), sessionId: sessionId2 });
38914
+ };
38915
+ const endTentacle = (id3, info) => {
38916
+ krakenTentacleEnd(id3, info);
38917
+ emitActivity({
38918
+ type: "agent_ended",
38919
+ agentId: id3,
38920
+ reason: info.detail ?? (info.ok === false ? "failed" : "completed"),
38921
+ ok: info.ok !== false,
38922
+ durationMs: info.durationMs ?? 0,
38923
+ ts: Date.now()
38924
+ });
38925
+ };
37047
38926
  let sub;
37048
38927
  try {
37049
38928
  sub = await deps.createSubAgentContext({
@@ -37061,7 +38940,7 @@ async function runTentacle(opts) {
37061
38940
  ok: false,
37062
38941
  durationMs: Date.now() - started
37063
38942
  });
37064
- krakenTentacleEnd(liveId, { ok: false, durationMs: Date.now() - started });
38943
+ endTentacle(liveId, { ok: false, durationMs: Date.now() - started });
37065
38944
  return {
37066
38945
  ok: false,
37067
38946
  agent,
@@ -37078,18 +38957,34 @@ async function runTentacle(opts) {
37078
38957
  ok: false,
37079
38958
  durationMs: Date.now() - started
37080
38959
  });
37081
- krakenTentacleEnd(liveId, { ok: false, durationMs: Date.now() - started });
38960
+ endTentacle(liveId, { ok: false, durationMs: Date.now() - started });
37082
38961
  return {
37083
38962
  ok: false,
37084
38963
  agent,
37085
38964
  error: "task: no provider configured for the sub-agent (set an API key / run /login)."
37086
38965
  };
37087
38966
  }
37088
- const userContent = buildTaskUserPrompt({
38967
+ emitActivity({
38968
+ type: "agent_spawned",
38969
+ agentId: liveId,
38970
+ role: agent,
38971
+ title: args.description,
38972
+ ...sub.model ? { model: sub.model } : {},
38973
+ ...sub.provider ? { provider: sub.provider } : {},
38974
+ ...args.scope && args.scope.length > 0 ? { scope: args.scope } : {},
38975
+ ...worktree ? { worktree: worktree.path } : {},
38976
+ ts: Date.now()
38977
+ });
38978
+ emitActivity({ type: "agent_status", agentId: liveId, status: "running", ts: Date.now() });
38979
+ const taskUserContent = buildTaskUserPrompt({
37089
38980
  prompt: args.prompt,
37090
38981
  scope: args.scope,
37091
38982
  acceptance: withKrakenRequiredChecks(agent, args.acceptance)
37092
38983
  });
38984
+ const parentBlock = parentContextForRole(agent, opts.parentTranscript ?? []);
38985
+ const userContent = parentBlock ? `${parentBlock.block}
38986
+
38987
+ ${taskUserContent}` : taskUserContent;
37093
38988
  const maxToolCalls = maxToolCallsForThoroughness(thoroughness, agent);
37094
38989
  const runCwd = sub.cwd || effectiveCwd;
37095
38990
  const config2 = {
@@ -37112,7 +39007,7 @@ async function runTentacle(opts) {
37112
39007
  ...deps.memoryService ? {
37113
39008
  memoryService: deps.memoryService,
37114
39009
  memoryQuery: `${args.description}
37115
- ${userContent}`,
39010
+ ${taskUserContent}`,
37116
39011
  memoryContextChars: 2400
37117
39012
  } : {}
37118
39013
  };
@@ -37126,15 +39021,24 @@ ${userContent}`,
37126
39021
  }
37127
39022
  } catch (err) {
37128
39023
  if (worktree) await cleanupKrakenWorktree(worktree);
37129
- krakenTentacleEnd(liveId, { ok: false, durationMs: Date.now() - started });
39024
+ endTentacle(liveId, { ok: false, durationMs: Date.now() - started });
37130
39025
  return {
37131
39026
  ok: false,
37132
39027
  agent,
37133
39028
  error: `task: failed to start sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`
37134
39029
  };
37135
39030
  }
39031
+ const startedTools = /* @__PURE__ */ new Map();
37136
39032
  const { result, error: error51, aborted: aborted2, usage, toolTrace } = await runSubAgent(harness, {
37137
- ...opts.signal ? { signal: opts.signal } : {}
39033
+ ...opts.signal ? { signal: opts.signal } : {},
39034
+ onEvent: (ev) => {
39035
+ if (ev.type === "tool_execution_start") {
39036
+ startedTools.set(ev.toolCallId, ev.toolName);
39037
+ emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: ev.toolName, status: "started", ...ev.args ? { summary: toolCommandHint(ev.args) } : {}, ts: Date.now() });
39038
+ } else if (ev.type === "tool_execution_end") {
39039
+ emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: startedTools.get(ev.toolCallId) ?? "unknown", status: ev.isError ? "failed" : "completed", durationMs: ev.durationMs, ts: Date.now() });
39040
+ }
39041
+ }
37138
39042
  });
37139
39043
  const durationMs = Date.now() - started;
37140
39044
  if (aborted2) {
@@ -37150,7 +39054,7 @@ ${userContent}`,
37150
39054
  durationMs,
37151
39055
  ok: false
37152
39056
  });
37153
- krakenTentacleEnd(liveId, {
39057
+ endTentacle(liveId, {
37154
39058
  ok: false,
37155
39059
  model: sub.model,
37156
39060
  detail: "cancelled",
@@ -37171,7 +39075,7 @@ ${userContent}`,
37171
39075
  durationMs,
37172
39076
  ok: false
37173
39077
  });
37174
- krakenTentacleEnd(liveId, { ok: false, model: sub.model, detail: error51, durationMs });
39078
+ endTentacle(liveId, { ok: false, model: sub.model, detail: error51, durationMs });
37175
39079
  return {
37176
39080
  ok: false,
37177
39081
  agent,
@@ -37210,7 +39114,7 @@ ${formatWorktreeFooter(worktree, { kept, merge: merge2 })}`;
37210
39114
  ${verifyHintForGeneral(args.acceptance)}`;
37211
39115
  g.__zelariLastGeneralAt = Date.now();
37212
39116
  }
37213
- krakenTentacleEnd(liveId, {
39117
+ endTentacle(liveId, {
37214
39118
  ok: true,
37215
39119
  model: sub.model,
37216
39120
  detail: result.slice(0, 160),
@@ -37393,6 +39297,7 @@ var init_taskTool = __esm({
37393
39297
  "src/cli/tools/taskTool.ts"() {
37394
39298
  "use strict";
37395
39299
  init_zod();
39300
+ init_context3();
37396
39301
  init_toolTypes();
37397
39302
  init_krakenRadio();
37398
39303
  init_krakenWorktree();
@@ -37849,14 +39754,14 @@ var init_askUser = __esm({
37849
39754
 
37850
39755
  // src/cli/skillsMd.ts
37851
39756
  import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync15 } from "node:fs";
37852
- import { join as join12 } from "node:path";
39757
+ import { join as join15 } from "node:path";
37853
39758
  import { homedir as homedir4 } from "node:os";
37854
39759
  function skillMdSearchDirs(projectRoot = process.cwd()) {
37855
39760
  return [
37856
- join12(projectRoot, ".zelari", "skills"),
37857
- join12(projectRoot, ".claude", "skills"),
37858
- join12(projectRoot, ".opencode", "skills"),
37859
- join12(homedir4(), ".zelari-code", "skills")
39761
+ join15(projectRoot, ".zelari", "skills"),
39762
+ join15(projectRoot, ".claude", "skills"),
39763
+ join15(projectRoot, ".opencode", "skills"),
39764
+ join15(homedir4(), ".zelari-code", "skills")
37860
39765
  ];
37861
39766
  }
37862
39767
  function parseSkillMd(content, sourcePath) {
@@ -37922,7 +39827,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
37922
39827
  continue;
37923
39828
  }
37924
39829
  for (const entry of entries) {
37925
- const skillPath = join12(dir, entry, "SKILL.md");
39830
+ const skillPath = join15(dir, entry, "SKILL.md");
37926
39831
  if (!existsSync17(skillPath)) continue;
37927
39832
  try {
37928
39833
  const parsed = parseSkillMd(readFileSync15(skillPath, "utf8"), skillPath);
@@ -38103,13 +40008,13 @@ import {
38103
40008
  constants,
38104
40009
  realpathSync
38105
40010
  } from "node:fs";
38106
- import { join as join13, basename } from "node:path";
40011
+ import { join as join16, basename } from "node:path";
38107
40012
  import { homedir as homedir5 } from "node:os";
38108
- import { createHash as createHash8 } from "node:crypto";
40013
+ import { createHash as createHash10 } from "node:crypto";
38109
40014
  function resolveWorkspaceRoot(projectRoot = process.cwd()) {
38110
40015
  const candidates = [
38111
- join13(projectRoot, ".zelari"),
38112
- join13(homedir5(), ".zelari-code", "workspace", hashProject(projectRoot))
40016
+ join16(projectRoot, ".zelari"),
40017
+ join16(homedir5(), ".zelari-code", "workspace", hashProject(projectRoot))
38113
40018
  ];
38114
40019
  for (const candidate of candidates) {
38115
40020
  if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
@@ -38121,7 +40026,7 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
38121
40026
  return candidates[0];
38122
40027
  }
38123
40028
  function hashProject(projectPath) {
38124
- return createHash8("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
40029
+ return createHash10("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
38125
40030
  }
38126
40031
  function isWritableDir(dir) {
38127
40032
  try {
@@ -38134,8 +40039,8 @@ function isWritableDir(dir) {
38134
40039
  }
38135
40040
  function ensureWorkspaceDir(workspaceDir) {
38136
40041
  mkdirSync9(workspaceDir, { recursive: true });
38137
- if (workspaceDir.endsWith("/.zelari") && existsSync18(join13(workspaceDir, "..", ".git"))) {
38138
- const gitignorePath = join13(workspaceDir, ".gitignore");
40042
+ if (workspaceDir.endsWith("/.zelari") && existsSync18(join16(workspaceDir, "..", ".git"))) {
40043
+ const gitignorePath = join16(workspaceDir, ".gitignore");
38139
40044
  if (!existsSync18(gitignorePath)) {
38140
40045
  writeFileSync12(gitignorePath, "*\n!.gitignore\n");
38141
40046
  }
@@ -38144,15 +40049,15 @@ function ensureWorkspaceDir(workspaceDir) {
38144
40049
  function workspaceFile(rootDir, kind2) {
38145
40050
  switch (kind2) {
38146
40051
  case "plan":
38147
- return join13(rootDir, "plan.md");
40052
+ return join16(rootDir, "plan.md");
38148
40053
  case "risks":
38149
- return join13(rootDir, "risks.md");
40054
+ return join16(rootDir, "risks.md");
38150
40055
  case "index":
38151
- return join13(rootDir, "workspace.json");
40056
+ return join16(rootDir, "workspace.json");
38152
40057
  }
38153
40058
  }
38154
40059
  function workspaceArtifact(rootDir, subdir, slug) {
38155
- return join13(rootDir, subdir, `${slug}.md`);
40060
+ return join16(rootDir, subdir, `${slug}.md`);
38156
40061
  }
38157
40062
  function projectName(projectRoot = process.cwd()) {
38158
40063
  return basename(realpathSync(projectRoot));
@@ -38181,7 +40086,7 @@ import {
38181
40086
  readdirSync as readdirSync4,
38182
40087
  renameSync
38183
40088
  } from "node:fs";
38184
- import { dirname as dirname2, join as join14 } from "node:path";
40089
+ import { dirname as dirname2, join as join17 } from "node:path";
38185
40090
  function parseFrontmatter(md) {
38186
40091
  const m = FRONTMATTER_RE.exec(md);
38187
40092
  if (!m) return { meta: {}, body: md };
@@ -38461,7 +40366,7 @@ var init_storage = __esm({
38461
40366
  /** List all .md files in a directory (non-recursive). */
38462
40367
  listMarkdown(dir) {
38463
40368
  if (!existsSync19(dir)) return [];
38464
- return readdirSync4(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join14(dir, f));
40369
+ return readdirSync4(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join17(dir, f));
38465
40370
  }
38466
40371
  };
38467
40372
  KeyedMutex = class {
@@ -38499,7 +40404,7 @@ import {
38499
40404
  renameSync as renameSync2,
38500
40405
  writeFileSync as writeFileSync14
38501
40406
  } from "node:fs";
38502
- import { dirname as dirname3, join as join15 } from "node:path";
40407
+ import { dirname as dirname3, join as join18 } from "node:path";
38503
40408
  async function withPlanStore(projectRoot, fn) {
38504
40409
  const rootDir = resolveWorkspaceRoot(projectRoot);
38505
40410
  return workspaceMutex.run(`${rootDir}:plan`, () => {
@@ -38518,7 +40423,7 @@ function nextPlanTaskId(store6) {
38518
40423
  return `t${store6.counter}`;
38519
40424
  }
38520
40425
  function writePlanTaskArtifact(rootDir, task) {
38521
- const path72 = join15(rootDir, "plan-tasks", `${task.id}.md`);
40426
+ const path72 = join18(rootDir, "plan-tasks", `${task.id}.md`);
38522
40427
  mkdirSync11(dirname3(path72), { recursive: true });
38523
40428
  const meta3 = {
38524
40429
  kind: "task",
@@ -38543,7 +40448,7 @@ function writePlanTaskArtifact(rootDir, task) {
38543
40448
  new Storage().write(path72, meta3, body);
38544
40449
  }
38545
40450
  function loadHandle(rootDir) {
38546
- const jsonPath = join15(rootDir, "plan.json");
40451
+ const jsonPath = join18(rootDir, "plan.json");
38547
40452
  if (!existsSync20(jsonPath)) {
38548
40453
  return { rootDir, tasks: [], counter: 0, rootFields: {} };
38549
40454
  }
@@ -38577,7 +40482,7 @@ function saveHandle(rootDir, handle) {
38577
40482
  "PLAN_TOO_MANY_TASKS"
38578
40483
  );
38579
40484
  }
38580
- const jsonPath = join15(rootDir, "plan.json");
40485
+ const jsonPath = join18(rootDir, "plan.json");
38581
40486
  mkdirSync11(rootDir, { recursive: true });
38582
40487
  if (existsSync20(jsonPath)) {
38583
40488
  copyFileSync(jsonPath, `${jsonPath}.bak`);
@@ -38939,7 +40844,7 @@ var init_inspectTypecheckSafety = __esm({
38939
40844
 
38940
40845
  // src/cli/tools/inspectCommand.ts
38941
40846
  import { spawn as spawn8 } from "node:child_process";
38942
- import { createHash as createHash9 } from "node:crypto";
40847
+ import { createHash as createHash11 } from "node:crypto";
38943
40848
  import { existsSync as existsSync21, promises as fs16 } from "node:fs";
38944
40849
  import os7 from "node:os";
38945
40850
  import path31 from "node:path";
@@ -39026,7 +40931,7 @@ function buildInspectCommand(op, ctx) {
39026
40931
  }
39027
40932
  case "typecheck": {
39028
40933
  const project = path31.resolve(ctx.cwd, op.project ?? "tsconfig.json");
39029
- const hash3 = createHash9("sha256").update(project).digest("hex").slice(0, 16);
40934
+ const hash3 = createHash11("sha256").update(project).digest("hex").slice(0, 16);
39030
40935
  const tsBuildInfoFile = path31.join(os7.tmpdir(), "zelari-inspect", `${hash3}.tsbuildinfo`);
39031
40936
  return {
39032
40937
  ok: true,
@@ -40184,7 +42089,7 @@ var init_manager = __esm({
40184
42089
  });
40185
42090
 
40186
42091
  // src/cli/ast/engine.ts
40187
- import { readFile } from "node:fs/promises";
42092
+ import { readFile as readFile2 } from "node:fs/promises";
40188
42093
  import path33 from "node:path";
40189
42094
  function loadTs() {
40190
42095
  if (!tsPromise) {
@@ -40218,7 +42123,7 @@ async function parseFileSymbolsDiag(file2, cwd) {
40218
42123
  }
40219
42124
  let text;
40220
42125
  try {
40221
- text = await readFile(resolvedPath, "utf8");
42126
+ text = await readFile2(resolvedPath, "utf8");
40222
42127
  } catch (err) {
40223
42128
  const code = err?.code;
40224
42129
  if (code === "ENOENT") {
@@ -40465,9 +42370,9 @@ var init_store2 = __esm({
40465
42370
  import { promises as fs17, existsSync as existsSync22, readFileSync as readFileSync19 } from "node:fs";
40466
42371
  import { homedir as homedir6 } from "node:os";
40467
42372
  import path34 from "node:path";
40468
- import { createHash as createHash10 } from "node:crypto";
42373
+ import { createHash as createHash12 } from "node:crypto";
40469
42374
  function getIndexPath(root) {
40470
- const hash3 = createHash10("sha1").update(path34.resolve(root)).digest("hex").slice(0, 16);
42375
+ const hash3 = createHash12("sha1").update(path34.resolve(root)).digest("hex").slice(0, 16);
40471
42376
  return process.env.ZELARI_SEMANTIC_FILE ?? path34.join(homedir6(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
40472
42377
  }
40473
42378
  async function collectSourceFiles(root, maxFiles = 1500) {
@@ -41479,10 +43384,10 @@ function asPlaywright(mod) {
41479
43384
  return null;
41480
43385
  }
41481
43386
  async function loadPlaywright(cwd) {
41482
- const base = cwd && cwd.length > 0 ? path36.resolve(cwd) : void 0;
41483
- if (base) {
43387
+ const base2 = cwd && cwd.length > 0 ? path36.resolve(cwd) : void 0;
43388
+ if (base2) {
41484
43389
  try {
41485
- const req = createRequire2(path36.join(base, "package.json"));
43390
+ const req = createRequire2(path36.join(base2, "package.json"));
41486
43391
  const resolved = req.resolve("playwright");
41487
43392
  const mod = await import(pathToFileURL(resolved).href);
41488
43393
  const pw = asPlaywright(mod);
@@ -41533,12 +43438,12 @@ async function runBrowserCheck(options, loader) {
41533
43438
  const pageErrors = [];
41534
43439
  const failedRequests = [];
41535
43440
  const evaluateResults = [];
41536
- const base = { ok: false, consoleErrors, pageErrors, failedRequests };
43441
+ const base2 = { ok: false, consoleErrors, pageErrors, failedRequests };
41537
43442
  const resolve7 = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
41538
43443
  const pw = await resolve7();
41539
43444
  if (!pw) {
41540
43445
  return {
41541
- ...base,
43446
+ ...base2,
41542
43447
  error: "browser automation unavailable \u2014 Playwright is not installed in this workspace. Install it with: `zelari-code --plugins-install playwright --cwd .` (or Desktop banner \u201CInstall\u201D, or CLI `/plugins install playwright`, or `npm i -D playwright && npx playwright install chromium`). Then re-run browser_check."
41543
43448
  };
41544
43449
  }
@@ -41678,7 +43583,7 @@ async function runBrowserCheck(options, loader) {
41678
43583
  ...screenshotPath ? { screenshotPath } : {}
41679
43584
  };
41680
43585
  } catch (err) {
41681
- return { ...base, error: err instanceof Error ? err.message : String(err) };
43586
+ return { ...base2, error: err instanceof Error ? err.message : String(err) };
41682
43587
  } finally {
41683
43588
  try {
41684
43589
  await browser?.close();
@@ -41813,14 +43718,14 @@ import {
41813
43718
  readFileSync as readFileSync20,
41814
43719
  writeFileSync as writeFileSync15
41815
43720
  } from "node:fs";
41816
- import { dirname as dirname4, join as join16 } from "node:path";
43721
+ import { dirname as dirname4, join as join19 } from "node:path";
41817
43722
  import { homedir as homedir7 } from "node:os";
41818
43723
  import { spawn as spawn10 } from "node:child_process";
41819
43724
  function getSshTargetsPath() {
41820
- return join16(homedir7(), ".zelari-code", "ssh-targets.json");
43725
+ return join19(homedir7(), ".zelari-code", "ssh-targets.json");
41821
43726
  }
41822
43727
  function getSshSecretsPath() {
41823
- return join16(homedir7(), ".zelari-code", "ssh-secrets.json");
43728
+ return join19(homedir7(), ".zelari-code", "ssh-secrets.json");
41824
43729
  }
41825
43730
  function normalizeAuth(auth) {
41826
43731
  if (auth === "keyPath") return "keyPath";
@@ -41993,16 +43898,16 @@ function buildSshBaseArgs(target) {
41993
43898
  return args;
41994
43899
  }
41995
43900
  function ensureAskpassHelper() {
41996
- const dir = join16(homedir7(), ".zelari-code", "ssh-helpers");
43901
+ const dir = join19(homedir7(), ".zelari-code", "ssh-helpers");
41997
43902
  mkdirSync12(dir, { recursive: true });
41998
- const cjs = join16(dir, "askpass.cjs");
43903
+ const cjs = join19(dir, "askpass.cjs");
41999
43904
  writeFileSync15(
42000
43905
  cjs,
42001
43906
  "process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
42002
43907
  "utf8"
42003
43908
  );
42004
43909
  if (process.platform === "win32") {
42005
- const cmd = join16(dir, "askpass.cmd");
43910
+ const cmd = join19(dir, "askpass.cmd");
42006
43911
  writeFileSync15(
42007
43912
  cmd,
42008
43913
  `@echo off\r
@@ -42012,7 +43917,7 @@ node "%~dp0askpass.cjs"\r
42012
43917
  );
42013
43918
  return cmd;
42014
43919
  }
42015
- const sh = join16(dir, "askpass.sh");
43920
+ const sh = join19(dir, "askpass.sh");
42016
43921
  writeFileSync15(
42017
43922
  sh,
42018
43923
  `#!/bin/sh
@@ -42757,8 +44662,8 @@ function anthropicMessagesProvider(config2) {
42757
44662
  if (t.degraded) console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
42758
44663
  else Object.assign(body, t.patch);
42759
44664
  }
42760
- const base = config2.baseUrl.replace(/\/$/, "").replace(/\/v1$/, "");
42761
- const url2 = `${base}/v1/messages`;
44665
+ const base2 = config2.baseUrl.replace(/\/$/, "").replace(/\/v1$/, "");
44666
+ const url2 = `${base2}/v1/messages`;
42762
44667
  let response;
42763
44668
  try {
42764
44669
  response = await fetch(url2, {
@@ -42969,8 +44874,8 @@ function chatgptResponsesProvider(config2) {
42969
44874
  if (t.degraded) console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
42970
44875
  else Object.assign(body, t.patch);
42971
44876
  }
42972
- const base = config2.baseUrl.replace(/\/$/, "");
42973
- const url2 = `${base}/responses`;
44877
+ const base2 = config2.baseUrl.replace(/\/$/, "");
44878
+ const url2 = `${base2}/responses`;
42974
44879
  let response;
42975
44880
  try {
42976
44881
  response = await fetch(url2, {
@@ -43293,13 +45198,13 @@ var init_folderTrust = __esm({
43293
45198
 
43294
45199
  // src/cli/safety/lifecycleHooks.ts
43295
45200
  import { homedir as homedir9 } from "node:os";
43296
- import { join as join17 } from "node:path";
45201
+ import { join as join20 } from "node:path";
43297
45202
  import { readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
43298
45203
  function globalHooksDir() {
43299
- return join17(homedir9(), ".zelari-code", "hooks");
45204
+ return join20(homedir9(), ".zelari-code", "hooks");
43300
45205
  }
43301
45206
  function projectHooksDir(projectRoot) {
43302
- return join17(projectRoot, ".zelari", "hooks");
45207
+ return join20(projectRoot, ".zelari", "hooks");
43303
45208
  }
43304
45209
  function fingerprintHookDirs(dirs) {
43305
45210
  const parts = [];
@@ -43320,7 +45225,7 @@ function fingerprintHookDirs(dirs) {
43320
45225
  continue;
43321
45226
  }
43322
45227
  for (const name of names) {
43323
- const full = join17(dir, name);
45228
+ const full = join20(dir, name);
43324
45229
  try {
43325
45230
  const st = statSync3(full);
43326
45231
  parts.push(`${full}:${st.mtimeMs}:${st.size}`);
@@ -43361,7 +45266,7 @@ var init_lifecycleHooks = __esm({
43361
45266
  });
43362
45267
 
43363
45268
  // src/cli/toolResultCache.ts
43364
- import { createHash as createHash11 } from "node:crypto";
45269
+ import { createHash as createHash13 } from "node:crypto";
43365
45270
  import { promises as fs19 } from "node:fs";
43366
45271
  import path40 from "node:path";
43367
45272
  function isToolCacheEnabled() {
@@ -43374,7 +45279,7 @@ function resolveToolCacheTtlMs() {
43374
45279
  return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
43375
45280
  }
43376
45281
  function hashKey(parts) {
43377
- return createHash11("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
45282
+ return createHash13("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
43378
45283
  }
43379
45284
  function resultBytes(result) {
43380
45285
  try {
@@ -43774,7 +45679,8 @@ function createBuiltinToolRegistry(options = {}) {
43774
45679
  ...options.subAgentModel ? { model: options.subAgentModel } : {}
43775
45680
  }),
43776
45681
  ...options.memoryService ? { memoryService: options.memoryService } : {},
43777
- ...options.memoryAutoWrite !== void 0 ? { memoryAutoWrite: options.memoryAutoWrite } : {}
45682
+ ...options.memoryAutoWrite !== void 0 ? { memoryAutoWrite: options.memoryAutoWrite } : {},
45683
+ ...options.onTentacleEvent ? { onTentacleEvent: options.onTentacleEvent } : {}
43778
45684
  },
43779
45685
  options.planMode === true ? { allowedAgents: ["explore"] } : void 0
43780
45686
  );
@@ -44219,8 +46125,8 @@ var init_metrics3 = __esm({
44219
46125
  maybeRotate() {
44220
46126
  if (!existsSync25(this.file)) return;
44221
46127
  try {
44222
- const stat = statSync4(this.file);
44223
- if (stat.size >= METRICS_ROTATE_BYTES) {
46128
+ const stat2 = statSync4(this.file);
46129
+ if (stat2.size >= METRICS_ROTATE_BYTES) {
44224
46130
  const rotated = this.file.replace(/\.jsonl$/, ".1.jsonl");
44225
46131
  renameSync3(this.file, rotated);
44226
46132
  }
@@ -44233,11 +46139,11 @@ var init_metrics3 = __esm({
44233
46139
  });
44234
46140
 
44235
46141
  // src/cli/state/fileStateStore.ts
44236
- import { createHash as createHash13, randomUUID as randomUUID2 } from "node:crypto";
46142
+ import { createHash as createHash15, randomUUID as randomUUID3 } from "node:crypto";
44237
46143
  import { promises as fs21 } from "node:fs";
44238
46144
  import * as path43 from "node:path";
44239
46145
  function shortId() {
44240
- return randomUUID2().replace(/-/g, "").slice(0, 12);
46146
+ return randomUUID3().replace(/-/g, "").slice(0, 12);
44241
46147
  }
44242
46148
  async function writeJsonAtomic(filePath, data) {
44243
46149
  await fs21.mkdir(path43.dirname(filePath), { recursive: true });
@@ -44285,7 +46191,7 @@ async function getStateStore(projectRoot, env = process.env) {
44285
46191
  }
44286
46192
  }
44287
46193
  function hashStablePrompt(stable) {
44288
- return createHash13("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
46194
+ return createHash15("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
44289
46195
  }
44290
46196
  var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
44291
46197
  var init_fileStateStore = __esm({
@@ -44931,9 +46837,9 @@ function compactHistoryDetailed(messages, opts) {
44931
46837
  );
44932
46838
  }
44933
46839
  async function compactHistoryAsync(messages, opts) {
44934
- const base = compactHistoryDetailed(messages, opts);
44935
- if (!base.compacted || base.messagesRemoved === 0) return base;
44936
- const cut = base.messagesRemoved;
46840
+ const base2 = compactHistoryDetailed(messages, opts);
46841
+ if (!base2.compacted || base2.messagesRemoved === 0) return base2;
46842
+ const cut = base2.messagesRemoved;
44937
46843
  const droppedMsgs = messages.slice(0, cut);
44938
46844
  const extractive = extractiveHistorySummary(droppedMsgs);
44939
46845
  let summaryText = extractive;
@@ -45050,7 +46956,7 @@ __export(conversationContext_exports, {
45050
46956
  setLastClarification: () => setLastClarification
45051
46957
  });
45052
46958
  import { existsSync as existsSync26 } from "node:fs";
45053
- import { join as join19 } from "node:path";
46959
+ import { join as join22 } from "node:path";
45054
46960
  function getHistory() {
45055
46961
  return history;
45056
46962
  }
@@ -45059,7 +46965,7 @@ function setHistory(messages) {
45059
46965
  history = projected === messages ? [...messages] : projected;
45060
46966
  }
45061
46967
  function compactInPlace(cwd = process.cwd()) {
45062
- const durableStatePresent = existsSync26(join19(cwd, ".zelari", "state", "HEAD.json"));
46968
+ const durableStatePresent = existsSync26(join22(cwd, ".zelari", "state", "HEAD.json"));
45063
46969
  history = applySessionSurface(compactHistory(history, { durableStatePresent }));
45064
46970
  }
45065
46971
  function appendMessages(msgs) {
@@ -46102,11 +48008,11 @@ var init_claudeProvider = __esm({
46102
48008
  });
46103
48009
 
46104
48010
  // src/cli/memory/legacyImport.ts
46105
- import { createHash as createHash14 } from "node:crypto";
48011
+ import { createHash as createHash16 } from "node:crypto";
46106
48012
  import { promises as fs22 } from "node:fs";
46107
48013
  import * as path44 from "node:path";
46108
48014
  function sourceId(fact, line) {
46109
- return `jsonl:${fact.id ?? createHash14("sha256").update(line).digest("hex")}`;
48015
+ return `jsonl:${fact.id ?? createHash16("sha256").update(line).digest("hex")}`;
46110
48016
  }
46111
48017
  function kind(metadata2) {
46112
48018
  const raw = metadata2.memoryKind;
@@ -46604,7 +48510,7 @@ WHERE NOT EXISTS (SELECT 1 FROM memory_fts f WHERE f.node_id = n.id);
46604
48510
  });
46605
48511
 
46606
48512
  // src/cli/memory/sqliteBackend.ts
46607
- import { createHash as createHash15, randomUUID as randomUUID3 } from "node:crypto";
48513
+ import { createHash as createHash17, randomUUID as randomUUID4 } from "node:crypto";
46608
48514
  import { promises as fs23 } from "node:fs";
46609
48515
  import * as path46 from "node:path";
46610
48516
  function boundedLimit(value, fallback = 50) {
@@ -46619,7 +48525,7 @@ function withOptional(target, key, value) {
46619
48525
  return { ...target, [key]: value };
46620
48526
  }
46621
48527
  function contentHash(content) {
46622
- return createHash15("sha256").update(content).digest("hex");
48528
+ return createHash17("sha256").update(content).digest("hex");
46623
48529
  }
46624
48530
  var NODE_COLUMNS, INSERT_NODE_SQL, SOURCE_COLUMNS, SQLiteMemoryBackend;
46625
48531
  var init_sqliteBackend = __esm({
@@ -46679,9 +48585,9 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
46679
48585
  const zelariDirectory = path46.join(resolved, ".zelari");
46680
48586
  const directory = path46.join(zelariDirectory, "memory");
46681
48587
  for (const candidate of [zelariDirectory, directory]) {
46682
- let stat;
48588
+ let stat2;
46683
48589
  try {
46684
- stat = await fs23.lstat(candidate);
48590
+ stat2 = await fs23.lstat(candidate);
46685
48591
  } catch (error51) {
46686
48592
  if (error51.code !== "ENOENT") throw error51;
46687
48593
  try {
@@ -46689,9 +48595,9 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
46689
48595
  } catch (mkdirError) {
46690
48596
  if (mkdirError.code !== "EEXIST") throw mkdirError;
46691
48597
  }
46692
- stat = await fs23.lstat(candidate);
48598
+ stat2 = await fs23.lstat(candidate);
46693
48599
  }
46694
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
48600
+ if (stat2.isSymbolicLink() || !stat2.isDirectory()) {
46695
48601
  throw new Error(`Memory directory is not a real directory: ${candidate}`);
46696
48602
  }
46697
48603
  }
@@ -46727,7 +48633,7 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
46727
48633
  const createdAt = input.createdAt ?? now;
46728
48634
  const recordedAt = input.recordedAt ?? createdAt;
46729
48635
  const node = MemoryNodeSchema.parse({
46730
- id: input.id ?? `mem_${randomUUID3()}`,
48636
+ id: input.id ?? `mem_${randomUUID4()}`,
46731
48637
  schemaVersion: 1,
46732
48638
  projectId: input.projectId,
46733
48639
  kind: input.kind,
@@ -46753,7 +48659,7 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
46753
48659
  (version_id, memory_id, revision, snapshot_json, recorded_at, actor, reason)
46754
48660
  VALUES (?, ?, 1, ?, ?, ?, ?)`,
46755
48661
  params: [
46756
- `ver_${randomUUID3()}`,
48662
+ `ver_${randomUUID4()}`,
46757
48663
  node.id,
46758
48664
  JSON.stringify(node),
46759
48665
  recordedAt,
@@ -46814,7 +48720,7 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
46814
48720
  (version_id, memory_id, revision, snapshot_json, recorded_at, actor, reason)
46815
48721
  VALUES (?, ?, (SELECT COALESCE(MAX(revision), 0) + 1 FROM memory_versions WHERE memory_id=?), ?, ?, ?, ?)`,
46816
48722
  params: [
46817
- `ver_${randomUUID3()}`,
48723
+ `ver_${randomUUID4()}`,
46818
48724
  updated.id,
46819
48725
  updated.id,
46820
48726
  JSON.stringify(updated),
@@ -47048,7 +48954,7 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
47048
48954
  if (decoded) return decoded;
47049
48955
  const edge = MemoryEdgeSchema.parse({
47050
48956
  ...input,
47051
- id: input.id ?? `edge_${randomUUID3()}`,
48957
+ id: input.id ?? `edge_${randomUUID4()}`,
47052
48958
  strength: input.strength ?? 1,
47053
48959
  confidence: input.confidence ?? 0.8,
47054
48960
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -47232,7 +49138,7 @@ __export(serviceFactory_exports, {
47232
49138
  isMemorySemanticEnabled: () => isMemorySemanticEnabled,
47233
49139
  isMemoryV2Enabled: () => isMemoryV2Enabled
47234
49140
  });
47235
- import { createHash as createHash16 } from "node:crypto";
49141
+ import { createHash as createHash18 } from "node:crypto";
47236
49142
  import { promises as fs24 } from "node:fs";
47237
49143
  import * as path47 from "node:path";
47238
49144
  function isMemoryV2Enabled(env = process.env) {
@@ -47259,7 +49165,7 @@ async function canonicalProjectId(projectRoot) {
47259
49165
  }
47260
49166
  canonical = canonical.replace(/\\/g, "/").replace(/\/$/, "");
47261
49167
  if (process.platform === "win32") canonical = canonical.toLocaleLowerCase("en-US");
47262
- return `project_${createHash16("sha256").update(canonical).digest("hex").slice(0, 24)}`;
49168
+ return `project_${createHash18("sha256").update(canonical).digest("hex").slice(0, 24)}`;
47263
49169
  }
47264
49170
  async function getMemoryService(projectRoot, env = process.env, options = {}) {
47265
49171
  const projectId2 = await canonicalProjectId(projectRoot);
@@ -47324,10 +49230,10 @@ var init_serviceFactory = __esm({
47324
49230
 
47325
49231
  // src/cli/workspace/projectInstructions.ts
47326
49232
  import { existsSync as existsSync28, readFileSync as readFileSync23 } from "node:fs";
47327
- import { join as join23 } from "node:path";
49233
+ import { join as join26 } from "node:path";
47328
49234
  function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
47329
49235
  for (const name of CANDIDATES) {
47330
- const full = join23(projectRoot, name);
49236
+ const full = join26(projectRoot, name);
47331
49237
  if (!existsSync28(full)) continue;
47332
49238
  try {
47333
49239
  let raw = readFileSync23(full, "utf8");
@@ -47375,7 +49281,7 @@ __export(workspaceSummary_exports, {
47375
49281
  buildZelariReadHint: () => buildZelariReadHint
47376
49282
  });
47377
49283
  import { existsSync as existsSync29, readFileSync as readFileSync24, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
47378
- import { join as join24, relative as relative2 } from "node:path";
49284
+ import { join as join27, relative as relative2 } from "node:path";
47379
49285
  function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
47380
49286
  const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
47381
49287
  const name = safeProjectName(projectRoot);
@@ -47408,7 +49314,7 @@ function formatTaskLine(t) {
47408
49314
  }
47409
49315
  function buildPlanSummary(projectRoot = process.cwd(), options) {
47410
49316
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
47411
- const planPath = join24(zelariRoot, "plan.json");
49317
+ const planPath = join27(zelariRoot, "plan.json");
47412
49318
  if (!existsSync29(planPath)) return null;
47413
49319
  let plan;
47414
49320
  try {
@@ -47551,7 +49457,7 @@ function pickNextTask(open) {
47551
49457
  )[0];
47552
49458
  }
47553
49459
  function buildZelariReadHint(projectRoot = process.cwd()) {
47554
- const planPath = join24(resolveWorkspaceRoot(projectRoot), "plan.json");
49460
+ const planPath = join27(resolveWorkspaceRoot(projectRoot), "plan.json");
47555
49461
  if (!existsSync29(planPath)) return "";
47556
49462
  return [
47557
49463
  "# Council workspace detected (.zelari/) \u2014 DRAFT vault",
@@ -47567,7 +49473,7 @@ function safeProjectName(root) {
47567
49473
  }
47568
49474
  }
47569
49475
  function readPackageJson(projectRoot) {
47570
- const p3 = join24(projectRoot, "package.json");
49476
+ const p3 = join27(projectRoot, "package.json");
47571
49477
  if (!existsSync29(p3)) return null;
47572
49478
  try {
47573
49479
  return JSON.parse(readFileSync24(p3, "utf8"));
@@ -47620,11 +49526,11 @@ function listShallow(projectRoot, maxEntries) {
47620
49526
  out.push(`\u2026 (+${top.length - count} more)`);
47621
49527
  break;
47622
49528
  }
47623
- const rel2 = relative2(projectRoot, join24(projectRoot, entry.name));
49529
+ const rel2 = relative2(projectRoot, join27(projectRoot, entry.name));
47624
49530
  if (entry.isDirectory()) {
47625
49531
  let inner = "";
47626
49532
  try {
47627
- const sub = readdirSync6(join24(projectRoot, entry.name), {
49533
+ const sub = readdirSync6(join27(projectRoot, entry.name), {
47628
49534
  withFileTypes: true
47629
49535
  }).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
47630
49536
  if (sub.length > 0)
@@ -47673,11 +49579,11 @@ var init_workspaceSummary = __esm({
47673
49579
 
47674
49580
  // src/cli/workspace/buildLessonsSummary.ts
47675
49581
  import { existsSync as existsSync30 } from "node:fs";
47676
- import { join as join25 } from "node:path";
49582
+ import { join as join28 } from "node:path";
47677
49583
  function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
47678
49584
  if (process.env["ZELARI_LESSONS"] === "0") return null;
47679
49585
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
47680
- if (!existsSync30(join25(zelariRoot, "lessons.jsonl"))) return null;
49586
+ if (!existsSync30(join28(zelariRoot, "lessons.jsonl"))) return null;
47681
49587
  const lessons = recallLessons(zelariRoot, {
47682
49588
  maxLessons: 5,
47683
49589
  maxBytes: 2048,
@@ -47699,7 +49605,7 @@ __export(composeContext_exports, {
47699
49605
  composeProjectContext: () => composeProjectContext
47700
49606
  });
47701
49607
  import { existsSync as existsSync31, readdirSync as readdirSync7, readFileSync as readFileSync25 } from "node:fs";
47702
- import { join as join26 } from "node:path";
49608
+ import { join as join29 } from "node:path";
47703
49609
  function cap2(text, max, label) {
47704
49610
  if (!text || text.length <= max) return { text: text || "", truncated: false };
47705
49611
  return {
@@ -47716,7 +49622,7 @@ function buildDesignIndex(projectRoot, maxChars) {
47716
49622
  "# Design vault index (.zelari/) \u2014 HYPOTHESES only",
47717
49623
  "Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
47718
49624
  ];
47719
- const docsDir = join26(root, "docs");
49625
+ const docsDir = join29(root, "docs");
47720
49626
  if (existsSync31(docsDir)) {
47721
49627
  try {
47722
49628
  const docs = readdirSync7(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
@@ -47731,11 +49637,11 @@ function buildDesignIndex(projectRoot, maxChars) {
47731
49637
  }
47732
49638
  }
47733
49639
  for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
47734
- if (existsSync31(join26(root, name))) {
49640
+ if (existsSync31(join29(root, name))) {
47735
49641
  lines.push(`- .zelari/${name} present`);
47736
49642
  }
47737
49643
  }
47738
- const decisionsDir = join26(root, "decisions");
49644
+ const decisionsDir = join29(root, "decisions");
47739
49645
  if (existsSync31(decisionsDir)) {
47740
49646
  try {
47741
49647
  const n = readdirSync7(decisionsDir).filter((f) => f.endsWith(".md")).length;
@@ -47837,14 +49743,14 @@ function composeProjectContext(input) {
47837
49743
  }
47838
49744
  function readDurableHeadSync(projectRoot) {
47839
49745
  try {
47840
- const headPath = join26(projectRoot, ".zelari", "state", "HEAD.json");
49746
+ const headPath = join29(projectRoot, ".zelari", "state", "HEAD.json");
47841
49747
  if (!existsSync31(headPath)) return "";
47842
49748
  const head = JSON.parse(readFileSync25(headPath, "utf8"));
47843
49749
  if (!head?.id) return "";
47844
- const metaPath = join26(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
49750
+ const metaPath = join29(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
47845
49751
  if (!existsSync31(metaPath)) return "";
47846
49752
  const meta3 = JSON.parse(readFileSync25(metaPath, "utf8"));
47847
- const discPath = meta3.artifactDir ? join26(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join26(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
49753
+ const discPath = meta3.artifactDir ? join29(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join29(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
47848
49754
  let discoveries = [];
47849
49755
  if (existsSync31(discPath)) {
47850
49756
  discoveries = JSON.parse(readFileSync25(discPath, "utf8"));
@@ -47881,9 +49787,9 @@ __export(planDetect_exports, {
47881
49787
  hasWorkspacePlan: () => hasWorkspacePlan
47882
49788
  });
47883
49789
  import { existsSync as existsSync32, readFileSync as readFileSync26 } from "node:fs";
47884
- import { join as join27 } from "node:path";
49790
+ import { join as join30 } from "node:path";
47885
49791
  function hasWorkspacePlan(projectRoot = process.cwd()) {
47886
- const planPath = join27(resolveWorkspaceRoot(projectRoot), "plan.json");
49792
+ const planPath = join30(resolveWorkspaceRoot(projectRoot), "plan.json");
47887
49793
  if (!existsSync32(planPath)) return false;
47888
49794
  try {
47889
49795
  const parsed = JSON.parse(readFileSync26(planPath, "utf8"));
@@ -47952,7 +49858,7 @@ import {
47952
49858
  mkdirSync as mkdirSync15,
47953
49859
  renameSync as renameSync4
47954
49860
  } from "node:fs";
47955
- import { join as join28, basename as basename3, dirname as dirname8, relative as relative3 } from "node:path";
49861
+ import { join as join31, basename as basename3, dirname as dirname8, relative as relative3 } from "node:path";
47956
49862
  function createWorkspaceContext(projectRoot = process.cwd()) {
47957
49863
  const rootDir = resolveWorkspaceRoot(projectRoot);
47958
49864
  return {
@@ -47962,7 +49868,7 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
47962
49868
  };
47963
49869
  }
47964
49870
  function planJsonPath(ctx) {
47965
- return join28(ctx.rootDir, "plan.json");
49871
+ return join31(ctx.rootDir, "plan.json");
47966
49872
  }
47967
49873
  function readPlan(ctx) {
47968
49874
  const jsonPath = planJsonPath(ctx);
@@ -48075,7 +49981,7 @@ function renderPlanBody(summary) {
48075
49981
  return lines.join("\n");
48076
49982
  }
48077
49983
  function nextAdrId(ctx) {
48078
- const decisionsDir = join28(ctx.rootDir, "decisions");
49984
+ const decisionsDir = join31(ctx.rootDir, "decisions");
48079
49985
  if (!existsSync33(decisionsDir)) return "001";
48080
49986
  const existing = readdirSync8(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
48081
49987
  const max = existing.length === 0 ? 0 : Math.max(...existing);
@@ -48118,7 +50024,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
48118
50024
  status: "pending",
48119
50025
  priority: t.priority
48120
50026
  });
48121
- const taskPath = join28(ctx.rootDir, "plan-tasks", `${id3}.md`);
50027
+ const taskPath = join31(ctx.rootDir, "plan-tasks", `${id3}.md`);
48122
50028
  const meta3 = {
48123
50029
  kind: "task",
48124
50030
  id: id3,
@@ -48160,7 +50066,7 @@ function addMilestoneRecord(ctx, summary, input) {
48160
50066
  dueDate: input.dueDate,
48161
50067
  targetVersion: version2
48162
50068
  });
48163
- const path72 = join28(ctx.rootDir, "milestones", `${id3}.md`);
50069
+ const path72 = join31(ctx.rootDir, "milestones", `${id3}.md`);
48164
50070
  const meta3 = {
48165
50071
  kind: "milestone",
48166
50072
  id: id3,
@@ -48458,7 +50364,7 @@ function createNfrSpecStub(ctx) {
48458
50364
  },
48459
50365
  planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
48460
50366
  };
48461
- const outPath = join28(ctx.rootDir, "nfr-spec.json");
50367
+ const outPath = join31(ctx.rootDir, "nfr-spec.json");
48462
50368
  writeFileSync17(outPath, JSON.stringify(spec, null, 2), "utf8");
48463
50369
  return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
48464
50370
  });
@@ -48522,11 +50428,11 @@ function searchDocumentsStub(ctx) {
48522
50428
  (w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
48523
50429
  );
48524
50430
  const files = [
48525
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "decisions")),
48526
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "docs")),
48527
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "reviews")),
48528
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "plan-tasks")),
48529
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "milestones")),
50431
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "decisions")),
50432
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "docs")),
50433
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "reviews")),
50434
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "plan-tasks")),
50435
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "milestones")),
48530
50436
  workspaceFile(ctx.rootDir, "plan"),
48531
50437
  workspaceFile(ctx.rootDir, "risks")
48532
50438
  ];
@@ -48584,9 +50490,9 @@ function linkDocumentsStub(ctx) {
48584
50490
  const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
48585
50491
  if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
48586
50492
  const allFiles = [
48587
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "decisions")),
48588
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "docs")),
48589
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "reviews"))
50493
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "decisions")),
50494
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "docs")),
50495
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "reviews"))
48590
50496
  ];
48591
50497
  const source2 = allFiles.find((f) => {
48592
50498
  try {
@@ -48617,9 +50523,9 @@ function getDocumentBacklinksStub(ctx) {
48617
50523
  const targetId = args["targetId"] ?? args["id"];
48618
50524
  if (!targetId) return "getDocumentBacklinks requires targetId.";
48619
50525
  const allFiles = [
48620
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "decisions")),
48621
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "docs")),
48622
- ...ctx.storage.listMarkdown(join28(ctx.rootDir, "reviews"))
50526
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "decisions")),
50527
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "docs")),
50528
+ ...ctx.storage.listMarkdown(join31(ctx.rootDir, "reviews"))
48623
50529
  ];
48624
50530
  const backlinks = [];
48625
50531
  for (const file2 of allFiles) {
@@ -48875,15 +50781,15 @@ import {
48875
50781
  readFileSync as readFileSync28,
48876
50782
  writeFileSync as writeFileSync18
48877
50783
  } from "node:fs";
48878
- import { dirname as dirname9, join as join29 } from "node:path";
50784
+ import { dirname as dirname9, join as join32 } from "node:path";
48879
50785
  import { homedir as homedir10 } from "node:os";
48880
50786
  function getUserMcpPath() {
48881
- return join29(homedir10(), ".zelari-code", "mcp.json");
50787
+ return join32(homedir10(), ".zelari-code", "mcp.json");
48882
50788
  }
48883
50789
  function getProjectMcpPath(projectRoot) {
48884
- return join29(projectRoot, ".zelari", "mcp.json");
50790
+ return join32(projectRoot, ".zelari", "mcp.json");
48885
50791
  }
48886
- function readFile3(path72) {
50792
+ function readFile4(path72) {
48887
50793
  if (!existsSync34(path72)) return {};
48888
50794
  try {
48889
50795
  const parsed = JSON.parse(readFileSync28(path72, "utf8"));
@@ -48902,7 +50808,7 @@ function readFile3(path72) {
48902
50808
  return {};
48903
50809
  }
48904
50810
  }
48905
- function writeFile(path72, servers) {
50811
+ function writeFile2(path72, servers) {
48906
50812
  mkdirSync16(dirname9(path72), { recursive: true });
48907
50813
  const body = { mcpServers: servers };
48908
50814
  writeFileSync18(path72, `${JSON.stringify(body, null, 2)}
@@ -48910,9 +50816,9 @@ function writeFile(path72, servers) {
48910
50816
  }
48911
50817
  function listMcpServers(projectRoot) {
48912
50818
  const userPath = getUserMcpPath();
48913
- const userServers = readFile3(userPath);
50819
+ const userServers = readFile4(userPath);
48914
50820
  const projectPath = projectRoot && projectRoot.trim() ? getProjectMcpPath(projectRoot.trim()) : null;
48915
- const projectServers = projectPath ? readFile3(projectPath) : {};
50821
+ const projectServers = projectPath ? readFile4(projectPath) : {};
48916
50822
  const servers = [];
48917
50823
  for (const [name, cfg] of Object.entries(userServers)) {
48918
50824
  servers.push({ name, ...cfg, scope: "user", path: userPath });
@@ -48951,14 +50857,14 @@ function upsertMcpServer(opts) {
48951
50857
  }
48952
50858
  path72 = getProjectMcpPath(root);
48953
50859
  }
48954
- const current = readFile3(path72);
50860
+ const current = readFile4(path72);
48955
50861
  current[name] = {
48956
50862
  command: opts.config.command.trim(),
48957
50863
  args: opts.config.args,
48958
50864
  env: opts.config.env,
48959
50865
  enabled: opts.config.enabled !== false
48960
50866
  };
48961
- writeFile(path72, current);
50867
+ writeFile2(path72, current);
48962
50868
  return { ok: true, path: path72 };
48963
50869
  }
48964
50870
  function removeMcpServer(opts) {
@@ -48966,12 +50872,12 @@ function removeMcpServer(opts) {
48966
50872
  if (!path72) {
48967
50873
  return { ok: false, error: "projectRoot required for project scope" };
48968
50874
  }
48969
- const current = readFile3(path72);
50875
+ const current = readFile4(path72);
48970
50876
  if (!(opts.name in current)) {
48971
50877
  return { ok: false, error: `Server "${opts.name}" not found in ${path72}` };
48972
50878
  }
48973
50879
  delete current[opts.name];
48974
- writeFile(path72, current);
50880
+ writeFile2(path72, current);
48975
50881
  return { ok: true, path: path72 };
48976
50882
  }
48977
50883
  var init_mcpConfigIo = __esm({
@@ -49115,16 +51021,16 @@ __export(mcpManager_exports, {
49115
51021
  registerMcpTools: () => registerMcpTools
49116
51022
  });
49117
51023
  import { existsSync as existsSync35, readFileSync as readFileSync29 } from "node:fs";
49118
- import { join as join30 } from "node:path";
51024
+ import { join as join33 } from "node:path";
49119
51025
  import { homedir as homedir11 } from "node:os";
49120
51026
  function readMcpConfig(projectRoot = process.cwd(), opts) {
49121
51027
  const merged = {};
49122
51028
  const paths = [];
49123
51029
  if (process.env["ZELARI_MCP_USER"] !== "0") {
49124
- paths.push(join30(homedir11(), ".zelari-code", "mcp.json"));
51030
+ paths.push(join33(homedir11(), ".zelari-code", "mcp.json"));
49125
51031
  }
49126
51032
  if (!opts?.skipProjectMcp) {
49127
- paths.push(join30(projectRoot, ".zelari", "mcp.json"));
51033
+ paths.push(join33(projectRoot, ".zelari", "mcp.json"));
49128
51034
  }
49129
51035
  for (const p3 of paths) {
49130
51036
  if (!existsSync35(p3)) continue;
@@ -49143,7 +51049,7 @@ async function ensureLoaded(projectRoot) {
49143
51049
  if (state2.loaded) return;
49144
51050
  state2.loaded = true;
49145
51051
  const trusted = isFolderTrusted(projectRoot);
49146
- if (!trusted && existsSync35(join30(projectRoot, ".zelari", "mcp.json"))) {
51052
+ if (!trusted && existsSync35(join33(projectRoot, ".zelari", "mcp.json"))) {
49147
51053
  state2.warnings.push(
49148
51054
  "[mcp] project .zelari/mcp.json ignored \u2014 folder not trusted (run /trust or `zelari-code --trust` to enable project MCP)"
49149
51055
  );
@@ -49369,14 +51275,14 @@ __export(agentsMd_exports, {
49369
51275
  updateAgentsMd: () => updateAgentsMd
49370
51276
  });
49371
51277
  import { existsSync as existsSync36, readFileSync as readFileSync30, writeFileSync as writeFileSync19 } from "node:fs";
49372
- import { createHash as createHash17 } from "node:crypto";
49373
- import { join as join31 } from "node:path";
49374
- import { readFile as readFile4 } from "node:fs/promises";
51278
+ import { createHash as createHash19 } from "node:crypto";
51279
+ import { join as join34 } from "node:path";
51280
+ import { readFile as readFile5 } from "node:fs/promises";
49375
51281
  async function readPackageJson2(projectRoot) {
49376
- const path72 = join31(projectRoot, "package.json");
51282
+ const path72 = join34(projectRoot, "package.json");
49377
51283
  if (!existsSync36(path72)) return null;
49378
51284
  try {
49379
- return JSON.parse(await readFile4(path72, "utf8"));
51285
+ return JSON.parse(await readFile5(path72, "utf8"));
49380
51286
  } catch {
49381
51287
  return null;
49382
51288
  }
@@ -49398,7 +51304,7 @@ async function genTechStack(ctx) {
49398
51304
  ].join("\n");
49399
51305
  }
49400
51306
  async function genDecisions(ctx) {
49401
- const decisionsDir = join31(ctx.rootDir, "decisions");
51307
+ const decisionsDir = join34(ctx.rootDir, "decisions");
49402
51308
  if (!existsSync36(decisionsDir)) return "_No ADRs yet._";
49403
51309
  const files = ctx.storage.listMarkdown(decisionsDir).sort();
49404
51310
  const accepted = [];
@@ -49424,7 +51330,7 @@ async function genDecisions(ctx) {
49424
51330
  }
49425
51331
  async function genConventions(ctx) {
49426
51332
  const lines = [];
49427
- const claudeMd = join31(ctx.projectRoot, "CLAUDE.MD");
51333
+ const claudeMd = join34(ctx.projectRoot, "CLAUDE.MD");
49428
51334
  if (existsSync36(claudeMd)) {
49429
51335
  const content = readFileSync30(claudeMd, "utf8");
49430
51336
  const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
@@ -49458,7 +51364,7 @@ async function genBuild(ctx) {
49458
51364
  ].join("\n");
49459
51365
  }
49460
51366
  async function genOpenQuestions(ctx) {
49461
- const path72 = join31(ctx.rootDir, "risks.md");
51367
+ const path72 = join34(ctx.rootDir, "risks.md");
49462
51368
  if (!existsSync36(path72)) return "_No open questions._";
49463
51369
  const content = readFileSync30(path72, "utf8");
49464
51370
  const lines = content.split("\n");
@@ -49534,7 +51440,7 @@ function titleCase(id3) {
49534
51440
  return id3.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
49535
51441
  }
49536
51442
  async function updateAgentsMd(ctx, projectRoot) {
49537
- const agentsPath = join31(projectRoot, "AGENTS.MD");
51443
+ const agentsPath = join34(projectRoot, "AGENTS.MD");
49538
51444
  if (existsSync36(agentsPath)) {
49539
51445
  const content = readFileSync30(agentsPath, "utf8");
49540
51446
  const hasAnyMarker = AUTO_SECTIONS.some((id3) => content.includes(MARKER_OPEN(id3)));
@@ -49584,7 +51490,7 @@ async function updateAgentsMd(ctx, projectRoot) {
49584
51490
  return { changed: true, sections: changedSections };
49585
51491
  }
49586
51492
  function hash2(s) {
49587
- return createHash17("sha256").update(s).digest("hex").slice(0, 16);
51493
+ return createHash19("sha256").update(s).digest("hex").slice(0, 16);
49588
51494
  }
49589
51495
  var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
49590
51496
  var init_agentsMd = __esm({
@@ -49706,11 +51612,11 @@ var init_completeDesign = __esm({
49706
51612
 
49707
51613
  // src/cli/workspace/planDriftCheck.ts
49708
51614
  import { existsSync as existsSync37, readFileSync as readFileSync31, readdirSync as readdirSync9, statSync as statSync6, writeFileSync as writeFileSync20 } from "node:fs";
49709
- import { join as join32 } from "node:path";
51615
+ import { join as join35 } from "node:path";
49710
51616
  function findCanonicalDoc(rootDir) {
49711
- const docsDir = join32(rootDir, "docs");
51617
+ const docsDir = join35(rootDir, "docs");
49712
51618
  if (!existsSync37(docsDir)) return null;
49713
- const candidates = readdirSync9(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync6(join32(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
51619
+ const candidates = readdirSync9(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync6(join35(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
49714
51620
  return candidates.length > 0 ? candidates[0].f : null;
49715
51621
  }
49716
51622
  function parseCanonicalDoc(text) {
@@ -49745,7 +51651,7 @@ async function runPlanDriftCheck(rootDir) {
49745
51651
  if (process.env["ZELARI_DRIFT_CHECK"] === "0") {
49746
51652
  return { ran: false, reason: "ZELARI_DRIFT_CHECK=0 (disabled)" };
49747
51653
  }
49748
- const planPath = join32(rootDir, "plan.json");
51654
+ const planPath = join35(rootDir, "plan.json");
49749
51655
  if (!existsSync37(planPath)) {
49750
51656
  return { ran: false, reason: ".zelari/plan.json missing (not design-phase)" };
49751
51657
  }
@@ -49761,7 +51667,7 @@ async function runPlanDriftCheck(rootDir) {
49761
51667
  const milestones = Array.isArray(plan.milestones) ? plan.milestones : [];
49762
51668
  const phaseIds = new Set(phases.map((p3) => typeof p3.id === "string" ? p3.id : "").filter(Boolean));
49763
51669
  const canonicalName = findCanonicalDoc(rootDir);
49764
- const canonicalText = canonicalName ? readFileSyncSafe(join32(rootDir, "docs", canonicalName)) : null;
51670
+ const canonicalText = canonicalName ? readFileSyncSafe(join35(rootDir, "docs", canonicalName)) : null;
49765
51671
  if (canonicalText !== null) {
49766
51672
  const { activePhases, blockedPrefixes } = parseCanonicalDoc(canonicalText);
49767
51673
  for (const id3 of activePhases) {
@@ -49840,7 +51746,7 @@ async function runPlanDriftCheck(rootDir) {
49840
51746
  const canonicalParsed = canonicalName !== null && canonicalText !== null;
49841
51747
  let reportPath;
49842
51748
  try {
49843
- reportPath = join32(rootDir, "drift-report.json");
51749
+ reportPath = join35(rootDir, "drift-report.json");
49844
51750
  writeFileSync20(
49845
51751
  reportPath,
49846
51752
  JSON.stringify(
@@ -49878,7 +51784,7 @@ var init_planDriftCheck = __esm({
49878
51784
  // src/cli/workspace/projectSmoke.ts
49879
51785
  import { spawn as spawn14 } from "node:child_process";
49880
51786
  import { existsSync as existsSync38, readFileSync as readFileSync32 } from "node:fs";
49881
- import { join as join33 } from "node:path";
51787
+ import { join as join36 } from "node:path";
49882
51788
  function pickSmokeScript(scripts) {
49883
51789
  if (!scripts) return null;
49884
51790
  for (const name of SMOKE_SCRIPT_PRIORITY) {
@@ -49890,7 +51796,7 @@ async function runProjectSmoke(projectRoot, timeoutMs2 = DEFAULT_TIMEOUT_MS3) {
49890
51796
  if (process.env["ZELARI_SMOKE"] === "0") {
49891
51797
  return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
49892
51798
  }
49893
- const pkgPath = join33(projectRoot, "package.json");
51799
+ const pkgPath = join36(projectRoot, "package.json");
49894
51800
  if (!existsSync38(pkgPath)) {
49895
51801
  return { ran: false, reason: "no package.json (skipped)" };
49896
51802
  }
@@ -49985,7 +51891,7 @@ __export(postCouncilHook_exports, {
49985
51891
  });
49986
51892
  import { spawn as spawn15 } from "node:child_process";
49987
51893
  import { existsSync as existsSync39, readFileSync as readFileSync33 } from "node:fs";
49988
- import { join as join34 } from "node:path";
51894
+ import { join as join37 } from "node:path";
49989
51895
  async function runCompleteDesignPostProcessor(ctx, options) {
49990
51896
  if (options?.runMode === "implementation") {
49991
51897
  return {
@@ -49996,8 +51902,8 @@ async function runCompleteDesignPostProcessor(ctx, options) {
49996
51902
  if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
49997
51903
  return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
49998
51904
  }
49999
- const planJsonPath2 = join34(ctx.rootDir, "plan.json");
50000
- const scriptPath = join34(ctx.projectRoot, "complete-design.mjs");
51905
+ const planJsonPath2 = join37(ctx.rootDir, "plan.json");
51906
+ const scriptPath = join37(ctx.projectRoot, "complete-design.mjs");
50001
51907
  if (!existsSync39(planJsonPath2)) {
50002
51908
  return {
50003
51909
  ran: false,
@@ -50431,7 +52337,7 @@ import { promisify as promisify2 } from "node:util";
50431
52337
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
50432
52338
  import { tmpdir as tmpdir2 } from "node:os";
50433
52339
  import path49 from "node:path";
50434
- import { randomUUID as randomUUID4 } from "node:crypto";
52340
+ import { randomUUID as randomUUID5 } from "node:crypto";
50435
52341
  async function git3(cwd, args, env) {
50436
52342
  const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
50437
52343
  maxBuffer: 64 * 1024 * 1024,
@@ -50474,7 +52380,7 @@ async function createCheckpoint(cwd, label = "checkpoint") {
50474
52380
  }
50475
52381
  try {
50476
52382
  const { tree, head } = await snapshotTree(cwd);
50477
- const id3 = randomUUID4().slice(0, 8);
52383
+ const id3 = randomUUID5().slice(0, 8);
50478
52384
  const createdAt = Date.now();
50479
52385
  const message = `zelari-checkpoint ${id3}: ${label}`;
50480
52386
  const commitArgs = ["commit-tree", tree, "-m", message];
@@ -50641,7 +52547,7 @@ __export(fileBackend_exports, {
50641
52547
  getMemoryBackend: () => getMemoryBackend,
50642
52548
  isMemoryEnabled: () => isMemoryEnabled
50643
52549
  });
50644
- import { randomUUID as randomUUID5 } from "node:crypto";
52550
+ import { randomUUID as randomUUID6 } from "node:crypto";
50645
52551
  import { promises as fs26 } from "node:fs";
50646
52552
  import * as path50 from "node:path";
50647
52553
  function tokenize(text) {
@@ -50700,7 +52606,7 @@ var init_fileBackend = __esm({
50700
52606
  }
50701
52607
  async add(content, metadata2 = {}, graph) {
50702
52608
  const fact = {
50703
- id: randomUUID5(),
52609
+ id: randomUUID6(),
50704
52610
  content,
50705
52611
  metadata: metadata2,
50706
52612
  ...graph ? { graph } : {},
@@ -50808,7 +52714,7 @@ __export(zelariMission_exports, {
50808
52714
  resolveMaxTokens: () => resolveMaxTokens,
50809
52715
  runZelariMission: () => runZelariMission
50810
52716
  });
50811
- import { randomUUID as randomUUID6 } from "node:crypto";
52717
+ import { randomUUID as randomUUID7 } from "node:crypto";
50812
52718
  import { promises as fs28 } from "node:fs";
50813
52719
  import * as path52 from "node:path";
50814
52720
  function resolveMaxIterations(env = process.env) {
@@ -50905,7 +52811,7 @@ async function runZelariMission(userMessage, brief, deps) {
50905
52811
  const maxStall = resolveMaxStall(deps.env);
50906
52812
  const maxCost = resolveMaxCost(deps.env);
50907
52813
  const maxTokens = resolveMaxTokens(deps.env);
50908
- const missionId = deps.missionId ?? `m_${randomUUID6().slice(0, 8)}`;
52814
+ const missionId = deps.missionId ?? `m_${randomUUID7().slice(0, 8)}`;
50909
52815
  const startedAt = now().toISOString();
50910
52816
  const state3 = {
50911
52817
  missionId,
@@ -52174,11 +54080,11 @@ ${prompt.trim()}`];
52174
54080
  parts.push("", "Return ONLY the JSON object described in the system prompt.");
52175
54081
  return parts.join("\n");
52176
54082
  }
52177
- function uniqueId(base, existing) {
52178
- if (!existing.has(base)) return base;
54083
+ function uniqueId(base2, existing) {
54084
+ if (!existing.has(base2)) return base2;
52179
54085
  let i = 2;
52180
- while (existing.has(`${base}-${i}`)) i += 1;
52181
- return `${base}-${i}`;
54086
+ while (existing.has(`${base2}-${i}`)) i += 1;
54087
+ return `${base2}-${i}`;
52182
54088
  }
52183
54089
  function buildAutoVerifyPrompt(general) {
52184
54090
  const taskPrompt = general.prompt.length > MAX_VERIFY_TASK_PROMPT_CHARS ? `${general.prompt.slice(0, MAX_VERIFY_TASK_PROMPT_CHARS)}
@@ -52987,7 +54893,7 @@ ${body}`);
52987
54893
  function thoroughnessForKind(kind2) {
52988
54894
  return kind2 === "general" || kind2 === "fix" ? "deep" : "medium";
52989
54895
  }
52990
- function firstLine(text, maxChars = 160) {
54896
+ function firstLine2(text, maxChars = 160) {
52991
54897
  const line = text.trim().split("\n").find((l) => l.trim() !== "")?.trim() ?? "";
52992
54898
  return line.length > maxChars ? `${line.slice(0, maxChars)}\u2026` : line;
52993
54899
  }
@@ -53743,7 +55649,7 @@ ${upstream}` : node.prompt,
53743
55649
  });
53744
55650
  writer.result = `${writer.result ?? ""}
53745
55651
 
53746
- [accepted with unresolved verify findings from ` + `"${verify.label}"]${findings ? `: ${firstLine(findings)}` : ""}`.trim();
55652
+ [accepted with unresolved verify findings from ` + `"${verify.label}"]${findings ? `: ${firstLine2(findings)}` : ""}`.trim();
53747
55653
  this.radio("node_end", {
53748
55654
  description: writer.label,
53749
55655
  agent: writer.kind,
@@ -56702,7 +58608,7 @@ import {
56702
58608
  rmSync as rmSync4,
56703
58609
  writeFileSync as writeFileSync24
56704
58610
  } from "node:fs";
56705
- import { dirname as dirname13, join as join42 } from "node:path";
58611
+ import { dirname as dirname13, join as join45 } from "node:path";
56706
58612
  import { homedir as homedir12 } from "node:os";
56707
58613
  function ensureBuiltinSkillsLoadedSync() {
56708
58614
  if (builtinsLoaded) return;
@@ -56714,13 +58620,13 @@ function ensureBuiltinSkillsLoadedSync() {
56714
58620
  }
56715
58621
  }
56716
58622
  function getUserSkillsDir() {
56717
- return join42(homedir12(), ".zelari-code", "skills");
58623
+ return join45(homedir12(), ".zelari-code", "skills");
56718
58624
  }
56719
58625
  function getProjectSkillsDir(projectRoot) {
56720
- return join42(projectRoot, ".zelari", "skills");
58626
+ return join45(projectRoot, ".zelari", "skills");
56721
58627
  }
56722
58628
  function skillFilePath(dir, name) {
56723
- return join42(dir, name, "SKILL.md");
58629
+ return join45(dir, name, "SKILL.md");
56724
58630
  }
56725
58631
  function classifyScope(skillPath, projectRoot) {
56726
58632
  const userDir = getUserSkillsDir().replace(/\\/g, "/");
@@ -56796,9 +58702,9 @@ function listSkillsSnapshot(projectRoot) {
56796
58702
  const skills = [];
56797
58703
  const seen = /* @__PURE__ */ new Set();
56798
58704
  if (root) {
56799
- scanSkillsDir(join42(root, ".zelari", "skills"), root, seen, skills);
56800
- scanSkillsDir(join42(root, ".claude", "skills"), root, seen, skills);
56801
- scanSkillsDir(join42(root, ".opencode", "skills"), root, seen, skills);
58705
+ scanSkillsDir(join45(root, ".zelari", "skills"), root, seen, skills);
58706
+ scanSkillsDir(join45(root, ".claude", "skills"), root, seen, skills);
58707
+ scanSkillsDir(join45(root, ".opencode", "skills"), root, seen, skills);
56802
58708
  }
56803
58709
  scanSkillsDir(userSkillsDir, root, seen, skills);
56804
58710
  for (const s of listCodingSkills()) {
@@ -56896,7 +58802,7 @@ function removeSkill(opts) {
56896
58802
  }
56897
58803
  dir = getProjectSkillsDir(root);
56898
58804
  }
56899
- const skillDir = join42(dir, name);
58805
+ const skillDir = join45(dir, name);
56900
58806
  const path72 = skillFilePath(dir, name);
56901
58807
  if (!existsSync49(path72) && !existsSync49(skillDir)) {
56902
58808
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
@@ -57437,7 +59343,7 @@ var init_mcpCli = __esm({
57437
59343
 
57438
59344
  // src/cli/mcp/mcpPermissionServer.ts
57439
59345
  import { createInterface as createInterface2 } from "node:readline";
57440
- import { randomUUID as randomUUID8 } from "node:crypto";
59346
+ import { randomUUID as randomUUID9 } from "node:crypto";
57441
59347
  function startPermissionMcpServer(opts) {
57442
59348
  const socketPath = opts.socketPath.trim();
57443
59349
  const requestTimeoutMs = opts.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
@@ -57533,7 +59439,7 @@ function startPermissionMcpServer(opts) {
57533
59439
  socketPath,
57534
59440
  {
57535
59441
  t: "ask",
57536
- id: randomUUID8(),
59442
+ id: randomUUID9(),
57537
59443
  kind: "permission",
57538
59444
  tool: toolName,
57539
59445
  input: input2,
@@ -57572,7 +59478,7 @@ function startPermissionMcpServer(opts) {
57572
59478
  socketPath,
57573
59479
  {
57574
59480
  t: "ask",
57575
- id: randomUUID8(),
59481
+ id: randomUUID9(),
57576
59482
  kind: "question",
57577
59483
  question,
57578
59484
  choices,
@@ -57708,17 +59614,17 @@ import {
57708
59614
  readFileSync as readFileSync39,
57709
59615
  writeFileSync as writeFileSync25
57710
59616
  } from "node:fs";
57711
- import { join as join43 } from "node:path";
59617
+ import { join as join46 } from "node:path";
57712
59618
  import { homedir as homedir13 } from "node:os";
57713
- import { createHash as createHash18, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
59619
+ import { createHash as createHash20, randomBytes as randomBytes6, timingSafeEqual } from "node:crypto";
57714
59620
  function getZelariHome() {
57715
- return join43(homedir13(), ".zelari-code");
59621
+ return join46(homedir13(), ".zelari-code");
57716
59622
  }
57717
59623
  function getCompanionConfigPath() {
57718
- return join43(getZelariHome(), "companion.json");
59624
+ return join46(getZelariHome(), "companion.json");
57719
59625
  }
57720
59626
  function getCompanionTokenPath() {
57721
- return join43(getZelariHome(), "companion.token");
59627
+ return join46(getZelariHome(), "companion.token");
57722
59628
  }
57723
59629
  function ensureHome() {
57724
59630
  const home = getZelariHome();
@@ -57775,7 +59681,7 @@ function loadOrCreateToken(explicit) {
57775
59681
  const t = readFileSync39(path72, "utf8").trim();
57776
59682
  if (t) return { token: t, created: false };
57777
59683
  }
57778
- const token = randomBytes5(24).toString("base64url");
59684
+ const token = randomBytes6(24).toString("base64url");
57779
59685
  writeFileSync25(path72, token + "\n", "utf8");
57780
59686
  try {
57781
59687
  const fs42 = __require("node:fs");
@@ -57786,8 +59692,8 @@ function loadOrCreateToken(explicit) {
57786
59692
  }
57787
59693
  function tokenMatches(expected, provided) {
57788
59694
  if (!provided) return false;
57789
- const a = createHash18("sha256").update(expected).digest();
57790
- const b = createHash18("sha256").update(provided).digest();
59695
+ const a = createHash20("sha256").update(expected).digest();
59696
+ const b = createHash20("sha256").update(provided).digest();
57791
59697
  try {
57792
59698
  return timingSafeEqual(a, b);
57793
59699
  } catch {
@@ -57795,8 +59701,8 @@ function tokenMatches(expected, provided) {
57795
59701
  }
57796
59702
  }
57797
59703
  function slugFromPath(p3) {
57798
- const base = p3.replace(/[/\\]+$/, "").split(/[/\\]/).pop() || "project";
57799
- return base.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "project";
59704
+ const base2 = p3.replace(/[/\\]+$/, "").split(/[/\\]/).pop() || "project";
59705
+ return base2.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "project";
57800
59706
  }
57801
59707
  function mergeProjects(cfg, extraPaths) {
57802
59708
  const byId = /* @__PURE__ */ new Map();
@@ -57864,9 +59770,9 @@ var init_config = __esm({
57864
59770
  // src/cli/companion/runManager.ts
57865
59771
  import { spawn as spawn17 } from "node:child_process";
57866
59772
  import { createInterface as createInterface3 } from "node:readline";
57867
- import { randomUUID as randomUUID9 } from "node:crypto";
59773
+ import { randomUUID as randomUUID10 } from "node:crypto";
57868
59774
  import { writeFileSync as writeFileSync26, unlinkSync as unlinkSync3 } from "node:fs";
57869
- import { join as join44 } from "node:path";
59775
+ import { join as join47 } from "node:path";
57870
59776
  import { tmpdir as tmpdir3 } from "node:os";
57871
59777
  var RunManager;
57872
59778
  var init_runManager = __esm({
@@ -57933,7 +59839,7 @@ var init_runManager = __esm({
57933
59839
  }
57934
59840
  const prompt = args.prompt?.trim();
57935
59841
  if (!prompt) return { ok: false, error: "prompt is required" };
57936
- const id3 = randomUUID9();
59842
+ const id3 = randomUUID10();
57937
59843
  const mode = (args.mode || "kraken").toLowerCase();
57938
59844
  const phase2 = (args.phase || "build").toLowerCase();
57939
59845
  const run = {
@@ -57970,7 +59876,7 @@ var init_runManager = __esm({
57970
59876
  }
57971
59877
  let historyFile;
57972
59878
  if (args.history && Array.isArray(args.history) && args.history.length > 0) {
57973
- historyFile = join44(tmpdir3(), `zelari-companion-hist-${id3}.json`);
59879
+ historyFile = join47(tmpdir3(), `zelari-companion-hist-${id3}.json`);
57974
59880
  try {
57975
59881
  writeFileSync26(historyFile, JSON.stringify(args.history), "utf8");
57976
59882
  argv.push("--history-file", historyFile);
@@ -62401,12 +64307,12 @@ init_completionGate();
62401
64307
  // src/cli/kraken/verificationBridge.ts
62402
64308
  init_candidateRegistry();
62403
64309
  init_completionGate();
62404
- import { createHash as createHash12 } from "node:crypto";
64310
+ import { createHash as createHash14 } from "node:crypto";
62405
64311
 
62406
64312
  // src/cli/kraken/nativeVerification.ts
62407
64313
  init_runtime2();
62408
64314
  init_verification2();
62409
- import { readFile as readFile2 } from "node:fs/promises";
64315
+ import { readFile as readFile3 } from "node:fs/promises";
62410
64316
  import path42 from "node:path";
62411
64317
  function nativePackEnabled(env = process.env) {
62412
64318
  const v = env.ZELARI_VERIFY_PACK?.toLowerCase();
@@ -62434,7 +64340,7 @@ function packTimeoutMs(env = process.env) {
62434
64340
  }
62435
64341
  async function readPackageScripts(cwd = process.cwd()) {
62436
64342
  try {
62437
- const raw = await readFile2(path42.join(cwd, "package.json"), "utf-8");
64343
+ const raw = await readFile3(path42.join(cwd, "package.json"), "utf-8");
62438
64344
  const parsed = JSON.parse(raw);
62439
64345
  if (parsed && typeof parsed === "object" && typeof parsed.scripts === "object") {
62440
64346
  return parsed.scripts;
@@ -62528,7 +64434,7 @@ function krakenResultsToContract(requiredChecks, results, now = Date.now()) {
62528
64434
  return { criteria, results: verifications };
62529
64435
  }
62530
64436
  function sha256Hex2(input) {
62531
- return createHash12("sha256").update(input).digest("hex");
64437
+ return createHash14("sha256").update(input).digest("hex");
62532
64438
  }
62533
64439
  function matchNoteToToolTrace(note, trace) {
62534
64440
  const n = normalize5(note);
@@ -62867,17 +64773,17 @@ function mergeCompactRange(into, r) {
62867
64773
  if (r.strategy === "llm") into.strategy = "llm";
62868
64774
  else if (r.strategy && !into.strategy) into.strategy = r.strategy;
62869
64775
  }
62870
- function estimateTokens(text) {
64776
+ function estimateTokens2(text) {
62871
64777
  if (!text) return 0;
62872
64778
  return Math.max(1, Math.ceil(text.length / 4));
62873
64779
  }
62874
64780
  function estimateHistoryTokens(messages) {
62875
64781
  let n = 0;
62876
64782
  for (const m of messages) {
62877
- n += estimateTokens(m.content);
64783
+ n += estimateTokens2(m.content);
62878
64784
  if (m.toolCalls) {
62879
64785
  for (const tc of m.toolCalls) {
62880
- n += estimateTokens(tc.name) + estimateTokens(JSON.stringify(tc.args ?? {}));
64786
+ n += estimateTokens2(tc.name) + estimateTokens2(JSON.stringify(tc.args ?? {}));
62881
64787
  }
62882
64788
  }
62883
64789
  }
@@ -66241,8 +68147,8 @@ async function promoteMemoryToAgentsMd(projectRoot, node) {
66241
68147
  const root = await fs29.realpath(projectRoot).catch(() => path54.resolve(projectRoot));
66242
68148
  const target = path54.join(root, "AGENTS.md");
66243
68149
  try {
66244
- const stat = await fs29.lstat(target);
66245
- if (stat.isSymbolicLink() || !stat.isFile()) throw new Error("AGENTS.md must be a regular project file.");
68150
+ const stat2 = await fs29.lstat(target);
68151
+ if (stat2.isSymbolicLink() || !stat2.isFile()) throw new Error("AGENTS.md must be a regular project file.");
66246
68152
  } catch (error51) {
66247
68153
  if (error51.code !== "ENOENT") throw error51;
66248
68154
  }
@@ -66321,8 +68227,8 @@ async function safeExportPath(cwd, requested) {
66321
68227
  for (const segment of relativeParent.split(path55.sep).filter(Boolean)) {
66322
68228
  cursor = path55.join(cursor, segment);
66323
68229
  try {
66324
- const stat = await fs30.lstat(cursor);
66325
- if (stat.isSymbolicLink()) {
68230
+ const stat2 = await fs30.lstat(cursor);
68231
+ if (stat2.isSymbolicLink()) {
66326
68232
  throw new Error("Export path must not traverse a symbolic link.");
66327
68233
  }
66328
68234
  } catch (error51) {
@@ -66690,7 +68596,7 @@ init_zod();
66690
68596
  init_taskTool();
66691
68597
  import { promises as fs33 } from "node:fs";
66692
68598
  import path59 from "node:path";
66693
- import { randomBytes as randomBytes4 } from "node:crypto";
68599
+ import { randomBytes as randomBytes5 } from "node:crypto";
66694
68600
  var CsvFanoutArgsSchema = external_exports.object({
66695
68601
  csv_path: external_exports.string().min(1),
66696
68602
  id_column: external_exports.string().min(1),
@@ -66861,7 +68767,7 @@ async function runCsvFanout(args, deps, opts) {
66861
68767
  };
66862
68768
  }
66863
68769
  async function atomicWrite(file2, contents) {
66864
- const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes4(6).toString("hex")}.tmp`;
68770
+ const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes5(6).toString("hex")}.tmp`;
66865
68771
  await fs33.writeFile(tmp, contents, "utf8");
66866
68772
  await fs33.rename(tmp, file2);
66867
68773
  }
@@ -67160,9 +69066,9 @@ async function handleKrakenWorkbench(ctx) {
67160
69066
  for (const f of files) {
67161
69067
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
67162
69068
  const full = path60.join(dir, f);
67163
- const stat = await fs35.stat(full);
67164
- if (stat.mtimeMs > latestMtime) {
67165
- latestMtime = stat.mtimeMs;
69069
+ const stat2 = await fs35.stat(full);
69070
+ if (stat2.mtimeMs > latestMtime) {
69071
+ latestMtime = stat2.mtimeMs;
67166
69072
  latest = full;
67167
69073
  }
67168
69074
  }
@@ -69525,7 +71431,7 @@ init_taskTool();
69525
71431
  init_sessionTodos();
69526
71432
  import { promises as fs41 } from "node:fs";
69527
71433
  import path68 from "node:path";
69528
- import { randomUUID as randomUUID7 } from "node:crypto";
71434
+ import { randomUUID as randomUUID8 } from "node:crypto";
69529
71435
 
69530
71436
  // src/cli/kraken/verifierLifecycle.ts
69531
71437
  init_verification2();
@@ -69615,6 +71521,215 @@ async function runAdvisoryVerifierReview(evaluation, deps = {}) {
69615
71521
  // src/cli/runHeadless.ts
69616
71522
  init_metrics3();
69617
71523
  init_headlessSpine();
71524
+ init_runtime2();
71525
+
71526
+ // src/cli/headless/controlReader.ts
71527
+ var CONTROL_TYPES = /* @__PURE__ */ new Set([
71528
+ "steer",
71529
+ "follow_up",
71530
+ "cancel",
71531
+ "pause",
71532
+ "resume"
71533
+ ]);
71534
+ function parseControlEvent(raw) {
71535
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
71536
+ return { ok: false, id: "", reason: "control event must be a JSON object" };
71537
+ }
71538
+ const candidate = raw;
71539
+ const type = candidate["type"];
71540
+ if (typeof type !== "string" || !CONTROL_TYPES.has(type)) {
71541
+ return {
71542
+ ok: false,
71543
+ id: typeof candidate["id"] === "string" ? candidate["id"] : "",
71544
+ reason: `unknown control type: ${String(type)}`
71545
+ };
71546
+ }
71547
+ if (typeof candidate["id"] !== "string" || candidate["id"].length === 0) {
71548
+ return { ok: false, id: "", reason: "missing control id" };
71549
+ }
71550
+ if (type === "steer" || type === "follow_up") {
71551
+ if (typeof candidate["text"] !== "string" || candidate["text"].trim().length === 0) {
71552
+ return {
71553
+ ok: false,
71554
+ id: candidate["id"],
71555
+ reason: `${type} requires a non-empty "text" field`
71556
+ };
71557
+ }
71558
+ }
71559
+ if (type === "cancel" && "reason" in candidate && candidate["reason"] !== void 0 && typeof candidate["reason"] !== "string") {
71560
+ return {
71561
+ ok: false,
71562
+ id: candidate["id"],
71563
+ reason: "cancel reason must be a string"
71564
+ };
71565
+ }
71566
+ return {
71567
+ ok: true,
71568
+ event: {
71569
+ ...candidate,
71570
+ ts: typeof candidate["ts"] === "number" ? candidate["ts"] : Date.now()
71571
+ }
71572
+ };
71573
+ }
71574
+ function parseControlLine(line) {
71575
+ const trimmed = line.trim();
71576
+ if (trimmed.length === 0) return null;
71577
+ try {
71578
+ return parseControlEvent(JSON.parse(trimmed));
71579
+ } catch (e) {
71580
+ return { ok: false, id: "", reason: `malformed JSON: ${e.message}` };
71581
+ }
71582
+ }
71583
+ function startControlReader(input, onLine) {
71584
+ let buffer = "";
71585
+ const onData = (chunk) => {
71586
+ buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
71587
+ let newlineIndex = buffer.indexOf("\n");
71588
+ while (newlineIndex !== -1) {
71589
+ const line = buffer.slice(0, newlineIndex);
71590
+ buffer = buffer.slice(newlineIndex + 1);
71591
+ if (line.trim().length > 0) onLine(line.replace(/\r$/, ""));
71592
+ newlineIndex = buffer.indexOf("\n");
71593
+ }
71594
+ };
71595
+ input.on("data", onData);
71596
+ return () => {
71597
+ input.removeListener("data", onData);
71598
+ buffer = "";
71599
+ };
71600
+ }
71601
+
71602
+ // src/cli/headless/protocol.ts
71603
+ var HEADLESS_PROTOCOL_VERSION = 2;
71604
+ var HEADLESS_PROTOCOL_CAPABILITIES = [
71605
+ "stdin-control",
71606
+ "steer",
71607
+ "follow_up",
71608
+ "cancel"
71609
+ ];
71610
+ function protocolInfoEvent() {
71611
+ return {
71612
+ type: "protocol_info",
71613
+ version: HEADLESS_PROTOCOL_VERSION,
71614
+ capabilities: HEADLESS_PROTOCOL_CAPABILITIES,
71615
+ ts: Date.now()
71616
+ };
71617
+ }
71618
+ function controlAcceptedEvent(controlId, controlType) {
71619
+ return {
71620
+ type: "control_accepted",
71621
+ controlId,
71622
+ controlType,
71623
+ ts: Date.now()
71624
+ };
71625
+ }
71626
+ function controlAppliedEvent(controlId, controlType, boundary) {
71627
+ return {
71628
+ type: "control_applied",
71629
+ controlId,
71630
+ controlType,
71631
+ boundary,
71632
+ ts: Date.now()
71633
+ };
71634
+ }
71635
+ function controlRejectedEvent(controlId, reason) {
71636
+ return {
71637
+ type: "control_rejected",
71638
+ controlId,
71639
+ reason,
71640
+ ts: Date.now()
71641
+ };
71642
+ }
71643
+
71644
+ // src/cli/headless/controlBridge.ts
71645
+ var APPLIED_BOUNDARY = {
71646
+ steer: "turn-end",
71647
+ follow_up: "run-end",
71648
+ cancel: "cancel"
71649
+ };
71650
+ function attachControlPlane(opts) {
71651
+ const { input, queue, emit, onCancel } = opts;
71652
+ let finalized = false;
71653
+ queue.onDrained = (events) => {
71654
+ for (const event of events) {
71655
+ emit(
71656
+ controlAppliedEvent(
71657
+ event.id,
71658
+ event.type,
71659
+ APPLIED_BOUNDARY[event.type] ?? "unknown"
71660
+ )
71661
+ );
71662
+ }
71663
+ const cancels = events.filter((e) => e.type === "cancel");
71664
+ if (cancels.length > 0 && onCancel) {
71665
+ const last = cancels[cancels.length - 1];
71666
+ onCancel(last.type === "cancel" ? last.reason : void 0);
71667
+ }
71668
+ };
71669
+ const disposeReader = startControlReader(input, (line) => {
71670
+ const outcome = parseControlLine(line);
71671
+ if (outcome === null) return;
71672
+ if (!outcome.ok) {
71673
+ emit(controlRejectedEvent(outcome.id, outcome.reason));
71674
+ return;
71675
+ }
71676
+ const event = outcome.event;
71677
+ if (event.type === "pause" || event.type === "resume") {
71678
+ emit(
71679
+ controlRejectedEvent(event.id, `${event.type} is not supported yet`)
71680
+ );
71681
+ return;
71682
+ }
71683
+ if (finalized) {
71684
+ if (event.type === "steer") {
71685
+ const converted = toFollowUp(event);
71686
+ queue.enqueue(converted);
71687
+ emit(controlAppliedEvent(event.id, "steer", "converted-to-follow-up"));
71688
+ emit(controlAcceptedEvent(converted.id, "follow_up"));
71689
+ } else if (event.type === "follow_up") {
71690
+ queue.enqueue(event);
71691
+ emit(controlAcceptedEvent(event.id, "follow_up"));
71692
+ } else {
71693
+ emit(controlRejectedEvent(event.id, "run already finished"));
71694
+ }
71695
+ return;
71696
+ }
71697
+ queue.enqueue(event);
71698
+ emit(controlAcceptedEvent(event.id, event.type));
71699
+ });
71700
+ return {
71701
+ dispose() {
71702
+ disposeReader();
71703
+ queue.onDrained = void 0;
71704
+ },
71705
+ finalize() {
71706
+ finalized = true;
71707
+ const lateSteers = queue.drainSteers();
71708
+ for (const steer of lateSteers) {
71709
+ queue.enqueue(toFollowUp(steer));
71710
+ emit(controlAppliedEvent(steer.id, "steer", "converted-to-follow-up"));
71711
+ }
71712
+ const followUps = queue.drainFollowUps();
71713
+ for (const followUp of followUps) {
71714
+ emit(controlAppliedEvent(followUp.id, "follow_up", "run-end"));
71715
+ }
71716
+ return followUps.map((f) => f.text);
71717
+ },
71718
+ get finalized() {
71719
+ return finalized;
71720
+ }
71721
+ };
71722
+ }
71723
+ function toFollowUp(steer) {
71724
+ return {
71725
+ type: "follow_up",
71726
+ id: `fu-${steer.id}`,
71727
+ text: steer.text,
71728
+ ts: steer.ts
71729
+ };
71730
+ }
71731
+
71732
+ // src/cli/runHeadless.ts
69618
71733
  async function runHeadless(opts) {
69619
71734
  resetTaskSpawnCount();
69620
71735
  if (opts.todos && opts.todos.length > 0) {
@@ -69822,7 +71937,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
69822
71937
  });
69823
71938
  log(formatKrakenGraphAscii2(graph));
69824
71939
  if (opts.planOnly) {
69825
- const planId = randomUUID7();
71940
+ const planId = randomUUID8();
69826
71941
  const planDir = path68.join(cwd, ".zelari", "radio");
69827
71942
  const planPath = path68.join(planDir, `plan-${planId}.json`);
69828
71943
  await fs41.mkdir(planDir, { recursive: true });
@@ -69949,7 +72064,19 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
69949
72064
  const memoryFactory = await Promise.resolve().then(() => (init_serviceFactory(), serviceFactory_exports));
69950
72065
  const nativeMemory = memoryFactory.isMemoryV2Enabled() ? await memoryFactory.getMemoryService(process.cwd(), process.env) : void 0;
69951
72066
  const memoryAutoWrite = memoryFactory.isMemoryAutoWriteEnabled();
72067
+ const controlQueue = new RuntimeControlQueue();
72068
+ const harnessHolder = {};
72069
+ const controlPlane = opts.output === "json" && process.stdin.isTTY !== true ? (() => {
72070
+ emitEvent(protocolInfoEvent());
72071
+ return attachControlPlane({
72072
+ input: process.stdin,
72073
+ queue: controlQueue,
72074
+ emit: emitEvent,
72075
+ onCancel: () => harnessHolder.cancel?.()
72076
+ });
72077
+ })() : void 0;
69952
72078
  const { registry: toolRegistry } = createBuiltinToolRegistry({
72079
+ onTentacleEvent: (ev) => emitEvent(ev),
69953
72080
  planMode: planModeFromOpts(opts),
69954
72081
  gauntletParent: Boolean(opts.gauntlet) && !planModeFromOpts(opts),
69955
72082
  // Fase 1 (ADR-0020): anchor tentacles to the provider/model THIS run
@@ -70168,12 +72295,15 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
70168
72295
  // command is a test/typecheck/build/git-diff line.
70169
72296
  toolCallGate: (name, args) => spine.gateResourceToolCall(name, args) ?? { allowed: true },
70170
72297
  maxToolLoopIterations: maxToolLoop,
72298
+ // PHASE 2: control queue — SteeringObserver drains it at turn ends.
72299
+ controlQueue,
70171
72300
  ...nativeMemory ? {
70172
72301
  memoryService: nativeMemory,
70173
72302
  memoryQuery: opts.task,
70174
72303
  memoryContextChars: 2e3
70175
72304
  } : {}
70176
72305
  });
72306
+ harnessHolder.cancel = () => harness.cancel();
70177
72307
  const readBuildProgress = () => {
70178
72308
  const getter = harness.getBuildProgress;
70179
72309
  return typeof getter === "function" ? getter.call(harness) : { mutationsAttempted: 0, mutationsSucceeded: 0 };
@@ -70390,6 +72520,11 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
70390
72520
  }
70391
72521
  }
70392
72522
  await nativeMemory?.close().catch(() => void 0);
72523
+ const pendingFollowUps = controlPlane?.finalize() ?? [];
72524
+ for (const followUp of pendingFollowUps) {
72525
+ emitEvent({ type: "log", message: `follow_up_queued: ${followUp.slice(0, 500)}` });
72526
+ }
72527
+ controlPlane?.dispose();
70393
72528
  if (pass.finalReason === "error") return 3;
70394
72529
  if (strictExit !== 0) return strictExit;
70395
72530
  return pass.exitCode;