@wrongstack/core 0.302.0 → 0.302.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/agent-status-tracker.d.ts +6 -2
  2. package/dist/chronicle/index.js +1836 -1645
  3. package/dist/chronicle/metrics-store.d.ts +14 -0
  4. package/dist/chronicle/project-server-protocol.d.ts +13 -0
  5. package/dist/chronicle/project-server.js +1759 -1583
  6. package/dist/chronicle/rollup-adapter.d.ts +2 -0
  7. package/dist/chronicle/sqlite-journal.d.ts +59 -0
  8. package/dist/coordination/index.js +790 -248
  9. package/dist/coordination/mail-tools.d.ts +2 -2
  10. package/dist/core/continue-intent.d.ts +2 -0
  11. package/dist/core/conversation-state.d.ts +5 -0
  12. package/dist/core/index.js +120 -19
  13. package/dist/defaults/index.js +927 -373
  14. package/dist/execution/index.js +27 -10
  15. package/dist/index.d.ts +3 -1
  16. package/dist/index.js +8762 -6780
  17. package/dist/infrastructure/index.js +722 -672
  18. package/dist/kernel/events/memory-events.d.ts +62 -0
  19. package/dist/plugin/index.js +2154 -1979
  20. package/dist/session-catalog/client.d.ts +62 -0
  21. package/dist/session-catalog/endpoint.d.ts +6 -0
  22. package/dist/session-catalog/index.d.ts +6 -0
  23. package/dist/session-catalog/index.js +1978 -0
  24. package/dist/session-catalog/project-server.d.ts +3 -0
  25. package/dist/session-catalog/project-server.js +1838 -0
  26. package/dist/session-catalog/protocol.d.ts +275 -0
  27. package/dist/session-catalog/registry.d.ts +59 -0
  28. package/dist/session-catalog/store.d.ts +55 -0
  29. package/dist/storage/index.d.ts +42 -38
  30. package/dist/storage/index.js +14279 -13393
  31. package/dist/storage/session-event-bridge.d.ts +2 -2
  32. package/dist/storage/session-store.d.ts +6 -0
  33. package/dist/tools/index.js +8 -2
  34. package/dist/types/context-evidence.d.ts +2 -0
  35. package/dist/types/messages.d.ts +8 -0
  36. package/dist/types/session.d.ts +19 -0
  37. package/dist/utils/context-evidence.d.ts +13 -1
  38. package/dist/utils/index.js +26 -2
  39. package/instructions/system-lite.md +11 -2
  40. package/instructions/system-pro.md +14 -0
  41. package/instructions/system.md +14 -0
  42. package/package.json +7 -3
@@ -295,7 +295,7 @@ var InMemoryAgentBridge = class {
295
295
  });
296
296
  }
297
297
  this.inflightGuards.add(correlationId);
298
- return new Promise((resolve19, reject) => {
298
+ return new Promise((resolve21, reject) => {
299
299
  const timer = setTimeout(() => {
300
300
  this.inflightGuards.delete(correlationId);
301
301
  this.pendingRequests.delete(correlationId);
@@ -314,7 +314,7 @@ var InMemoryAgentBridge = class {
314
314
  return;
315
315
  }
316
316
  this.pendingRequests.set(correlationId, {
317
- resolve: resolve19,
317
+ resolve: resolve21,
318
318
  reject,
319
319
  timer
320
320
  });
@@ -663,13 +663,13 @@ var SubagentBudget = class _SubagentBudget {
663
663
  if (!bus?.hasListenerFor("budget.threshold_reached")) {
664
664
  return Promise.resolve("stop");
665
665
  }
666
- return new Promise((resolve19) => {
666
+ return new Promise((resolve21) => {
667
667
  let resolved = false;
668
668
  const respond = (d) => {
669
669
  if (resolved) return;
670
670
  resolved = true;
671
671
  clearTimeout(fallback);
672
- resolve19(d);
672
+ resolve21(d);
673
673
  };
674
674
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
675
675
  const sessionId = this.currentSessionId();
@@ -5510,7 +5510,7 @@ function createDelegateTool(opts) {
5510
5510
  };
5511
5511
  }
5512
5512
  async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abortSignal) {
5513
- return new Promise((resolve19) => {
5513
+ return new Promise((resolve21) => {
5514
5514
  let settled = false;
5515
5515
  let timer;
5516
5516
  let offAbort = () => {
@@ -5523,7 +5523,7 @@ async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abo
5523
5523
  offIter();
5524
5524
  offProgress();
5525
5525
  offAbort();
5526
- resolve19(value);
5526
+ resolve21(value);
5527
5527
  };
5528
5528
  const arm = () => {
5529
5529
  if (timer) clearTimeout(timer);
@@ -5734,7 +5734,7 @@ async function readSubagentPartial(opts, subagentId) {
5734
5734
  }
5735
5735
 
5736
5736
  // src/coordination/director.ts
5737
- import { randomUUID as randomUUID11 } from "node:crypto";
5737
+ import { randomUUID as randomUUID12 } from "node:crypto";
5738
5738
  import * as fsp19 from "node:fs/promises";
5739
5739
 
5740
5740
  // src/core/instruction-template.ts
@@ -7524,10 +7524,10 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
7524
7524
  pending: taskIds.filter((id) => !done.has(id))
7525
7525
  });
7526
7526
  }
7527
- return new Promise((resolve19) => {
7527
+ return new Promise((resolve21) => {
7528
7528
  const entry = {
7529
7529
  ids: new Set(taskIds),
7530
- resolve: (result) => resolve19({
7530
+ resolve: (result) => resolve21({
7531
7531
  completed: [result],
7532
7532
  pending: taskIds.filter((id) => id !== result.taskId)
7533
7533
  })
@@ -7535,7 +7535,7 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
7535
7535
  if (opts?.timeoutMs !== void 0) {
7536
7536
  entry.timer = setTimeout(() => {
7537
7537
  this.anyWaiters.delete(entry);
7538
- resolve19({ completed: [], pending: [...taskIds], timedOut: true });
7538
+ resolve21({ completed: [], pending: [...taskIds], timedOut: true });
7539
7539
  }, opts.timeoutMs);
7540
7540
  }
7541
7541
  this.anyWaiters.add(entry);
@@ -7653,11 +7653,11 @@ ${JSON.stringify(result.result, null, 2)}
7653
7653
  this.makeStoppedResult(taskId, "director", `Unknown task id "${taskId}" \u2014 never assigned`)
7654
7654
  );
7655
7655
  }
7656
- let resolve19;
7656
+ let resolve21;
7657
7657
  const promise = new Promise((done) => {
7658
- resolve19 = done;
7658
+ resolve21 = done;
7659
7659
  });
7660
- this.taskWaiters.set(taskId, { promise, resolve: resolve19 });
7660
+ this.taskWaiters.set(taskId, { promise, resolve: resolve21 });
7661
7661
  return promise;
7662
7662
  }
7663
7663
  recordAssignment(task) {
@@ -9547,11 +9547,12 @@ function rosterSummaryFromConfigs(roster) {
9547
9547
 
9548
9548
  // src/coordination/director-session.ts
9549
9549
  import * as fsp18 from "node:fs/promises";
9550
- import * as path21 from "node:path";
9550
+ import * as path23 from "node:path";
9551
9551
 
9552
9552
  // src/storage/session-store.ts
9553
+ import { randomUUID as randomUUID10 } from "node:crypto";
9553
9554
  import * as fsp17 from "node:fs/promises";
9554
- import * as path20 from "node:path";
9555
+ import * as path22 from "node:path";
9555
9556
 
9556
9557
  // src/security/file-permissions.ts
9557
9558
  import { chmod } from "node:fs/promises";
@@ -9881,6 +9882,388 @@ var DefaultSecretScrubber = class {
9881
9882
  }
9882
9883
  };
9883
9884
 
9885
+ // src/session-catalog/client.ts
9886
+ import { spawn } from "node:child_process";
9887
+ import * as fs2 from "node:fs";
9888
+ import * as net from "node:net";
9889
+ import * as path10 from "node:path";
9890
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
9891
+
9892
+ // src/session-catalog/endpoint.ts
9893
+ import { createHash as createHash2 } from "node:crypto";
9894
+ import * as os2 from "node:os";
9895
+ import * as path9 from "node:path";
9896
+
9897
+ // src/session-catalog/protocol.ts
9898
+ var SESSION_CATALOG_PROTOCOL_VERSION = 1;
9899
+ var SESSION_CATALOG_MAX_FRAME_CHARS = 4 * 1024 * 1024;
9900
+ function encodeSessionCatalogMessage(message) {
9901
+ return `${JSON.stringify(message)}
9902
+ `;
9903
+ }
9904
+
9905
+ // src/session-catalog/endpoint.ts
9906
+ var SESSION_CATALOG_METADATA_FILE = ".session-catalog-server.json";
9907
+ function normalizedPath(value) {
9908
+ const resolved = path9.resolve(value);
9909
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
9910
+ }
9911
+ function sessionCatalogProjectServerKey(projectDir) {
9912
+ return createHash2("sha256").update(normalizedPath(projectDir)).digest("hex").slice(0, 24);
9913
+ }
9914
+ function sessionCatalogProjectServerEndpoint(projectDir) {
9915
+ const key = sessionCatalogProjectServerKey(projectDir);
9916
+ if (process.platform === "win32") {
9917
+ return `\\\\.\\pipe\\wrongstack-session-catalog-v${SESSION_CATALOG_PROTOCOL_VERSION}-${key}`;
9918
+ }
9919
+ return path9.join(os2.tmpdir(), `wssc-v${SESSION_CATALOG_PROTOCOL_VERSION}`, `${key}.sock`);
9920
+ }
9921
+ function sessionCatalogProjectServerMetadataPath(projectDir) {
9922
+ return path9.join(projectDir, SESSION_CATALOG_METADATA_FILE);
9923
+ }
9924
+
9925
+ // src/session-catalog/client.ts
9926
+ var CONNECT_TIMEOUT_MS = 750;
9927
+ var START_TIMEOUT_MS = 1e4;
9928
+ var CALL_TIMEOUT_MS = 3e4;
9929
+ var MAX_PENDING_REQUESTS = 1024;
9930
+ var MAX_EVENT_LISTENERS = 64;
9931
+ function locateServer(moduleUrl, exists) {
9932
+ for (const candidate of [
9933
+ "./project-server.js",
9934
+ "../session-catalog/project-server.js",
9935
+ "./session-catalog/project-server.js",
9936
+ "../../dist/session-catalog/project-server.js"
9937
+ ]) {
9938
+ try {
9939
+ const url = new URL(candidate, moduleUrl);
9940
+ if (url.protocol === "file:" && exists(fileURLToPath3(url))) return url;
9941
+ } catch {
9942
+ }
9943
+ }
9944
+ return null;
9945
+ }
9946
+ function resolveSessionCatalogDaemonAvailability(moduleUrl = import.meta.url, exists = fs2.existsSync) {
9947
+ if (process.env["WRONGSTACK_SESSION_CATALOG_INLINE"] || process.env["WRONGSTACK_SESSION_CATALOG_SERVER"] === "0")
9948
+ return { kind: "inline-requested" };
9949
+ const url = locateServer(moduleUrl, exists);
9950
+ return url ? { kind: "available", url } : { kind: "missing-build" };
9951
+ }
9952
+ function resolveSessionCatalogProjectServerUrl(moduleUrl = import.meta.url, exists = fs2.existsSync) {
9953
+ const availability = resolveSessionCatalogDaemonAvailability(moduleUrl, exists);
9954
+ return availability.kind === "available" ? availability.url : null;
9955
+ }
9956
+ function normalize2(value) {
9957
+ const resolved = path10.resolve(value);
9958
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
9959
+ }
9960
+ function delay(ms) {
9961
+ return new Promise((resolve21) => setTimeout(resolve21, ms));
9962
+ }
9963
+ var SessionCatalogProjectClient = class {
9964
+ constructor(options) {
9965
+ this.options = options;
9966
+ this.endpoint = sessionCatalogProjectServerEndpoint(options.projectDir);
9967
+ }
9968
+ options;
9969
+ endpoint;
9970
+ socket = null;
9971
+ info = null;
9972
+ buffer = "";
9973
+ connecting = null;
9974
+ connectResolve = null;
9975
+ connectReject = null;
9976
+ authToken;
9977
+ nextId = 1;
9978
+ pending = /* @__PURE__ */ new Map();
9979
+ eventListeners = /* @__PURE__ */ new Set();
9980
+ reconnectTimer;
9981
+ explicitlyClosed = false;
9982
+ async call(op, args, options = {}) {
9983
+ await this.ensureConnected(true);
9984
+ return this.request({ type: "request", op, args }, options.timeoutMs ?? CALL_TIMEOUT_MS);
9985
+ }
9986
+ /**
9987
+ * Call an already-running project daemon without starting one.
9988
+ *
9989
+ * Cross-project discovery must use this path: observing another project is
9990
+ * never sufficient authority to wake that project's IPC owner.
9991
+ */
9992
+ async callExisting(op, args, options = {}) {
9993
+ await this.ensureConnected(false);
9994
+ return this.request({ type: "request", op, args }, options.timeoutMs ?? CALL_TIMEOUT_MS);
9995
+ }
9996
+ ping() {
9997
+ return this.call("ping", {}, { timeoutMs: 3e3 });
9998
+ }
9999
+ async subscribe(listener) {
10000
+ if (!this.eventListeners.has(listener) && this.eventListeners.size >= MAX_EVENT_LISTENERS) {
10001
+ throw new Error(`Session Catalog listener limit reached (${MAX_EVENT_LISTENERS})`);
10002
+ }
10003
+ this.explicitlyClosed = false;
10004
+ this.eventListeners.add(listener);
10005
+ try {
10006
+ await this.call("subscribe", {});
10007
+ } catch (error2) {
10008
+ this.eventListeners.delete(listener);
10009
+ throw error2;
10010
+ }
10011
+ return async () => {
10012
+ this.eventListeners.delete(listener);
10013
+ if (this.eventListeners.size === 0)
10014
+ await this.callExisting("unsubscribe", {}).catch(() => void 0);
10015
+ };
10016
+ }
10017
+ async shutdown(reason) {
10018
+ try {
10019
+ await this.ensureConnected(false);
10020
+ } catch {
10021
+ return { stopped: false };
10022
+ }
10023
+ const result = await this.request({ type: "shutdown", ...reason !== void 0 ? { reason } : {} }, 5e3).catch(
10024
+ () => ({ stopped: false })
10025
+ );
10026
+ if (result.stopped) {
10027
+ const metadataPath = sessionCatalogProjectServerMetadataPath(this.options.projectDir);
10028
+ const deadline = Date.now() + 5e3;
10029
+ while (fs2.existsSync(metadataPath) && Date.now() < deadline) await delay(20);
10030
+ if (result.pid && result.pid !== process.pid) {
10031
+ while (Date.now() < deadline) {
10032
+ try {
10033
+ process.kill(result.pid, 0);
10034
+ await delay(20);
10035
+ } catch {
10036
+ break;
10037
+ }
10038
+ }
10039
+ }
10040
+ }
10041
+ return result;
10042
+ }
10043
+ async close() {
10044
+ this.explicitlyClosed = true;
10045
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
10046
+ this.reconnectTimer = void 0;
10047
+ const socket = this.socket;
10048
+ this.socket = null;
10049
+ this.info = null;
10050
+ if (socket && !socket.destroyed)
10051
+ await new Promise((resolve21) => {
10052
+ socket.once("close", resolve21);
10053
+ socket.end();
10054
+ });
10055
+ }
10056
+ async ensureConnected(spawnIfMissing) {
10057
+ if (this.socket && !this.socket.destroyed && this.info) return;
10058
+ if (this.connecting) return this.connecting;
10059
+ this.connecting = this.connectWithElection(spawnIfMissing).finally(() => {
10060
+ this.connecting = null;
10061
+ });
10062
+ return this.connecting;
10063
+ }
10064
+ async connectWithElection(spawnIfMissing) {
10065
+ const deadline = Date.now() + (spawnIfMissing ? START_TIMEOUT_MS : CONNECT_TIMEOUT_MS);
10066
+ let spawned = false;
10067
+ let lastError = new Error("Session Catalog project server unavailable");
10068
+ while (Date.now() < deadline) {
10069
+ try {
10070
+ await this.connectOnce();
10071
+ return;
10072
+ } catch (error2) {
10073
+ lastError = error2;
10074
+ }
10075
+ if (!spawnIfMissing) break;
10076
+ if (!spawned) {
10077
+ this.spawnDetached();
10078
+ spawned = true;
10079
+ }
10080
+ await delay(75);
10081
+ }
10082
+ throw lastError;
10083
+ }
10084
+ connectOnce() {
10085
+ this.socket?.destroy();
10086
+ this.socket = null;
10087
+ this.info = null;
10088
+ this.authToken = void 0;
10089
+ this.buffer = "";
10090
+ return new Promise((resolve21, reject) => {
10091
+ const socket = net.createConnection(this.endpoint);
10092
+ this.socket = socket;
10093
+ socket.setEncoding("utf8");
10094
+ const timer = setTimeout(() => {
10095
+ reject(new Error("Session Catalog handshake timed out"));
10096
+ socket.destroy();
10097
+ }, CONNECT_TIMEOUT_MS);
10098
+ timer.unref?.();
10099
+ this.connectResolve = () => {
10100
+ clearTimeout(timer);
10101
+ this.connectResolve = null;
10102
+ this.connectReject = null;
10103
+ resolve21();
10104
+ };
10105
+ this.connectReject = (error2) => {
10106
+ clearTimeout(timer);
10107
+ this.connectResolve = null;
10108
+ this.connectReject = null;
10109
+ reject(error2);
10110
+ };
10111
+ socket.on("data", (chunk) => this.onData(socket, chunk));
10112
+ socket.on("error", (error2) => {
10113
+ if (!this.info) this.connectReject?.(error2);
10114
+ });
10115
+ socket.on("close", () => this.onClose(socket));
10116
+ });
10117
+ }
10118
+ currentAuthToken() {
10119
+ if (this.authToken === void 0) {
10120
+ try {
10121
+ const parsed = JSON.parse(
10122
+ fs2.readFileSync(sessionCatalogProjectServerMetadataPath(this.options.projectDir), "utf8")
10123
+ );
10124
+ if (typeof parsed.authToken === "string" && parsed.authToken)
10125
+ this.authToken = parsed.authToken;
10126
+ } catch {
10127
+ }
10128
+ }
10129
+ return this.authToken;
10130
+ }
10131
+ request(message, timeoutMs) {
10132
+ const socket = this.socket;
10133
+ if (!socket || socket.destroyed)
10134
+ return Promise.reject(new Error("Session Catalog connection is unavailable"));
10135
+ const id = this.nextId++;
10136
+ if (this.pending.size >= MAX_PENDING_REQUESTS) {
10137
+ return Promise.reject(
10138
+ new Error(`Session Catalog pending request limit reached (${MAX_PENDING_REQUESTS})`)
10139
+ );
10140
+ }
10141
+ const encoded = encodeSessionCatalogMessage({
10142
+ ...message,
10143
+ id,
10144
+ authToken: this.currentAuthToken()
10145
+ });
10146
+ if (encoded.length > SESSION_CATALOG_MAX_FRAME_CHARS)
10147
+ return Promise.reject(new Error("Session Catalog request exceeded frame limit"));
10148
+ return new Promise((resolve21, reject) => {
10149
+ const timer = setTimeout(() => {
10150
+ const pending = this.pending.get(id);
10151
+ if (!pending) return;
10152
+ this.pending.delete(id);
10153
+ pending.reject(
10154
+ new Error(
10155
+ `Session Catalog ${message.type === "request" ? message.op : message.type} timed out`
10156
+ )
10157
+ );
10158
+ }, timeoutMs);
10159
+ timer.unref?.();
10160
+ this.pending.set(id, { resolve: resolve21, reject, timer });
10161
+ socket.write(encoded);
10162
+ });
10163
+ }
10164
+ onData(socket, chunk) {
10165
+ if (socket !== this.socket) return;
10166
+ this.buffer += chunk;
10167
+ if (this.buffer.length > SESSION_CATALOG_MAX_FRAME_CHARS) {
10168
+ socket.destroy(new Error("Session Catalog response exceeded frame limit"));
10169
+ return;
10170
+ }
10171
+ while (true) {
10172
+ const newline = this.buffer.indexOf("\n");
10173
+ if (newline < 0) return;
10174
+ const line = this.buffer.slice(0, newline);
10175
+ this.buffer = this.buffer.slice(newline + 1);
10176
+ if (!line) continue;
10177
+ try {
10178
+ this.onMessage(JSON.parse(line));
10179
+ } catch {
10180
+ socket.destroy(new Error("Invalid Session Catalog response"));
10181
+ return;
10182
+ }
10183
+ }
10184
+ }
10185
+ onMessage(message) {
10186
+ if (message.type === "hello") {
10187
+ if (message.protocolVersion !== SESSION_CATALOG_PROTOCOL_VERSION) {
10188
+ this.connectReject?.(
10189
+ new Error(
10190
+ `Session Catalog protocol mismatch: client=${SESSION_CATALOG_PROTOCOL_VERSION}, server=${message.protocolVersion}`
10191
+ )
10192
+ );
10193
+ this.socket?.destroy();
10194
+ return;
10195
+ }
10196
+ if (normalize2(message.projectDir) !== normalize2(this.options.projectDir) || normalize2(message.projectRoot) !== normalize2(this.options.projectRoot)) {
10197
+ this.connectReject?.(new Error("Session Catalog project identity mismatch"));
10198
+ this.socket?.destroy();
10199
+ return;
10200
+ }
10201
+ this.info = message;
10202
+ this.connectResolve?.();
10203
+ return;
10204
+ }
10205
+ if (message.type === "event") {
10206
+ for (const listener of this.eventListeners) {
10207
+ try {
10208
+ listener(message.event);
10209
+ } catch {
10210
+ }
10211
+ }
10212
+ return;
10213
+ }
10214
+ const pending = this.pending.get(message.id);
10215
+ if (!pending) return;
10216
+ this.pending.delete(message.id);
10217
+ clearTimeout(pending.timer);
10218
+ if (message.ok) pending.resolve(message.result);
10219
+ else {
10220
+ const error2 = new Error(message.error);
10221
+ if (message.errorName) error2.name = message.errorName;
10222
+ pending.reject(error2);
10223
+ }
10224
+ }
10225
+ onClose(socket) {
10226
+ if (socket !== this.socket) return;
10227
+ this.socket = null;
10228
+ this.info = null;
10229
+ this.authToken = void 0;
10230
+ const error2 = new Error("Session Catalog connection closed");
10231
+ this.connectReject?.(error2);
10232
+ this.connectResolve = null;
10233
+ this.connectReject = null;
10234
+ for (const pending of this.pending.values()) {
10235
+ clearTimeout(pending.timer);
10236
+ pending.reject(error2);
10237
+ }
10238
+ this.pending.clear();
10239
+ this.scheduleSubscriptionReconnect();
10240
+ }
10241
+ scheduleSubscriptionReconnect() {
10242
+ if (this.explicitlyClosed || this.eventListeners.size === 0 || this.reconnectTimer) return;
10243
+ this.reconnectTimer = setTimeout(() => {
10244
+ this.reconnectTimer = void 0;
10245
+ void this.call("subscribe", {}).catch(() => this.scheduleSubscriptionReconnect());
10246
+ }, 250);
10247
+ this.reconnectTimer.unref?.();
10248
+ }
10249
+ spawnDetached() {
10250
+ const url = resolveSessionCatalogProjectServerUrl();
10251
+ if (!url) throw new Error("Built Session Catalog project server is unavailable");
10252
+ const child = spawn(
10253
+ process.execPath,
10254
+ [
10255
+ fileURLToPath3(url),
10256
+ "--project-dir",
10257
+ this.options.projectDir,
10258
+ "--project-root",
10259
+ this.options.projectRoot
10260
+ ],
10261
+ { detached: true, stdio: "ignore", windowsHide: true, env: process.env }
10262
+ );
10263
+ child.unref();
10264
+ }
10265
+ };
10266
+
9884
10267
  // src/utils/assert-never.ts
9885
10268
  function assertNever(x, message) {
9886
10269
  const err = new Error(
@@ -10087,20 +10470,20 @@ var color = {
10087
10470
  };
10088
10471
 
10089
10472
  // src/utils/config-backup.ts
10090
- import * as fs2 from "node:fs/promises";
10091
- import * as path9 from "node:path";
10473
+ import * as fs3 from "node:fs/promises";
10474
+ import * as path11 from "node:path";
10092
10475
  function configHistoryDir(globalRoot) {
10093
- return path9.join(globalRoot, "config-history");
10476
+ return path11.join(globalRoot, "config-history");
10094
10477
  }
10095
10478
  function configSlug(absolutePath, globalRoot) {
10096
- const rel = path9.relative(globalRoot, absolutePath);
10479
+ const rel = path11.relative(globalRoot, absolutePath);
10097
10480
  const normalized = rel.replace(/\\/g, "/").replace(/\.json$/i, "");
10098
10481
  return normalized.replace(/\//g, "-");
10099
10482
  }
10100
10483
  async function backupConfigFile(filePath, paths) {
10101
10484
  let currentContent;
10102
10485
  try {
10103
- currentContent = await fs2.readFile(filePath, "utf8");
10486
+ currentContent = await fs3.readFile(filePath, "utf8");
10104
10487
  if (!currentContent.trim()) return;
10105
10488
  } catch {
10106
10489
  return;
@@ -10109,10 +10492,10 @@ async function backupConfigFile(filePath, paths) {
10109
10492
  const ts = now.toISOString().replace(/[:.]/g, "-").replace(/Z$/, "");
10110
10493
  const slug = configSlug(filePath, paths.globalRoot);
10111
10494
  const backupDir = configHistoryDir(paths.globalRoot);
10112
- const backupFile = path9.join(backupDir, `${slug}-${ts}.json`);
10495
+ const backupFile = path11.join(backupDir, `${slug}-${ts}.json`);
10113
10496
  try {
10114
- await fs2.mkdir(backupDir, { recursive: true });
10115
- await fs2.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
10497
+ await fs3.mkdir(backupDir, { recursive: true });
10498
+ await fs3.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
10116
10499
  } catch {
10117
10500
  }
10118
10501
  }
@@ -10185,6 +10568,7 @@ function isTextBlock(b) {
10185
10568
  var MAX_DIGEST_CHARS = 4e3;
10186
10569
  function createContextEvidenceState() {
10187
10570
  return {
10571
+ recentUserTurns: [],
10188
10572
  sessionGoals: [],
10189
10573
  implicitFacts: [],
10190
10574
  activeErrors: [],
@@ -10201,6 +10585,11 @@ function buildContextEvidenceDigest(ctx) {
10201
10585
  if (state.currentIntent?.text) {
10202
10586
  lines.push(`intent: ${state.currentIntent.text}`);
10203
10587
  }
10588
+ const priorTurns = (state.recentUserTurns ?? []).slice(-6, -1);
10589
+ if (priorTurns.length > 0) {
10590
+ lines.push("recent_human_instructions:");
10591
+ for (const turn of priorTurns) lines.push(`- ${turn.text}`);
10592
+ }
10204
10593
  const goals = state.sessionGoals.slice(-3);
10205
10594
  if (goals.length > 0) {
10206
10595
  lines.push("session_goals:");
@@ -10290,6 +10679,7 @@ function ensureEvidence(ctx) {
10290
10679
  ctx.contextEvidence = createContextEvidenceState();
10291
10680
  }
10292
10681
  ctx.contextEvidence.completedWork ??= [];
10682
+ ctx.contextEvidence.recentUserTurns ??= [];
10293
10683
  return ctx.contextEvidence;
10294
10684
  }
10295
10685
 
@@ -10400,7 +10790,7 @@ function isEmptyMessage(msg) {
10400
10790
  }
10401
10791
 
10402
10792
  // src/core/context.ts
10403
- import * as path10 from "node:path";
10793
+ import * as path12 from "node:path";
10404
10794
 
10405
10795
  // src/utils/tool-wire-compact.ts
10406
10796
  var TOOL_DESCRIPTION_MAX_CHARS = 400;
@@ -10804,12 +11194,11 @@ var ConversationState = class {
10804
11194
  }
10805
11195
  this.ctx.messages.splice(this.ctx.messages.length, 0, message);
10806
11196
  const overflow = this.overflowCount(this.ctx.messages);
11197
+ this.emit({ kind: "message_appended", message });
10807
11198
  if (overflow > 0) {
10808
11199
  this.ctx.messages.splice(0, overflow);
10809
11200
  this.ctx.toolAdjacencyDirty = true;
10810
- this.emit({ kind: "messages_replaced", messages: [...this.ctx.messages] });
10811
- } else {
10812
- this.emit({ kind: "message_appended", message });
11201
+ this.emit({ kind: "messages_dropped", count: overflow });
10813
11202
  }
10814
11203
  }
10815
11204
  /**
@@ -11307,6 +11696,11 @@ var Context = class _Context {
11307
11696
  ts,
11308
11697
  version: 1,
11309
11698
  messages: [...change.messages]
11699
+ } : change.kind === "messages_dropped" ? {
11700
+ type: "messages_dropped",
11701
+ ts,
11702
+ version: 1,
11703
+ count: change.count
11310
11704
  } : null;
11311
11705
  if (!event) return;
11312
11706
  this.enqueueConversationJournal(event, this.session);
@@ -11592,11 +11986,11 @@ var Context = class _Context {
11592
11986
  * Returns the resolved absolute path.
11593
11987
  */
11594
11988
  setWorkingDir(dir) {
11595
- const resolved = path10.isAbsolute(dir) ? path10.resolve(dir) : path10.resolve(this.projectRoot, dir);
11989
+ const resolved = path12.isAbsolute(dir) ? path12.resolve(dir) : path12.resolve(this.projectRoot, dir);
11596
11990
  if (!this.allowOutsideProjectRoot) {
11597
- const root = path10.resolve(this.projectRoot);
11598
- const rel = path10.relative(root, resolved);
11599
- if (rel.startsWith("..") || path10.isAbsolute(rel)) {
11991
+ const root = path12.resolve(this.projectRoot);
11992
+ const rel = path12.relative(root, resolved);
11993
+ if (rel.startsWith("..") || path12.isAbsolute(rel)) {
11600
11994
  throw new Error(`Working directory "${resolved}" is outside project root "${root}"`);
11601
11995
  }
11602
11996
  }
@@ -12025,10 +12419,10 @@ function validateAgainstSchema(value, schema) {
12025
12419
  return { ok: errors.length === 0, errors };
12026
12420
  }
12027
12421
  var MAX_SCHEMA_DEPTH = 64;
12028
- function walk(value, schema, path42, errors, depth) {
12422
+ function walk(value, schema, path44, errors, depth) {
12029
12423
  if (depth > MAX_SCHEMA_DEPTH) {
12030
12424
  errors.push({
12031
- path: path42 || "<root>",
12425
+ path: path44 || "<root>",
12032
12426
  message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
12033
12427
  });
12034
12428
  return;
@@ -12036,7 +12430,7 @@ function walk(value, schema, path42, errors, depth) {
12036
12430
  if (schema.enum !== void 0) {
12037
12431
  if (!enumIncludes(schema.enum, value)) {
12038
12432
  errors.push({
12039
- path: path42 || "<root>",
12433
+ path: path44 || "<root>",
12040
12434
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
12041
12435
  });
12042
12436
  return;
@@ -12045,7 +12439,7 @@ function walk(value, schema, path42, errors, depth) {
12045
12439
  if (typeof schema.type === "string") {
12046
12440
  if (!checkType(value, schema.type)) {
12047
12441
  errors.push({
12048
- path: path42 || "<root>",
12442
+ path: path44 || "<root>",
12049
12443
  message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
12050
12444
  });
12051
12445
  return;
@@ -12057,7 +12451,7 @@ function walk(value, schema, path42, errors, depth) {
12057
12451
  if (!(req in obj)) {
12058
12452
  const expected = schema.properties?.[req]?.type;
12059
12453
  errors.push({
12060
- path: joinPath(path42, req),
12454
+ path: joinPath(path44, req),
12061
12455
  message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
12062
12456
  });
12063
12457
  }
@@ -12065,14 +12459,14 @@ function walk(value, schema, path42, errors, depth) {
12065
12459
  if (schema.properties) {
12066
12460
  for (const [key, subSchema] of Object.entries(schema.properties)) {
12067
12461
  if (key in obj) {
12068
- walk(obj[key], subSchema, joinPath(path42, key), errors, depth + 1);
12462
+ walk(obj[key], subSchema, joinPath(path44, key), errors, depth + 1);
12069
12463
  }
12070
12464
  }
12071
12465
  }
12072
12466
  }
12073
12467
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
12074
12468
  for (let i = 0; i < value.length; i++) {
12075
- walk(value[i], schema.items, `${path42}[${i}]`, errors, depth + 1);
12469
+ walk(value[i], schema.items, `${path44}[${i}]`, errors, depth + 1);
12076
12470
  }
12077
12471
  }
12078
12472
  }
@@ -12432,14 +12826,14 @@ function compileUserRegex(pattern, flags) {
12432
12826
  var MAX_SUBJECT_LEN = 64 * 1024;
12433
12827
 
12434
12828
  // src/utils/session-scoped-path.ts
12435
- import * as path11 from "node:path";
12829
+ import * as path13 from "node:path";
12436
12830
  function sessionScopedPath(dir, sessionId, suffix) {
12437
12831
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
12438
12832
  throw invalid(sessionId);
12439
12833
  }
12440
- const resolved = path11.resolve(dir, `${sessionId}${suffix}`);
12441
- const rel = path11.relative(path11.resolve(dir), resolved);
12442
- if (rel.startsWith("..") || path11.isAbsolute(rel)) {
12834
+ const resolved = path13.resolve(dir, `${sessionId}${suffix}`);
12835
+ const rel = path13.relative(path13.resolve(dir), resolved);
12836
+ if (rel.startsWith("..") || path13.isAbsolute(rel)) {
12443
12837
  throw invalid(sessionId);
12444
12838
  }
12445
12839
  return resolved;
@@ -12455,7 +12849,7 @@ function invalid(sessionId) {
12455
12849
 
12456
12850
  // src/utils/sleep.ts
12457
12851
  function sleep(ms) {
12458
- return new Promise((resolve19) => setTimeout(resolve19, ms));
12852
+ return new Promise((resolve21) => setTimeout(resolve21, ms));
12459
12853
  }
12460
12854
 
12461
12855
  // src/utils/slug.ts
@@ -13238,7 +13632,7 @@ function subjectForToolInput(toolName, input, subjectKey) {
13238
13632
  // src/storage/file-session-writer.ts
13239
13633
  import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from "node:fs";
13240
13634
  import * as fsp7 from "node:fs/promises";
13241
- import * as path12 from "node:path";
13635
+ import * as path14 from "node:path";
13242
13636
  import { createInterface } from "node:readline";
13243
13637
 
13244
13638
  // src/storage/session-workspace-checkpoints.ts
@@ -13504,7 +13898,7 @@ var FileSessionWriter = class _FileSessionWriter {
13504
13898
  this.meta = meta;
13505
13899
  this.events = events;
13506
13900
  this.resumed = opts.resumed ?? false;
13507
- this.manifestFile = opts.dir ? path12.join(opts.dir, `${path12.basename(id)}.summary.json`) : "";
13901
+ this.manifestFile = opts.dir ? path14.join(opts.dir, `${path14.basename(id)}.summary.json`) : "";
13508
13902
  this.filePath = opts.filePath ?? "";
13509
13903
  this.secretScrubber = opts.secretScrubber;
13510
13904
  this.checkpointCas = opts.checkpointCas;
@@ -14393,10 +14787,10 @@ var FileSessionWriter = class _FileSessionWriter {
14393
14787
  };
14394
14788
 
14395
14789
  // src/storage/session-checkpoint-cas.ts
14396
- import { spawn } from "node:child_process";
14397
- import { createHash as createHash2, randomUUID as randomUUID9 } from "node:crypto";
14790
+ import { spawn as spawn2 } from "node:child_process";
14791
+ import { createHash as createHash3, randomUUID as randomUUID9 } from "node:crypto";
14398
14792
  import * as fsp8 from "node:fs/promises";
14399
- import * as path13 from "node:path";
14793
+ import * as path15 from "node:path";
14400
14794
 
14401
14795
  // src/storage/storage-concurrency.ts
14402
14796
  async function mapWithConcurrency(items, concurrency, mapper) {
@@ -14424,16 +14818,16 @@ var MAX_GIT_OUTPUT = 16 * 1024 * 1024;
14424
14818
  var MAX_BLOB_BYTES = 64 * 1024 * 1024;
14425
14819
  var MAX_CHECKPOINT_BYTES = 512 * 1024 * 1024;
14426
14820
  function sha256(content) {
14427
- return createHash2("sha256").update(content).digest("hex");
14821
+ return createHash3("sha256").update(content).digest("hex");
14428
14822
  }
14429
14823
  function isInside(root, target) {
14430
- const relative8 = path13.relative(root, target);
14431
- return relative8 === "" || !relative8.startsWith("..") && !path13.isAbsolute(relative8);
14824
+ const relative8 = path15.relative(root, target);
14825
+ return relative8 === "" || !relative8.startsWith("..") && !path15.isAbsolute(relative8);
14432
14826
  }
14433
14827
  function normalizeRelative(input) {
14434
- if (!input || path13.isAbsolute(input)) return null;
14828
+ if (!input || path15.isAbsolute(input)) return null;
14435
14829
  const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
14436
- const resolved = path13.posix.normalize(normalized);
14830
+ const resolved = path15.posix.normalize(normalized);
14437
14831
  if (!resolved || resolved === "." || resolved === ".." || resolved.startsWith("../")) return null;
14438
14832
  return resolved;
14439
14833
  }
@@ -14448,8 +14842,8 @@ var SessionCheckpointCas = class {
14448
14842
  projectRoot;
14449
14843
  runGit;
14450
14844
  constructor(opts) {
14451
- this.rootDir = path13.resolve(opts.rootDir);
14452
- this.projectRoot = path13.resolve(opts.projectRoot);
14845
+ this.rootDir = path15.resolve(opts.rootDir);
14846
+ this.projectRoot = path15.resolve(opts.projectRoot);
14453
14847
  this.runGit = opts.runGit ?? defaultRunGit;
14454
14848
  }
14455
14849
  async capture(_sessionId, _promptIndex) {
@@ -14474,7 +14868,7 @@ var SessionCheckpointCas = class {
14474
14868
  relativePaths,
14475
14869
  CAPTURE_CONCURRENCY,
14476
14870
  async (relative8) => {
14477
- const absolute = path13.resolve(this.projectRoot, ...relative8.split("/"));
14871
+ const absolute = path15.resolve(this.projectRoot, ...relative8.split("/"));
14478
14872
  if (!isInside(this.projectRoot, absolute)) {
14479
14873
  unresolved.push({ path: relative8, reason: "path escapes project root" });
14480
14874
  return null;
@@ -14483,8 +14877,8 @@ var SessionCheckpointCas = class {
14483
14877
  const stat15 = await fsp8.lstat(absolute);
14484
14878
  if (stat15.isSymbolicLink()) {
14485
14879
  const linkTarget = await fsp8.readlink(absolute);
14486
- const resolvedLink = path13.resolve(path13.dirname(absolute), linkTarget);
14487
- if (path13.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
14880
+ const resolvedLink = path15.resolve(path15.dirname(absolute), linkTarget);
14881
+ if (path15.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
14488
14882
  unresolved.push({
14489
14883
  path: relative8,
14490
14884
  reason: "symlink target escapes project root"
@@ -14546,7 +14940,7 @@ var SessionCheckpointCas = class {
14546
14940
  };
14547
14941
  }
14548
14942
  async materialize(checkpoint, targetRoot) {
14549
- const target = path13.resolve(targetRoot);
14943
+ const target = path15.resolve(targetRoot);
14550
14944
  if (target === this.projectRoot) {
14551
14945
  throw new Error("Refusing to materialize a workspace checkpoint over the parent project root");
14552
14946
  }
@@ -14586,10 +14980,10 @@ var SessionCheckpointCas = class {
14586
14980
  try {
14587
14981
  const output = await this.safeOutputPath(target, realTarget, entry.path);
14588
14982
  if (entry.state === "symlink") {
14589
- if (path13.isAbsolute(entry.linkTarget)) {
14983
+ if (path15.isAbsolute(entry.linkTarget)) {
14590
14984
  throw new Error("absolute symlink target refused");
14591
14985
  }
14592
- const resolvedLink = path13.resolve(path13.dirname(output), entry.linkTarget);
14986
+ const resolvedLink = path15.resolve(path15.dirname(output), entry.linkTarget);
14593
14987
  if (!isInside(target, resolvedLink)) throw new Error("symlink target escapes checkpoint root");
14594
14988
  }
14595
14989
  prepared.push({
@@ -14617,7 +15011,7 @@ var SessionCheckpointCas = class {
14617
15011
  await fsp8.unlink(output).catch((err) => {
14618
15012
  if (err.code !== "ENOENT") throw err;
14619
15013
  });
14620
- await fsp8.mkdir(path13.dirname(output), { recursive: true });
15014
+ await fsp8.mkdir(path15.dirname(output), { recursive: true });
14621
15015
  await fsp8.symlink(entry.linkTarget, output);
14622
15016
  writtenFiles.push(entry.path);
14623
15017
  continue;
@@ -14634,15 +15028,15 @@ var SessionCheckpointCas = class {
14634
15028
  }
14635
15029
  objectPath(hash2) {
14636
15030
  if (!HASH_RE.test(hash2)) throw new Error(`Invalid CAS object hash: ${hash2}`);
14637
- return path13.join(this.rootDir, "objects", hash2.slice(0, 2), hash2.slice(2));
15031
+ return path15.join(this.rootDir, "objects", hash2.slice(0, 2), hash2.slice(2));
14638
15032
  }
14639
15033
  manifestPath(hash2) {
14640
15034
  if (!HASH_RE.test(hash2)) throw new Error(`Invalid checkpoint manifest hash: ${hash2}`);
14641
- return path13.join(this.rootDir, "manifests", `${hash2}.json`);
15035
+ return path15.join(this.rootDir, "manifests", `${hash2}.json`);
14642
15036
  }
14643
15037
  async putBlob(hash2, content) {
14644
15038
  const target = this.objectPath(hash2);
14645
- await fsp8.mkdir(path13.dirname(target), { recursive: true });
15039
+ await fsp8.mkdir(path15.dirname(target), { recursive: true });
14646
15040
  try {
14647
15041
  const existing = await fsp8.readFile(target);
14648
15042
  if (sha256(existing) !== hash2) throw new Error(`Corrupt CAS object collision: ${hash2}`);
@@ -14650,9 +15044,9 @@ var SessionCheckpointCas = class {
14650
15044
  } catch (err) {
14651
15045
  if (err.code !== "ENOENT") throw err;
14652
15046
  }
14653
- const temp = path13.join(
14654
- path13.dirname(target),
14655
- `.${path13.basename(target)}.${process.pid}.${randomUUID9()}.tmp`
15047
+ const temp = path15.join(
15048
+ path15.dirname(target),
15049
+ `.${path15.basename(target)}.${process.pid}.${randomUUID9()}.tmp`
14656
15050
  );
14657
15051
  let handle;
14658
15052
  try {
@@ -14713,7 +15107,7 @@ var SessionCheckpointCas = class {
14713
15107
  async safeOutputPath(target, realTarget, relative8) {
14714
15108
  const normalized = normalizeRelative(relative8);
14715
15109
  if (!normalized) throw new Error("invalid relative path");
14716
- const output = path13.resolve(target, ...normalized.split("/"));
15110
+ const output = path15.resolve(target, ...normalized.split("/"));
14717
15111
  if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
14718
15112
  let probe = output;
14719
15113
  for (; ; ) {
@@ -14723,7 +15117,7 @@ var SessionCheckpointCas = class {
14723
15117
  return output;
14724
15118
  } catch (err) {
14725
15119
  if (err.code !== "ENOENT") throw err;
14726
- const parent = path13.dirname(probe);
15120
+ const parent = path15.dirname(probe);
14727
15121
  if (parent === probe) throw err;
14728
15122
  probe = parent;
14729
15123
  }
@@ -14731,14 +15125,14 @@ var SessionCheckpointCas = class {
14731
15125
  }
14732
15126
  };
14733
15127
  function defaultRunGit(args, cwd) {
14734
- return new Promise((resolve19) => {
15128
+ return new Promise((resolve21) => {
14735
15129
  const stdoutChunks = [];
14736
15130
  const stderrChunks = [];
14737
15131
  let stdoutBytes = 0;
14738
15132
  let stderrBytes = 0;
14739
15133
  let stdoutTruncated = false;
14740
15134
  let stderrTruncated = false;
14741
- const child = spawn("git", args, {
15135
+ const child = spawn2("git", args, {
14742
15136
  cwd,
14743
15137
  env: buildChildEnv(),
14744
15138
  stdio: ["ignore", "pipe", "pipe"],
@@ -14774,8 +15168,8 @@ function defaultRunGit(args, cwd) {
14774
15168
  stdoutTruncated,
14775
15169
  stderrTruncated
14776
15170
  });
14777
- child.on("error", (err) => resolve19(result(1, err.message)));
14778
- child.on("close", (code) => resolve19(result(code ?? 1)));
15171
+ child.on("error", (err) => resolve21(result(1, err.message)));
15172
+ child.on("close", (code) => resolve21(result(code ?? 1)));
14779
15173
  });
14780
15174
  }
14781
15175
 
@@ -14787,13 +15181,13 @@ function generateSessionId(startedAt, _model) {
14787
15181
  }
14788
15182
 
14789
15183
  // src/storage/session-id-resolver.ts
14790
- import * as path14 from "node:path";
15184
+ import * as path16 from "node:path";
14791
15185
  function resolveSessionId(query, candidateIds) {
14792
15186
  const normalized = query.trim();
14793
15187
  if (!normalized) return { status: "missing", query: normalized };
14794
15188
  const uniqueIds = [...new Set(candidateIds)];
14795
15189
  if (uniqueIds.includes(normalized)) return { status: "resolved", id: normalized };
14796
- const leaf = (id) => path14.posix.basename(id.replace(/\\/g, "/"));
15190
+ const leaf = (id) => path16.posix.basename(id.replace(/\\/g, "/"));
14797
15191
  const exactLeafMatches = uniqueIds.filter((id) => leaf(id) === normalized);
14798
15192
  if (exactLeafMatches.length === 1) {
14799
15193
  return { status: "resolved", id: exactLeafMatches[0] };
@@ -14845,15 +15239,15 @@ function scrubPersistedSessionSummary(summary, scrubber2) {
14845
15239
  }
14846
15240
 
14847
15241
  // src/storage/session-resume-validation.ts
14848
- import { createHash as createHash3 } from "node:crypto";
15242
+ import { createHash as createHash4 } from "node:crypto";
14849
15243
  import * as fsp9 from "node:fs/promises";
14850
- import * as path15 from "node:path";
15244
+ import * as path17 from "node:path";
14851
15245
  var MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;
14852
15246
  var VALIDATION_CONCURRENCY = 8;
14853
15247
  var NOTICE_PATH_LIMIT = 20;
14854
15248
  function isInside2(root, target) {
14855
- const relative8 = path15.relative(root, target);
14856
- return relative8 === "" || !relative8.startsWith("..") && !path15.isAbsolute(relative8);
15249
+ const relative8 = path17.relative(root, target);
15250
+ return relative8 === "" || !relative8.startsWith("..") && !path17.isAbsolute(relative8);
14857
15251
  }
14858
15252
  function errno(err) {
14859
15253
  return err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
@@ -14864,7 +15258,7 @@ function latestObservations(events, projectRoot) {
14864
15258
  if (event.type !== "file_observation" || typeof event.path !== "string" || event.path.length === 0 || typeof event.hash !== "string" || !/^[a-f\d]{64}$/i.test(event.hash)) {
14865
15259
  continue;
14866
15260
  }
14867
- const normalized = path15.resolve(projectRoot, event.path);
15261
+ const normalized = path17.resolve(projectRoot, event.path);
14868
15262
  latest.set(normalized, {
14869
15263
  path: normalized,
14870
15264
  hash: event.hash.toLowerCase(),
@@ -14913,7 +15307,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
14913
15307
  };
14914
15308
  }
14915
15309
  const content = await fsp9.readFile(realFile, "utf8");
14916
- const actualHash = createHash3("sha256").update(content, "utf8").digest("hex");
15310
+ const actualHash = createHash4("sha256").update(content, "utf8").digest("hex");
14917
15311
  if (actualHash === observation.hash) return null;
14918
15312
  return { ...base, status: "modified", actualHash };
14919
15313
  } catch (err) {
@@ -14922,7 +15316,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
14922
15316
  }
14923
15317
  }
14924
15318
  async function validateResumeFileObservations(events, projectRoot) {
14925
- const lexicalRoot = path15.resolve(projectRoot);
15319
+ const lexicalRoot = path17.resolve(projectRoot);
14926
15320
  const realRoot = await fsp9.realpath(lexicalRoot).catch(() => lexicalRoot);
14927
15321
  const observations = latestObservations(events, lexicalRoot);
14928
15322
  const results = await mapWithConcurrency(
@@ -14938,9 +15332,9 @@ async function validateResumeFileObservations(events, projectRoot) {
14938
15332
  }
14939
15333
  function formatResumeValidationNotice(validation, projectRoot) {
14940
15334
  if (validation.staleFiles.length === 0) return null;
14941
- const root = path15.resolve(projectRoot);
15335
+ const root = path17.resolve(projectRoot);
14942
15336
  const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
14943
- const relative8 = path15.relative(root, entry.path);
15337
+ const relative8 = path17.relative(root, entry.path);
14944
15338
  const display = isInside2(root, entry.path) ? relative8 || "." : entry.path;
14945
15339
  return `- ${JSON.stringify(display)} [${entry.status}]`;
14946
15340
  });
@@ -14965,22 +15359,22 @@ function formatInterruptedToolNotice(pendingToolUseCount) {
14965
15359
 
14966
15360
  // src/storage/session-store/delete-session-artifacts.ts
14967
15361
  import * as fsp10 from "node:fs/promises";
14968
- import * as path17 from "node:path";
15362
+ import * as path19 from "node:path";
14969
15363
 
14970
15364
  // src/storage/session-store/paths.ts
14971
- import * as path16 from "node:path";
15365
+ import * as path18 from "node:path";
14972
15366
  function sessionPath(storeDir, id, ext) {
14973
15367
  return sessionScopedPath(storeDir, id, ext);
14974
15368
  }
14975
15369
  function shardManifestPath(storeDir, shardKey) {
14976
- return shardKey ? path16.join(storeDir, shardKey, "_manifest.json") : path16.join(storeDir, "_manifest.json");
15370
+ return shardKey ? path18.join(storeDir, shardKey, "_manifest.json") : path18.join(storeDir, "_manifest.json");
14977
15371
  }
14978
15372
  function shardKeyForSessionId(id) {
14979
- const dirName = path16.dirname(id);
15373
+ const dirName = path18.dirname(id);
14980
15374
  return dirName === "." ? "" : dirName;
14981
15375
  }
14982
15376
  async function ensureShardDir(storeDir, id) {
14983
- const dirPath = path16.dirname(sessionScopedPath(storeDir, id, ""));
15377
+ const dirPath = path18.dirname(sessionScopedPath(storeDir, id, ""));
14984
15378
  await ensureDir(dirPath);
14985
15379
  return dirPath;
14986
15380
  }
@@ -14991,9 +15385,9 @@ async function deleteSessionArtifacts({
14991
15385
  id,
14992
15386
  jsonlPath
14993
15387
  }) {
14994
- const shardDir = path17.dirname(jsonlPath);
14995
- const base = path17.basename(id);
14996
- const sessDir = path17.join(shardDir, base);
15388
+ const shardDir = path19.dirname(jsonlPath);
15389
+ const base = path19.basename(id);
15390
+ const sessDir = path19.join(shardDir, base);
14997
15391
  const deletions = [
14998
15392
  fsp10.unlink(jsonlPath),
14999
15393
  fsp10.unlink(sessionPath(rootDir, id, ".summary.json")),
@@ -15052,7 +15446,7 @@ function isSessionJsonlFileName(name) {
15052
15446
 
15053
15447
  // src/storage/session-store/directory-session-files.ts
15054
15448
  import * as fsp11 from "node:fs/promises";
15055
- import * as path18 from "node:path";
15449
+ import * as path20 from "node:path";
15056
15450
  function sessionIdForFile(prefix, name) {
15057
15451
  const base = name.replace(/\.jsonl$/, "");
15058
15452
  return prefix ? `${prefix}/${base}` : base;
@@ -15074,12 +15468,12 @@ async function collectSessionFiles(dir, prefix = "", depth = 0) {
15074
15468
  if (entry.isDirectory()) {
15075
15469
  dirEntries.push(entry);
15076
15470
  } else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {
15077
- files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path18.join(dir, entry.name) });
15471
+ files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path20.join(dir, entry.name) });
15078
15472
  }
15079
15473
  }
15080
15474
  const childFileArrays = await Promise.all(
15081
15475
  dirEntries.map(
15082
- (entry) => collectSessionFiles(path18.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15476
+ (entry) => collectSessionFiles(path20.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15083
15477
  )
15084
15478
  );
15085
15479
  return [...childFileArrays.flat(), ...files];
@@ -15103,7 +15497,7 @@ async function collectSessionIds(dir, prefix = "", depth = 0) {
15103
15497
  }
15104
15498
  const childIdArrays = await Promise.all(
15105
15499
  dirEntries.map(
15106
- (entry) => collectSessionIds(path18.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15500
+ (entry) => collectSessionIds(path20.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15107
15501
  )
15108
15502
  );
15109
15503
  return [...childIdArrays.flat(), ...fileIds];
@@ -15145,7 +15539,7 @@ function emitSessionStoreError(events, sessionId, filePath, operation, error2, r
15145
15539
  }
15146
15540
 
15147
15541
  // src/storage/session-store/fork-session.ts
15148
- import { createHash as createHash4 } from "node:crypto";
15542
+ import { createHash as createHash5 } from "node:crypto";
15149
15543
 
15150
15544
  // src/storage/session-store/replay.ts
15151
15545
  function isReplayableMessage(value) {
@@ -15217,7 +15611,7 @@ async function forkSession(host, id, opts = {}) {
15217
15611
  }
15218
15612
  const parentPrefix = parent.events.slice(0, boundary + 1);
15219
15613
  const workspaceCheckpoint = targetCheckpoint?.workspaceCheckpoint;
15220
- const checkpointHash = createHash4("sha256").update(parentPrefix.map((event) => JSON.stringify(event)).join("\n") + "\n", "utf8").digest("hex");
15614
+ const checkpointHash = createHash5("sha256").update(parentPrefix.map((event) => JSON.stringify(event)).join("\n") + "\n", "utf8").digest("hex");
15221
15615
  const inherited = parentPrefix.filter(inheritsIntoFork);
15222
15616
  const writer = await host.create({
15223
15617
  id: "",
@@ -15560,6 +15954,16 @@ function replaySessionEvent(params) {
15560
15954
  } else {
15561
15955
  emitDamaged(params, "Ignored malformed messages_replaced event");
15562
15956
  }
15957
+ } else if (ev.type === "messages_dropped" && ev.version === 1) {
15958
+ if (exactJournalActive && Number.isInteger(ev.count) && ev.count > 0) {
15959
+ messages.splice(0, Math.min(ev.count, messages.length));
15960
+ openToolUses.clear();
15961
+ for (const current of messages) trackMessageToolState(current, openToolUses);
15962
+ } else if (!exactJournalActive) {
15963
+ emitDamaged(params, "Ignored messages_dropped event outside the exact journal");
15964
+ } else {
15965
+ emitDamaged(params, `Ignored malformed messages_dropped event (count ${String(ev.count)})`);
15966
+ }
15563
15967
  } else if (ev.type === "context_snapshot") {
15564
15968
  if (!applyContextSnapshot(messages, openToolUses, ev.messages)) {
15565
15969
  emitDamaged(params, "Ignored malformed context_snapshot event");
@@ -15614,7 +16018,7 @@ function emitDamaged(params, detail) {
15614
16018
 
15615
16019
  // src/storage/session-store/prune-helpers.ts
15616
16020
  import * as fsp13 from "node:fs/promises";
15617
- import * as path19 from "node:path";
16021
+ import * as path21 from "node:path";
15618
16022
  function isPrunableSessionJsonl(name) {
15619
16023
  return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
15620
16024
  }
@@ -15622,7 +16026,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
15622
16026
  const cutoff = Date.now() - maxAgeDays * 864e5;
15623
16027
  let deleted = 0;
15624
16028
  const pruneFile = async (dir, name, prefix) => {
15625
- const jsonlPath = path19.join(dir, name);
16029
+ const jsonlPath = path21.join(dir, name);
15626
16030
  try {
15627
16031
  const stat15 = await fsp13.stat(jsonlPath);
15628
16032
  if (stat15.mtimeMs >= cutoff) return;
@@ -15641,7 +16045,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
15641
16045
  continue;
15642
16046
  }
15643
16047
  if (!entry.isDirectory()) continue;
15644
- const dateDir = path19.join(storeDir, entry.name);
16048
+ const dateDir = path21.join(storeDir, entry.name);
15645
16049
  const files = await fsp13.readdir(dateDir, { withFileTypes: true }).catch(() => []);
15646
16050
  for (const file of files) {
15647
16051
  if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
@@ -15650,7 +16054,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
15650
16054
  }
15651
16055
  for (const entry of entries) {
15652
16056
  if (!entry.isDirectory()) continue;
15653
- const dateDir = path19.join(storeDir, entry.name);
16057
+ const dateDir = path21.join(storeDir, entry.name);
15654
16058
  try {
15655
16059
  const remaining = await fsp13.readdir(dateDir);
15656
16060
  if (remaining.length === 0) {
@@ -15983,6 +16387,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
15983
16387
  logger;
15984
16388
  onAppend;
15985
16389
  onAppendBatch;
16390
+ /** Present in built production output; source-only tests retain the local compatibility path. */
16391
+ catalogClient;
16392
+ maintenanceHolderId = randomUUID10();
15986
16393
  /**
15987
16394
  * In-memory cache for load() results, keyed by session ID. The cache is
15988
16395
  * invalidated when the file's mtimeMs or size changes (indicating the
@@ -16000,9 +16407,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
16000
16407
  static LIST_SCAN_CONCURRENCY = 32;
16001
16408
  constructor(opts) {
16002
16409
  this.dir = opts.dir;
16003
- this.projectRoot = opts.projectRoot ? path20.resolve(opts.projectRoot) : void 0;
16410
+ this.projectRoot = opts.projectRoot ? path22.resolve(opts.projectRoot) : void 0;
16004
16411
  this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
16005
- rootDir: path20.join(this.dir, "_cas"),
16412
+ rootDir: path22.join(this.dir, "_cas"),
16006
16413
  projectRoot: this.projectRoot
16007
16414
  }) : void 0;
16008
16415
  this.events = opts.events;
@@ -16011,6 +16418,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
16011
16418
  this.logger = opts.logger;
16012
16419
  this.onAppend = opts.onAppend;
16013
16420
  this.onAppendBatch = opts.onAppendBatch;
16421
+ const builtRuntime = import.meta.url.includes("/dist/");
16422
+ this.catalogClient = this.projectRoot && (builtRuntime || process.env["WRONGSTACK_SESSION_CATALOG_FORCE"] === "1") && resolveSessionCatalogProjectServerUrl() ? new SessionCatalogProjectClient({
16423
+ projectDir: path22.dirname(this.dir),
16424
+ projectRoot: this.projectRoot
16425
+ }) : void 0;
16014
16426
  }
16015
16427
  /**
16016
16428
  * Emit a structured warning. Uses the configured Logger when available;
@@ -16033,10 +16445,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
16033
16445
  clearLoadCache(sessionId) {
16034
16446
  this.loadCache.clear(sessionId);
16035
16447
  }
16448
+ async dispose() {
16449
+ await this.catalogClient?.close();
16450
+ this.clearLoadCache();
16451
+ }
16036
16452
  // ── Storage event helpers ───────────────────────────────────────────────────
16037
16453
  /** Absolute path to the session index file. */
16038
16454
  get indexFile() {
16039
- return path20.join(this.dir, "_index.jsonl");
16455
+ return path22.join(this.dir, "_index.jsonl");
16040
16456
  }
16041
16457
  /** Join session ID to its absolute path within the store directory. */
16042
16458
  sessionPath(id, ext) {
@@ -16094,8 +16510,23 @@ var DefaultSessionStore = class _DefaultSessionStore {
16094
16510
  if (!current) return null;
16095
16511
  return current.name === void 0 ? {} : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };
16096
16512
  },
16097
- onClose: (s) => this.appendToIndex(s)
16513
+ onClose: (s) => this.persistCatalogSummary(s)
16098
16514
  });
16515
+ if (this.catalogClient) {
16516
+ await this.catalogClient.call("upsert_summary", {
16517
+ summary: {
16518
+ id,
16519
+ title: meta.title ?? "",
16520
+ startedAt,
16521
+ model: meta.model ?? "",
16522
+ provider: meta.provider ?? "",
16523
+ tokenTotal: 0,
16524
+ lastActivityAt: startedAt
16525
+ },
16526
+ transcriptRelativePath: `${id}.jsonl`,
16527
+ summaryRelativePath: `${id}.summary.json`
16528
+ });
16529
+ }
16099
16530
  emitSessionStoreWrite(this.events, id, file, "create", "success", Date.now() - t0);
16100
16531
  return writer;
16101
16532
  } catch (err) {
@@ -16125,6 +16556,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
16125
16556
  return materializeCheckpoint(this.checkpointCas, checkpoint, targetRoot);
16126
16557
  }
16127
16558
  async resolveId(query) {
16559
+ if (this.catalogClient) {
16560
+ return this.catalogClient.call("resolve_id", { query });
16561
+ }
16128
16562
  const normalized = query.trim();
16129
16563
  if (!normalized) throw new Error("Session not found: (empty query)");
16130
16564
  if (normalized) {
@@ -16231,7 +16665,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16231
16665
  // Shard directory (sessions/<date>/) — must match create() so the
16232
16666
  // .summary.json sidecar lands next to the JSONL instead of the
16233
16667
  // sessions root (where summaryFor() would never find it).
16234
- dir: path20.dirname(file),
16668
+ dir: path22.dirname(file),
16235
16669
  filePath: file,
16236
16670
  secretScrubber: this.secretScrubber,
16237
16671
  checkpointCas: this.checkpointCas,
@@ -16242,7 +16676,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16242
16676
  if (!current) return null;
16243
16677
  return current.name === void 0 ? {} : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };
16244
16678
  },
16245
- onClose: (s) => this.appendToIndex(s)
16679
+ onClose: (s) => this.persistCatalogSummary(s)
16246
16680
  }
16247
16681
  );
16248
16682
  emitSessionStoreWrite(this.events, canonicalId, file, "resume", "success", Date.now() - t0);
@@ -16355,6 +16789,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
16355
16789
  });
16356
16790
  }
16357
16791
  async list(limit = 20) {
16792
+ if (this.catalogClient) {
16793
+ const records = await this.catalogClient.call("list_catalog", { limit });
16794
+ return this.scrubSummaries(records);
16795
+ }
16358
16796
  try {
16359
16797
  const indexed = await this.readIndex();
16360
16798
  if (indexed.length > 0) {
@@ -16377,6 +16815,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
16377
16815
  */
16378
16816
  async listFiltered(criteria) {
16379
16817
  const limit = criteria.limit ?? 100;
16818
+ if (this.catalogClient) {
16819
+ const records = await this.catalogClient.call("list_catalog", {
16820
+ limit: Math.min(1e3, Math.max(limit, 100)),
16821
+ ...criteria.titleContains ? { search: criteria.titleContains } : {}
16822
+ });
16823
+ return this.scrubSummaries(records).filter((summary) => matchesSessionFilter(summary, criteria)).slice(0, limit);
16824
+ }
16380
16825
  try {
16381
16826
  const indexed = await this.readIndex();
16382
16827
  if (indexed.length === 0) {
@@ -16426,6 +16871,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
16426
16871
  await this.appendToIndexStrict(summary).catch(() => {
16427
16872
  });
16428
16873
  }
16874
+ /** Final summary boundary: daemon is authoritative when available. */
16875
+ async persistCatalogSummary(summary) {
16876
+ if (!this.catalogClient) {
16877
+ await this.appendToIndex(summary);
16878
+ return;
16879
+ }
16880
+ await this.catalogClient.call("upsert_summary", {
16881
+ summary,
16882
+ transcriptRelativePath: `${summary.id}.jsonl`,
16883
+ summaryRelativePath: `${summary.id}.summary.json`
16884
+ });
16885
+ }
16429
16886
  /** Append a tombstone entry for a deleted session. */
16430
16887
  async writeTombstone(id) {
16431
16888
  try {
@@ -16535,6 +16992,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
16535
16992
  * fresh _index.jsonl. Useful after manual cleanup or index corruption.
16536
16993
  */
16537
16994
  async rebuildIndex() {
16995
+ if (this.catalogClient) {
16996
+ const result = await this.catalogClient.call("rebuild_catalog", {}, { timeoutMs: 12e4 });
16997
+ return result.indexed;
16998
+ }
16538
16999
  const ids = await this.collectSessionIds(this.dir);
16539
17000
  const summaries = await Promise.all(
16540
17001
  ids.map((id) => this.summaryFor(id).catch(() => null))
@@ -16604,7 +17065,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16604
17065
  });
16605
17066
  }
16606
17067
  async collectSessionFilesInShard(shardKey) {
16607
- const dir = shardKey ? path20.join(this.dir, shardKey) : this.dir;
17068
+ const dir = shardKey ? path22.join(this.dir, shardKey) : this.dir;
16608
17069
  const entries = await this.collectSessionFiles(dir, shardKey);
16609
17070
  return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
16610
17071
  }
@@ -16700,10 +17161,35 @@ var DefaultSessionStore = class _DefaultSessionStore {
16700
17161
  await this.writeTombstone(id);
16701
17162
  }
16702
17163
  async delete(id) {
17164
+ if (this.catalogClient) {
17165
+ const canonical = await this.resolveId(id);
17166
+ const lease = await this.catalogClient.call("acquire_maintenance", {
17167
+ sessionId: canonical,
17168
+ operation: "delete",
17169
+ holderId: this.maintenanceHolderId
17170
+ });
17171
+ try {
17172
+ await this.catalogClient.call("delete", { sessionId: canonical, lease });
17173
+ } catch (error2) {
17174
+ await this.catalogClient.call("release_maintenance", { lease }).catch(() => void 0);
17175
+ throw error2;
17176
+ }
17177
+ this.clearLoadCache(canonical);
17178
+ return;
17179
+ }
16703
17180
  await assertSessionCanBeDeleted(id, this.isSessionInUse);
16704
17181
  await this.deleteSession(id);
16705
17182
  }
16706
17183
  async rename(id, name) {
17184
+ if (this.catalogClient) {
17185
+ const canonical = await this.resolveId(id);
17186
+ const summary = await this.catalogClient.call("rename", {
17187
+ sessionId: canonical,
17188
+ name: sessionContentText(this.secretScrubber.scrub(name))
17189
+ });
17190
+ this.clearLoadCache(canonical);
17191
+ return summary;
17192
+ }
16707
17193
  const trimmed = sessionContentText(this.secretScrubber.scrub(name));
16708
17194
  const manifest = this.sessionPath(id, ".summary.json");
16709
17195
  const jsonlPath = this.sessionPath(id, ".jsonl");
@@ -16755,6 +17241,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
16755
17241
  return updated;
16756
17242
  }
16757
17243
  async prune(maxAgeDays = 30) {
17244
+ if (this.catalogClient) {
17245
+ return this.catalogClient.call("prune", {
17246
+ maxAgeDays,
17247
+ holderId: this.maintenanceHolderId
17248
+ });
17249
+ }
16758
17250
  const deleted = await pruneSessionFiles(this.dir, maxAgeDays, (id) => this.deleteSession(id));
16759
17251
  if (deleted > 0) {
16760
17252
  await this.compactIndex().catch(() => void 0);
@@ -16762,20 +17254,81 @@ var DefaultSessionStore = class _DefaultSessionStore {
16762
17254
  return deleted;
16763
17255
  }
16764
17256
  async clearHistory(id) {
16765
- await this.ensureShardDir(id);
16766
- const file = this.sessionPath(id, ".jsonl");
16767
- const meta = this.sessionPath(id, ".summary.json");
17257
+ const canonical = this.catalogClient ? await this.resolveId(id) : id;
17258
+ const maintenance = this.catalogClient ? await this.catalogClient.call("acquire_maintenance", {
17259
+ sessionId: canonical,
17260
+ operation: "clear",
17261
+ holderId: this.maintenanceHolderId
17262
+ }) : void 0;
17263
+ await this.ensureShardDir(canonical);
17264
+ const file = this.sessionPath(canonical, ".jsonl");
17265
+ const meta = this.sessionPath(canonical, ".summary.json");
17266
+ const backupSuffix = maintenance ? `.${maintenance.leaseId}.clear-backup` : void 0;
17267
+ const fileBackup = backupSuffix ? `${file}${backupSuffix}` : void 0;
17268
+ const metaBackup = backupSuffix ? `${meta}${backupSuffix}` : void 0;
17269
+ let fileStaged = false;
17270
+ let metaStaged = false;
16768
17271
  const record = `${JSON.stringify({
16769
17272
  type: "session_start",
16770
17273
  ts: (/* @__PURE__ */ new Date()).toISOString(),
16771
- id,
17274
+ id: canonical,
16772
17275
  model: "unknown",
16773
17276
  provider: "unknown"
16774
17277
  })}
16775
17278
  `;
16776
- await atomicWrite(file, record);
16777
- await fsp17.unlink(meta).catch(() => void 0);
16778
- this.clearLoadCache(id);
17279
+ try {
17280
+ if (fileBackup) {
17281
+ try {
17282
+ await fsp17.rename(file, fileBackup);
17283
+ fileStaged = true;
17284
+ } catch (error2) {
17285
+ if (error2.code !== "ENOENT") throw error2;
17286
+ }
17287
+ }
17288
+ if (metaBackup) {
17289
+ try {
17290
+ await fsp17.rename(meta, metaBackup);
17291
+ metaStaged = true;
17292
+ } catch (error2) {
17293
+ if (error2.code !== "ENOENT") throw error2;
17294
+ }
17295
+ }
17296
+ await atomicWrite(file, record);
17297
+ if (!metaBackup) await fsp17.unlink(meta).catch(() => void 0);
17298
+ if (this.catalogClient) {
17299
+ const now = (/* @__PURE__ */ new Date()).toISOString();
17300
+ await this.catalogClient.call("upsert_summary", {
17301
+ summary: {
17302
+ id: canonical,
17303
+ title: "",
17304
+ startedAt: now,
17305
+ model: "unknown",
17306
+ provider: "unknown",
17307
+ tokenTotal: 0,
17308
+ lastActivityAt: now
17309
+ },
17310
+ transcriptRelativePath: `${canonical}.jsonl`,
17311
+ summaryRelativePath: `${canonical}.summary.json`
17312
+ });
17313
+ }
17314
+ if (fileStaged && fileBackup) await fsp17.unlink(fileBackup).catch(() => void 0);
17315
+ if (metaStaged && metaBackup) await fsp17.unlink(metaBackup).catch(() => void 0);
17316
+ } catch (error2) {
17317
+ if (fileStaged && fileBackup) {
17318
+ await fsp17.unlink(file).catch(() => void 0);
17319
+ await fsp17.rename(fileBackup, file).catch(() => void 0);
17320
+ }
17321
+ if (metaStaged && metaBackup) {
17322
+ await fsp17.unlink(meta).catch(() => void 0);
17323
+ await fsp17.rename(metaBackup, meta).catch(() => void 0);
17324
+ }
17325
+ throw error2;
17326
+ } finally {
17327
+ if (maintenance && this.catalogClient) {
17328
+ await this.catalogClient.call("release_maintenance", { lease: maintenance }).catch(() => void 0);
17329
+ }
17330
+ }
17331
+ this.clearLoadCache(canonical);
16779
17332
  }
16780
17333
  async summarize(id, mtime) {
16781
17334
  return summarizeSessionFile({
@@ -16795,9 +17348,9 @@ function makeDirectorSessionFactory(opts) {
16795
17348
  let dir;
16796
17349
  if (opts.store) {
16797
17350
  store = opts.store;
16798
- dir = opts.sessionsRoot ? path21.join(opts.sessionsRoot, runId) : "(caller-managed)";
17351
+ dir = opts.sessionsRoot ? path23.join(opts.sessionsRoot, runId) : "(caller-managed)";
16799
17352
  } else if (opts.sessionsRoot) {
16800
- dir = path21.join(opts.sessionsRoot, runId);
17353
+ dir = path23.join(opts.sessionsRoot, runId);
16801
17354
  store = new DefaultSessionStore({ dir });
16802
17355
  } else {
16803
17356
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -16821,7 +17374,7 @@ function makeDirectorSessionFactory(opts) {
16821
17374
  }
16822
17375
  async function readDirectorSubagentSession(args) {
16823
17376
  if (!args.sessionsRoot) return null;
16824
- const filePath = path21.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
17377
+ const filePath = path23.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
16825
17378
  let raw;
16826
17379
  try {
16827
17380
  raw = await fsp18.readFile(filePath, "utf8");
@@ -17393,7 +17946,7 @@ function formatRole(role) {
17393
17946
  }
17394
17947
 
17395
17948
  // src/coordination/fleet-spawn.ts
17396
- async function spawn2(host, config, priceLookup) {
17949
+ async function spawn3(host, config, priceLookup) {
17397
17950
  if (host.workCompleteFlag) {
17398
17951
  throw new FleetSpawnBudgetError(
17399
17952
  "max_spawns",
@@ -17677,7 +18230,7 @@ function hashStr(s) {
17677
18230
  }
17678
18231
 
17679
18232
  // src/coordination/multi-agent-coordinator.ts
17680
- import { randomUUID as randomUUID10 } from "node:crypto";
18233
+ import { randomUUID as randomUUID11 } from "node:crypto";
17681
18234
  import { EventEmitter as EventEmitter2 } from "node:events";
17682
18235
 
17683
18236
  // src/coordination/coordinator/error-classifier.ts
@@ -17816,12 +18369,12 @@ async function executeSubagentWithTimeout({
17816
18369
  }
17817
18370
  return new Promise((resolveDecision) => {
17818
18371
  let settled = false;
17819
- const resolve19 = (d) => {
18372
+ const resolve21 = (d) => {
17820
18373
  if (settled) return;
17821
18374
  settled = true;
17822
18375
  resolveDecision(d);
17823
18376
  };
17824
- const fallback = setTimeout(() => resolve19("stop"), DECISION_TIMEOUT_MS);
18377
+ const fallback = setTimeout(() => resolve21("stop"), DECISION_TIMEOUT_MS);
17825
18378
  const sessionId = currentSessionId();
17826
18379
  budget._events?.emit("budget.threshold_reached", {
17827
18380
  ...sessionId ? { sessionId } : {},
@@ -17831,11 +18384,11 @@ async function executeSubagentWithTimeout({
17831
18384
  timeoutMs: DECISION_TIMEOUT_MS,
17832
18385
  extend: (extra) => {
17833
18386
  clearTimeout(fallback);
17834
- queueMicrotask(() => resolve19({ extend: extra }));
18387
+ queueMicrotask(() => resolve21({ extend: extra }));
17835
18388
  },
17836
18389
  deny: () => {
17837
18390
  clearTimeout(fallback);
17838
- resolve19("stop");
18391
+ resolve21("stop");
17839
18392
  }
17840
18393
  });
17841
18394
  });
@@ -18047,7 +18600,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18047
18600
  return { ...subagent, name: display };
18048
18601
  }
18049
18602
  async spawn(subagent) {
18050
- const id = subagent.id || randomUUID10();
18603
+ const id = subagent.id || randomUUID11();
18051
18604
  const cfg = this.withNickname(subagent, id);
18052
18605
  if (this.subagents.has(id)) {
18053
18606
  throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
@@ -18222,7 +18775,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18222
18775
  taskIds.map((id) => {
18223
18776
  const cached = this.completedResults.find((r) => r.taskId === id);
18224
18777
  if (cached) return cached;
18225
- return new Promise((resolve19, reject) => {
18778
+ return new Promise((resolve21, reject) => {
18226
18779
  const timeout = setTimeout(() => {
18227
18780
  this.off("task.completed", handler);
18228
18781
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -18231,7 +18784,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18231
18784
  if (result.taskId === id) {
18232
18785
  clearTimeout(timeout);
18233
18786
  this.off("task.completed", handler);
18234
- resolve19(result);
18787
+ resolve21(result);
18235
18788
  }
18236
18789
  };
18237
18790
  this.on("task.completed", handler);
@@ -18256,13 +18809,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18256
18809
  const done = new Set(completed.map((r) => r.taskId));
18257
18810
  return { completed, pending: taskIds.filter((id) => !done.has(id)) };
18258
18811
  }
18259
- return new Promise((resolve19) => {
18812
+ return new Promise((resolve21) => {
18260
18813
  let timer;
18261
18814
  const handler = ({ result }) => {
18262
18815
  if (!ids.has(result.taskId)) return;
18263
18816
  if (timer) clearTimeout(timer);
18264
18817
  this.off("task.completed", handler);
18265
- resolve19({
18818
+ resolve21({
18266
18819
  completed: [result],
18267
18820
  pending: taskIds.filter((id) => id !== result.taskId)
18268
18821
  });
@@ -18270,7 +18823,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18270
18823
  if (opts?.timeoutMs !== void 0) {
18271
18824
  timer = setTimeout(() => {
18272
18825
  this.off("task.completed", handler);
18273
- resolve19({ completed: [], pending: [...taskIds], timedOut: true });
18826
+ resolve21({ completed: [], pending: [...taskIds], timedOut: true });
18274
18827
  }, opts.timeoutMs);
18275
18828
  }
18276
18829
  this.on("task.completed", handler);
@@ -18932,7 +19485,7 @@ var Director = class _Director {
18932
19485
  sessionProvider;
18933
19486
  sessionModel;
18934
19487
  constructor(opts) {
18935
- this.id = opts.config.coordinatorId || randomUUID11();
19488
+ this.id = opts.config.coordinatorId || randomUUID12();
18936
19489
  this.manifestPath = opts.manifestPath;
18937
19490
  this.roster = opts.roster;
18938
19491
  this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
@@ -19194,7 +19747,7 @@ var Director = class _Director {
19194
19747
  }
19195
19748
  const config = { ...callerConfig };
19196
19749
  this.resolveSpawnModel(config);
19197
- const subagentId = await spawn2(this, config, priceLookup);
19750
+ const subagentId = await spawn3(this, config, priceLookup);
19198
19751
  const perSubagentIdleMs = typeof config.idleTimeoutMs === "number" && Number.isFinite(config.idleTimeoutMs) && config.idleTimeoutMs >= 0 ? config.idleTimeoutMs : this.subagentIdleTimeoutMs;
19199
19752
  this.armSubagentIdleRetirement(subagentId, perSubagentIdleMs);
19200
19753
  return subagentId;
@@ -19215,7 +19768,7 @@ var Director = class _Director {
19215
19768
  );
19216
19769
  }
19217
19770
  const msg = {
19218
- id: randomUUID11(),
19771
+ id: randomUUID12(),
19219
19772
  type: "task",
19220
19773
  from: this.id,
19221
19774
  to: subagentId,
@@ -21135,7 +21688,7 @@ function resolveContextWindowPolicy(config = {}, overrideMode) {
21135
21688
 
21136
21689
  // src/infrastructure/logger.ts
21137
21690
  import * as fsp20 from "node:fs/promises";
21138
- import * as path22 from "node:path";
21691
+ import * as path24 from "node:path";
21139
21692
  var LEVEL_RANK2 = {
21140
21693
  error: 0,
21141
21694
  warn: 1,
@@ -21206,7 +21759,7 @@ var DefaultLogger = class _DefaultLogger {
21206
21759
  this.stderr = opts.stderr !== false;
21207
21760
  this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
21208
21761
  if (this.file) {
21209
- const dir = path22.dirname(this.file);
21762
+ const dir = path24.dirname(this.file);
21210
21763
  this._tail = this._tail.then(async () => {
21211
21764
  await fsp20.mkdir(dir, { recursive: true });
21212
21765
  }).catch(() => void 0);
@@ -21964,10 +22517,10 @@ var KNOWN_TOKEN_GROUPS = {
21964
22517
  var KNOWN_TOKEN_NAMES = Object.values(KNOWN_TOKEN_GROUPS).flat();
21965
22518
 
21966
22519
  // src/execution/design-kit-loader.ts
21967
- import { existsSync as existsSync3 } from "node:fs";
21968
- import * as fs3 from "node:fs/promises";
21969
- import * as path23 from "node:path";
21970
- import { fileURLToPath as fileURLToPath3 } from "node:url";
22520
+ import { existsSync as existsSync4 } from "node:fs";
22521
+ import * as fs4 from "node:fs/promises";
22522
+ import * as path25 from "node:path";
22523
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
21971
22524
  var KIT_FILE = "KIT.md";
21972
22525
  var TOKENS_FILE = "tokens.json";
21973
22526
  var FOUNDATIONS_ID = "_foundations";
@@ -22066,15 +22619,15 @@ var DefaultDesignKitLoader = class {
22066
22619
  for (const { dir, source } of this.dirs) {
22067
22620
  let entries;
22068
22621
  try {
22069
- entries = await fs3.readdir(dir, { withFileTypes: true });
22622
+ entries = await fs4.readdir(dir, { withFileTypes: true });
22070
22623
  } catch {
22071
22624
  continue;
22072
22625
  }
22073
22626
  for (const e of entries) {
22074
22627
  if (!e.isDirectory()) continue;
22075
- const kitFile = path23.join(dir, e.name, KIT_FILE);
22628
+ const kitFile = path25.join(dir, e.name, KIT_FILE);
22076
22629
  try {
22077
- const raw = await fs3.readFile(kitFile, "utf8");
22630
+ const raw = await fs4.readFile(kitFile, "utf8");
22078
22631
  const fm = parseKitFrontmatter(raw);
22079
22632
  const id = fm.id ?? e.name;
22080
22633
  if (!fm.name) continue;
@@ -22135,7 +22688,7 @@ var DefaultDesignKitLoader = class {
22135
22688
  if (cached !== void 0) return cached;
22136
22689
  const m = await this.find(id);
22137
22690
  if (!m) throw new Error(`Design kit "${id}" not found`);
22138
- const raw = await fs3.readFile(m.path, "utf8");
22691
+ const raw = await fs4.readFile(m.path, "utf8");
22139
22692
  const body = narrowStackSections(stripFrontmatter(raw), stack);
22140
22693
  this.bodyCache.set(key, body);
22141
22694
  return body;
@@ -22147,9 +22700,9 @@ var DefaultDesignKitLoader = class {
22147
22700
  const m = await this.find(id);
22148
22701
  let tokens;
22149
22702
  if (m) {
22150
- const tokensPath = path23.join(path23.dirname(m.path), TOKENS_FILE);
22703
+ const tokensPath = path25.join(path25.dirname(m.path), TOKENS_FILE);
22151
22704
  try {
22152
- const raw = await fs3.readFile(tokensPath, "utf8");
22705
+ const raw = await fs4.readFile(tokensPath, "utf8");
22153
22706
  tokens = JSON.parse(raw);
22154
22707
  } catch {
22155
22708
  tokens = void 0;
@@ -22209,15 +22762,15 @@ function mergeKitTokens(base, own) {
22209
22762
  }
22210
22763
  function resolveBundledDesignKitsDir() {
22211
22764
  try {
22212
- const here = path23.dirname(fileURLToPath3(import.meta.url));
22765
+ const here = path25.dirname(fileURLToPath4(import.meta.url));
22213
22766
  const candidates = [
22214
- path23.join(here, "design-kits"),
22215
- path23.join(here, "..", "design-kits"),
22216
- path23.join(here, "..", "..", "design-kits"),
22217
- path23.join(here, "..", "..", "..", "design-kits")
22767
+ path25.join(here, "design-kits"),
22768
+ path25.join(here, "..", "design-kits"),
22769
+ path25.join(here, "..", "..", "design-kits"),
22770
+ path25.join(here, "..", "..", "..", "design-kits")
22218
22771
  ];
22219
22772
  for (const c of candidates) {
22220
- if (existsSync3(c)) return c;
22773
+ if (existsSync4(c)) return c;
22221
22774
  }
22222
22775
  } catch {
22223
22776
  }
@@ -22251,12 +22804,12 @@ function _resetDesignKitLoaderMemo() {
22251
22804
  }
22252
22805
 
22253
22806
  // src/execution/design-project-store.ts
22254
- import { existsSync as existsSync4 } from "node:fs";
22255
- import * as fs4 from "node:fs/promises";
22256
- import * as path24 from "node:path";
22807
+ import { existsSync as existsSync5 } from "node:fs";
22808
+ import * as fs5 from "node:fs/promises";
22809
+ import * as path26 from "node:path";
22257
22810
  var DESIGN_DIR = ".design";
22258
22811
  function designProjectDir(projectRoot) {
22259
- return path24.join(projectRoot, DESIGN_DIR);
22812
+ return path26.join(projectRoot, DESIGN_DIR);
22260
22813
  }
22261
22814
  var RULE_FILES = ["rules.md", "RULES.md", "design.md"];
22262
22815
  var rulesCache = /* @__PURE__ */ new Map();
@@ -22271,7 +22824,7 @@ async function loadProjectDesignRules(projectRoot) {
22271
22824
  let rules;
22272
22825
  for (const name of RULE_FILES) {
22273
22826
  try {
22274
- const txt = await fs4.readFile(path24.join(designProjectDir(projectRoot), name), "utf8");
22827
+ const txt = await fs5.readFile(path26.join(designProjectDir(projectRoot), name), "utf8");
22275
22828
  if (txt.trim()) {
22276
22829
  rules = txt.trim();
22277
22830
  break;
@@ -22297,7 +22850,7 @@ function parseOverrides(value) {
22297
22850
  }
22298
22851
  async function loadActiveKit(projectRoot) {
22299
22852
  try {
22300
- const raw = await fs4.readFile(path24.join(designProjectDir(projectRoot), "active.json"), "utf8");
22853
+ const raw = await fs5.readFile(path26.join(designProjectDir(projectRoot), "active.json"), "utf8");
22301
22854
  const parsed = JSON.parse(raw);
22302
22855
  if (parsed && typeof parsed.kit === "string") {
22303
22856
  return {
@@ -22330,11 +22883,11 @@ function applyTokenOverrides(tokens, overrides) {
22330
22883
  }
22331
22884
  async function ensureDesignDir(projectRoot) {
22332
22885
  const dir = designProjectDir(projectRoot);
22333
- await fs4.mkdir(dir, { recursive: true });
22334
- const gi = path24.join(dir, ".gitignore");
22335
- if (!existsSync4(gi)) {
22886
+ await fs5.mkdir(dir, { recursive: true });
22887
+ const gi = path26.join(dir, ".gitignore");
22888
+ if (!existsSync5(gi)) {
22336
22889
  try {
22337
- await fs4.writeFile(gi, "*\n");
22890
+ await fs5.writeFile(gi, "*\n");
22338
22891
  } catch {
22339
22892
  }
22340
22893
  }
@@ -22345,11 +22898,11 @@ async function recordKitChoice(projectRoot, kit, stack, source, isoTime, overrid
22345
22898
  const dir = await ensureDesignDir(projectRoot);
22346
22899
  const record = { kit, stack: stack ?? null };
22347
22900
  if (overrides && Object.keys(overrides).length > 0) record.overrides = overrides;
22348
- await fs4.writeFile(path24.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22901
+ await fs5.writeFile(path26.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22349
22902
  `);
22350
22903
  const line = `- ${isoTime} \xB7 kit=${kit}${stack ? ` stack=${stack}` : ""} \xB7 via=${source}
22351
22904
  `;
22352
- await fs4.appendFile(path24.join(dir, "decisions.md"), line);
22905
+ await fs5.appendFile(path26.join(dir, "decisions.md"), line);
22353
22906
  } catch {
22354
22907
  }
22355
22908
  }
@@ -22365,11 +22918,11 @@ async function recordOverrides(projectRoot, patch, isoTime) {
22365
22918
  const dir = await ensureDesignDir(projectRoot);
22366
22919
  const record = { kit: active.kit, stack: active.stack ?? null };
22367
22920
  if (Object.keys(merged).length > 0) record.overrides = merged;
22368
- await fs4.writeFile(path24.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22921
+ await fs5.writeFile(path26.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22369
22922
  `);
22370
22923
  const keys = Object.keys(patch).join(",");
22371
- await fs4.appendFile(
22372
- path24.join(dir, "decisions.md"),
22924
+ await fs5.appendFile(
22925
+ path26.join(dir, "decisions.md"),
22373
22926
  `- ${isoTime} \xB7 kit=${active.kit} \xB7 override=${keys} \xB7 via=set
22374
22927
  `
22375
22928
  );
@@ -22379,7 +22932,7 @@ async function recordOverrides(projectRoot, patch, isoTime) {
22379
22932
  }
22380
22933
  async function clearPersistedActiveKit(projectRoot) {
22381
22934
  try {
22382
- await fs4.rm(path24.join(designProjectDir(projectRoot), "active.json"), { force: true });
22935
+ await fs5.rm(path26.join(designProjectDir(projectRoot), "active.json"), { force: true });
22383
22936
  } catch {
22384
22937
  }
22385
22938
  }
@@ -22519,12 +23072,12 @@ function verifyFiles(tokens, files) {
22519
23072
  const violations = [];
22520
23073
  let onPalette = 0;
22521
23074
  let offPalette = 0;
22522
- for (const { path: path42, text } of files) {
23075
+ for (const { path: path44, text } of files) {
22523
23076
  const lines = text.split("\n");
22524
23077
  lines.forEach((lineText, i) => {
22525
23078
  const lineNo = i + 1;
22526
23079
  const flag = (snippet, reason, axis = "color") => {
22527
- violations.push({ file: path42, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
23080
+ violations.push({ file: path44, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
22528
23081
  };
22529
23082
  for (const re of [HEX_RE, FUNC_COLOR_RE]) {
22530
23083
  re.lastIndex = 0;
@@ -22591,14 +23144,14 @@ var SKIP_DIR = /* @__PURE__ */ new Set([
22591
23144
  ".design"
22592
23145
  ]);
22593
23146
  async function walkUiFiles(root, max = 200) {
22594
- const { default: fs15 } = await import("node:fs/promises");
23147
+ const { default: fs16 } = await import("node:fs/promises");
22595
23148
  const { default: nodePath } = await import("node:path");
22596
23149
  const found = [];
22597
23150
  async function rec(dir, depth) {
22598
23151
  if (found.length >= max || depth > 8) return;
22599
23152
  let entries;
22600
23153
  try {
22601
- entries = await fs15.readdir(dir, { withFileTypes: true });
23154
+ entries = await fs16.readdir(dir, { withFileTypes: true });
22602
23155
  } catch {
22603
23156
  return;
22604
23157
  }
@@ -22616,13 +23169,13 @@ async function walkUiFiles(root, max = 200) {
22616
23169
  return found;
22617
23170
  }
22618
23171
  async function runDesignVerify(projectRoot, tokens, explicitFiles) {
22619
- const { default: fs15 } = await import("node:fs/promises");
23172
+ const { default: fs16 } = await import("node:fs/promises");
22620
23173
  const { default: nodePath } = await import("node:path");
22621
23174
  const abs = explicitFiles && explicitFiles.length > 0 ? explicitFiles.map((f) => nodePath.isAbsolute(f) ? f : nodePath.join(projectRoot, f)) : await walkUiFiles(projectRoot);
22622
23175
  const files = [];
22623
23176
  for (const a of abs) {
22624
23177
  try {
22625
- files.push({ path: nodePath.relative(projectRoot, a), text: await fs15.readFile(a, "utf8") });
23178
+ files.push({ path: nodePath.relative(projectRoot, a), text: await fs16.readFile(a, "utf8") });
22626
23179
  } catch {
22627
23180
  }
22628
23181
  }
@@ -22753,10 +23306,10 @@ function makeDesignVerifyToolCallMiddleware() {
22753
23306
  const p = typeof input?.path === "string" ? input.path : "";
22754
23307
  if (!p || !detectFrontendFile(p)) return out;
22755
23308
  const ctx = out.ctx;
22756
- const { default: fs15 } = await import("node:fs/promises");
23309
+ const { default: fs16 } = await import("node:fs/promises");
22757
23310
  const { default: nodePath } = await import("node:path");
22758
23311
  const abs = nodePath.isAbsolute(p) ? p : nodePath.join(ctx.projectRoot, p);
22759
- const text = await fs15.readFile(abs, "utf8").catch(() => "");
23312
+ const text = await fs16.readFile(abs, "utf8").catch(() => "");
22760
23313
  if (!text) return out;
22761
23314
  const loader = getDesignKitLoader(ctx.projectRoot);
22762
23315
  const rawTokens = await loader.readTokens(state.activeKit);
@@ -23205,11 +23758,11 @@ function buildRecoveryStrategies(opts) {
23205
23758
  async attempt(err, ctx) {
23206
23759
  if (!(err instanceof ProviderError) || err.kind !== "rate_limit") return null;
23207
23760
  const delayMs = err.body?.retryAfterMs ?? 5e3;
23208
- const delay = Math.min(6e4, Math.max(1e3, delayMs));
23761
+ const delay2 = Math.min(6e4, Math.max(1e3, delayMs));
23209
23762
  if (ctx?.signal) {
23210
- await new Promise((resolve19) => {
23763
+ await new Promise((resolve21) => {
23211
23764
  if (ctx.signal.aborted) {
23212
- resolve19();
23765
+ resolve21();
23213
23766
  return;
23214
23767
  }
23215
23768
  let settled = false;
@@ -23220,13 +23773,13 @@ function buildRecoveryStrategies(opts) {
23220
23773
  settled = true;
23221
23774
  if (timer !== void 0) clearTimeout(timer);
23222
23775
  ctx.signal.removeEventListener("abort", onAbort);
23223
- resolve19();
23776
+ resolve21();
23224
23777
  };
23225
- timer = setTimeout(finish, delay);
23778
+ timer = setTimeout(finish, delay2);
23226
23779
  ctx.signal.addEventListener("abort", onAbort, { once: true });
23227
23780
  });
23228
23781
  } else {
23229
- await new Promise((r) => setTimeout(r, delay));
23782
+ await new Promise((r) => setTimeout(r, delay2));
23230
23783
  }
23231
23784
  return { action: "retry", reason: "rate_limit_backoff" };
23232
23785
  }
@@ -23856,10 +24409,10 @@ var EternalAutonomyEngine = class {
23856
24409
  if (status === "failure") {
23857
24410
  if (isTransientFailure) {
23858
24411
  this.consecutiveTransientRetries++;
23859
- const delay = this.computeTransientBackoffMs();
23860
- if (delay > 0) {
23861
- emit2({ phase: "sleep", ms: delay });
23862
- await this.sleepInterruptible(delay);
24412
+ const delay2 = this.computeTransientBackoffMs();
24413
+ if (delay2 > 0) {
24414
+ emit2({ phase: "sleep", ms: delay2 });
24415
+ await this.sleepInterruptible(delay2);
23863
24416
  }
23864
24417
  return false;
23865
24418
  }
@@ -24361,7 +24914,7 @@ function buildGoalPreamble(goal, deliverables) {
24361
24914
  }
24362
24915
 
24363
24916
  // src/execution/compaction-summary-cache.ts
24364
- import { createHash as createHash5 } from "node:crypto";
24917
+ import { createHash as createHash6 } from "node:crypto";
24365
24918
 
24366
24919
  // src/utils/lru-cache.ts
24367
24920
  var LruCache = class {
@@ -24499,7 +25052,7 @@ function semanticBlock(block) {
24499
25052
  }
24500
25053
  }
24501
25054
  function compactionSummaryKey(model, prompt, messages) {
24502
- const hash2 = createHash5("sha256");
25055
+ const hash2 = createHash6("sha256");
24503
25056
  hash2.update("wrongstack-compaction-summary-v2\0").update(model).update("\0").update(prompt);
24504
25057
  for (const message of messages) {
24505
25058
  hash2.update("\0").update(message.role).update("\0");
@@ -24713,7 +25266,7 @@ ${summaryText}` : summaryText;
24713
25266
  };
24714
25267
 
24715
25268
  // src/execution/parallel-eternal-engine.ts
24716
- import { randomUUID as randomUUID12 } from "node:crypto";
25269
+ import { randomUUID as randomUUID13 } from "node:crypto";
24717
25270
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
24718
25271
  var ParallelEternalEngine = class {
24719
25272
  constructor(opts) {
@@ -24790,7 +25343,7 @@ var ParallelEternalEngine = class {
24790
25343
  this.state = "running";
24791
25344
  await this.persistState("running");
24792
25345
  const config = {
24793
- coordinatorId: `parallel-${randomUUID12().slice(0, 8)}`,
25346
+ coordinatorId: `parallel-${randomUUID13().slice(0, 8)}`,
24794
25347
  maxConcurrent: this.slots,
24795
25348
  doneCondition: { type: "all_tasks_done" }
24796
25349
  };
@@ -24844,7 +25397,7 @@ var ParallelEternalEngine = class {
24844
25397
  }
24845
25398
  if (!this.coordinator) {
24846
25399
  const config = {
24847
- coordinatorId: `parallel-${randomUUID12().slice(0, 8)}`,
25400
+ coordinatorId: `parallel-${randomUUID13().slice(0, 8)}`,
24848
25401
  maxConcurrent: this.slots,
24849
25402
  doneCondition: { type: "all_tasks_done" }
24850
25403
  };
@@ -24930,7 +25483,7 @@ ${recentJournal}` : "No prior iterations.",
24930
25483
  const task = expectDefined(tasks[i]);
24931
25484
  const route = routes[i] ?? null;
24932
25485
  const subagentId = `parallel-${this.iterations}-${i}`;
24933
- const taskId = randomUUID12();
25486
+ const taskId = randomUUID13();
24934
25487
  const personaLine = route ? `Acting agent: ${route.definition.config.name} \u2014 ${route.definition.capability.summary}
24935
25488
  ` : "";
24936
25489
  const spec = {
@@ -25145,7 +25698,7 @@ ${lastFew}` : "No prior iterations.",
25145
25698
  };
25146
25699
 
25147
25700
  // src/core/streaming-response-builder.ts
25148
- import { randomUUID as randomUUID13 } from "node:crypto";
25701
+ import { randomUUID as randomUUID14 } from "node:crypto";
25149
25702
  var STREAM_DRAIN_TIMEOUT_MS = 500;
25150
25703
  function buildResponse(state) {
25151
25704
  const content = [];
@@ -25205,7 +25758,7 @@ function handleContentBlockStart(state, ev) {
25205
25758
  state.textBuffers.push("");
25206
25759
  state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
25207
25760
  } else if (kind === "tool_use") {
25208
- const id = ev.id ?? randomUUID13();
25761
+ const id = ev.id ?? randomUUID14();
25209
25762
  state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
25210
25763
  state.blockOrder.push({ kind: "tool", id });
25211
25764
  state.currentTextIndex = -1;
@@ -25414,8 +25967,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
25414
25967
  });
25415
25968
  await Promise.race([
25416
25969
  drainPromise,
25417
- new Promise((resolve19) => {
25418
- drainTimer = setTimeout(resolve19, STREAM_DRAIN_TIMEOUT_MS);
25970
+ new Promise((resolve21) => {
25971
+ drainTimer = setTimeout(resolve21, STREAM_DRAIN_TIMEOUT_MS);
25419
25972
  })
25420
25973
  ]);
25421
25974
  } finally {
@@ -25439,7 +25992,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
25439
25992
  }
25440
25993
 
25441
25994
  // src/chronicle/prompt-manifest.ts
25442
- import { createHash as createHash6 } from "node:crypto";
25995
+ import { createHash as createHash7 } from "node:crypto";
25443
25996
  function createChroniclePromptManifest(request) {
25444
25997
  const roleCounts = {}, blockCounts = {};
25445
25998
  const messages = request.messages.map((message, index) => messageSummary(message, index, roleCounts, blockCounts));
@@ -25526,11 +26079,11 @@ function countBlocks(blocks) {
25526
26079
  return counts;
25527
26080
  }
25528
26081
  function hash(value) {
25529
- return createHash6("sha256").update(value).digest("hex");
26082
+ return createHash7("sha256").update(value).digest("hex");
25530
26083
  }
25531
26084
  function contentIdentity(content) {
25532
26085
  if (typeof content === "string") return { hash: hash(content), bytes: Buffer.byteLength(content) };
25533
- const digest = createHash6("sha256");
26086
+ const digest = createHash7("sha256");
25534
26087
  let bytes = 0;
25535
26088
  const add = (value) => {
25536
26089
  if (!value) return;
@@ -25612,7 +26165,7 @@ function replaceAllCaseInsensitive(haystack, needle, replacement) {
25612
26165
  }
25613
26166
 
25614
26167
  // src/core/provider-runner.ts
25615
- import { randomUUID as randomUUID14 } from "node:crypto";
26168
+ import { randomUUID as randomUUID15 } from "node:crypto";
25616
26169
  function scrubProviderBody(body) {
25617
26170
  if (!body) return void 0;
25618
26171
  return {
@@ -25634,11 +26187,11 @@ function providerLogCtx(p, r) {
25634
26187
  }
25635
26188
  async function runProviderWithRetry(opts) {
25636
26189
  const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
25637
- const logicalRequestId = randomUUID14();
26190
+ const logicalRequestId = randomUUID15();
25638
26191
  const promptManifest = createChroniclePromptManifest(request);
25639
26192
  let attempt = 0;
25640
26193
  for (; ; ) {
25641
- const attemptId = randomUUID14();
26194
+ const attemptId = randomUUID15();
25642
26195
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
25643
26196
  const startedNs = process.hrtime.bigint();
25644
26197
  const correlation = {
@@ -25711,7 +26264,7 @@ async function runProviderWithRetry(opts) {
25711
26264
  const description = scrubErrorText(
25712
26265
  isProviderErr ? err.describe() : errAsErr.message
25713
26266
  );
25714
- const delay = canRetry ? Math.round(retry.delayMs(attempt, isProviderErr ? err : errAsErr)) : void 0;
26267
+ const delay2 = canRetry ? Math.round(retry.delayMs(attempt, isProviderErr ? err : errAsErr)) : void 0;
25715
26268
  events.emit("provider.attempt.failed", {
25716
26269
  ...correlation,
25717
26270
  startedAt,
@@ -25722,7 +26275,7 @@ async function runProviderWithRetry(opts) {
25722
26275
  description,
25723
26276
  retryable: canRetry,
25724
26277
  retryScheduled: canRetry,
25725
- ...delay !== void 0 ? { retryDelayMs: delay } : {},
26278
+ ...delay2 !== void 0 ? { retryDelayMs: delay2 } : {},
25726
26279
  ...providerErrorBody?.requestId ? { providerRequestId: providerErrorBody.requestId } : {},
25727
26280
  ...providerErrorBody ? { errorBody: providerErrorBody } : {}
25728
26281
  });
@@ -25748,11 +26301,11 @@ async function runProviderWithRetry(opts) {
25748
26301
  }
25749
26302
  const attemptNum = attempt + 1;
25750
26303
  const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
25751
- logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
26304
+ logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay2}ms \u2014 ${description}`, {
25752
26305
  ...providerLogCtx(provider, request),
25753
26306
  attempt: attemptNum,
25754
26307
  maxAttempts,
25755
- delayMs: delay,
26308
+ delayMs: delay2,
25756
26309
  errorDescription: description,
25757
26310
  status: isProviderErr ? err.status : void 0,
25758
26311
  errorBody: providerErrorBody
@@ -25761,12 +26314,12 @@ async function runProviderWithRetry(opts) {
25761
26314
  sessionId: resolveEventSessionId(ctx),
25762
26315
  providerId: isProviderErr ? err.providerId : provider.id,
25763
26316
  attempt: attemptNum,
25764
- delayMs: delay,
26317
+ delayMs: delay2,
25765
26318
  status: isProviderErr ? err.status : 0,
25766
26319
  description,
25767
26320
  ...providerErrorBody ? { errorBody: providerErrorBody } : {}
25768
26321
  });
25769
- await new Promise((resolve19, reject) => {
26322
+ await new Promise((resolve21, reject) => {
25770
26323
  let settled = false;
25771
26324
  const cleanup = () => {
25772
26325
  clearTimeout(t);
@@ -25782,8 +26335,8 @@ async function runProviderWithRetry(opts) {
25782
26335
  if (settled) return;
25783
26336
  settled = true;
25784
26337
  cleanup();
25785
- resolve19();
25786
- }, delay);
26338
+ resolve21();
26339
+ }, delay2);
25787
26340
  if (signal.aborted) {
25788
26341
  onAbort();
25789
26342
  return;
@@ -26406,8 +26959,8 @@ Summarize the following message range:`;
26406
26959
  };
26407
26960
 
26408
26961
  // src/execution/skill-loader.ts
26409
- import * as fs5 from "node:fs/promises";
26410
- import * as path25 from "node:path";
26962
+ import * as fs6 from "node:fs/promises";
26963
+ import * as path27 from "node:path";
26411
26964
 
26412
26965
  // src/skills/foreign-sources.ts
26413
26966
  var FOREIGN_SKILL_TOOLS = [
@@ -26471,7 +27024,7 @@ async function entryIsDirectory(dir, entry) {
26471
27024
  if (entry.isDirectory()) return true;
26472
27025
  if (entry.isSymbolicLink()) {
26473
27026
  try {
26474
- return (await fs5.stat(path25.join(dir, entry.name))).isDirectory();
27027
+ return (await fs6.stat(path27.join(dir, entry.name))).isDirectory();
26475
27028
  } catch {
26476
27029
  return false;
26477
27030
  }
@@ -26495,7 +27048,7 @@ var DefaultSkillLoader = class {
26495
27048
  for (const tool of FOREIGN_SKILL_TOOLS) {
26496
27049
  if (!foreignIds.includes(tool.id)) continue;
26497
27050
  dirs.push({
26498
- dir: path25.join(root, "." + tool.id, tool.subdir),
27051
+ dir: path27.join(root, "." + tool.id, tool.subdir),
26499
27052
  source: "foreign",
26500
27053
  originTool: tool.id
26501
27054
  });
@@ -26520,15 +27073,15 @@ var DefaultSkillLoader = class {
26520
27073
  this.shadowed = [];
26521
27074
  for (const { dir, source, originTool } of this.dirs) {
26522
27075
  try {
26523
- const entries = (await fs5.readdir(dir, { withFileTypes: true })).sort(
27076
+ const entries = (await fs6.readdir(dir, { withFileTypes: true })).sort(
26524
27077
  (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
26525
27078
  );
26526
27079
  for (const e of entries) {
26527
27080
  if (!await entryIsDirectory(dir, e)) continue;
26528
- const skillFile = path25.join(dir, e.name, "SKILL.md");
27081
+ const skillFile = path27.join(dir, e.name, "SKILL.md");
26529
27082
  let raw;
26530
27083
  try {
26531
- raw = await fs5.readFile(skillFile, "utf8");
27084
+ raw = await fs6.readFile(skillFile, "utf8");
26532
27085
  } catch {
26533
27086
  continue;
26534
27087
  }
@@ -26647,7 +27200,7 @@ var DefaultSkillLoader = class {
26647
27200
  if (cached !== void 0) return cached;
26648
27201
  const m = await this.find(name);
26649
27202
  if (!m) throw new Error(`Skill "${name}" not found`);
26650
- const body = await fs5.readFile(m.path, "utf8");
27203
+ const body = await fs6.readFile(m.path, "utf8");
26651
27204
  this.bodyCache.set(key, body);
26652
27205
  return body;
26653
27206
  }
@@ -26657,12 +27210,12 @@ var DefaultSkillLoader = class {
26657
27210
  if (cached !== void 0) return cached;
26658
27211
  const m = await this.find(name);
26659
27212
  if (!m) throw new Error(`Skill "${name}" not found`);
26660
- const savePath = path25.join(path25.dirname(m.path), "SKILL.save.md");
27213
+ const savePath = path27.join(path27.dirname(m.path), "SKILL.save.md");
26661
27214
  let result;
26662
27215
  try {
26663
- result = await fs5.readFile(savePath, "utf8");
27216
+ result = await fs6.readFile(savePath, "utf8");
26664
27217
  } catch {
26665
- const full = await fs5.readFile(m.path, "utf8");
27218
+ const full = await fs6.readFile(m.path, "utf8");
26666
27219
  const body = stripFrontmatter(full);
26667
27220
  const compact = compactSkillBody(body);
26668
27221
  if (compact) {
@@ -26690,12 +27243,12 @@ function parseDescriptionFromText(desc) {
26690
27243
  }
26691
27244
 
26692
27245
  // src/execution/prompt-loader.ts
26693
- import * as fs7 from "node:fs/promises";
26694
- import * as path27 from "node:path";
27246
+ import * as fs8 from "node:fs/promises";
27247
+ import * as path29 from "node:path";
26695
27248
 
26696
27249
  // src/storage/prompt-store.ts
26697
- import * as fs6 from "node:fs/promises";
26698
- import * as path26 from "node:path";
27250
+ import * as fs7 from "node:fs/promises";
27251
+ import * as path28 from "node:path";
26699
27252
  var SCHEMA_VERSION = 2;
26700
27253
  function migratePromptEntry(raw) {
26701
27254
  if (!raw || typeof raw !== "object") return null;
@@ -26756,12 +27309,12 @@ var DefaultPromptStore = class {
26756
27309
  await ensureDir(this.dir);
26757
27310
  const entries = [];
26758
27311
  try {
26759
- const files = await fs6.readdir(this.dir);
27312
+ const files = await fs7.readdir(this.dir);
26760
27313
  for (const file of files) {
26761
27314
  if (!file.endsWith(".json")) continue;
26762
27315
  try {
26763
27316
  const raw = JSON.parse(
26764
- await fs6.readFile(path26.join(this.dir, file), "utf8")
27317
+ await fs7.readFile(path28.join(this.dir, file), "utf8")
26765
27318
  );
26766
27319
  const migrated = migratePromptEntry(raw.entry);
26767
27320
  if (migrated) entries.push(migrated);
@@ -26775,9 +27328,9 @@ var DefaultPromptStore = class {
26775
27328
  );
26776
27329
  }
26777
27330
  async get(id) {
26778
- const file = path26.join(this.dir, `${id}.json`);
27331
+ const file = path28.join(this.dir, `${id}.json`);
26779
27332
  try {
26780
- const raw = JSON.parse(await fs6.readFile(file, "utf8"));
27333
+ const raw = JSON.parse(await fs7.readFile(file, "utf8"));
26781
27334
  return migratePromptEntry(raw.entry);
26782
27335
  } catch {
26783
27336
  return null;
@@ -26785,14 +27338,14 @@ var DefaultPromptStore = class {
26785
27338
  }
26786
27339
  async save(entry) {
26787
27340
  await ensureDir(this.dir);
26788
- const file = path26.join(this.dir, `${entry.id}.json`);
27341
+ const file = path28.join(this.dir, `${entry.id}.json`);
26789
27342
  const raw = { version: SCHEMA_VERSION, entry };
26790
27343
  await atomicWrite(file, JSON.stringify(raw, null, 2));
26791
27344
  }
26792
27345
  async delete(id) {
26793
- const file = path26.join(this.dir, `${id}.json`);
27346
+ const file = path28.join(this.dir, `${id}.json`);
26794
27347
  try {
26795
- await fs6.unlink(file);
27348
+ await fs7.unlink(file);
26796
27349
  return true;
26797
27350
  } catch {
26798
27351
  return false;
@@ -26879,7 +27432,7 @@ var DefaultPromptLoader = class {
26879
27432
  constructor(opts) {
26880
27433
  this.projectStore = typeof opts.paths.inProjectPrompts === "string" ? new DefaultPromptStore(opts.paths.inProjectPrompts) : void 0;
26881
27434
  this.userStore = typeof opts.paths.globalPrompts === "string" ? new DefaultPromptStore(opts.paths.globalPrompts) : void 0;
26882
- this.builtinDir = opts.bundledDir ? path27.join(opts.bundledDir, "prompts") : void 0;
27435
+ this.builtinDir = opts.bundledDir ? path29.join(opts.bundledDir, "prompts") : void 0;
26883
27436
  }
26884
27437
  async list() {
26885
27438
  if (this.cache) return this.cache;
@@ -26992,7 +27545,7 @@ var DefaultPromptLoader = class {
26992
27545
  const files = await walkJson(dir);
26993
27546
  for (const file of files) {
26994
27547
  try {
26995
- const parsed = JSON.parse(await fs7.readFile(file, "utf8"));
27548
+ const parsed = JSON.parse(await fs8.readFile(file, "utf8"));
26996
27549
  const migrated = migratePromptEntry(parsed);
26997
27550
  if (migrated) out.push({ ...migrated, source: "builtin" });
26998
27551
  } catch {
@@ -27006,12 +27559,12 @@ async function walkJson(dir) {
27006
27559
  const out = [];
27007
27560
  let entries;
27008
27561
  try {
27009
- entries = await fs7.readdir(dir, { withFileTypes: true });
27562
+ entries = await fs8.readdir(dir, { withFileTypes: true });
27010
27563
  } catch {
27011
27564
  return out;
27012
27565
  }
27013
27566
  for (const e of entries) {
27014
- const full = path27.join(dir, e.name);
27567
+ const full = path29.join(dir, e.name);
27015
27568
  if (e.isDirectory()) {
27016
27569
  out.push(...await walkJson(full));
27017
27570
  } else if (e.name.endsWith(".json") && e.name !== "index.json" && e.name !== "schema.json") {
@@ -27198,9 +27751,9 @@ function readPolicy(ctx) {
27198
27751
  }
27199
27752
 
27200
27753
  // src/execution/tool-executor.ts
27201
- import { randomUUID as randomUUID16 } from "node:crypto";
27202
- import * as fs9 from "node:fs/promises";
27203
- import * as path30 from "node:path";
27754
+ import { randomUUID as randomUUID17 } from "node:crypto";
27755
+ import * as fs10 from "node:fs/promises";
27756
+ import * as path32 from "node:path";
27204
27757
  import { isDeepStrictEqual } from "node:util";
27205
27758
 
27206
27759
  // src/observability/process-telemetry.ts
@@ -27212,7 +27765,7 @@ function runWithProcessTelemetry(context, run) {
27212
27765
 
27213
27766
  // src/security/kanban-boundary.ts
27214
27767
  import { realpath as realpath3 } from "node:fs/promises";
27215
- import * as path28 from "node:path";
27768
+ import * as path30 from "node:path";
27216
27769
  import {
27217
27770
  evaluateKanbanBoundaryOpaque,
27218
27771
  evaluateKanbanBoundaryPath,
@@ -27301,10 +27854,10 @@ function resolveKanbanIdentity(ctx) {
27301
27854
  async function extractCandidatePaths(toolName, input, ctx) {
27302
27855
  if (toolName === "patch" && typeof input["patch"] === "string") {
27303
27856
  const directoryInput = stringValue(input["directory"]) ?? ctx.workingDir;
27304
- const directory = path28.isAbsolute(directoryInput) ? directoryInput : path28.resolve(ctx.workingDir, directoryInput);
27857
+ const directory = path30.isAbsolute(directoryInput) ? directoryInput : path30.resolve(ctx.workingDir, directoryInput);
27305
27858
  const strip = Math.max(1, numericValue(input["strip"]) ?? 1);
27306
27859
  const targets = extractPatchTargets(input["patch"], strip).map(
27307
- (target) => relativeToProject(path28.resolve(directory, target), ctx.projectRoot)
27860
+ (target) => relativeToProject(path30.resolve(directory, target), ctx.projectRoot)
27308
27861
  );
27309
27862
  return Promise.all(targets.map((target) => canonicalizeCandidatePath(target, ctx)));
27310
27863
  }
@@ -27314,7 +27867,7 @@ async function extractCandidatePaths(toolName, input, ctx) {
27314
27867
  collectPathValues(input, values, pathKeys);
27315
27868
  if (toolName === "scaffold" && typeof input["name"] === "string") {
27316
27869
  const cwd = stringValue(input["cwd"]) ?? ctx.workingDir;
27317
- values.push(path28.join(cwd, input["name"]));
27870
+ values.push(path30.join(cwd, input["name"]));
27318
27871
  }
27319
27872
  const candidates = [
27320
27873
  ...new Set(values.flatMap(splitPathList).map((value) => resolveInputPath(value, ctx)))
@@ -27322,21 +27875,21 @@ async function extractCandidatePaths(toolName, input, ctx) {
27322
27875
  return Promise.all(candidates.map((candidate) => canonicalizeCandidatePath(candidate, ctx)));
27323
27876
  }
27324
27877
  async function canonicalizeCandidatePath(candidate, ctx) {
27325
- if (path28.isAbsolute(candidate)) return candidate;
27326
- const absolute = path28.resolve(ctx.projectRoot, candidate);
27878
+ if (path30.isAbsolute(candidate)) return candidate;
27879
+ const absolute = path30.resolve(ctx.projectRoot, candidate);
27327
27880
  const canonicalRoot = await realpath3(ctx.projectRoot).catch(() => ctx.projectRoot);
27328
27881
  let probe = absolute;
27329
27882
  const missingSegments = [];
27330
27883
  while (true) {
27331
27884
  try {
27332
- const canonical = path28.join(await realpath3(probe), ...missingSegments);
27885
+ const canonical = path30.join(await realpath3(probe), ...missingSegments);
27333
27886
  return relativeToProject(canonical, canonicalRoot);
27334
27887
  } catch (cause) {
27335
27888
  const code = cause.code;
27336
27889
  if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
27337
- const parent = path28.dirname(probe);
27890
+ const parent = path30.dirname(probe);
27338
27891
  if (parent === probe) return absolute;
27339
- missingSegments.unshift(path28.basename(probe));
27892
+ missingSegments.unshift(path30.basename(probe));
27340
27893
  probe = parent;
27341
27894
  }
27342
27895
  }
@@ -27359,12 +27912,12 @@ function splitPathList(value) {
27359
27912
  return value.split(",").map((item) => item.trim()).filter(Boolean);
27360
27913
  }
27361
27914
  function resolveInputPath(value, ctx) {
27362
- const absolute = path28.isAbsolute(value) ? value : path28.resolve(ctx.workingDir, value);
27915
+ const absolute = path30.isAbsolute(value) ? value : path30.resolve(ctx.workingDir, value);
27363
27916
  return relativeToProject(absolute, ctx.projectRoot);
27364
27917
  }
27365
27918
  function relativeToProject(absolute, projectRoot) {
27366
- const relative8 = path28.relative(projectRoot, absolute).replace(/\\/g, "/");
27367
- return relative8.startsWith("../") || path28.isAbsolute(relative8) ? absolute : relative8 || ".";
27919
+ const relative8 = path30.relative(projectRoot, absolute).replace(/\\/g, "/");
27920
+ return relative8.startsWith("../") || path30.isAbsolute(relative8) ? absolute : relative8 || ".";
27368
27921
  }
27369
27922
  function extractPatchTargets(patchText, strip) {
27370
27923
  const targets = [];
@@ -27387,9 +27940,9 @@ function numericValue(value) {
27387
27940
  var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
27388
27941
 
27389
27942
  // src/execution/tool-executor-support.ts
27390
- import { createHash as createHash7, randomUUID as randomUUID15 } from "node:crypto";
27391
- import * as fs8 from "node:fs/promises";
27392
- import * as path29 from "node:path";
27943
+ import { createHash as createHash8, randomUUID as randomUUID16 } from "node:crypto";
27944
+ import * as fs9 from "node:fs/promises";
27945
+ import * as path31 from "node:path";
27393
27946
 
27394
27947
  // src/types/tool-markers.ts
27395
27948
  var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
@@ -27514,12 +28067,12 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
27514
28067
  return content;
27515
28068
  }
27516
28069
  try {
27517
- const dir = path29.join(wstackGlobalRoot(), "tool-output");
27518
- await fs8.mkdir(dir, { recursive: true });
28070
+ const dir = path31.join(wstackGlobalRoot(), "tool-output");
28071
+ await fs9.mkdir(dir, { recursive: true });
27519
28072
  const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
27520
28073
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27521
- const filePath = path29.join(dir, `${stamp}-${safeTool}-${randomUUID15()}.log`);
27522
- await fs8.writeFile(filePath, content, "utf8");
28074
+ const filePath = path31.join(dir, `${stamp}-${safeTool}-${randomUUID16()}.log`);
28075
+ await fs9.writeFile(filePath, content, "utf8");
27523
28076
  const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
27524
28077
  const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
27525
28078
  const previewBytes = Math.min(
@@ -27544,7 +28097,7 @@ function hashPermissionInput(input, scrubber2) {
27544
28097
  } catch {
27545
28098
  serialized = String(input);
27546
28099
  }
27547
- return createHash7("sha256").update(scrubber2.scrub(serialized), "utf8").digest("hex");
28100
+ return createHash8("sha256").update(scrubber2.scrub(serialized), "utf8").digest("hex");
27548
28101
  }
27549
28102
  function sliceUtf8Prefix(text, maxBytes) {
27550
28103
  if (maxBytes <= 0) return "";
@@ -28059,18 +28612,18 @@ ${errorDetails}`,
28059
28612
  if (this.opts.confirmAwaiter) {
28060
28613
  const awaiter = this.opts.confirmAwaiter;
28061
28614
  const choice = await new Promise(
28062
- (resolve19, reject) => {
28615
+ (resolve21, reject) => {
28063
28616
  const signal = ctx.signal;
28064
- const onAbort = () => resolve19("abort");
28617
+ const onAbort = () => resolve21("abort");
28065
28618
  if (signal.aborted) {
28066
- resolve19("abort");
28619
+ resolve21("abort");
28067
28620
  return;
28068
28621
  }
28069
28622
  signal.addEventListener("abort", onAbort, { once: true });
28070
28623
  awaiter(tool, use.input, use.id, suggestedPattern).then(
28071
28624
  (c) => {
28072
28625
  signal.removeEventListener("abort", onAbort);
28073
- resolve19(c);
28626
+ resolve21(c);
28074
28627
  },
28075
28628
  (e) => {
28076
28629
  signal.removeEventListener("abort", onAbort);
@@ -28116,10 +28669,10 @@ ${errorDetails}`,
28116
28669
  const inputPath = use.input && typeof use.input === "object" ? use.input.path : void 0;
28117
28670
  const caps = tool.capabilities ?? [];
28118
28671
  const hasFileCapability = caps.includes("fs.read") || caps.includes("fs.write");
28119
- const absPath = hasFileCapability && typeof inputPath === "string" ? path30.isAbsolute(inputPath) ? inputPath : path30.resolve(ctx.projectRoot, inputPath) : void 0;
28672
+ const absPath = hasFileCapability && typeof inputPath === "string" ? path32.isAbsolute(inputPath) ? inputPath : path32.resolve(ctx.projectRoot, inputPath) : void 0;
28120
28673
  let writeTargetExisted;
28121
28674
  if (tool.name === "write" && caps.includes("fs.write") && absPath) {
28122
- writeTargetExisted = await fs9.stat(absPath).then(
28675
+ writeTargetExisted = await fs10.stat(absPath).then(
28123
28676
  (stat15) => stat15.isFile(),
28124
28677
  (error2) => error2.code === "ENOENT" ? false : void 0
28125
28678
  );
@@ -28281,7 +28834,7 @@ ${post.additionalContext}`;
28281
28834
  const bridge = async (toolName, input) => {
28282
28835
  const nestedUse = {
28283
28836
  type: "tool_use",
28284
- id: `nested-${randomUUID16()}`,
28837
+ id: `nested-${randomUUID17()}`,
28285
28838
  name: toolName,
28286
28839
  input
28287
28840
  };
@@ -28404,7 +28957,7 @@ ${preToolContext.text}` : preToolContext.text;
28404
28957
  ]);
28405
28958
  let output;
28406
28959
  const execute = () => typeof tool.executeStream === "function" ? this.runStreamedTool(tool, input, ctx, combined, toolUseId) : (async () => tool.execute(input, ctx, { signal: combined }))();
28407
- const telemetryToolCallId = toolUseId ?? `nested-${randomUUID16()}`;
28960
+ const telemetryToolCallId = toolUseId ?? `nested-${randomUUID17()}`;
28408
28961
  const toolPromise = this.opts.events ? runWithNetworkTelemetry(
28409
28962
  {
28410
28963
  events: this.opts.events,
@@ -28430,7 +28983,7 @@ ${preToolContext.text}` : preToolContext.text;
28430
28983
  toolPromise.catch(() => {
28431
28984
  });
28432
28985
  try {
28433
- output = await new Promise((resolve19, reject) => {
28986
+ output = await new Promise((resolve21, reject) => {
28434
28987
  const onAbort = () => {
28435
28988
  setTimeout(() => reject(abortReasonToError(combined.reason)), 0);
28436
28989
  };
@@ -28438,7 +28991,7 @@ ${preToolContext.text}` : preToolContext.text;
28438
28991
  toolPromise.then(
28439
28992
  (v) => {
28440
28993
  combined.removeEventListener("abort", onAbort);
28441
- resolve19(v);
28994
+ resolve21(v);
28442
28995
  },
28443
28996
  (e) => {
28444
28997
  combined.removeEventListener("abort", onAbort);
@@ -28991,28 +29544,28 @@ function codexModelMeta(id) {
28991
29544
  }
28992
29545
 
28993
29546
  // src/models/mode-store.ts
28994
- import * as fs10 from "node:fs/promises";
28995
- import * as path32 from "node:path";
29547
+ import * as fs11 from "node:fs/promises";
29548
+ import * as path34 from "node:path";
28996
29549
 
28997
29550
  // src/types/mode-prompts.ts
28998
- import { readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
28999
- import * as path31 from "node:path";
29000
- import { fileURLToPath as fileURLToPath4 } from "node:url";
29551
+ import { readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
29552
+ import * as path33 from "node:path";
29553
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
29001
29554
  function modePrompt(id) {
29002
29555
  for (const dir of modePromptDirCandidates()) {
29003
29556
  try {
29004
- return readFileSync7(path31.join(dir, `${id}.md`), "utf8").trimEnd();
29557
+ return readFileSync8(path33.join(dir, `${id}.md`), "utf8").trimEnd();
29005
29558
  } catch {
29006
29559
  }
29007
29560
  }
29008
29561
  return "";
29009
29562
  }
29010
29563
  function modePromptDirCandidates() {
29011
- const here = path31.dirname(fileURLToPath4(import.meta.url));
29564
+ const here = path33.dirname(fileURLToPath5(import.meta.url));
29012
29565
  const candidates = [
29013
- path31.resolve(here, "../../instructions/modes"),
29014
- path31.resolve(here, "../instructions/modes"),
29015
- path31.resolve(here, "instructions/modes")
29566
+ path33.resolve(here, "../../instructions/modes"),
29567
+ path33.resolve(here, "../instructions/modes"),
29568
+ path33.resolve(here, "instructions/modes")
29016
29569
  ];
29017
29570
  return candidates.sort((a, b) => Number(!isDirectory3(a)) - Number(!isDirectory3(b)));
29018
29571
  }
@@ -29244,8 +29797,8 @@ var DefaultModeStore = class {
29244
29797
  }
29245
29798
  async loadActiveMode() {
29246
29799
  try {
29247
- const configPath = path32.join(this.configDir, "mode.json");
29248
- const content = await fs10.readFile(configPath, "utf8");
29800
+ const configPath = path34.join(this.configDir, "mode.json");
29801
+ const content = await fs11.readFile(configPath, "utf8");
29249
29802
  const data = JSON.parse(content);
29250
29803
  this.activeModeId = data.activeMode ?? null;
29251
29804
  } catch {
@@ -29254,8 +29807,8 @@ var DefaultModeStore = class {
29254
29807
  }
29255
29808
  async saveActiveMode() {
29256
29809
  try {
29257
- await fs10.mkdir(this.configDir, { recursive: true });
29258
- const configPath = path32.join(this.configDir, "mode.json");
29810
+ await fs11.mkdir(this.configDir, { recursive: true });
29811
+ const configPath = path34.join(this.configDir, "mode.json");
29259
29812
  await atomicWrite(
29260
29813
  configPath,
29261
29814
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -29267,14 +29820,14 @@ var DefaultModeStore = class {
29267
29820
  async function loadProjectModes(modesDir) {
29268
29821
  const modes = [];
29269
29822
  try {
29270
- const entries = await fs10.readdir(modesDir);
29823
+ const entries = await fs11.readdir(modesDir);
29271
29824
  for (const entry of entries) {
29272
29825
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
29273
- const filePath = path32.join(modesDir, entry);
29274
- const stat15 = await fs10.stat(filePath);
29826
+ const filePath = path34.join(modesDir, entry);
29827
+ const stat15 = await fs11.stat(filePath);
29275
29828
  if (!stat15.isFile()) continue;
29276
- const content = await fs10.readFile(filePath, "utf8");
29277
- const id = path32.basename(entry, path32.extname(entry));
29829
+ const content = await fs11.readFile(filePath, "utf8");
29830
+ const id = path34.basename(entry, path34.extname(entry));
29278
29831
  modes.push({
29279
29832
  id,
29280
29833
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -29290,8 +29843,8 @@ async function loadProjectModes(modesDir) {
29290
29843
  async function loadUserModes(modesDir) {
29291
29844
  const modes = [];
29292
29845
  try {
29293
- const manifestPath = path32.join(modesDir, "modes.json");
29294
- const content = await fs10.readFile(manifestPath, "utf8");
29846
+ const manifestPath = path34.join(modesDir, "modes.json");
29847
+ const content = await fs11.readFile(manifestPath, "utf8");
29295
29848
  const manifest = JSON.parse(content);
29296
29849
  for (const mode of manifest.modes) {
29297
29850
  modes.push(mode);
@@ -29302,8 +29855,8 @@ async function loadUserModes(modesDir) {
29302
29855
  }
29303
29856
 
29304
29857
  // src/models/models-registry.ts
29305
- import * as fs11 from "node:fs/promises";
29306
- import * as path33 from "node:path";
29858
+ import * as fs12 from "node:fs/promises";
29859
+ import * as path35 from "node:path";
29307
29860
  var DEFAULT_URL = "https://models.dev/api.json";
29308
29861
  var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
29309
29862
  var DEFAULT_TTL_SECONDS = 3 * 3600;
@@ -29380,7 +29933,7 @@ var DefaultModelsRegistry = class {
29380
29933
  this.overlay = opts.overlay;
29381
29934
  this.overlayUrl = opts.overlayUrl;
29382
29935
  this.overlayFile = opts.overlayFile;
29383
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path33.join(path33.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
29936
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path35.join(path35.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
29384
29937
  this.logger = opts.logger ?? noOpLogger;
29385
29938
  }
29386
29939
  async load(opts = {}) {
@@ -29568,7 +30121,7 @@ var DefaultModelsRegistry = class {
29568
30121
  async readOverlayFile() {
29569
30122
  if (!this.overlayFile) return void 0;
29570
30123
  try {
29571
- const raw = await fs11.readFile(this.overlayFile, "utf8");
30124
+ const raw = await fs12.readFile(this.overlayFile, "utf8");
29572
30125
  return JSON.parse(raw);
29573
30126
  } catch {
29574
30127
  return void 0;
@@ -29647,7 +30200,7 @@ var DefaultModelsRegistry = class {
29647
30200
  }
29648
30201
  async readCacheAt(file) {
29649
30202
  try {
29650
- const raw = await fs11.readFile(file, "utf8");
30203
+ const raw = await fs12.readFile(file, "utf8");
29651
30204
  return JSON.parse(raw);
29652
30205
  } catch {
29653
30206
  return void 0;
@@ -29655,7 +30208,7 @@ var DefaultModelsRegistry = class {
29655
30208
  }
29656
30209
  /** Used by `wstack models refresh` to expose where the cache lives. */
29657
30210
  cacheLocation() {
29658
- return path33.resolve(this.cacheFile);
30211
+ return path35.resolve(this.cacheFile);
29659
30212
  }
29660
30213
  };
29661
30214
  var REASONING_EFFORTS = /* @__PURE__ */ new Set([
@@ -30006,9 +30559,9 @@ var DefaultHealthRegistry = class {
30006
30559
  }
30007
30560
  async runOne(check) {
30008
30561
  let timer = null;
30009
- const timeout = new Promise((resolve19) => {
30562
+ const timeout = new Promise((resolve21) => {
30010
30563
  timer = setTimeout(
30011
- () => resolve19({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
30564
+ () => resolve21({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
30012
30565
  this.timeoutMs
30013
30566
  );
30014
30567
  });
@@ -30206,7 +30759,7 @@ async function startMetricsServer(opts) {
30206
30759
  const tls = opts.tls;
30207
30760
  const useHttps = !!(tls?.cert && tls?.key);
30208
30761
  const host = opts.host ?? "127.0.0.1";
30209
- const path42 = opts.path ?? "/metrics";
30762
+ const path44 = opts.path ?? "/metrics";
30210
30763
  const healthPath = opts.healthPath ?? "/healthz";
30211
30764
  const healthRegistry = opts.healthRegistry;
30212
30765
  const token = opts.token;
@@ -30229,7 +30782,7 @@ async function startMetricsServer(opts) {
30229
30782
  return;
30230
30783
  }
30231
30784
  const url = req.url.split("?")[0];
30232
- if (url === path42) {
30785
+ if (url === path44) {
30233
30786
  let body;
30234
30787
  try {
30235
30788
  body = renderPrometheus(opts.sink.snapshot());
@@ -30266,23 +30819,23 @@ async function startMetricsServer(opts) {
30266
30819
  let server;
30267
30820
  if (useHttps && tls) {
30268
30821
  const { createServer } = await import("node:https");
30269
- const { readFileSync: readFileSync9 } = await import("node:fs");
30822
+ const { readFileSync: readFileSync10 } = await import("node:fs");
30270
30823
  server = createServer(
30271
- { cert: readFileSync9(tls.cert), key: readFileSync9(tls.key) },
30824
+ { cert: readFileSync10(tls.cert), key: readFileSync10(tls.key) },
30272
30825
  listener
30273
30826
  );
30274
30827
  } else {
30275
30828
  const { createServer } = await import("node:http");
30276
30829
  server = createServer(listener);
30277
30830
  }
30278
- await new Promise((resolve19, reject) => {
30831
+ await new Promise((resolve21, reject) => {
30279
30832
  const onError = (err) => {
30280
30833
  server.off("listening", onListening);
30281
30834
  reject(err);
30282
30835
  };
30283
30836
  const onListening = () => {
30284
30837
  server.off("error", onError);
30285
- resolve19();
30838
+ resolve21();
30286
30839
  };
30287
30840
  server.once("error", onError);
30288
30841
  server.once("listening", onListening);
@@ -30293,9 +30846,9 @@ async function startMetricsServer(opts) {
30293
30846
  const protocol = useHttps ? "https" : "http";
30294
30847
  return {
30295
30848
  port: boundPort,
30296
- url: `${protocol}://${host}:${boundPort}${path42}`,
30297
- close: () => new Promise((resolve19, reject) => {
30298
- server.close((err) => err ? reject(err) : resolve19());
30849
+ url: `${protocol}://${host}:${boundPort}${path44}`,
30850
+ close: () => new Promise((resolve21, reject) => {
30851
+ server.close((err) => err ? reject(err) : resolve21());
30299
30852
  })
30300
30853
  };
30301
30854
  }
@@ -30565,8 +31118,8 @@ function startOtlpTraceExporter(opts) {
30565
31118
  }
30566
31119
 
30567
31120
  // src/security/permission-policy.ts
30568
- import * as fs12 from "node:fs/promises";
30569
- import * as path36 from "node:path";
31121
+ import * as fs13 from "node:fs/promises";
31122
+ import * as path38 from "node:path";
30570
31123
 
30571
31124
  // src/security/permission-policy-schema.ts
30572
31125
  var TRUST_POLICY_LIMITS = Object.freeze({
@@ -30611,19 +31164,19 @@ var UNSAFE_PROPERTY_NAMES = /* @__PURE__ */ new Set(["__proto__", "prototype", "
30611
31164
  function isRecord5(value) {
30612
31165
  return typeof value === "object" && value !== null && !Array.isArray(value);
30613
31166
  }
30614
- function error(diagnostics, code, path42, message) {
30615
- diagnostics.push({ severity: "error", code, path: path42, message });
31167
+ function error(diagnostics, code, path44, message) {
31168
+ diagnostics.push({ severity: "error", code, path: path44, message });
30616
31169
  }
30617
- function validatePatterns(value, path42, diagnostics) {
31170
+ function validatePatterns(value, path44, diagnostics) {
30618
31171
  if (!Array.isArray(value)) {
30619
- error(diagnostics, "invalid_pattern_list", path42, "must be an array of strings");
31172
+ error(diagnostics, "invalid_pattern_list", path44, "must be an array of strings");
30620
31173
  return void 0;
30621
31174
  }
30622
31175
  if (value.length > TRUST_POLICY_LIMITS.maxPatternsPerRule) {
30623
31176
  error(
30624
31177
  diagnostics,
30625
31178
  "too_many_patterns",
30626
- path42,
31179
+ path44,
30627
31180
  `must contain at most ${TRUST_POLICY_LIMITS.maxPatternsPerRule} patterns`
30628
31181
  );
30629
31182
  return void 0;
@@ -30631,7 +31184,7 @@ function validatePatterns(value, path42, diagnostics) {
30631
31184
  const patterns = [];
30632
31185
  const seen = /* @__PURE__ */ new Set();
30633
31186
  for (const [index, pattern] of value.entries()) {
30634
- const itemPath = `${path42}[${index}]`;
31187
+ const itemPath = `${path44}[${index}]`;
30635
31188
  if (typeof pattern !== "string" || pattern.length === 0 || pattern.length > TRUST_POLICY_LIMITS.maxPatternChars) {
30636
31189
  error(
30637
31190
  diagnostics,
@@ -30735,7 +31288,7 @@ function validateTrustPolicy(value) {
30735
31288
  }
30736
31289
 
30737
31290
  // src/security/yolo-risk.ts
30738
- import * as path34 from "node:path";
31291
+ import * as path36 from "node:path";
30739
31292
  var CATASTROPHIC_PATTERNS = [
30740
31293
  /\b(?:mkfs(?:\.[a-z0-9]+)?|mke2fs|newfs)\b/i,
30741
31294
  // make a filesystem — wipes a partition
@@ -30795,9 +31348,9 @@ function getInputString(input, key) {
30795
31348
  function pathLooksInsideProject(rawPath, projectRoot) {
30796
31349
  if (!projectRoot) return false;
30797
31350
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
30798
- const resolved = path34.resolve(projectRoot, rawPath);
30799
- const relative8 = path34.relative(projectRoot, resolved);
30800
- return !!relative8 && !relative8.startsWith("..") && !path34.isAbsolute(relative8);
31351
+ const resolved = path36.resolve(projectRoot, rawPath);
31352
+ const relative8 = path36.relative(projectRoot, resolved);
31353
+ return !!relative8 && !relative8.startsWith("..") && !path36.isAbsolute(relative8);
30801
31354
  }
30802
31355
  function tokenizeShell(command) {
30803
31356
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -31047,7 +31600,7 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
31047
31600
 
31048
31601
  // src/security/permission-helpers.ts
31049
31602
  import { realpathSync } from "node:fs";
31050
- import * as path35 from "node:path";
31603
+ import * as path37 from "node:path";
31051
31604
  function matchesTrust(patterns, subject) {
31052
31605
  return patterns.includes(subject) || matchAny(patterns, subject);
31053
31606
  }
@@ -31131,24 +31684,24 @@ function realpathOfNearestExisting(p) {
31131
31684
  const tail = [];
31132
31685
  for (; ; ) {
31133
31686
  try {
31134
- return tail.length === 0 ? realpathSync(probe) : path35.join(realpathSync(probe), ...tail);
31687
+ return tail.length === 0 ? realpathSync(probe) : path37.join(realpathSync(probe), ...tail);
31135
31688
  } catch {
31136
- const parent = path35.dirname(probe);
31689
+ const parent = path37.dirname(probe);
31137
31690
  if (parent === probe) return p;
31138
- tail.unshift(path35.basename(probe));
31691
+ tail.unshift(path37.basename(probe));
31139
31692
  probe = parent;
31140
31693
  }
31141
31694
  }
31142
31695
  }
31143
31696
  var agentStateRootRealCache = /* @__PURE__ */ new Map();
31144
31697
  function isInsideAgentStateRoot(absPath) {
31145
- const lexicalRoot = normalizeForCompare(path35.resolve(wstackGlobalRoot()));
31698
+ const lexicalRoot = normalizeForCompare(path37.resolve(wstackGlobalRoot()));
31146
31699
  if (!lexicalRoot) return false;
31147
31700
  const target = normalizeForCompare(absPath);
31148
31701
  if (target === lexicalRoot || target.startsWith(`${lexicalRoot}/`)) return true;
31149
31702
  let realRoot = agentStateRootRealCache.get(lexicalRoot);
31150
31703
  if (realRoot === void 0) {
31151
- realRoot = normalizeForCompare(realpathOfNearestExisting(path35.resolve(wstackGlobalRoot())));
31704
+ realRoot = normalizeForCompare(realpathOfNearestExisting(path37.resolve(wstackGlobalRoot())));
31152
31705
  agentStateRootRealCache.set(lexicalRoot, realRoot);
31153
31706
  }
31154
31707
  if (!realRoot || realRoot === lexicalRoot) return false;
@@ -31157,7 +31710,7 @@ function isInsideAgentStateRoot(absPath) {
31157
31710
  }
31158
31711
  function isProtectedAgentStatePath(absPath) {
31159
31712
  if (!isInsideAgentStateRoot(absPath)) return false;
31160
- return AGENT_STATE_SENSITIVE_BASENAMES.test(path35.basename(normalizeForCompare(absPath)));
31713
+ return AGENT_STATE_SENSITIVE_BASENAMES.test(path37.basename(normalizeForCompare(absPath)));
31161
31714
  }
31162
31715
  function pathLooksSensitive(rawPath) {
31163
31716
  const normalized = stripShellQuotes(rawPath).replace(/\\/g, "/");
@@ -31318,7 +31871,7 @@ var DefaultPermissionPolicy = class {
31318
31871
  if (!hasCapability(tool, ToolCapabilities.FS_WRITE)) return false;
31319
31872
  for (const targetPath of fsWriteTargetPaths(input)) {
31320
31873
  const base = ctx.workingDir ?? ctx.cwd;
31321
- const resolved = base ? path36.resolve(base, targetPath) : path36.resolve(targetPath);
31874
+ const resolved = base ? path38.resolve(base, targetPath) : path38.resolve(targetPath);
31322
31875
  if (isInsideAgentStateRoot(resolved)) return true;
31323
31876
  }
31324
31877
  return false;
@@ -31363,7 +31916,7 @@ var DefaultPermissionPolicy = class {
31363
31916
  this.policyDiagnostics = [];
31364
31917
  this.policyInvalid = false;
31365
31918
  try {
31366
- const raw = await fs12.readFile(this.trustFile, "utf8");
31919
+ const raw = await fs13.readFile(this.trustFile, "utf8");
31367
31920
  const parsed = safeParse(raw);
31368
31921
  if (!parsed.ok) {
31369
31922
  this.policy = {};
@@ -31985,9 +32538,9 @@ var DefaultPermissionPolicy = class {
31985
32538
 
31986
32539
  // src/security/secret-vault.ts
31987
32540
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scryptSync } from "node:crypto";
31988
- import * as fs13 from "node:fs";
32541
+ import * as fs14 from "node:fs";
31989
32542
  import * as fsp22 from "node:fs/promises";
31990
- import * as path37 from "node:path";
32543
+ import * as path39 from "node:path";
31991
32544
 
31992
32545
  // src/types/secret-vault.ts
31993
32546
  var ENCRYPTED_PREFIX_PATTERN = /^enc:v(\d+):/;
@@ -32127,7 +32680,7 @@ function checkKeyFilePermissions(keyFile, opts) {
32127
32680
  if (process.platform === "win32") return;
32128
32681
  const warn = opts?.warn ?? ((msg) => console.warn(msg));
32129
32682
  try {
32130
- const stat15 = fs13.statSync(keyFile);
32683
+ const stat15 = fs14.statSync(keyFile);
32131
32684
  const actualMode = stat15.mode & 511;
32132
32685
  if (actualMode !== KEY_FILE_MODE) {
32133
32686
  void restrictFilePermissions(keyFile, {
@@ -32143,21 +32696,21 @@ function checkKeyFilePermissions(keyFile, opts) {
32143
32696
  }
32144
32697
  function writeKeyFileAtomicSync(keyFile, content) {
32145
32698
  const tmp = `${keyFile}.${randomBytes3(4).toString("hex")}.tmp`;
32146
- const fd = fs13.openSync(tmp, "w", 384);
32699
+ const fd = fs14.openSync(tmp, "w", 384);
32147
32700
  try {
32148
- fs13.writeFileSync(fd, content);
32701
+ fs14.writeFileSync(fd, content);
32149
32702
  try {
32150
- fs13.fsyncSync(fd);
32703
+ fs14.fsyncSync(fd);
32151
32704
  } catch {
32152
32705
  }
32153
32706
  } finally {
32154
- fs13.closeSync(fd);
32707
+ fs14.closeSync(fd);
32155
32708
  }
32156
32709
  try {
32157
- fs13.renameSync(tmp, keyFile);
32710
+ fs14.renameSync(tmp, keyFile);
32158
32711
  } catch (err) {
32159
32712
  try {
32160
- fs13.unlinkSync(tmp);
32713
+ fs14.unlinkSync(tmp);
32161
32714
  } catch {
32162
32715
  }
32163
32716
  throw err;
@@ -32280,7 +32833,7 @@ var DefaultSecretVault = class {
32280
32833
  const oldVersion = this._keyVersion;
32281
32834
  const newKey = randomBytes3(KEY_BYTES);
32282
32835
  const newVersion = oldVersion + 1;
32283
- fs13.mkdirSync(path37.dirname(this.keyFile), { recursive: true });
32836
+ fs14.mkdirSync(path39.dirname(this.keyFile), { recursive: true });
32284
32837
  const passphrase = getVaultPassphrase();
32285
32838
  if (passphrase) {
32286
32839
  writeKeyFileAtomicSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase));
@@ -32320,7 +32873,7 @@ var DefaultSecretVault = class {
32320
32873
  loadOrCreateKey() {
32321
32874
  if (this.key) return this.key;
32322
32875
  try {
32323
- const buf = fs13.readFileSync(this.keyFile);
32876
+ const buf = fs14.readFileSync(this.keyFile);
32324
32877
  if (isWrappedKeyFile(buf)) {
32325
32878
  const { key: key2, version } = unwrapDataKey(buf, this.keyFile);
32326
32879
  this.key = key2;
@@ -32367,17 +32920,17 @@ var DefaultSecretVault = class {
32367
32920
  } catch (err) {
32368
32921
  if (err.code !== "ENOENT") throw err;
32369
32922
  }
32370
- fs13.mkdirSync(path37.dirname(this.keyFile), { recursive: true });
32923
+ fs14.mkdirSync(path39.dirname(this.keyFile), { recursive: true });
32371
32924
  const key = randomBytes3(KEY_BYTES);
32372
32925
  const passphrase = getVaultPassphrase();
32373
32926
  const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
32374
32927
  try {
32375
- fs13.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
32928
+ fs14.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
32376
32929
  this.scheduleKeyHardening();
32377
32930
  } catch (err) {
32378
32931
  if (err.code !== "EEXIST") throw err;
32379
32932
  this.scheduleKeyHardening();
32380
- const buf = fs13.readFileSync(this.keyFile);
32933
+ const buf = fs14.readFileSync(this.keyFile);
32381
32934
  if (isWrappedKeyFile(buf)) {
32382
32935
  const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
32383
32936
  this.key = winnerKey;
@@ -32427,7 +32980,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
32427
32980
  }
32428
32981
  const merged = deepMerge(current, patch ?? {});
32429
32982
  const encrypted = encryptConfigSecrets(merged, vault);
32430
- await fsp22.mkdir(path37.dirname(configPath), { recursive: true });
32983
+ await fsp22.mkdir(path39.dirname(configPath), { recursive: true });
32431
32984
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
32432
32985
  await restrictFilePermissions2(configPath);
32433
32986
  await vault.flushHardening?.();
@@ -32482,7 +33035,7 @@ function walkCount(node, vault, counter) {
32482
33035
  // src/storage/attachment-store.ts
32483
33036
  import { randomBytes as randomBytes4 } from "node:crypto";
32484
33037
  import * as fsp23 from "node:fs/promises";
32485
- import * as path38 from "node:path";
33038
+ import * as path40 from "node:path";
32486
33039
  var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
32487
33040
  var PLACEHOLDER_RE2 = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
32488
33041
  var DefaultAttachmentStore = class {
@@ -32503,7 +33056,7 @@ var DefaultAttachmentStore = class {
32503
33056
  let data = input.data;
32504
33057
  if (this.spoolDir && bytes >= this.spoolThreshold) {
32505
33058
  await fsp23.mkdir(this.spoolDir, { recursive: true });
32506
- spooledPath = path38.join(this.spoolDir, `${id}.bin`);
33059
+ spooledPath = path40.join(this.spoolDir, `${id}.bin`);
32507
33060
  await atomicWrite(spooledPath, input.data, {
32508
33061
  encoding: input.kind === "image" ? "base64" : "utf8"
32509
33062
  });
@@ -32622,7 +33175,7 @@ function mergeAdjacentText(blocks) {
32622
33175
  }
32623
33176
 
32624
33177
  // src/storage/config-loader.ts
32625
- import * as fs14 from "node:fs/promises";
33178
+ import * as fs15 from "node:fs/promises";
32626
33179
 
32627
33180
  // src/storage/config-loader/bootstrap.ts
32628
33181
  var BOOTSTRAP_KEYS = /* @__PURE__ */ new Set(["version", "activeProfile"]);
@@ -33041,8 +33594,8 @@ var IN_PROJECT_DENIED_PATHS = [
33041
33594
  reason: "The other half of the filesystem confinement switch."
33042
33595
  }
33043
33596
  ];
33044
- function deleteNestedPath(target, path42) {
33045
- const segments = path42.split(".");
33597
+ function deleteNestedPath(target, path44) {
33598
+ const segments = path44.split(".");
33046
33599
  const last = segments[segments.length - 1];
33047
33600
  if (last === void 0) return false;
33048
33601
  let cursor = target;
@@ -33099,16 +33652,16 @@ function assertInProjectAllowListComplete() {
33099
33652
  );
33100
33653
  }
33101
33654
  const orphanedPaths = IN_PROJECT_DENIED_PATHS.filter(
33102
- ({ path: path42 }) => !IN_PROJECT_ALLOWED_KEYS.has(path42.split(".")[0] ?? "")
33103
- ).map(({ path: path42 }) => path42);
33655
+ ({ path: path44 }) => !IN_PROJECT_ALLOWED_KEYS.has(path44.split(".")[0] ?? "")
33656
+ ).map(({ path: path44 }) => path44);
33104
33657
  if (orphanedPaths.length > 0) {
33105
33658
  problems.push(
33106
33659
  `IN_PROJECT_DENIED_PATHS entr(ies) whose top-level parent is not allowed: ` + orphanedPaths.join(", ") + ". The parent is already stripped wholesale, so the nested denial is dead \u2014 remove it."
33107
33660
  );
33108
33661
  }
33109
33662
  const malformedPaths = IN_PROJECT_DENIED_PATHS.filter(
33110
- ({ path: path42 }) => path42.split(".").length < 2 || path42.split(".").some((s) => s.length === 0)
33111
- ).map(({ path: path42 }) => path42);
33663
+ ({ path: path44 }) => path44.split(".").length < 2 || path44.split(".").some((s) => s.length === 0)
33664
+ ).map(({ path: path44 }) => path44);
33112
33665
  if (malformedPaths.length > 0) {
33113
33666
  problems.push(
33114
33667
  `IN_PROJECT_DENIED_PATHS entr(ies) are not dotted nested paths: ` + malformedPaths.join(", ") + ". Top-level keys belong in KNOWN_DENIED_IN_PROJECT instead."
@@ -33136,8 +33689,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
33136
33689
  }
33137
33690
  stripped.push(k);
33138
33691
  }
33139
- for (const { path: path42 } of IN_PROJECT_DENIED_PATHS) {
33140
- if (deleteNestedPath(out, path42)) stripped.push(path42);
33692
+ for (const { path: path44 } of IN_PROJECT_DENIED_PATHS) {
33693
+ if (deleteNestedPath(out, path44)) stripped.push(path44);
33141
33694
  }
33142
33695
  if (stripped.length > 0) {
33143
33696
  warn(
@@ -33326,10 +33879,10 @@ function removeLegacySageEngine(config) {
33326
33879
  }
33327
33880
 
33328
33881
  // src/storage/config-loader/path-identity.ts
33329
- import * as path39 from "node:path";
33882
+ import * as path41 from "node:path";
33330
33883
  function samePath(a, b) {
33331
- let ra = path39.resolve(a);
33332
- let rb = path39.resolve(b);
33884
+ let ra = path41.resolve(a);
33885
+ let rb = path41.resolve(b);
33333
33886
  if (process.platform === "win32" || process.platform === "darwin") {
33334
33887
  ra = ra.toLowerCase();
33335
33888
  rb = rb.toLowerCase();
@@ -33489,7 +34042,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33489
34042
  let parsed;
33490
34043
  let fileExisted = true;
33491
34044
  try {
33492
- const raw = await fs14.readFile(fp, "utf8");
34045
+ const raw = await fs15.readFile(fp, "utf8");
33493
34046
  const result = safeParse(raw);
33494
34047
  if (!result.ok || !isPlainRecord(result.value)) {
33495
34048
  return;
@@ -33585,7 +34138,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33585
34138
  let parsed;
33586
34139
  let existed = true;
33587
34140
  try {
33588
- const raw = await fs14.readFile(profileFp, "utf8");
34141
+ const raw = await fs15.readFile(profileFp, "utf8");
33589
34142
  const result = safeParse(raw);
33590
34143
  if (!result.ok || !isPlainRecord(result.value)) {
33591
34144
  this.logWarn("Profile config parse failed \u2014 falling back to defaults", {
@@ -33684,7 +34237,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33684
34237
  const fp = this.paths.syncConfig;
33685
34238
  const t0 = Date.now();
33686
34239
  try {
33687
- const raw = await fs14.readFile(fp, "utf8");
34240
+ const raw = await fs15.readFile(fp, "utf8");
33688
34241
  const parsed = safeParse(raw);
33689
34242
  if (!parsed.ok || !parsed.value) {
33690
34243
  this.events?.emit("storage.read", {
@@ -33746,7 +34299,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33746
34299
  const t0 = Date.now();
33747
34300
  let mtimeMs = null;
33748
34301
  try {
33749
- const stat15 = await fs14.stat(file);
34302
+ const stat15 = await fs15.stat(file);
33750
34303
  mtimeMs = stat15.mtimeMs;
33751
34304
  const cached = this.jsonCache.get(file);
33752
34305
  if (cached && cached.mtimeMs === mtimeMs) {
@@ -33776,7 +34329,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33776
34329
  }
33777
34330
  let raw;
33778
34331
  try {
33779
- raw = await fs14.readFile(file, "utf8");
34332
+ raw = await fs15.readFile(file, "utf8");
33780
34333
  } catch (err) {
33781
34334
  if (err.code !== "ENOENT") {
33782
34335
  this.events?.emit("storage.read", {
@@ -34012,7 +34565,7 @@ function deepFreeze(obj) {
34012
34565
 
34013
34566
  // src/storage/plan-store.ts
34014
34567
  import * as fsp24 from "node:fs/promises";
34015
- import { randomUUID as randomUUID17 } from "node:crypto";
34568
+ import { randomUUID as randomUUID18 } from "node:crypto";
34016
34569
  async function loadPlan(filePath, events) {
34017
34570
  const t0 = Date.now();
34018
34571
  let raw;
@@ -34110,7 +34663,7 @@ function emptyPlan(sessionId, title) {
34110
34663
  function addPlanItem(plan, title, details) {
34111
34664
  const now = (/* @__PURE__ */ new Date()).toISOString();
34112
34665
  const item = {
34113
- id: `plan_${Date.now()}_${randomUUID17().slice(0, 6)}`,
34666
+ id: `plan_${Date.now()}_${randomUUID18().slice(0, 6)}`,
34114
34667
  title,
34115
34668
  details,
34116
34669
  status: "open",
@@ -34182,7 +34735,7 @@ function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
34182
34735
  if (subtasks && subtasks.length > 0) {
34183
34736
  for (const st of subtasks) {
34184
34737
  todos.push({
34185
- id: `todo_${Date.now()}_${randomUUID17().slice(0, 6)}`,
34738
+ id: `todo_${Date.now()}_${randomUUID18().slice(0, 6)}`,
34186
34739
  content: st,
34187
34740
  status: "pending",
34188
34741
  promotedFromPlan: item.id
@@ -34310,7 +34863,7 @@ ${cat}:`);
34310
34863
 
34311
34864
  // src/storage/queue-store.ts
34312
34865
  import * as fsp25 from "node:fs/promises";
34313
- import * as path40 from "node:path";
34866
+ import * as path42 from "node:path";
34314
34867
  var QUEUE_MAX_ITEMS = 100;
34315
34868
  var QUEUE_MAX_BYTES = 16 * 1024 * 1024;
34316
34869
  var QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;
@@ -34343,7 +34896,7 @@ var QueueStore = class {
34343
34896
  traceId;
34344
34897
  logger;
34345
34898
  constructor(opts) {
34346
- this.file = path40.join(opts.dir, "queue.json");
34899
+ this.file = path42.join(opts.dir, "queue.json");
34347
34900
  this.events = opts.events;
34348
34901
  this.traceId = opts.traceId;
34349
34902
  this.logger = opts.logger;
@@ -34558,8 +35111,8 @@ function isPersistedQueueItem(v) {
34558
35111
 
34559
35112
  // src/storage/recovery-lock.ts
34560
35113
  import * as fsp26 from "node:fs/promises";
34561
- import * as os2 from "node:os";
34562
- import * as path41 from "node:path";
35114
+ import * as os3 from "node:os";
35115
+ import * as path43 from "node:path";
34563
35116
  var LOCK_FILE = "active.json";
34564
35117
  var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
34565
35118
  var RecoveryLock = class {
@@ -34570,9 +35123,9 @@ var RecoveryLock = class {
34570
35123
  sessionStore;
34571
35124
  probe;
34572
35125
  constructor(opts) {
34573
- this.file = path41.join(opts.dir, LOCK_FILE);
35126
+ this.file = path43.join(opts.dir, LOCK_FILE);
34574
35127
  this.pid = opts.pid ?? process.pid;
34575
- this.hostname = opts.hostname ?? os2.hostname();
35128
+ this.hostname = opts.hostname ?? os3.hostname();
34576
35129
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
34577
35130
  this.sessionStore = opts.sessionStore;
34578
35131
  this.probe = opts.isPidAlive ?? defaultIsPidAlive;
@@ -34644,7 +35197,7 @@ var RecoveryLock = class {
34644
35197
  * null return before calling this.
34645
35198
  */
34646
35199
  async write(sessionId) {
34647
- await ensureDir(path41.dirname(this.file));
35200
+ await ensureDir(path43.dirname(this.file));
34648
35201
  const lock = {
34649
35202
  v: 1,
34650
35203
  sessionId,
@@ -34812,6 +35365,7 @@ var CORE_RECONSTRUCT_EVENTS = /* @__PURE__ */ new Set([
34812
35365
  "message_appended",
34813
35366
  "message_updated",
34814
35367
  "messages_replaced",
35368
+ "messages_dropped",
34815
35369
  "context_snapshot",
34816
35370
  "checkpoint",
34817
35371
  "file_snapshot",