zelari-code 2.22.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3288,10 +3288,10 @@ function mergeDefs(...defs) {
3288
3288
  function cloneDef(schema) {
3289
3289
  return mergeDefs(schema._zod.def);
3290
3290
  }
3291
- function getElementAtPath(obj, path91) {
3292
- if (!path91)
3291
+ function getElementAtPath(obj, path92) {
3292
+ if (!path92)
3293
3293
  return obj;
3294
- return path91.reduce((acc, key) => acc?.[key], obj);
3294
+ return path92.reduce((acc, key) => acc?.[key], obj);
3295
3295
  }
3296
3296
  function promiseAllObject(promisesObj) {
3297
3297
  const keys = Object.keys(promisesObj);
@@ -3619,11 +3619,11 @@ function explicitlyAborted(x, startIndex = 0) {
3619
3619
  }
3620
3620
  return false;
3621
3621
  }
3622
- function prefixIssues(path91, issues) {
3622
+ function prefixIssues(path92, issues) {
3623
3623
  return issues.map((iss) => {
3624
3624
  var _a3;
3625
3625
  (_a3 = iss).path ?? (_a3.path = []);
3626
- iss.path.unshift(path91);
3626
+ iss.path.unshift(path92);
3627
3627
  return iss;
3628
3628
  });
3629
3629
  }
@@ -3841,16 +3841,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
3841
3841
  }
3842
3842
  function formatError(error51, mapper = (issue2) => issue2.message) {
3843
3843
  const fieldErrors = { _errors: [] };
3844
- const processError = (error52, path91 = []) => {
3844
+ const processError = (error52, path92 = []) => {
3845
3845
  for (const issue2 of error52.issues) {
3846
3846
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3847
- issue2.errors.map((issues) => processError({ issues }, [...path91, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path92, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path91, ...issue2.path];
3853
+ const fullpath = [...path92, ...issue2.path];
3854
3854
  if (fullpath.length === 0) {
3855
3855
  fieldErrors._errors.push(mapper(issue2));
3856
3856
  } else {
@@ -3877,17 +3877,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
3877
3877
  }
3878
3878
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
3879
3879
  const result = { errors: [] };
3880
- const processError = (error52, path91 = []) => {
3880
+ const processError = (error52, path92 = []) => {
3881
3881
  var _a3, _b;
3882
3882
  for (const issue2 of error52.issues) {
3883
3883
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3884
- issue2.errors.map((issues) => processError({ issues }, [...path91, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path92, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path91, ...issue2.path];
3890
+ const fullpath = [...path92, ...issue2.path];
3891
3891
  if (fullpath.length === 0) {
3892
3892
  result.errors.push(mapper(issue2));
3893
3893
  continue;
@@ -3919,8 +3919,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
3919
3919
  }
3920
3920
  function toDotPath(_path) {
3921
3921
  const segs = [];
3922
- const path91 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path91) {
3922
+ const path92 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
+ for (const seg of path92) {
3924
3924
  if (typeof seg === "number")
3925
3925
  segs.push(`[${seg}]`);
3926
3926
  else if (typeof seg === "symbol")
@@ -17423,13 +17423,13 @@ function resolveRef(ref, ctx) {
17423
17423
  if (!ref.startsWith("#")) {
17424
17424
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17425
17425
  }
17426
- const path91 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path91.length === 0) {
17426
+ const path92 = ref.slice(1).split("/").filter(Boolean);
17427
+ if (path92.length === 0) {
17428
17428
  return ctx.rootSchema;
17429
17429
  }
17430
17430
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17431
- if (path91[0] === defsKey) {
17432
- const key = path91[1];
17431
+ if (path92[0] === defsKey) {
17432
+ const key = path92[1];
17433
17433
  if (!key || !ctx.defs[key]) {
17434
17434
  throw new Error(`Reference not found: ${ref}`);
17435
17435
  }
@@ -19887,11 +19887,11 @@ var init_tools = __esm({
19887
19887
  if (!ctx.addDocument)
19888
19888
  return "Knowledge vault tool not available.";
19889
19889
  const title = args["title"] || "New Document";
19890
- const path91 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19890
+ const path92 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19891
19891
  const content = args["content"] || "";
19892
19892
  const tags = args["tags"] || [];
19893
19893
  ctx.addDocument({
19894
- path: path91,
19894
+ path: path92,
19895
19895
  title,
19896
19896
  content,
19897
19897
  format: "markdown",
@@ -19900,7 +19900,7 @@ var init_tools = __esm({
19900
19900
  workspaceId: ctx.workspaceId
19901
19901
  });
19902
19902
  ctx.addActivity("vault", "created document", title);
19903
- return `Document "${title}" created at "${path91}".`;
19903
+ return `Document "${title}" created at "${path92}".`;
19904
19904
  }
19905
19905
  }
19906
19906
  ];
@@ -26246,11 +26246,11 @@ var init_synthesisAudit = __esm({
26246
26246
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
26247
26247
  import { join as join4 } from "node:path";
26248
26248
  function loadNfrSpec(zelariRoot) {
26249
- const path91 = join4(zelariRoot, "nfr-spec.json");
26250
- if (!existsSync7(path91))
26249
+ const path92 = join4(zelariRoot, "nfr-spec.json");
26250
+ if (!existsSync7(path92))
26251
26251
  return null;
26252
26252
  try {
26253
- const raw = JSON.parse(readFileSync7(path91, "utf8"));
26253
+ const raw = JSON.parse(readFileSync7(path92, "utf8"));
26254
26254
  if (raw.version !== 1 || !Array.isArray(raw.targets))
26255
26255
  return null;
26256
26256
  return raw;
@@ -28556,9 +28556,9 @@ var init_types5 = __esm({
28556
28556
  import { readFileSync as readFileSync12 } from "node:fs";
28557
28557
  import { join as join10 } from "node:path";
28558
28558
  function readLessonsDeduped(zelariRoot) {
28559
- const path91 = join10(zelariRoot, LESSONS_FILE);
28559
+ const path92 = join10(zelariRoot, LESSONS_FILE);
28560
28560
  try {
28561
- const raw = readFileSync12(path91, "utf8");
28561
+ const raw = readFileSync12(path92, "utf8");
28562
28562
  const byId = /* @__PURE__ */ new Map();
28563
28563
  for (const line of raw.split(/\r?\n/)) {
28564
28564
  if (!line.trim())
@@ -28659,8 +28659,8 @@ function keywordsFrom(check2, signature) {
28659
28659
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
28660
28660
  }
28661
28661
  function writeLesson(zelariRoot, lesson) {
28662
- const path91 = join11(zelariRoot, LESSONS_FILE);
28663
- appendFileSync(path91, `${JSON.stringify(lesson)}
28662
+ const path92 = join11(zelariRoot, LESSONS_FILE);
28663
+ appendFileSync(path92, `${JSON.stringify(lesson)}
28664
28664
  `, "utf8");
28665
28665
  }
28666
28666
  function findSimilar(lessons, signature) {
@@ -30624,9 +30624,9 @@ function findCycle(nodes) {
30624
30624
  if (color.get(start) !== WHITE)
30625
30625
  continue;
30626
30626
  const stack = [[start, 0]];
30627
- const path91 = [];
30627
+ const path92 = [];
30628
30628
  color.set(start, GRAY);
30629
- path91.push(start);
30629
+ path92.push(start);
30630
30630
  while (stack.length > 0) {
30631
30631
  const top = stack[stack.length - 1];
30632
30632
  const [id3, idx] = top;
@@ -30639,17 +30639,17 @@ function findCycle(nodes) {
30639
30639
  continue;
30640
30640
  const c = color.get(dep);
30641
30641
  if (c === GRAY) {
30642
- const at = path91.indexOf(dep);
30643
- return [...path91.slice(at), dep];
30642
+ const at = path92.indexOf(dep);
30643
+ return [...path92.slice(at), dep];
30644
30644
  }
30645
30645
  if (c === WHITE) {
30646
30646
  color.set(dep, GRAY);
30647
- path91.push(dep);
30647
+ path92.push(dep);
30648
30648
  stack.push([dep, 0]);
30649
30649
  }
30650
30650
  } else {
30651
30651
  color.set(id3, BLACK);
30652
- path91.pop();
30652
+ path92.pop();
30653
30653
  stack.pop();
30654
30654
  }
30655
30655
  }
@@ -31569,8 +31569,8 @@ var init_runner = __esm({
31569
31569
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
31570
31570
  pending: []
31571
31571
  };
31572
- const path91 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31573
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path91}`);
31572
+ const path92 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31573
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path92}`);
31574
31574
  return snapshot;
31575
31575
  }
31576
31576
  callLog(msg, data) {
@@ -32206,7 +32206,40 @@ var init_agentAdapter = __esm({
32206
32206
  import { promises as fs9 } from "node:fs";
32207
32207
  import path14 from "node:path";
32208
32208
  import crypto2 from "node:crypto";
32209
- var SessionLogLockedError, DEFAULT_STALE_LOCK_MS, SessionLogWriter;
32209
+ function defaultLockLivenessProbe(pid) {
32210
+ try {
32211
+ process.kill(pid, 0);
32212
+ return true;
32213
+ } catch (err) {
32214
+ return err.code !== "ESRCH";
32215
+ }
32216
+ }
32217
+ function resolveHeartbeatStaleMs(env = process.env) {
32218
+ const raw = env.ZELARI_SPINE_HEARTBEAT_STALE_MS;
32219
+ if (raw) {
32220
+ const parsed = Number.parseInt(raw, 10);
32221
+ if (Number.isFinite(parsed) && parsed > 0)
32222
+ return parsed;
32223
+ }
32224
+ return DEFAULT_HEARTBEAT_STALE_MS;
32225
+ }
32226
+ function evaluateLockTakeover(lockInfo, opts) {
32227
+ const heartbeatStaleMs = opts.heartbeatStaleMs ?? DEFAULT_HEARTBEAT_STALE_MS;
32228
+ const staleLockMs = opts.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
32229
+ const ts = typeof lockInfo.ts === "number" && Number.isFinite(lockInfo.ts) && lockInfo.ts > 0 ? lockInfo.ts : 0;
32230
+ const pid = typeof lockInfo.pid === "number" && Number.isFinite(lockInfo.pid) && lockInfo.pid > 0 ? lockInfo.pid : void 0;
32231
+ if (pid !== void 0) {
32232
+ if (!opts.probe(pid))
32233
+ return { takeover: true, reason: "pid-dead" };
32234
+ if (ts > 0 && opts.now - ts < heartbeatStaleMs)
32235
+ return { takeover: false, reason: "alive-fresh" };
32236
+ return { takeover: true, reason: "heartbeat-stale" };
32237
+ }
32238
+ if (ts > 0 && opts.now - ts > staleLockMs)
32239
+ return { takeover: true, reason: "stale" };
32240
+ return { takeover: false, reason: "no-pid-locked" };
32241
+ }
32242
+ var SessionLogLockedError, DEFAULT_STALE_LOCK_MS, DEFAULT_HEARTBEAT_STALE_MS, SessionLogWriter;
32210
32243
  var init_writer = __esm({
32211
32244
  "packages/core/dist/session/writer.js"() {
32212
32245
  "use strict";
@@ -32223,13 +32256,18 @@ var init_writer = __esm({
32223
32256
  }
32224
32257
  };
32225
32258
  DEFAULT_STALE_LOCK_MS = 10 * 60 * 1e3;
32259
+ DEFAULT_HEARTBEAT_STALE_MS = 12e4;
32226
32260
  SessionLogWriter = class _SessionLogWriter {
32227
32261
  sessionId;
32228
32262
  eventsPath;
32229
32263
  lockPath;
32230
32264
  ownership = crypto2.randomUUID();
32231
32265
  staleLockMs;
32266
+ heartbeatStaleMs;
32267
+ probe;
32232
32268
  now;
32269
+ /** Last heartbeat ts written into the lock (throttle window anchor). */
32270
+ lastHeartbeatAt = 0;
32233
32271
  nextSeq;
32234
32272
  closed = false;
32235
32273
  chain = Promise.resolve();
@@ -32239,6 +32277,8 @@ var init_writer = __esm({
32239
32277
  this.lockPath = path14.join(sessionDir, "writer.lock");
32240
32278
  this.nextSeq = startSeq;
32241
32279
  this.staleLockMs = options.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
32280
+ this.heartbeatStaleMs = options.heartbeatStaleMs ?? resolveHeartbeatStaleMs();
32281
+ this.probe = options.probe ?? defaultLockLivenessProbe;
32242
32282
  this.now = options.now ?? Date.now;
32243
32283
  }
32244
32284
  /** Create the writer and acquire the ownership lock. */
@@ -32261,6 +32301,7 @@ var init_writer = __esm({
32261
32301
  const payload = this.lockPayload();
32262
32302
  try {
32263
32303
  await fs9.writeFile(this.lockPath, payload, { encoding: "utf-8", flag: "wx", mode: 420 });
32304
+ this.lastHeartbeatAt = this.now();
32264
32305
  return;
32265
32306
  } catch (err) {
32266
32307
  if (err.code !== "EEXIST")
@@ -32268,19 +32309,43 @@ var init_writer = __esm({
32268
32309
  }
32269
32310
  const existing = await fs9.readFile(this.lockPath, "utf-8").catch(() => null);
32270
32311
  let owner = "unknown";
32271
- let ts = 0;
32312
+ const lockInfo = {};
32272
32313
  if (existing) {
32273
32314
  try {
32274
32315
  const parsed = JSON.parse(existing);
32275
32316
  owner = parsed.ownership ?? owner;
32276
- ts = parsed.ts ?? ts;
32317
+ lockInfo.ownership = parsed.ownership;
32318
+ lockInfo.pid = parsed.pid;
32319
+ lockInfo.ts = parsed.ts;
32277
32320
  } catch {
32278
32321
  }
32279
32322
  }
32280
- const stale = ts > 0 && this.now() - ts > this.staleLockMs;
32281
- if (!stale)
32323
+ const verdict = evaluateLockTakeover(lockInfo, {
32324
+ now: this.now(),
32325
+ probe: this.probe,
32326
+ heartbeatStaleMs: this.heartbeatStaleMs,
32327
+ staleLockMs: this.staleLockMs
32328
+ });
32329
+ if (!verdict.takeover)
32282
32330
  throw new SessionLogLockedError(this.eventsPath, owner);
32283
32331
  await fs9.writeFile(this.lockPath, payload, { encoding: "utf-8" });
32332
+ this.lastHeartbeatAt = this.now();
32333
+ }
32334
+ /** Min interval between lock-ts refreshes (≤ ~1 rewrite/second). */
32335
+ static HEARTBEAT_MIN_INTERVAL_MS = 1e3;
32336
+ /**
32337
+ * Lock heartbeat: while we own the lock, refresh its ts so concurrent
32338
+ * writers can tell a live owner from a hung one. Best-effort and throttled
32339
+ * to at most ~1 rewrite/second; it rides the append chain so close() always
32340
+ * lands AFTER the last heartbeat (never resurrects a removed lock).
32341
+ */
32342
+ maybeHeartbeat() {
32343
+ const ts = this.now();
32344
+ if (ts - this.lastHeartbeatAt < _SessionLogWriter.HEARTBEAT_MIN_INTERVAL_MS)
32345
+ return;
32346
+ this.lastHeartbeatAt = ts;
32347
+ void fs9.writeFile(this.lockPath, this.lockPayload(), { encoding: "utf-8" }).catch(() => {
32348
+ });
32284
32349
  }
32285
32350
  /** Append one event; returns the validated envelope with its assigned seq. */
32286
32351
  async append(input) {
@@ -32297,6 +32362,7 @@ var init_writer = __esm({
32297
32362
  };
32298
32363
  SessionEventEnvelopeSchema.parse(envelope);
32299
32364
  this.nextSeq += 1;
32365
+ this.chain = this.chain.then(() => this.maybeHeartbeat());
32300
32366
  const line = JSON.stringify(envelope) + "\n";
32301
32367
  this.chain = this.chain.then(() => fs9.appendFile(this.eventsPath, line, "utf-8"));
32302
32368
  await this.chain;
@@ -33771,7 +33837,7 @@ async function createExecutionContext(options = {}) {
33771
33837
  workspaceKind: workspace.kind
33772
33838
  }
33773
33839
  });
33774
- const fs45 = options.fs ?? new NodeFsProvider(workspace);
33840
+ const fs46 = options.fs ?? new NodeFsProvider(workspace);
33775
33841
  const shell = options.shell ?? new NodeShellProvider(workspace);
33776
33842
  const subagent = options.subagent ?? NOOP_SUBAGENT_PROVIDER;
33777
33843
  const env = options.env ?? process.env;
@@ -33779,7 +33845,7 @@ async function createExecutionContext(options = {}) {
33779
33845
  sessionId: sessionId2,
33780
33846
  profile,
33781
33847
  workspace,
33782
- fs: fs45,
33848
+ fs: fs46,
33783
33849
  shell,
33784
33850
  subagent,
33785
33851
  appendSessionEvent: (input) => writer.append(input),
@@ -34030,16 +34096,16 @@ function runRetentionFromEnv() {
34030
34096
  maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
34031
34097
  };
34032
34098
  }
34033
- async function dirSize(path91) {
34099
+ async function dirSize(path92) {
34034
34100
  let total = 0;
34035
34101
  let entries;
34036
34102
  try {
34037
- entries = await readdir(path91, { withFileTypes: true });
34103
+ entries = await readdir(path92, { withFileTypes: true });
34038
34104
  } catch {
34039
34105
  return 0;
34040
34106
  }
34041
34107
  for (const entry of entries) {
34042
- const child = join13(path91, entry.name);
34108
+ const child = join13(path92, entry.name);
34043
34109
  if (entry.isDirectory())
34044
34110
  total += await dirSize(child);
34045
34111
  else {
@@ -34066,19 +34132,19 @@ async function enforceRunRetention(runsDir, options = {}) {
34066
34132
  for (const entry of entries) {
34067
34133
  if (!entry.isDirectory())
34068
34134
  continue;
34069
- const path91 = join13(runsDir, entry.name);
34135
+ const path92 = join13(runsDir, entry.name);
34070
34136
  let startedAt = 0;
34071
34137
  let endedAt;
34072
34138
  let completed = false;
34073
34139
  try {
34074
- const manifest = JSON.parse(await readFile(join13(path91, "manifest.json"), "utf8"));
34140
+ const manifest = JSON.parse(await readFile(join13(path92, "manifest.json"), "utf8"));
34075
34141
  startedAt = manifest.startedAt ?? 0;
34076
34142
  endedAt = manifest.endedAt;
34077
34143
  completed = Boolean(endedAt) && manifest.status !== "running";
34078
34144
  } catch {
34079
34145
  completed = false;
34080
34146
  }
34081
- infos.push({ name: entry.name, path: path91, startedAt, endedAt, completed, bytes: await dirSize(path91) });
34147
+ infos.push({ name: entry.name, path: path92, startedAt, endedAt, completed, bytes: await dirSize(path92) });
34082
34148
  }
34083
34149
  const remove = async (info) => {
34084
34150
  await rm(info.path, { recursive: true, force: true });
@@ -34850,12 +34916,12 @@ var init_engine = __esm({
34850
34916
  * content digest) and the returned ref carries the event seq when the
34851
34917
  * emitter resolved one.
34852
34918
  */
34853
- async fsEvidence(observation, path91, sha256, content, extra = {}) {
34919
+ async fsEvidence(observation, path92, sha256, content, extra = {}) {
34854
34920
  const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
34855
- const seq = await this.emitEvidence({ observation, path: path91, ...extra, ...digest ? { digest } : {} });
34921
+ const seq = await this.emitEvidence({ observation, path: path92, ...extra, ...digest ? { digest } : {} });
34856
34922
  return {
34857
34923
  tier: "fs-observation",
34858
- ref: path91,
34924
+ ref: path92,
34859
34925
  capturedAt: Date.now(),
34860
34926
  ...digest ? { digest } : {},
34861
34927
  ...seq !== void 0 ? { seq } : {}
@@ -34863,25 +34929,25 @@ var init_engine = __esm({
34863
34929
  }
34864
34930
  async evalFileExists(check2, done) {
34865
34931
  const sha256 = this.options.sha256 ?? defaultSha256;
34866
- const fs45 = this.services.fs;
34867
- if (!fs45)
34932
+ const fs46 = this.services.fs;
34933
+ if (!fs46)
34868
34934
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
34869
- const exists = await fs45.exists(check2.path);
34935
+ const exists = await fs46.exists(check2.path);
34870
34936
  if (!exists) {
34871
34937
  await this.emitEvidence({ observation: "file-exists", path: check2.path, exists: false });
34872
34938
  return done({ status: "fail", evidence: [], detail: `file not found: ${check2.path}` });
34873
34939
  }
34874
- const content = await fs45.readFile(check2.path).catch(() => "");
34940
+ const content = await fs46.readFile(check2.path).catch(() => "");
34875
34941
  return done({
34876
34942
  status: "pass",
34877
34943
  evidence: [await this.fsEvidence("file-exists", check2.path, sha256, content, { exists: true })]
34878
34944
  });
34879
34945
  }
34880
34946
  async evalFileAbsent(check2, done) {
34881
- const fs45 = this.services.fs;
34882
- if (!fs45)
34947
+ const fs46 = this.services.fs;
34948
+ if (!fs46)
34883
34949
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
34884
- const exists = await fs45.exists(check2.path);
34950
+ const exists = await fs46.exists(check2.path);
34885
34951
  if (exists) {
34886
34952
  await this.emitEvidence({ observation: "file-absent", path: check2.path, exists: true });
34887
34953
  return done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` });
@@ -34893,12 +34959,12 @@ var init_engine = __esm({
34893
34959
  }
34894
34960
  async evalFileContains(check2, done) {
34895
34961
  const sha256 = this.options.sha256 ?? defaultSha256;
34896
- const fs45 = this.services.fs;
34897
- if (!fs45)
34962
+ const fs46 = this.services.fs;
34963
+ if (!fs46)
34898
34964
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
34899
34965
  let content;
34900
34966
  try {
34901
- content = await fs45.readFile(check2.path);
34967
+ content = await fs46.readFile(check2.path);
34902
34968
  } catch {
34903
34969
  await this.emitEvidence({ observation: "file-contains", path: check2.path, readable: false });
34904
34970
  return done({ status: "fail", evidence: [], detail: `file not readable: ${check2.path}` });
@@ -35762,6 +35828,7 @@ __export(dist_exports, {
35762
35828
  CriterionSchema: () => CriterionSchema,
35763
35829
  CriterionSourceSchema: () => CriterionSourceSchema,
35764
35830
  DEFAULT_CONTEXT_POLICY: () => DEFAULT_CONTEXT_POLICY,
35831
+ DEFAULT_HEARTBEAT_STALE_MS: () => DEFAULT_HEARTBEAT_STALE_MS,
35765
35832
  DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
35766
35833
  DEFAULT_MAX_TENTACLES: () => DEFAULT_MAX_TENTACLES,
35767
35834
  DEFAULT_MEMORY_SCORING_WEIGHTS: () => DEFAULT_MEMORY_SCORING_WEIGHTS,
@@ -35992,6 +36059,7 @@ __export(dist_exports, {
35992
36059
  createGraph: () => createGraph,
35993
36060
  createRecorderObserver: () => createRecorderObserver,
35994
36061
  createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
36062
+ defaultLockLivenessProbe: () => defaultLockLivenessProbe,
35995
36063
  defaultPersonaParse: () => defaultPersonaParse,
35996
36064
  defaultResourcePolicy: () => defaultResourcePolicy,
35997
36065
  deriveInitialContract: () => deriveInitialContract,
@@ -36009,6 +36077,7 @@ __export(dist_exports, {
36009
36077
  enforceRunRetention: () => enforceRunRetention,
36010
36078
  evaluateBudgetContinuation: () => evaluateBudgetContinuation,
36011
36079
  evaluateCompletion: () => evaluateCompletion,
36080
+ evaluateLockTakeover: () => evaluateLockTakeover,
36012
36081
  evaluateMissionContinuation: () => evaluateMissionContinuation,
36013
36082
  evaluateResourceReserveGate: () => evaluateResourceReserveGate,
36014
36083
  executeTool: () => executeTool,
@@ -36160,6 +36229,7 @@ __export(dist_exports, {
36160
36229
  replayChairmanTextTools: () => replayChairmanTextTools,
36161
36230
  resolveAgentSkills: () => resolveAgentSkills,
36162
36231
  resolveCouncilRunMode: () => resolveCouncilRunMode,
36232
+ resolveHeartbeatStaleMs: () => resolveHeartbeatStaleMs,
36163
36233
  resolveInterventions: () => resolveInterventions,
36164
36234
  resolveJailed: () => resolveJailed,
36165
36235
  resolveMaxTentacles: () => resolveMaxTentacles,
@@ -37360,6 +37430,11 @@ var init_sessionSpine = __esm({
37360
37430
  } catch (err) {
37361
37431
  if (err instanceof SessionLogLockedError) {
37362
37432
  mirror.status = "locked";
37433
+ mirror.warnOnce(
37434
+ err,
37435
+ `session lock held by another (possibly dead) owner: ${err.message} \u2014 the turn resumed WITHOUT derived spine context`,
37436
+ "locked"
37437
+ );
37363
37438
  } else {
37364
37439
  mirror.status = "degraded";
37365
37440
  mirror.warnOnce(err);
@@ -37662,13 +37737,13 @@ var init_sessionSpine = __esm({
37662
37737
  appendEvent(input) {
37663
37738
  return this.append(input);
37664
37739
  }
37665
- warnOnce(err) {
37740
+ warnOnce(err, message, label = "degraded") {
37666
37741
  if (this.warned || this.options.quiet) return;
37667
37742
  this.warned = true;
37668
- const msg = err instanceof Error ? err.message : String(err);
37743
+ const msg = message ?? (err instanceof Error ? err.message : String(err));
37669
37744
  try {
37670
37745
  process.stderr.write(
37671
- `[zelari] session spine degraded for ${this.sessionId.slice(0, 8)}\u2026: ${msg} (transcript continues on the 1.x log)
37746
+ `[zelari] session spine ${label} for ${this.sessionId.slice(0, 8)}\u2026: ${msg} (transcript continues on the 1.x log)
37672
37747
  `
37673
37748
  );
37674
37749
  } catch {
@@ -38029,9 +38104,9 @@ function spillToolOutput(fullText, meta3) {
38029
38104
  const rnd = randomBytes3(3).toString("hex");
38030
38105
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
38031
38106
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
38032
- const path91 = join14(dir, file2);
38033
- writeFileSync11(path91, fullText, "utf8");
38034
- return path91;
38107
+ const path92 = join14(dir, file2);
38108
+ writeFileSync11(path92, fullText, "utf8");
38109
+ return path92;
38035
38110
  } catch {
38036
38111
  return null;
38037
38112
  }
@@ -38077,10 +38152,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
38077
38152
  ${tail2}`;
38078
38153
  }
38079
38154
  if (doSpill) {
38080
- const path91 = spillToolOutput(text, { toolName: opts.toolName });
38081
- if (path91) {
38155
+ const path92 = spillToolOutput(text, { toolName: opts.toolName });
38156
+ if (path92) {
38082
38157
  const spillNote = `
38083
- \u2026 [full output spilled to: ${path91} \u2014 re-read with read_file if you need the complete text] \u2026`;
38158
+ \u2026 [full output spilled to: ${path92} \u2014 re-read with read_file if you need the complete text] \u2026`;
38084
38159
  if (preview.includes("] \u2026\n")) {
38085
38160
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
38086
38161
  `);
@@ -42052,28 +42127,28 @@ var init_storage = __esm({
42052
42127
  VALID_SCALARS = /^(true|false|null|~)$/i;
42053
42128
  Storage = class {
42054
42129
  /** Read a Markdown file with frontmatter. Throws if not found. */
42055
- read(path91) {
42056
- if (!existsSync19(path91)) {
42057
- throw new Error(`File not found: ${path91}`);
42130
+ read(path92) {
42131
+ if (!existsSync19(path92)) {
42132
+ throw new Error(`File not found: ${path92}`);
42058
42133
  }
42059
- const md = readFileSync16(path91, "utf8");
42134
+ const md = readFileSync16(path92, "utf8");
42060
42135
  return parseFrontmatter(md);
42061
42136
  }
42062
42137
  /** Read a Markdown file; returns null if not found. */
42063
- readIfExists(path91) {
42064
- if (!existsSync19(path91)) return null;
42065
- return this.read(path91);
42138
+ readIfExists(path92) {
42139
+ if (!existsSync19(path92)) return null;
42140
+ return this.read(path92);
42066
42141
  }
42067
42142
  /**
42068
42143
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
42069
42144
  * The meta object is serialized as YAML frontmatter; body as Markdown.
42070
42145
  */
42071
- write(path91, meta3, body) {
42072
- mkdirSync10(dirname2(path91), { recursive: true });
42073
- const tmp = path91 + ".tmp-" + process.pid;
42146
+ write(path92, meta3, body) {
42147
+ mkdirSync10(dirname2(path92), { recursive: true });
42148
+ const tmp = path92 + ".tmp-" + process.pid;
42074
42149
  const md = serializeFrontmatter(meta3, body);
42075
42150
  writeFileSync13(tmp, md, "utf8");
42076
- renameSync(tmp, path91);
42151
+ renameSync(tmp, path92);
42077
42152
  }
42078
42153
  /** List all .md files in a directory (non-recursive). */
42079
42154
  listMarkdown(dir) {
@@ -42135,8 +42210,8 @@ function nextPlanTaskId(store6) {
42135
42210
  return `t${store6.counter}`;
42136
42211
  }
42137
42212
  function writePlanTaskArtifact(rootDir, task) {
42138
- const path91 = join18(rootDir, "plan-tasks", `${task.id}.md`);
42139
- mkdirSync11(dirname3(path91), { recursive: true });
42213
+ const path92 = join18(rootDir, "plan-tasks", `${task.id}.md`);
42214
+ mkdirSync11(dirname3(path92), { recursive: true });
42140
42215
  const meta3 = {
42141
42216
  kind: "task",
42142
42217
  id: task.id,
@@ -42157,7 +42232,7 @@ function writePlanTaskArtifact(rootDir, task) {
42157
42232
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
42158
42233
  ""
42159
42234
  ].filter((l) => l !== null).join("\n");
42160
- new Storage().write(path91, meta3, body);
42235
+ new Storage().write(path92, meta3, body);
42161
42236
  }
42162
42237
  function loadHandle(rootDir) {
42163
42238
  const jsonPath = join18(rootDir, "plan.json");
@@ -42863,6 +42938,100 @@ var init_inspectCommand = __esm({
42863
42938
  }
42864
42939
  });
42865
42940
 
42941
+ // src/cli/safety/selfKillGuard.ts
42942
+ function cleanToken(raw) {
42943
+ return raw.replace(/^["']+|["',]+\s*$/g, "").trim();
42944
+ }
42945
+ function isProtectedImage(name) {
42946
+ return PROTECTED_IMAGE_RE.test(cleanToken(name));
42947
+ }
42948
+ function stripSubstitutions(cmd) {
42949
+ return cmd.replace(/\$\([^)]*\)/g, " ( ) ").replace(/\$\{[^}]*\}/g, " ").replace(/`[^`]*`/g, " ` ` ");
42950
+ }
42951
+ function findImageKill(cmd) {
42952
+ const im = cmd.match(/\/{1,2}im\s+["']?([a-z0-9_.-]+)/i);
42953
+ if (im && /\btaskkill\b/i.test(cmd) && isProtectedImage(im[1])) {
42954
+ return `taskkill /IM ${cleanToken(im[1])}`;
42955
+ }
42956
+ const fi = cmd.match(/imagename\s+eq\s+["']?([a-z0-9_.-]+)/i);
42957
+ if (fi && /\btaskkill\b/i.test(cmd) && isProtectedImage(fi[1])) {
42958
+ return `taskkill /FI "IMAGENAME eq ${cleanToken(fi[1])}"`;
42959
+ }
42960
+ const nm = cmd.match(/\b(?:stop-process|kill)\b[^|;&]*-name\s+["']?([a-z0-9_.-]+)/i);
42961
+ if (nm && isProtectedImage(nm[1])) {
42962
+ return `Stop-Process -Name ${cleanToken(nm[1])}`;
42963
+ }
42964
+ if (/\bget-process\b[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd) && /\|\s*[^|]*?(?:stop-process|\.\s*kill\s*\(|\bkill\b)/i.test(cmd)) {
42965
+ return "Get-Process node | Stop-Process";
42966
+ }
42967
+ if (/\bpkill\b[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd)) return "pkill node";
42968
+ if (/\bkillall\b[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd)) return "killall node";
42969
+ if (/\bwmic\b[^|;&]*name\s*=\s*["']?node(?:\.exe)?["']?/i.test(cmd) && /\b(?:delete|terminate)\b/i.test(cmd)) {
42970
+ return "wmic process delete node.exe";
42971
+ }
42972
+ return null;
42973
+ }
42974
+ function findPsPipelineKill(cmd) {
42975
+ if (!/\bps\b[^|;&]*\|\s*(?:grep|findstr|awk|sed)[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd)) {
42976
+ return null;
42977
+ }
42978
+ if (!/\b(?:xargs\s+)?(?:kill|taskkill|pkill|killall|stop-process)\b/i.test(cmd)) {
42979
+ return null;
42980
+ }
42981
+ return "ps | grep node | kill";
42982
+ }
42983
+ function findProtectedPidKill(cmd, ctx) {
42984
+ const protectedPids = /* @__PURE__ */ new Set([
42985
+ ctx.selfPid,
42986
+ ...ctx.parentPid !== void 0 ? [ctx.parentPid] : [],
42987
+ ...ctx.extraProtectedPids ?? []
42988
+ ]);
42989
+ const hit = (pid, shape) => protectedPids.has(pid) ? `${shape} ${pid}` : null;
42990
+ const stripped = stripSubstitutions(cmd);
42991
+ for (const m of stripped.matchAll(/\/{1,2}pid(?:list)?\s+([\d,\s]+)/gi)) {
42992
+ for (const tok of m[1].split(/[\s,]+/)) {
42993
+ const pid = Number.parseInt(tok, 10);
42994
+ if (Number.isFinite(pid) && pid > 0) {
42995
+ const h = hit(pid, "taskkill /PID");
42996
+ if (h) return h;
42997
+ }
42998
+ }
42999
+ }
43000
+ for (const m of stripped.matchAll(/\b(?:stop-process|kill)\b[^|;&]*?-(?:id|pid)\s+(\d+)/gi)) {
43001
+ const h = hit(Number.parseInt(m[1], 10), "Stop-Process -Id");
43002
+ if (h) return h;
43003
+ }
43004
+ for (const m of stripped.matchAll(/\bkill\b([^|;&]*)/gi)) {
43005
+ for (const tok of m[1].split(/\s+/)) {
43006
+ if (!/^\d+$/.test(tok)) continue;
43007
+ const h = hit(Number.parseInt(tok, 10), "kill");
43008
+ if (h) return h;
43009
+ }
43010
+ }
43011
+ return null;
43012
+ }
43013
+ function blockedVerdict(reason, match) {
43014
+ return { blocked: true, reason, match, message: SELF_KILL_DENIAL_MESSAGE };
43015
+ }
43016
+ function inspectCommand(command, ctx) {
43017
+ if (typeof command !== "string" || command.length === 0) return { blocked: false };
43018
+ const imageMatch = findImageKill(command);
43019
+ if (imageMatch) return blockedVerdict("node-image", imageMatch);
43020
+ const pipelineMatch = findPsPipelineKill(command);
43021
+ if (pipelineMatch) return blockedVerdict("ps-pipeline", pipelineMatch);
43022
+ const pidMatch = findProtectedPidKill(command, ctx);
43023
+ if (pidMatch) return blockedVerdict("protected-pid", pidMatch);
43024
+ return { blocked: false };
43025
+ }
43026
+ var SELF_KILL_DENIAL_MESSAGE, PROTECTED_IMAGE_RE;
43027
+ var init_selfKillGuard = __esm({
43028
+ "src/cli/safety/selfKillGuard.ts"() {
43029
+ "use strict";
43030
+ SELF_KILL_DENIAL_MESSAGE = "Blocked: this command would kill the agent host process itself (node image / own process tree). To stop a dev server, kill by PORT instead: run `netstat -ano | findstr :<port>` to find the listener PID, then `taskkill //PID <listener-pid>` \u2014 never kill node by image name.";
43031
+ PROTECTED_IMAGE_RE = /^(?:node(?:\.exe)?|zelari(?:-code)?(?:\.exe)?)$/i;
43032
+ }
43033
+ });
43034
+
42866
43035
  // src/cli/safety/policyLoadMode.ts
42867
43036
  var policyLoadMode_exports = {};
42868
43037
  __export(policyLoadMode_exports, {
@@ -43252,6 +43421,13 @@ function createExecProcessTool(root, opts = {}) {
43252
43421
  if (blocked) {
43253
43422
  return typedErr(`[shell-blocked] exec_process refused (${blocked.reason}): ${blocked.pattern}`);
43254
43423
  }
43424
+ const selfKill = inspectCommand([input.program, ...argv].join(" "), {
43425
+ selfPid: process.pid,
43426
+ parentPid: process.ppid
43427
+ });
43428
+ if (selfKill.blocked && selfKill.message) {
43429
+ return typedErr(`[self-kill] exec_process refused: ${selfKill.message}`);
43430
+ }
43255
43431
  let cwd;
43256
43432
  try {
43257
43433
  cwd = resolveSandboxedPath(input.cwd ?? ".", { root });
@@ -43326,6 +43502,7 @@ var init_execProcess = __esm({
43326
43502
  init_zod();
43327
43503
  init_toolTypes();
43328
43504
  init_shellBlocklist();
43505
+ init_selfKillGuard();
43329
43506
  init_sandboxPath();
43330
43507
  init_osJail();
43331
43508
  execProcessInputSchema = external_exports.object({
@@ -45831,21 +46008,21 @@ function normalizeAuth(auth) {
45831
46008
  return "agent";
45832
46009
  }
45833
46010
  function readSecrets() {
45834
- const path91 = getSshSecretsPath();
45835
- if (!existsSync25(path91)) return {};
46011
+ const path92 = getSshSecretsPath();
46012
+ if (!existsSync25(path92)) return {};
45836
46013
  try {
45837
- return JSON.parse(readFileSync20(path91, "utf8"));
46014
+ return JSON.parse(readFileSync20(path92, "utf8"));
45838
46015
  } catch {
45839
46016
  return {};
45840
46017
  }
45841
46018
  }
45842
46019
  function writeSecrets(data) {
45843
- const path91 = getSshSecretsPath();
45844
- mkdirSync12(dirname4(path91), { recursive: true });
45845
- writeFileSync15(path91, `${JSON.stringify(data, null, 2)}
46020
+ const path92 = getSshSecretsPath();
46021
+ mkdirSync12(dirname4(path92), { recursive: true });
46022
+ writeFileSync15(path92, `${JSON.stringify(data, null, 2)}
45846
46023
  `, "utf8");
45847
46024
  try {
45848
- chmodSync(path91, 384);
46025
+ chmodSync(path92, 384);
45849
46026
  } catch {
45850
46027
  }
45851
46028
  }
@@ -45874,10 +46051,10 @@ function deleteSshPassword(id3) {
45874
46051
  writeSecrets({ passwords });
45875
46052
  }
45876
46053
  function readStore2() {
45877
- const path91 = getSshTargetsPath();
45878
- if (!existsSync25(path91)) return [];
46054
+ const path92 = getSshTargetsPath();
46055
+ if (!existsSync25(path92)) return [];
45879
46056
  try {
45880
- const parsed = JSON.parse(readFileSync20(path91, "utf8"));
46057
+ const parsed = JSON.parse(readFileSync20(path92, "utf8"));
45881
46058
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
45882
46059
  return list.filter(
45883
46060
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -45892,11 +46069,11 @@ function readStore2() {
45892
46069
  }
45893
46070
  }
45894
46071
  function writeStore2(targets) {
45895
- const path91 = getSshTargetsPath();
45896
- mkdirSync12(dirname4(path91), { recursive: true });
46072
+ const path92 = getSshTargetsPath();
46073
+ mkdirSync12(dirname4(path92), { recursive: true });
45897
46074
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
45898
46075
  writeFileSync15(
45899
- path91,
46076
+ path92,
45900
46077
  `${JSON.stringify({ targets: clean }, null, 2)}
45901
46078
  `,
45902
46079
  "utf8"
@@ -46142,11 +46319,11 @@ function formatSshTargetsForPrompt() {
46142
46319
  ];
46143
46320
  for (const t of targets) {
46144
46321
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
46145
- const path91 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
46322
+ const path92 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
46146
46323
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
46147
46324
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
46148
46325
  lines.push(
46149
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path91}${tags}${allow}`
46326
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path92}${tags}${allow}`
46150
46327
  );
46151
46328
  }
46152
46329
  return lines.join("\n");
@@ -49201,6 +49378,23 @@ function wrapWithShellSafety(original, audit, sessionId2, root, jail) {
49201
49378
  }
49202
49379
  throw err;
49203
49380
  }
49381
+ const selfKill = inspectCommand(cmd, { selfPid: process.pid, parentPid: process.ppid });
49382
+ if (selfKill.blocked && selfKill.message) {
49383
+ await audit.append({
49384
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
49385
+ sessionId: sessionId2,
49386
+ tool: original.name,
49387
+ args: redactForAudit(args),
49388
+ ok: false,
49389
+ resultSummary: selfKill.match ?? "self_kill",
49390
+ durationMs: 0,
49391
+ error: "self_kill_blocked"
49392
+ });
49393
+ return {
49394
+ ok: false,
49395
+ error: `[self-kill] ${selfKill.message}`
49396
+ };
49397
+ }
49204
49398
  }
49205
49399
  const rawCwd = args["cwd"];
49206
49400
  try {
@@ -49327,6 +49521,7 @@ var init_toolRegistry = __esm({
49327
49521
  init_resourceClaims();
49328
49522
  init_osJail();
49329
49523
  init_toolResultCache();
49524
+ init_selfKillGuard();
49330
49525
  init_toolTypes();
49331
49526
  init_skills2();
49332
49527
  HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
@@ -54634,8 +54829,8 @@ function readPlan(ctx) {
54634
54829
  } catch {
54635
54830
  }
54636
54831
  }
54637
- const path91 = workspaceFile(ctx.rootDir, "plan");
54638
- const doc = ctx.storage.readIfExists(path91);
54832
+ const path92 = workspaceFile(ctx.rootDir, "plan");
54833
+ const doc = ctx.storage.readIfExists(path92);
54639
54834
  if (!doc) return { phases: [], tasks: [], milestones: [] };
54640
54835
  const meta3 = doc.meta;
54641
54836
  return {
@@ -54813,7 +55008,7 @@ function addMilestoneRecord(ctx, summary, input) {
54813
55008
  dueDate: input.dueDate,
54814
55009
  targetVersion: version2
54815
55010
  });
54816
- const path91 = join31(ctx.rootDir, "milestones", `${id3}.md`);
55011
+ const path92 = join31(ctx.rootDir, "milestones", `${id3}.md`);
54817
55012
  const meta3 = {
54818
55013
  kind: "milestone",
54819
55014
  id: id3,
@@ -54830,7 +55025,7 @@ function addMilestoneRecord(ctx, summary, input) {
54830
55025
  `Target version: ${version2}`,
54831
55026
  ""
54832
55027
  ].join("\n");
54833
- ctx.storage.write(path91, meta3, body);
55028
+ ctx.storage.write(path92, meta3, body);
54834
55029
  return { id: id3, created: true };
54835
55030
  }
54836
55031
  function readPlanSummary(ctx) {
@@ -55034,7 +55229,7 @@ function addIdeaStub(ctx) {
55034
55229
  const tags = args["tags"] ?? [];
55035
55230
  const category = args["category"] ?? "General";
55036
55231
  const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
55037
- const path91 = workspaceArtifact(ctx.rootDir, "decisions", id3);
55232
+ const path92 = workspaceArtifact(ctx.rootDir, "decisions", id3);
55038
55233
  const meta3 = {
55039
55234
  kind: "adr",
55040
55235
  status: "proposed",
@@ -55060,7 +55255,7 @@ function addIdeaStub(ctx) {
55060
55255
  ...consequences.map((c) => `- ${c}`),
55061
55256
  ""
55062
55257
  ].join("\n");
55063
- ctx.storage.write(path91, meta3, body);
55258
+ ctx.storage.write(path92, meta3, body);
55064
55259
  return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
55065
55260
  });
55066
55261
  }
@@ -55142,14 +55337,14 @@ function createDocumentStub(ctx) {
55142
55337
  ctx.storage.write(risksPath, riskMeta, content);
55143
55338
  return `Document "${title}" created at risks.md (workspace root).`;
55144
55339
  }
55145
- const path91 = workspaceArtifact(ctx.rootDir, "docs", slug);
55340
+ const path92 = workspaceArtifact(ctx.rootDir, "docs", slug);
55146
55341
  const meta3 = {
55147
55342
  kind: "doc",
55148
55343
  id: slug,
55149
55344
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
55150
55345
  tags
55151
55346
  };
55152
- ctx.storage.write(path91, meta3, content);
55347
+ ctx.storage.write(path92, meta3, content);
55153
55348
  return `Document "${title}" created at docs/${slug}.md.`;
55154
55349
  });
55155
55350
  }
@@ -55775,10 +55970,10 @@ function getUserMcpPath() {
55775
55970
  function getProjectMcpPath(projectRoot) {
55776
55971
  return join32(projectRoot, ".zelari", "mcp.json");
55777
55972
  }
55778
- function readFile6(path91) {
55779
- if (!existsSync37(path91)) return {};
55973
+ function readFile6(path92) {
55974
+ if (!existsSync37(path92)) return {};
55780
55975
  try {
55781
- const parsed = JSON.parse(readFileSync29(path91, "utf8"));
55976
+ const parsed = JSON.parse(readFileSync29(path92, "utf8"));
55782
55977
  const out = {};
55783
55978
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
55784
55979
  const hasCommand = !!cfg && typeof cfg.command === "string" && !!cfg.command.trim();
@@ -55800,10 +55995,10 @@ function readFile6(path91) {
55800
55995
  return {};
55801
55996
  }
55802
55997
  }
55803
- function writeFile2(path91, servers) {
55804
- mkdirSync16(dirname9(path91), { recursive: true });
55998
+ function writeFile2(path92, servers) {
55999
+ mkdirSync16(dirname9(path92), { recursive: true });
55805
56000
  const body = { mcpServers: servers };
55806
- writeFileSync18(path91, `${JSON.stringify(body, null, 2)}
56001
+ writeFileSync18(path92, `${JSON.stringify(body, null, 2)}
55807
56002
  `, "utf8");
55808
56003
  }
55809
56004
  function listMcpServers(projectRoot) {
@@ -55841,9 +56036,9 @@ function upsertMcpServer(opts) {
55841
56036
  error: "either command (stdio) or url (http) is required"
55842
56037
  };
55843
56038
  }
55844
- let path91;
56039
+ let path92;
55845
56040
  if (opts.scope === "user") {
55846
- path91 = getUserMcpPath();
56041
+ path92 = getUserMcpPath();
55847
56042
  } else {
55848
56043
  const root = opts.projectRoot?.trim();
55849
56044
  if (!root) {
@@ -55852,9 +56047,9 @@ function upsertMcpServer(opts) {
55852
56047
  error: "projectRoot required for project scope (Open Folder first)"
55853
56048
  };
55854
56049
  }
55855
- path91 = getProjectMcpPath(root);
56050
+ path92 = getProjectMcpPath(root);
55856
56051
  }
55857
- const current = readFile6(path91);
56052
+ const current = readFile6(path92);
55858
56053
  current[name] = {
55859
56054
  command: hasCommand ? opts.config.command.trim() : void 0,
55860
56055
  args: opts.config.args,
@@ -55865,21 +56060,21 @@ function upsertMcpServer(opts) {
55865
56060
  serial: opts.config.serial,
55866
56061
  enabled: opts.config.enabled !== false
55867
56062
  };
55868
- writeFile2(path91, current);
55869
- return { ok: true, path: path91 };
56063
+ writeFile2(path92, current);
56064
+ return { ok: true, path: path92 };
55870
56065
  }
55871
56066
  function removeMcpServer(opts) {
55872
- const path91 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
55873
- if (!path91) {
56067
+ const path92 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
56068
+ if (!path92) {
55874
56069
  return { ok: false, error: "projectRoot required for project scope" };
55875
56070
  }
55876
- const current = readFile6(path91);
56071
+ const current = readFile6(path92);
55877
56072
  if (!(opts.name in current)) {
55878
- return { ok: false, error: `Server "${opts.name}" not found in ${path91}` };
56073
+ return { ok: false, error: `Server "${opts.name}" not found in ${path92}` };
55879
56074
  }
55880
56075
  delete current[opts.name];
55881
- writeFile2(path91, current);
55882
- return { ok: true, path: path91 };
56076
+ writeFile2(path92, current);
56077
+ return { ok: true, path: path92 };
55883
56078
  }
55884
56079
  var init_mcpConfigIo = __esm({
55885
56080
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -56348,10 +56543,10 @@ import { createHash as createHash20 } from "node:crypto";
56348
56543
  import { join as join34 } from "node:path";
56349
56544
  import { readFile as readFile7 } from "node:fs/promises";
56350
56545
  async function readPackageJson3(projectRoot) {
56351
- const path91 = join34(projectRoot, "package.json");
56352
- if (!existsSync39(path91)) return null;
56546
+ const path92 = join34(projectRoot, "package.json");
56547
+ if (!existsSync39(path92)) return null;
56353
56548
  try {
56354
- return JSON.parse(await readFile7(path91, "utf8"));
56549
+ return JSON.parse(await readFile7(path92, "utf8"));
56355
56550
  } catch {
56356
56551
  return null;
56357
56552
  }
@@ -56433,9 +56628,9 @@ async function genBuild(ctx) {
56433
56628
  ].join("\n");
56434
56629
  }
56435
56630
  async function genOpenQuestions(ctx) {
56436
- const path91 = join34(ctx.rootDir, "risks.md");
56437
- if (!existsSync39(path91)) return "_No open questions._";
56438
- const content = readFileSync31(path91, "utf8");
56631
+ const path92 = join34(ctx.rootDir, "risks.md");
56632
+ if (!existsSync39(path92)) return "_No open questions._";
56633
+ const content = readFileSync31(path92, "utf8");
56439
56634
  const lines = content.split("\n");
56440
56635
  const questions = [];
56441
56636
  let currentTitle = "";
@@ -56709,9 +56904,9 @@ function versionKey(value) {
56709
56904
  function firstString2(v) {
56710
56905
  return typeof v === "string" && v.trim().length > 0 ? v : null;
56711
56906
  }
56712
- function readFileSyncSafe(path91) {
56907
+ function readFileSyncSafe(path92) {
56713
56908
  try {
56714
- return readFileSync32(path91, "utf8");
56909
+ return readFileSync32(path92, "utf8");
56715
56910
  } catch {
56716
56911
  return null;
56717
56912
  }
@@ -57174,8 +57369,8 @@ async function runPostCouncilHook(ctx, options) {
57174
57369
  sources: scope.sources
57175
57370
  } : void 0
57176
57371
  });
57177
- const path91 = writeCouncilCompletion(ctx.rootDir, completion);
57178
- completionHook = { ran: true, path: path91, completion };
57372
+ const path92 = writeCouncilCompletion(ctx.rootDir, completion);
57373
+ completionHook = { ran: true, path: path92, completion };
57179
57374
  } catch (err) {
57180
57375
  completionHook = {
57181
57376
  ran: true,
@@ -58511,7 +58706,7 @@ function safeSocketPath(socketPath) {
58511
58706
  return socketPath.trim();
58512
58707
  }
58513
58708
  function startPermissionBroker(socketPath, handlers, opts) {
58514
- const path91 = safeSocketPath(socketPath);
58709
+ const path92 = safeSocketPath(socketPath);
58515
58710
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
58516
58711
  const sockets = /* @__PURE__ */ new Set();
58517
58712
  const server = createServer2((socket) => {
@@ -58611,10 +58806,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
58611
58806
  return new Promise((resolve9, reject) => {
58612
58807
  const onError = (err) => reject(err);
58613
58808
  server.once("error", onError);
58614
- server.listen(path91, () => {
58809
+ server.listen(path92, () => {
58615
58810
  server.removeListener("error", onError);
58616
58811
  resolve9({
58617
- socketPath: path91,
58812
+ socketPath: path92,
58618
58813
  stop: () => new Promise((res) => {
58619
58814
  for (const s of sockets) s.destroy();
58620
58815
  sockets.clear();
@@ -58625,7 +58820,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
58625
58820
  if (done) return;
58626
58821
  done = true;
58627
58822
  if (process.platform !== "win32") {
58628
- unlink(path91, () => res());
58823
+ unlink(path92, () => res());
58629
58824
  } else {
58630
58825
  res();
58631
58826
  }
@@ -58638,11 +58833,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
58638
58833
  });
58639
58834
  }
58640
58835
  function requestBrokerAsk(socketPath, ask, opts) {
58641
- const path91 = safeSocketPath(socketPath);
58836
+ const path92 = safeSocketPath(socketPath);
58642
58837
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
58643
58838
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
58644
58839
  return new Promise((resolve9, reject) => {
58645
- const socket = connect(path91);
58840
+ const socket = connect(path92);
58646
58841
  let buffer = "";
58647
58842
  let settled = false;
58648
58843
  const settle = (fn) => {
@@ -58657,7 +58852,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
58657
58852
  settle(
58658
58853
  () => reject(
58659
58854
  new Error(
58660
- `permission broker unavailable at "${path91}" (connect timed out after ${connectTimeoutMs}ms)`
58855
+ `permission broker unavailable at "${path92}" (connect timed out after ${connectTimeoutMs}ms)`
58661
58856
  )
58662
58857
  )
58663
58858
  );
@@ -64475,7 +64670,7 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
64475
64670
  skipped
64476
64671
  };
64477
64672
  const fsRoot = options.fsRoot ?? process.cwd();
64478
- let fs45 = null;
64673
+ let fs46 = null;
64479
64674
  const pending = [];
64480
64675
  for (const dir of dirs) {
64481
64676
  const files = candidateFiles(dir.path);
@@ -64530,9 +64725,9 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
64530
64725
  skipped.push(`${file2}: missing ZelariExtension export`);
64531
64726
  continue;
64532
64727
  }
64533
- if (!fs45) fs45 = bindSandboxedFs(fsRoot);
64728
+ if (!fs46) fs46 = bindSandboxedFs(fsRoot);
64534
64729
  try {
64535
- await runtime.registry.registerExtension(ext, { fs: fs45 });
64730
+ await runtime.registry.registerExtension(ext, { fs: fs46 });
64536
64731
  runtime.loaded.push({ id: ext.id, file: file2, scope });
64537
64732
  } catch (err) {
64538
64733
  const msg = err instanceof Error ? err.message : String(err);
@@ -67846,7 +68041,7 @@ function upsertSkill(opts) {
67846
68041
  }
67847
68042
  dir = getProjectSkillsDir(root);
67848
68043
  }
67849
- const path91 = skillFilePath(dir, name);
68044
+ const path92 = skillFilePath(dir, name);
67850
68045
  const content = serializeSkillMd({
67851
68046
  name,
67852
68047
  description,
@@ -67855,13 +68050,13 @@ function upsertSkill(opts) {
67855
68050
  tools: opts.tools,
67856
68051
  cost: opts.cost
67857
68052
  });
67858
- const parsed = parseSkillMd(content, path91);
68053
+ const parsed = parseSkillMd(content, path92);
67859
68054
  if (!parsed) {
67860
68055
  return { ok: false, error: "Generated SKILL.md failed validation" };
67861
68056
  }
67862
- mkdirSync21(dirname13(path91), { recursive: true });
67863
- writeFileSync24(path91, content, "utf8");
67864
- return { ok: true, path: path91 };
68057
+ mkdirSync21(dirname13(path92), { recursive: true });
68058
+ writeFileSync24(path92, content, "utf8");
68059
+ return { ok: true, path: path92 };
67865
68060
  }
67866
68061
  function removeSkill(opts) {
67867
68062
  const name = opts.name.trim().toLowerCase();
@@ -67879,8 +68074,8 @@ function removeSkill(opts) {
67879
68074
  dir = getProjectSkillsDir(root);
67880
68075
  }
67881
68076
  const skillDir = join46(dir, name);
67882
- const path91 = skillFilePath(dir, name);
67883
- if (!existsSync52(path91) && !existsSync52(skillDir)) {
68077
+ const path92 = skillFilePath(dir, name);
68078
+ if (!existsSync52(path92) && !existsSync52(skillDir)) {
67884
68079
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
67885
68080
  }
67886
68081
  try {
@@ -67891,7 +68086,7 @@ function removeSkill(opts) {
67891
68086
  error: err instanceof Error ? err.message : String(err)
67892
68087
  };
67893
68088
  }
67894
- return { ok: true, path: path91 };
68089
+ return { ok: true, path: path92 };
67895
68090
  }
67896
68091
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
67897
68092
  var init_skillConfigIo = __esm({
@@ -68710,12 +68905,12 @@ function ensureHome() {
68710
68905
  }
68711
68906
  }
68712
68907
  function loadCompanionConfig() {
68713
- const path91 = getCompanionConfigPath();
68714
- if (!existsSync53(path91)) {
68908
+ const path92 = getCompanionConfigPath();
68909
+ if (!existsSync53(path92)) {
68715
68910
  return { projects: [] };
68716
68911
  }
68717
68912
  try {
68718
- const raw = JSON.parse(readFileSync41(path91, "utf8"));
68913
+ const raw = JSON.parse(readFileSync41(path92, "utf8"));
68719
68914
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
68720
68915
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
68721
68916
  ).map((p3) => ({
@@ -68753,16 +68948,16 @@ function loadOrCreateToken(explicit) {
68753
68948
  return { token: explicit.trim(), created: false };
68754
68949
  }
68755
68950
  ensureHome();
68756
- const path91 = getCompanionTokenPath();
68757
- if (existsSync53(path91)) {
68758
- const t = readFileSync41(path91, "utf8").trim();
68951
+ const path92 = getCompanionTokenPath();
68952
+ if (existsSync53(path92)) {
68953
+ const t = readFileSync41(path92, "utf8").trim();
68759
68954
  if (t) return { token: t, created: false };
68760
68955
  }
68761
68956
  const token = randomBytes7(24).toString("base64url");
68762
- writeFileSync25(path91, token + "\n", "utf8");
68957
+ writeFileSync25(path92, token + "\n", "utf8");
68763
68958
  try {
68764
- const fs45 = __require("node:fs");
68765
- fs45.chmodSync?.(path91, 384);
68959
+ const fs46 = __require("node:fs");
68960
+ fs46.chmodSync?.(path92, 384);
68766
68961
  } catch {
68767
68962
  }
68768
68963
  return { token, created: true };
@@ -68787,17 +68982,17 @@ function mergeProjects(cfg, extraPaths) {
68787
68982
  byId.set(p3.id, p3);
68788
68983
  }
68789
68984
  for (const raw of extraPaths) {
68790
- const path91 = raw.trim();
68791
- if (!path91) continue;
68792
- let id3 = slugFromPath(path91);
68985
+ const path92 = raw.trim();
68986
+ if (!path92) continue;
68987
+ let id3 = slugFromPath(path92);
68793
68988
  let n = 2;
68794
- while (byId.has(id3) && byId.get(id3).path !== path91) {
68795
- id3 = `${slugFromPath(path91)}-${n++}`;
68989
+ while (byId.has(id3) && byId.get(id3).path !== path92) {
68990
+ id3 = `${slugFromPath(path92)}-${n++}`;
68796
68991
  }
68797
68992
  byId.set(id3, {
68798
68993
  id: id3,
68799
- name: slugFromPath(path91),
68800
- path: path91
68994
+ name: slugFromPath(path92),
68995
+ path: path92
68801
68996
  });
68802
68997
  }
68803
68998
  return [...byId.values()];
@@ -68844,6 +69039,63 @@ var init_config = __esm({
68844
69039
  }
68845
69040
  });
68846
69041
 
69042
+ // src/cli/serve/spineLockSweep.ts
69043
+ import { promises as fs45 } from "node:fs";
69044
+ import path88 from "node:path";
69045
+ async function sweepOrphanSpineLocks(sessionsDir, options = {}) {
69046
+ const dir = sessionsDir ?? resolveSessionsDir();
69047
+ const onSwept = options.onSwept ?? ((sessionId2, reason) => {
69048
+ console.error(
69049
+ `[zelari] spine-lock sweep: removed orphan writer.lock for session ${sessionId2} (${reason})`
69050
+ );
69051
+ });
69052
+ const now = options.now ?? Date.now;
69053
+ const probe = options.probe ?? defaultLockLivenessProbe;
69054
+ const result = { swept: 0, kept: 0, errors: 0 };
69055
+ let entries;
69056
+ try {
69057
+ entries = await fs45.readdir(dir);
69058
+ } catch {
69059
+ return result;
69060
+ }
69061
+ for (const sessionId2 of entries) {
69062
+ const lockPath2 = path88.join(dir, sessionId2, "writer.lock");
69063
+ try {
69064
+ const raw = await fs45.readFile(lockPath2, "utf-8");
69065
+ let lockInfo = {};
69066
+ try {
69067
+ lockInfo = JSON.parse(raw);
69068
+ } catch {
69069
+ result.kept += 1;
69070
+ continue;
69071
+ }
69072
+ const verdict = evaluateLockTakeover(lockInfo, {
69073
+ now: now(),
69074
+ probe,
69075
+ heartbeatStaleMs: options.heartbeatStaleMs ?? resolveHeartbeatStaleMs()
69076
+ });
69077
+ if (!verdict.takeover) {
69078
+ result.kept += 1;
69079
+ continue;
69080
+ }
69081
+ await fs45.rm(lockPath2, { force: true });
69082
+ result.swept += 1;
69083
+ onSwept(sessionId2, verdict.reason);
69084
+ } catch (err) {
69085
+ const code = err.code;
69086
+ if (code === "ENOENT" || code === "ENOTDIR") continue;
69087
+ result.errors += 1;
69088
+ }
69089
+ }
69090
+ return result;
69091
+ }
69092
+ var init_spineLockSweep = __esm({
69093
+ "src/cli/serve/spineLockSweep.ts"() {
69094
+ "use strict";
69095
+ init_session();
69096
+ }
69097
+ });
69098
+
68847
69099
  // src/cli/serve/harnessServer.ts
68848
69100
  var harnessServer_exports = {};
68849
69101
  __export(harnessServer_exports, {
@@ -69091,6 +69343,10 @@ function startHarnessServer(options = {}) {
69091
69343
  async function runHarnessServer() {
69092
69344
  setActivePolicyLoadSurface("headless");
69093
69345
  setActiveProofPersistenceSurface("headless");
69346
+ try {
69347
+ await sweepOrphanSpineLocks();
69348
+ } catch {
69349
+ }
69094
69350
  process.env.ZELARI_SERVE_HARNESS = "1";
69095
69351
  const started = startHarnessServer();
69096
69352
  await new Promise((resolvePromise) => {
@@ -69115,6 +69371,7 @@ var init_harnessServer = __esm({
69115
69371
  init_completionProofPersist();
69116
69372
  init_policyGate();
69117
69373
  init_policyLoadMode();
69374
+ init_spineLockSweep();
69118
69375
  }
69119
69376
  });
69120
69377
 
@@ -69772,9 +70029,9 @@ async function runCompanionServe(opts = {}) {
69772
70029
  return;
69773
70030
  }
69774
70031
  const url2 = parseUrl(req);
69775
- const path91 = url2.pathname.replace(/\/+$/, "") || "/";
70032
+ const path92 = url2.pathname.replace(/\/+$/, "") || "/";
69776
70033
  try {
69777
- if (req.method === "GET" && (path91 === "/health" || path91 === "/v1/health")) {
70034
+ if (req.method === "GET" && (path92 === "/health" || path92 === "/v1/health")) {
69778
70035
  sendJson2(res, 200, {
69779
70036
  ok: true,
69780
70037
  service: "zelari-companion",
@@ -69786,18 +70043,18 @@ async function runCompanionServe(opts = {}) {
69786
70043
  });
69787
70044
  return;
69788
70045
  }
69789
- if (path91.startsWith("/v1")) {
70046
+ if (path92.startsWith("/v1")) {
69790
70047
  if (!tokenMatches(token, getBearer(req))) {
69791
70048
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
69792
70049
  return;
69793
70050
  }
69794
70051
  }
69795
- if (req.method === "GET" && path91 === "/v1/config") {
70052
+ if (req.method === "GET" && path92 === "/v1/config") {
69796
70053
  const snap = buildDesktopConfigSnapshot();
69797
70054
  sendJson2(res, 200, { ok: true, ...snap });
69798
70055
  return;
69799
70056
  }
69800
- if (req.method === "GET" && path91 === "/v1/projects") {
70057
+ if (req.method === "GET" && path92 === "/v1/projects") {
69801
70058
  sendJson2(res, 200, {
69802
70059
  ok: true,
69803
70060
  projects: projects.map((p3) => ({
@@ -69808,7 +70065,7 @@ async function runCompanionServe(opts = {}) {
69808
70065
  });
69809
70066
  return;
69810
70067
  }
69811
- if (req.method === "GET" && path91 === "/v1/runs") {
70068
+ if (req.method === "GET" && path92 === "/v1/runs") {
69812
70069
  sendJson2(res, 200, {
69813
70070
  ok: true,
69814
70071
  active: runs.getActive(),
@@ -69826,7 +70083,7 @@ async function runCompanionServe(opts = {}) {
69826
70083
  });
69827
70084
  return;
69828
70085
  }
69829
- if (req.method === "POST" && path91 === "/v1/runs") {
70086
+ if (req.method === "POST" && path92 === "/v1/runs") {
69830
70087
  const raw = await readBody(req);
69831
70088
  let body = {};
69832
70089
  try {
@@ -69874,7 +70131,7 @@ async function runCompanionServe(opts = {}) {
69874
70131
  });
69875
70132
  return;
69876
70133
  }
69877
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path91);
70134
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path92);
69878
70135
  if (req.method === "GET" && eventsMatch) {
69879
70136
  const runId = eventsMatch[1];
69880
70137
  const run = runs.getRun(runId);
@@ -69939,7 +70196,7 @@ async function runCompanionServe(opts = {}) {
69939
70196
  }, 500);
69940
70197
  return;
69941
70198
  }
69942
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path91);
70199
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path92);
69943
70200
  if (req.method === "POST" && cancelMatch) {
69944
70201
  const runId = cancelMatch[1];
69945
70202
  const result = runs.cancel(runId);
@@ -69950,7 +70207,7 @@ async function runCompanionServe(opts = {}) {
69950
70207
  sendJson2(res, 200, { ok: true, cancelled: runId });
69951
70208
  return;
69952
70209
  }
69953
- const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path91);
70210
+ const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path92);
69954
70211
  if (req.method === "POST" && steerMatch) {
69955
70212
  const runId = steerMatch[1];
69956
70213
  const raw = await readBody(req);
@@ -70117,11 +70374,11 @@ import { execSync as execSync2 } from "node:child_process";
70117
70374
  import { existsSync as existsSync55, readFileSync as readFileSync42, readlinkSync, statSync as statSync10 } from "node:fs";
70118
70375
  import { createRequire as createRequire3 } from "node:module";
70119
70376
  import { fileURLToPath as fileURLToPath3 } from "node:url";
70120
- import path88 from "node:path";
70377
+ import path89 from "node:path";
70121
70378
  function findPackageRoot(start) {
70122
70379
  let dir = start;
70123
70380
  for (let i = 0; i < 6; i += 1) {
70124
- const candidate = path88.join(dir, "package.json");
70381
+ const candidate = path89.join(dir, "package.json");
70125
70382
  if (existsSync55(candidate)) {
70126
70383
  try {
70127
70384
  const pkg = JSON.parse(readFileSync42(candidate, "utf8"));
@@ -70129,11 +70386,11 @@ function findPackageRoot(start) {
70129
70386
  } catch {
70130
70387
  }
70131
70388
  }
70132
- const parent = path88.dirname(dir);
70389
+ const parent = path89.dirname(dir);
70133
70390
  if (parent === dir) break;
70134
70391
  dir = parent;
70135
70392
  }
70136
- return path88.resolve(__dirname3, "..", "..", "..");
70393
+ return path89.resolve(__dirname3, "..", "..", "..");
70137
70394
  }
70138
70395
  function tryExec(cmd) {
70139
70396
  try {
@@ -70147,7 +70404,7 @@ function tryExec(cmd) {
70147
70404
  }
70148
70405
  function readPackageJson4() {
70149
70406
  try {
70150
- const pkgPath = path88.join(packageRoot, "package.json");
70407
+ const pkgPath = path89.join(packageRoot, "package.json");
70151
70408
  return JSON.parse(readFileSync42(pkgPath, "utf8"));
70152
70409
  } catch {
70153
70410
  return null;
@@ -70163,7 +70420,7 @@ function checkShim(pkgName) {
70163
70420
  }
70164
70421
  const isWin = process.platform === "win32";
70165
70422
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
70166
- const shimPath = path88.join(prefix, shimName);
70423
+ const shimPath = path89.join(prefix, shimName);
70167
70424
  if (!existsSync55(shimPath)) {
70168
70425
  return FAIL(
70169
70426
  `shim not found at ${shimPath}
@@ -70191,8 +70448,8 @@ function checkShim(pkgName) {
70191
70448
  fix: npm install -g ${pkgName}@latest --force`
70192
70449
  );
70193
70450
  }
70194
- const resolved = path88.resolve(path88.dirname(shimPath), target);
70195
- const expected = path88.join(
70451
+ const resolved = path89.resolve(path89.dirname(shimPath), target);
70452
+ const expected = path89.join(
70196
70453
  prefix,
70197
70454
  "node_modules",
70198
70455
  pkgName,
@@ -70234,7 +70491,7 @@ function checkNode(pkg) {
70234
70491
  return OK(`node ${raw} (engines.node ${enginesNode ?? ">= 20.0.0"})`);
70235
70492
  }
70236
70493
  function checkBundle() {
70237
- const bundle = path88.join(packageRoot, "dist", "cli", "main.bundled.js");
70494
+ const bundle = path89.join(packageRoot, "dist", "cli", "main.bundled.js");
70238
70495
  if (!existsSync55(bundle)) {
70239
70496
  return FAIL(
70240
70497
  `dist/cli/main.bundled.js missing at ${bundle}
@@ -70255,7 +70512,7 @@ function checkRuntimeDeps() {
70255
70512
  const missing = [];
70256
70513
  for (const dep of required2) {
70257
70514
  try {
70258
- const localReq = createRequire3(path88.join(packageRoot, "package.json"));
70515
+ const localReq = createRequire3(path89.join(packageRoot, "package.json"));
70259
70516
  localReq.resolve(dep);
70260
70517
  } catch {
70261
70518
  missing.push(dep);
@@ -70455,7 +70712,7 @@ var init_doctor = __esm({
70455
70712
  init_metrics3();
70456
70713
  init_contextGrowthSummary();
70457
70714
  require3 = createRequire3(import.meta.url);
70458
- __dirname3 = path88.dirname(fileURLToPath3(import.meta.url));
70715
+ __dirname3 = path89.dirname(fileURLToPath3(import.meta.url));
70459
70716
  packageRoot = findPackageRoot(__dirname3);
70460
70717
  OK = (message) => ({
70461
70718
  ok: true,
@@ -70639,7 +70896,7 @@ __export(inspectSession_exports, {
70639
70896
  renderInspectReport: () => renderInspectReport,
70640
70897
  runInspectSession: () => runInspectSession
70641
70898
  });
70642
- import path89 from "node:path";
70899
+ import path90 from "node:path";
70643
70900
  import { existsSync as existsSync56 } from "node:fs";
70644
70901
  function formatLimit(limit) {
70645
70902
  return `${Math.round(limit / 1e3)}k`;
@@ -70673,8 +70930,8 @@ function renderInspectReport(state3) {
70673
70930
  }
70674
70931
  async function runInspectSession(opts) {
70675
70932
  const sessionsDir = resolveSessionsDir({ workspaceRoot: opts.cwd ?? process.cwd() });
70676
- const sessionDir = path89.join(sessionsDir, opts.sessionId);
70677
- const eventsPath = path89.join(sessionDir, "events.jsonl");
70933
+ const sessionDir = path90.join(sessionsDir, opts.sessionId);
70934
+ const eventsPath = path90.join(sessionDir, "events.jsonl");
70678
70935
  if (!existsSync56(sessionDir)) {
70679
70936
  console.error(`zelari-code inspect: no session directory at ${sessionDir}`);
70680
70937
  return 1;
@@ -70708,15 +70965,15 @@ __export(inspect_exports, {
70708
70965
  collectInspectReport: () => collectInspectReport,
70709
70966
  runInspect: () => runInspect
70710
70967
  });
70711
- import path90 from "node:path";
70968
+ import path91 from "node:path";
70712
70969
  import { existsSync as existsSync57, readFileSync as readFileSync43, readdirSync as readdirSync12 } from "node:fs";
70713
70970
  import { homedir as homedir17 } from "node:os";
70714
70971
  async function collectInspectReport(cwd = process.cwd()) {
70715
70972
  ensureBuiltinSkillsLoadedSync();
70716
70973
  const snap = listSkillsSnapshot(cwd);
70717
70974
  const mcp = listMcpServers(cwd);
70718
- const userMcpPath = path90.join(homedir17(), ".zelari-code", "mcp.json");
70719
- const projectMcpPath = path90.join(cwd, ".zelari", "mcp.json");
70975
+ const userMcpPath = path91.join(homedir17(), ".zelari-code", "mcp.json");
70976
+ const projectMcpPath = path91.join(cwd, ".zelari", "mcp.json");
70720
70977
  const globalHooks = globalHooksDir();
70721
70978
  const projectHooks = projectHooksDir(cwd);
70722
70979
  const projectTrusted = isFolderTrusted(cwd);
@@ -70744,9 +71001,9 @@ async function collectInspectReport(cwd = process.cwd()) {
70744
71001
  configSources: [
70745
71002
  { path: userMcpPath, exists: existsSync57(userMcpPath) },
70746
71003
  { path: projectMcpPath, exists: existsSync57(projectMcpPath) },
70747
- { path: path90.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync57(path90.join(homedir17(), ".zelari-code", "provider.json")) },
70748
- { path: path90.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync57(path90.join(cwd, ".zelari", "AGENTS.md")) },
70749
- { path: path90.join(cwd, "AGENTS.md"), exists: existsSync57(path90.join(cwd, "AGENTS.md")) }
71004
+ { path: path91.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync57(path91.join(homedir17(), ".zelari-code", "provider.json")) },
71005
+ { path: path91.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync57(path91.join(cwd, ".zelari", "AGENTS.md")) },
71006
+ { path: path91.join(cwd, "AGENTS.md"), exists: existsSync57(path91.join(cwd, "AGENTS.md")) }
70750
71007
  ],
70751
71008
  skills: {
70752
71009
  total: snap.skills.length,
@@ -70786,8 +71043,8 @@ function listJsonFiles(dir) {
70786
71043
  }
70787
71044
  function findAgentsMd(cwd) {
70788
71045
  const candidates = [
70789
- path90.join(cwd, "AGENTS.md"),
70790
- path90.join(cwd, ".zelari", "AGENTS.md")
71046
+ path91.join(cwd, "AGENTS.md"),
71047
+ path91.join(cwd, ".zelari", "AGENTS.md")
70791
71048
  ];
70792
71049
  const found = [];
70793
71050
  for (const c of candidates) {
@@ -80432,8 +80689,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
80432
80689
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
80433
80690
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
80434
80691
  try {
80435
- const path91 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
80436
- name = path91 && /^[a-z0-9]/.test(path91) ? path91 : "imported-skill";
80692
+ const path92 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
80693
+ name = path92 && /^[a-z0-9]/.test(path92) ? path92 : "imported-skill";
80437
80694
  } catch {
80438
80695
  name = "imported-skill";
80439
80696
  }