@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.
- package/dist/agent-status-tracker.d.ts +6 -2
- package/dist/chronicle/index.js +1836 -1645
- package/dist/chronicle/metrics-store.d.ts +14 -0
- package/dist/chronicle/project-server-protocol.d.ts +13 -0
- package/dist/chronicle/project-server.js +1759 -1583
- package/dist/chronicle/rollup-adapter.d.ts +2 -0
- package/dist/chronicle/sqlite-journal.d.ts +59 -0
- package/dist/coordination/index.js +790 -248
- package/dist/coordination/mail-tools.d.ts +2 -2
- package/dist/core/continue-intent.d.ts +2 -0
- package/dist/core/conversation-state.d.ts +5 -0
- package/dist/core/index.js +120 -19
- package/dist/defaults/index.js +927 -373
- package/dist/execution/index.js +27 -10
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8762 -6780
- package/dist/infrastructure/index.js +722 -672
- package/dist/kernel/events/memory-events.d.ts +62 -0
- package/dist/plugin/index.js +2154 -1979
- package/dist/session-catalog/client.d.ts +62 -0
- package/dist/session-catalog/endpoint.d.ts +6 -0
- package/dist/session-catalog/index.d.ts +6 -0
- package/dist/session-catalog/index.js +1978 -0
- package/dist/session-catalog/project-server.d.ts +3 -0
- package/dist/session-catalog/project-server.js +1838 -0
- package/dist/session-catalog/protocol.d.ts +275 -0
- package/dist/session-catalog/registry.d.ts +59 -0
- package/dist/session-catalog/store.d.ts +55 -0
- package/dist/storage/index.d.ts +42 -38
- package/dist/storage/index.js +14279 -13393
- package/dist/storage/session-event-bridge.d.ts +2 -2
- package/dist/storage/session-store.d.ts +6 -0
- package/dist/tools/index.js +8 -2
- package/dist/types/context-evidence.d.ts +2 -0
- package/dist/types/messages.d.ts +8 -0
- package/dist/types/session.d.ts +19 -0
- package/dist/utils/context-evidence.d.ts +13 -1
- package/dist/utils/index.js +26 -2
- package/instructions/system-lite.md +11 -2
- package/instructions/system-pro.md +14 -0
- package/instructions/system.md +14 -0
- package/package.json +7 -3
package/dist/defaults/index.js
CHANGED
|
@@ -295,7 +295,7 @@ var InMemoryAgentBridge = class {
|
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
this.inflightGuards.add(correlationId);
|
|
298
|
-
return new Promise((
|
|
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:
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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((
|
|
7527
|
+
return new Promise((resolve21) => {
|
|
7528
7528
|
const entry = {
|
|
7529
7529
|
ids: new Set(taskIds),
|
|
7530
|
-
resolve: (result) =>
|
|
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
|
-
|
|
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
|
|
7656
|
+
let resolve21;
|
|
7657
7657
|
const promise = new Promise((done) => {
|
|
7658
|
-
|
|
7658
|
+
resolve21 = done;
|
|
7659
7659
|
});
|
|
7660
|
-
this.taskWaiters.set(taskId, { promise, resolve:
|
|
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
|
|
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
|
|
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
|
|
10091
|
-
import * as
|
|
10473
|
+
import * as fs3 from "node:fs/promises";
|
|
10474
|
+
import * as path11 from "node:path";
|
|
10092
10475
|
function configHistoryDir(globalRoot) {
|
|
10093
|
-
return
|
|
10476
|
+
return path11.join(globalRoot, "config-history");
|
|
10094
10477
|
}
|
|
10095
10478
|
function configSlug(absolutePath, globalRoot) {
|
|
10096
|
-
const rel =
|
|
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
|
|
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 =
|
|
10495
|
+
const backupFile = path11.join(backupDir, `${slug}-${ts}.json`);
|
|
10113
10496
|
try {
|
|
10114
|
-
await
|
|
10115
|
-
await
|
|
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
|
|
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: "
|
|
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 =
|
|
11989
|
+
const resolved = path12.isAbsolute(dir) ? path12.resolve(dir) : path12.resolve(this.projectRoot, dir);
|
|
11596
11990
|
if (!this.allowOutsideProjectRoot) {
|
|
11597
|
-
const root =
|
|
11598
|
-
const rel =
|
|
11599
|
-
if (rel.startsWith("..") ||
|
|
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,
|
|
12422
|
+
function walk(value, schema, path44, errors, depth) {
|
|
12029
12423
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
12030
12424
|
errors.push({
|
|
12031
|
-
path:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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, `${
|
|
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
|
|
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 =
|
|
12441
|
-
const rel =
|
|
12442
|
-
if (rel.startsWith("..") ||
|
|
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((
|
|
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
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
14821
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
14428
14822
|
}
|
|
14429
14823
|
function isInside(root, target) {
|
|
14430
|
-
const relative8 =
|
|
14431
|
-
return relative8 === "" || !relative8.startsWith("..") && !
|
|
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 ||
|
|
14828
|
+
if (!input || path15.isAbsolute(input)) return null;
|
|
14435
14829
|
const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
14436
|
-
const resolved =
|
|
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 =
|
|
14452
|
-
this.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 =
|
|
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 =
|
|
14487
|
-
if (
|
|
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 =
|
|
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 (
|
|
14983
|
+
if (path15.isAbsolute(entry.linkTarget)) {
|
|
14590
14984
|
throw new Error("absolute symlink target refused");
|
|
14591
14985
|
}
|
|
14592
|
-
const resolvedLink =
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
14654
|
-
|
|
14655
|
-
`.${
|
|
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 =
|
|
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 =
|
|
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((
|
|
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 =
|
|
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) =>
|
|
14778
|
-
child.on("close", (code) =>
|
|
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
|
|
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) =>
|
|
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
|
|
15242
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
14849
15243
|
import * as fsp9 from "node:fs/promises";
|
|
14850
|
-
import * as
|
|
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 =
|
|
14856
|
-
return relative8 === "" || !relative8.startsWith("..") && !
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
15335
|
+
const root = path17.resolve(projectRoot);
|
|
14942
15336
|
const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
|
|
14943
|
-
const relative8 =
|
|
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
|
|
15362
|
+
import * as path19 from "node:path";
|
|
14969
15363
|
|
|
14970
15364
|
// src/storage/session-store/paths.ts
|
|
14971
|
-
import * as
|
|
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 ?
|
|
15370
|
+
return shardKey ? path18.join(storeDir, shardKey, "_manifest.json") : path18.join(storeDir, "_manifest.json");
|
|
14977
15371
|
}
|
|
14978
15372
|
function shardKeyForSessionId(id) {
|
|
14979
|
-
const dirName =
|
|
15373
|
+
const dirName = path18.dirname(id);
|
|
14980
15374
|
return dirName === "." ? "" : dirName;
|
|
14981
15375
|
}
|
|
14982
15376
|
async function ensureShardDir(storeDir, id) {
|
|
14983
|
-
const dirPath =
|
|
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 =
|
|
14995
|
-
const base =
|
|
14996
|
-
const sessDir =
|
|
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
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
16410
|
+
this.projectRoot = opts.projectRoot ? path22.resolve(opts.projectRoot) : void 0;
|
|
16004
16411
|
this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
|
|
16005
|
-
rootDir:
|
|
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
|
|
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.
|
|
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:
|
|
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.
|
|
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 ?
|
|
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.
|
|
16766
|
-
const
|
|
16767
|
-
|
|
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
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
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 ?
|
|
17351
|
+
dir = opts.sessionsRoot ? path23.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
16799
17352
|
} else if (opts.sessionsRoot) {
|
|
16800
|
-
dir =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
18372
|
+
const resolve21 = (d) => {
|
|
17820
18373
|
if (settled) return;
|
|
17821
18374
|
settled = true;
|
|
17822
18375
|
resolveDecision(d);
|
|
17823
18376
|
};
|
|
17824
|
-
const fallback = setTimeout(() =>
|
|
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(() =>
|
|
18387
|
+
queueMicrotask(() => resolve21({ extend: extra }));
|
|
17835
18388
|
},
|
|
17836
18389
|
deny: () => {
|
|
17837
18390
|
clearTimeout(fallback);
|
|
17838
|
-
|
|
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 ||
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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 ||
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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
|
|
21968
|
-
import * as
|
|
21969
|
-
import * as
|
|
21970
|
-
import { fileURLToPath as
|
|
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
|
|
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 =
|
|
22628
|
+
const kitFile = path25.join(dir, e.name, KIT_FILE);
|
|
22076
22629
|
try {
|
|
22077
|
-
const raw = await
|
|
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
|
|
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 =
|
|
22703
|
+
const tokensPath = path25.join(path25.dirname(m.path), TOKENS_FILE);
|
|
22151
22704
|
try {
|
|
22152
|
-
const raw = await
|
|
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 =
|
|
22765
|
+
const here = path25.dirname(fileURLToPath4(import.meta.url));
|
|
22213
22766
|
const candidates = [
|
|
22214
|
-
|
|
22215
|
-
|
|
22216
|
-
|
|
22217
|
-
|
|
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 (
|
|
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
|
|
22255
|
-
import * as
|
|
22256
|
-
import * as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
22334
|
-
const gi =
|
|
22335
|
-
if (!
|
|
22886
|
+
await fs5.mkdir(dir, { recursive: true });
|
|
22887
|
+
const gi = path26.join(dir, ".gitignore");
|
|
22888
|
+
if (!existsSync5(gi)) {
|
|
22336
22889
|
try {
|
|
22337
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
22372
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
23761
|
+
const delay2 = Math.min(6e4, Math.max(1e3, delayMs));
|
|
23209
23762
|
if (ctx?.signal) {
|
|
23210
|
-
await new Promise((
|
|
23763
|
+
await new Promise((resolve21) => {
|
|
23211
23764
|
if (ctx.signal.aborted) {
|
|
23212
|
-
|
|
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
|
-
|
|
23776
|
+
resolve21();
|
|
23224
23777
|
};
|
|
23225
|
-
timer = setTimeout(finish,
|
|
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,
|
|
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
|
|
23860
|
-
if (
|
|
23861
|
-
emit2({ phase: "sleep", ms:
|
|
23862
|
-
await this.sleepInterruptible(
|
|
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
|
|
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 =
|
|
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
|
|
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-${
|
|
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-${
|
|
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 =
|
|
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
|
|
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 ??
|
|
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((
|
|
25418
|
-
drainTimer = setTimeout(
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
26190
|
+
const logicalRequestId = randomUUID15();
|
|
25638
26191
|
const promptManifest = createChroniclePromptManifest(request);
|
|
25639
26192
|
let attempt = 0;
|
|
25640
26193
|
for (; ; ) {
|
|
25641
|
-
const attemptId =
|
|
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
|
|
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
|
-
...
|
|
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 ${
|
|
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:
|
|
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:
|
|
26317
|
+
delayMs: delay2,
|
|
25765
26318
|
status: isProviderErr ? err.status : 0,
|
|
25766
26319
|
description,
|
|
25767
26320
|
...providerErrorBody ? { errorBody: providerErrorBody } : {}
|
|
25768
26321
|
});
|
|
25769
|
-
await new Promise((
|
|
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
|
-
|
|
25786
|
-
},
|
|
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
|
|
26410
|
-
import * as
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
27081
|
+
const skillFile = path27.join(dir, e.name, "SKILL.md");
|
|
26529
27082
|
let raw;
|
|
26530
27083
|
try {
|
|
26531
|
-
raw = await
|
|
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
|
|
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 =
|
|
27213
|
+
const savePath = path27.join(path27.dirname(m.path), "SKILL.save.md");
|
|
26661
27214
|
let result;
|
|
26662
27215
|
try {
|
|
26663
|
-
result = await
|
|
27216
|
+
result = await fs6.readFile(savePath, "utf8");
|
|
26664
27217
|
} catch {
|
|
26665
|
-
const full = await
|
|
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
|
|
26694
|
-
import * as
|
|
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
|
|
26698
|
-
import * as
|
|
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
|
|
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
|
|
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 =
|
|
27331
|
+
const file = path28.join(this.dir, `${id}.json`);
|
|
26779
27332
|
try {
|
|
26780
|
-
const raw = JSON.parse(await
|
|
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 =
|
|
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 =
|
|
27346
|
+
const file = path28.join(this.dir, `${id}.json`);
|
|
26794
27347
|
try {
|
|
26795
|
-
await
|
|
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 ?
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
27202
|
-
import * as
|
|
27203
|
-
import * as
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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 (
|
|
27326
|
-
const absolute =
|
|
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 =
|
|
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 =
|
|
27890
|
+
const parent = path30.dirname(probe);
|
|
27338
27891
|
if (parent === probe) return absolute;
|
|
27339
|
-
missingSegments.unshift(
|
|
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 =
|
|
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 =
|
|
27367
|
-
return relative8.startsWith("../") ||
|
|
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
|
|
27391
|
-
import * as
|
|
27392
|
-
import * as
|
|
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 =
|
|
27518
|
-
await
|
|
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 =
|
|
27522
|
-
await
|
|
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
|
|
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
|
-
(
|
|
28615
|
+
(resolve21, reject) => {
|
|
28063
28616
|
const signal = ctx.signal;
|
|
28064
|
-
const onAbort = () =>
|
|
28617
|
+
const onAbort = () => resolve21("abort");
|
|
28065
28618
|
if (signal.aborted) {
|
|
28066
|
-
|
|
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
|
-
|
|
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" ?
|
|
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
|
|
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-${
|
|
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-${
|
|
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((
|
|
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
|
-
|
|
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
|
|
28995
|
-
import * as
|
|
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
|
|
28999
|
-
import * as
|
|
29000
|
-
import { fileURLToPath as
|
|
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
|
|
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 =
|
|
29564
|
+
const here = path33.dirname(fileURLToPath5(import.meta.url));
|
|
29012
29565
|
const candidates = [
|
|
29013
|
-
|
|
29014
|
-
|
|
29015
|
-
|
|
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 =
|
|
29248
|
-
const content = await
|
|
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
|
|
29258
|
-
const configPath =
|
|
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
|
|
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 =
|
|
29274
|
-
const stat15 = await
|
|
29826
|
+
const filePath = path34.join(modesDir, entry);
|
|
29827
|
+
const stat15 = await fs11.stat(filePath);
|
|
29275
29828
|
if (!stat15.isFile()) continue;
|
|
29276
|
-
const content = await
|
|
29277
|
-
const id =
|
|
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 =
|
|
29294
|
-
const content = await
|
|
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
|
|
29306
|
-
import * as
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
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((
|
|
30562
|
+
const timeout = new Promise((resolve21) => {
|
|
30010
30563
|
timer = setTimeout(
|
|
30011
|
-
() =>
|
|
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
|
|
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 ===
|
|
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:
|
|
30822
|
+
const { readFileSync: readFileSync10 } = await import("node:fs");
|
|
30270
30823
|
server = createServer(
|
|
30271
|
-
{ cert:
|
|
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((
|
|
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
|
-
|
|
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}${
|
|
30297
|
-
close: () => new Promise((
|
|
30298
|
-
server.close((err) => err ? reject(err) :
|
|
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
|
|
30569
|
-
import * as
|
|
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,
|
|
30615
|
-
diagnostics.push({ severity: "error", code, path:
|
|
31167
|
+
function error(diagnostics, code, path44, message) {
|
|
31168
|
+
diagnostics.push({ severity: "error", code, path: path44, message });
|
|
30616
31169
|
}
|
|
30617
|
-
function validatePatterns(value,
|
|
31170
|
+
function validatePatterns(value, path44, diagnostics) {
|
|
30618
31171
|
if (!Array.isArray(value)) {
|
|
30619
|
-
error(diagnostics, "invalid_pattern_list",
|
|
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
|
-
|
|
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 = `${
|
|
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
|
|
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 =
|
|
30799
|
-
const relative8 =
|
|
30800
|
-
return !!relative8 && !relative8.startsWith("..") && !
|
|
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
|
|
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) :
|
|
31687
|
+
return tail.length === 0 ? realpathSync(probe) : path37.join(realpathSync(probe), ...tail);
|
|
31135
31688
|
} catch {
|
|
31136
|
-
const parent =
|
|
31689
|
+
const parent = path37.dirname(probe);
|
|
31137
31690
|
if (parent === probe) return p;
|
|
31138
|
-
tail.unshift(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ?
|
|
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
|
|
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
|
|
32541
|
+
import * as fs14 from "node:fs";
|
|
31989
32542
|
import * as fsp22 from "node:fs/promises";
|
|
31990
|
-
import * as
|
|
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 =
|
|
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 =
|
|
32699
|
+
const fd = fs14.openSync(tmp, "w", 384);
|
|
32147
32700
|
try {
|
|
32148
|
-
|
|
32701
|
+
fs14.writeFileSync(fd, content);
|
|
32149
32702
|
try {
|
|
32150
|
-
|
|
32703
|
+
fs14.fsyncSync(fd);
|
|
32151
32704
|
} catch {
|
|
32152
32705
|
}
|
|
32153
32706
|
} finally {
|
|
32154
|
-
|
|
32707
|
+
fs14.closeSync(fd);
|
|
32155
32708
|
}
|
|
32156
32709
|
try {
|
|
32157
|
-
|
|
32710
|
+
fs14.renameSync(tmp, keyFile);
|
|
32158
32711
|
} catch (err) {
|
|
32159
32712
|
try {
|
|
32160
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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,
|
|
33045
|
-
const segments =
|
|
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:
|
|
33103
|
-
).map(({ path:
|
|
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:
|
|
33111
|
-
).map(({ path:
|
|
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:
|
|
33140
|
-
if (deleteNestedPath(out,
|
|
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
|
|
33882
|
+
import * as path41 from "node:path";
|
|
33330
33883
|
function samePath(a, b) {
|
|
33331
|
-
let ra =
|
|
33332
|
-
let rb =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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()}_${
|
|
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()}_${
|
|
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
|
|
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 =
|
|
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
|
|
34562
|
-
import * as
|
|
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 =
|
|
35126
|
+
this.file = path43.join(opts.dir, LOCK_FILE);
|
|
34574
35127
|
this.pid = opts.pid ?? process.pid;
|
|
34575
|
-
this.hostname = opts.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(
|
|
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",
|