@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
|
@@ -224,7 +224,7 @@ var InMemoryAgentBridge = class {
|
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
this.inflightGuards.add(correlationId);
|
|
227
|
-
return new Promise((
|
|
227
|
+
return new Promise((resolve16, reject) => {
|
|
228
228
|
const timer = setTimeout(() => {
|
|
229
229
|
this.inflightGuards.delete(correlationId);
|
|
230
230
|
this.pendingRequests.delete(correlationId);
|
|
@@ -243,7 +243,7 @@ var InMemoryAgentBridge = class {
|
|
|
243
243
|
return;
|
|
244
244
|
}
|
|
245
245
|
this.pendingRequests.set(correlationId, {
|
|
246
|
-
resolve:
|
|
246
|
+
resolve: resolve16,
|
|
247
247
|
reject,
|
|
248
248
|
timer
|
|
249
249
|
});
|
|
@@ -592,13 +592,13 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
592
592
|
if (!bus?.hasListenerFor("budget.threshold_reached")) {
|
|
593
593
|
return Promise.resolve("stop");
|
|
594
594
|
}
|
|
595
|
-
return new Promise((
|
|
595
|
+
return new Promise((resolve16) => {
|
|
596
596
|
let resolved = false;
|
|
597
597
|
const respond = (d) => {
|
|
598
598
|
if (resolved) return;
|
|
599
599
|
resolved = true;
|
|
600
600
|
clearTimeout(fallback);
|
|
601
|
-
|
|
601
|
+
resolve16(d);
|
|
602
602
|
};
|
|
603
603
|
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
604
604
|
const sessionId = this.currentSessionId();
|
|
@@ -6265,13 +6265,13 @@ var BrainDecisionQueue = class {
|
|
|
6265
6265
|
options: request.options,
|
|
6266
6266
|
rationale: "Decision escalated to human authority."
|
|
6267
6267
|
};
|
|
6268
|
-
const pending = new Promise((
|
|
6269
|
-
const entry = { request, resolve:
|
|
6268
|
+
const pending = new Promise((resolve16) => {
|
|
6269
|
+
const entry = { request, resolve: resolve16 };
|
|
6270
6270
|
if (this.opts.timeoutMs && this.opts.timeoutMs > 0) {
|
|
6271
6271
|
entry.timer = setTimeout(() => {
|
|
6272
6272
|
this.pending.delete(request.id);
|
|
6273
6273
|
markDecisionTier(request, "terminal");
|
|
6274
|
-
|
|
6274
|
+
resolve16(
|
|
6275
6275
|
this.opts.onTimeout?.(request) ?? {
|
|
6276
6276
|
type: "deny",
|
|
6277
6277
|
reason: "Brain human decision timed out."
|
|
@@ -7050,26 +7050,26 @@ var BrainMonitor = class {
|
|
|
7050
7050
|
trackFileChurn(toolName, ok, input) {
|
|
7051
7051
|
if (!this.signals.fileChurn) return;
|
|
7052
7052
|
if (!ok || !this.fileEditTools.has(toolName.toLowerCase())) return;
|
|
7053
|
-
const
|
|
7054
|
-
if (!
|
|
7053
|
+
const path36 = editedPath(input);
|
|
7054
|
+
if (!path36) return;
|
|
7055
7055
|
const now = Date.now();
|
|
7056
|
-
const stamps = (this.editTimestamps.get(
|
|
7056
|
+
const stamps = (this.editTimestamps.get(path36) ?? []).filter(
|
|
7057
7057
|
(t) => now - t <= this.fileChurnWindowMs
|
|
7058
7058
|
);
|
|
7059
7059
|
stamps.push(now);
|
|
7060
7060
|
if (stamps.length >= this.fileChurnThreshold) {
|
|
7061
|
-
this.editTimestamps.delete(
|
|
7061
|
+
this.editTimestamps.delete(path36);
|
|
7062
7062
|
void this.engage("file_churn", {
|
|
7063
|
-
question: `The file "${
|
|
7063
|
+
question: `The file "${path36}" has been edited ${stamps.length} times within ${Math.round(this.fileChurnWindowMs / 6e4)} minutes \u2014 the agent may be oscillating (edit/revert loop) instead of converging. Should it be steered?`,
|
|
7064
7064
|
context: [
|
|
7065
|
-
`File: ${
|
|
7065
|
+
`File: ${path36}`,
|
|
7066
7066
|
`Edits in window: ${stamps.length}`,
|
|
7067
7067
|
`Window: ${Math.round(this.fileChurnWindowMs / 1e3)}s`
|
|
7068
7068
|
].join("\n")
|
|
7069
7069
|
});
|
|
7070
7070
|
return;
|
|
7071
7071
|
}
|
|
7072
|
-
this.editTimestamps.set(
|
|
7072
|
+
this.editTimestamps.set(path36, stamps);
|
|
7073
7073
|
}
|
|
7074
7074
|
async engage(kind, input) {
|
|
7075
7075
|
const last = this.lastEngagedAt.get(kind) ?? 0;
|
|
@@ -8998,7 +8998,7 @@ async function gitOtherWorktrees(cwd, signal) {
|
|
|
8998
8998
|
return branches.slice(1);
|
|
8999
8999
|
}
|
|
9000
9000
|
function runGit(args, cwd, signal) {
|
|
9001
|
-
return new Promise((
|
|
9001
|
+
return new Promise((resolve16, reject) => {
|
|
9002
9002
|
let stdout = "";
|
|
9003
9003
|
let stderr = "";
|
|
9004
9004
|
const child = spawn("git", args, {
|
|
@@ -9017,7 +9017,7 @@ function runGit(args, cwd, signal) {
|
|
|
9017
9017
|
});
|
|
9018
9018
|
child.on("error", (err) => reject(err));
|
|
9019
9019
|
child.on("close", (code) => {
|
|
9020
|
-
if (code === 0)
|
|
9020
|
+
if (code === 0) resolve16(stdout);
|
|
9021
9021
|
else reject(new Error(stderr || `git ${args[0]} exited ${code}`));
|
|
9022
9022
|
});
|
|
9023
9023
|
});
|
|
@@ -9516,7 +9516,7 @@ function createDelegateTool(opts) {
|
|
|
9516
9516
|
};
|
|
9517
9517
|
}
|
|
9518
9518
|
async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abortSignal) {
|
|
9519
|
-
return new Promise((
|
|
9519
|
+
return new Promise((resolve16) => {
|
|
9520
9520
|
let settled = false;
|
|
9521
9521
|
let timer;
|
|
9522
9522
|
let offAbort = () => {
|
|
@@ -9529,7 +9529,7 @@ async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abo
|
|
|
9529
9529
|
offIter();
|
|
9530
9530
|
offProgress();
|
|
9531
9531
|
offAbort();
|
|
9532
|
-
|
|
9532
|
+
resolve16(value);
|
|
9533
9533
|
};
|
|
9534
9534
|
const arm = () => {
|
|
9535
9535
|
if (timer) clearTimeout(timer);
|
|
@@ -9894,7 +9894,7 @@ function attachDepWatcherBridge(opts) {
|
|
|
9894
9894
|
}
|
|
9895
9895
|
|
|
9896
9896
|
// src/coordination/director.ts
|
|
9897
|
-
import { randomUUID as
|
|
9897
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
9898
9898
|
import * as fsp19 from "node:fs/promises";
|
|
9899
9899
|
|
|
9900
9900
|
// src/core/instruction-template.ts
|
|
@@ -10746,10 +10746,10 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
|
|
|
10746
10746
|
pending: taskIds.filter((id) => !done.has(id))
|
|
10747
10747
|
});
|
|
10748
10748
|
}
|
|
10749
|
-
return new Promise((
|
|
10749
|
+
return new Promise((resolve16) => {
|
|
10750
10750
|
const entry = {
|
|
10751
10751
|
ids: new Set(taskIds),
|
|
10752
|
-
resolve: (result) =>
|
|
10752
|
+
resolve: (result) => resolve16({
|
|
10753
10753
|
completed: [result],
|
|
10754
10754
|
pending: taskIds.filter((id) => id !== result.taskId)
|
|
10755
10755
|
})
|
|
@@ -10757,7 +10757,7 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
|
|
|
10757
10757
|
if (opts?.timeoutMs !== void 0) {
|
|
10758
10758
|
entry.timer = setTimeout(() => {
|
|
10759
10759
|
this.anyWaiters.delete(entry);
|
|
10760
|
-
|
|
10760
|
+
resolve16({ completed: [], pending: [...taskIds], timedOut: true });
|
|
10761
10761
|
}, opts.timeoutMs);
|
|
10762
10762
|
}
|
|
10763
10763
|
this.anyWaiters.add(entry);
|
|
@@ -10875,11 +10875,11 @@ ${JSON.stringify(result.result, null, 2)}
|
|
|
10875
10875
|
this.makeStoppedResult(taskId, "director", `Unknown task id "${taskId}" \u2014 never assigned`)
|
|
10876
10876
|
);
|
|
10877
10877
|
}
|
|
10878
|
-
let
|
|
10878
|
+
let resolve16;
|
|
10879
10879
|
const promise = new Promise((done) => {
|
|
10880
|
-
|
|
10880
|
+
resolve16 = done;
|
|
10881
10881
|
});
|
|
10882
|
-
this.taskWaiters.set(taskId, { promise, resolve:
|
|
10882
|
+
this.taskWaiters.set(taskId, { promise, resolve: resolve16 });
|
|
10883
10883
|
return promise;
|
|
10884
10884
|
}
|
|
10885
10885
|
recordAssignment(task) {
|
|
@@ -12769,11 +12769,394 @@ function rosterSummaryFromConfigs(roster) {
|
|
|
12769
12769
|
|
|
12770
12770
|
// src/coordination/director-session.ts
|
|
12771
12771
|
import * as fsp18 from "node:fs/promises";
|
|
12772
|
-
import * as
|
|
12772
|
+
import * as path26 from "node:path";
|
|
12773
12773
|
|
|
12774
12774
|
// src/storage/session-store.ts
|
|
12775
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
12775
12776
|
import * as fsp17 from "node:fs/promises";
|
|
12776
|
-
import * as
|
|
12777
|
+
import * as path25 from "node:path";
|
|
12778
|
+
|
|
12779
|
+
// src/session-catalog/client.ts
|
|
12780
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
12781
|
+
import * as fs3 from "node:fs";
|
|
12782
|
+
import * as net from "node:net";
|
|
12783
|
+
import * as path15 from "node:path";
|
|
12784
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12785
|
+
|
|
12786
|
+
// src/session-catalog/endpoint.ts
|
|
12787
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
12788
|
+
import * as os2 from "node:os";
|
|
12789
|
+
import * as path14 from "node:path";
|
|
12790
|
+
|
|
12791
|
+
// src/session-catalog/protocol.ts
|
|
12792
|
+
var SESSION_CATALOG_PROTOCOL_VERSION = 1;
|
|
12793
|
+
var SESSION_CATALOG_MAX_FRAME_CHARS = 4 * 1024 * 1024;
|
|
12794
|
+
function encodeSessionCatalogMessage(message) {
|
|
12795
|
+
return `${JSON.stringify(message)}
|
|
12796
|
+
`;
|
|
12797
|
+
}
|
|
12798
|
+
|
|
12799
|
+
// src/session-catalog/endpoint.ts
|
|
12800
|
+
var SESSION_CATALOG_METADATA_FILE = ".session-catalog-server.json";
|
|
12801
|
+
function normalizedPath(value) {
|
|
12802
|
+
const resolved = path14.resolve(value);
|
|
12803
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12804
|
+
}
|
|
12805
|
+
function sessionCatalogProjectServerKey(projectDir) {
|
|
12806
|
+
return createHash2("sha256").update(normalizedPath(projectDir)).digest("hex").slice(0, 24);
|
|
12807
|
+
}
|
|
12808
|
+
function sessionCatalogProjectServerEndpoint(projectDir) {
|
|
12809
|
+
const key = sessionCatalogProjectServerKey(projectDir);
|
|
12810
|
+
if (process.platform === "win32") {
|
|
12811
|
+
return `\\\\.\\pipe\\wrongstack-session-catalog-v${SESSION_CATALOG_PROTOCOL_VERSION}-${key}`;
|
|
12812
|
+
}
|
|
12813
|
+
return path14.join(os2.tmpdir(), `wssc-v${SESSION_CATALOG_PROTOCOL_VERSION}`, `${key}.sock`);
|
|
12814
|
+
}
|
|
12815
|
+
function sessionCatalogProjectServerMetadataPath(projectDir) {
|
|
12816
|
+
return path14.join(projectDir, SESSION_CATALOG_METADATA_FILE);
|
|
12817
|
+
}
|
|
12818
|
+
|
|
12819
|
+
// src/session-catalog/client.ts
|
|
12820
|
+
var CONNECT_TIMEOUT_MS = 750;
|
|
12821
|
+
var START_TIMEOUT_MS = 1e4;
|
|
12822
|
+
var CALL_TIMEOUT_MS = 3e4;
|
|
12823
|
+
var MAX_PENDING_REQUESTS = 1024;
|
|
12824
|
+
var MAX_EVENT_LISTENERS = 64;
|
|
12825
|
+
function locateServer(moduleUrl, exists) {
|
|
12826
|
+
for (const candidate of [
|
|
12827
|
+
"./project-server.js",
|
|
12828
|
+
"../session-catalog/project-server.js",
|
|
12829
|
+
"./session-catalog/project-server.js",
|
|
12830
|
+
"../../dist/session-catalog/project-server.js"
|
|
12831
|
+
]) {
|
|
12832
|
+
try {
|
|
12833
|
+
const url = new URL(candidate, moduleUrl);
|
|
12834
|
+
if (url.protocol === "file:" && exists(fileURLToPath3(url))) return url;
|
|
12835
|
+
} catch {
|
|
12836
|
+
}
|
|
12837
|
+
}
|
|
12838
|
+
return null;
|
|
12839
|
+
}
|
|
12840
|
+
function resolveSessionCatalogDaemonAvailability(moduleUrl = import.meta.url, exists = fs3.existsSync) {
|
|
12841
|
+
if (process.env["WRONGSTACK_SESSION_CATALOG_INLINE"] || process.env["WRONGSTACK_SESSION_CATALOG_SERVER"] === "0")
|
|
12842
|
+
return { kind: "inline-requested" };
|
|
12843
|
+
const url = locateServer(moduleUrl, exists);
|
|
12844
|
+
return url ? { kind: "available", url } : { kind: "missing-build" };
|
|
12845
|
+
}
|
|
12846
|
+
function resolveSessionCatalogProjectServerUrl(moduleUrl = import.meta.url, exists = fs3.existsSync) {
|
|
12847
|
+
const availability = resolveSessionCatalogDaemonAvailability(moduleUrl, exists);
|
|
12848
|
+
return availability.kind === "available" ? availability.url : null;
|
|
12849
|
+
}
|
|
12850
|
+
function normalize2(value) {
|
|
12851
|
+
const resolved = path15.resolve(value);
|
|
12852
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12853
|
+
}
|
|
12854
|
+
function delay(ms) {
|
|
12855
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
12856
|
+
}
|
|
12857
|
+
var SessionCatalogProjectClient = class {
|
|
12858
|
+
constructor(options) {
|
|
12859
|
+
this.options = options;
|
|
12860
|
+
this.endpoint = sessionCatalogProjectServerEndpoint(options.projectDir);
|
|
12861
|
+
}
|
|
12862
|
+
options;
|
|
12863
|
+
endpoint;
|
|
12864
|
+
socket = null;
|
|
12865
|
+
info = null;
|
|
12866
|
+
buffer = "";
|
|
12867
|
+
connecting = null;
|
|
12868
|
+
connectResolve = null;
|
|
12869
|
+
connectReject = null;
|
|
12870
|
+
authToken;
|
|
12871
|
+
nextId = 1;
|
|
12872
|
+
pending = /* @__PURE__ */ new Map();
|
|
12873
|
+
eventListeners = /* @__PURE__ */ new Set();
|
|
12874
|
+
reconnectTimer;
|
|
12875
|
+
explicitlyClosed = false;
|
|
12876
|
+
async call(op, args, options = {}) {
|
|
12877
|
+
await this.ensureConnected(true);
|
|
12878
|
+
return this.request({ type: "request", op, args }, options.timeoutMs ?? CALL_TIMEOUT_MS);
|
|
12879
|
+
}
|
|
12880
|
+
/**
|
|
12881
|
+
* Call an already-running project daemon without starting one.
|
|
12882
|
+
*
|
|
12883
|
+
* Cross-project discovery must use this path: observing another project is
|
|
12884
|
+
* never sufficient authority to wake that project's IPC owner.
|
|
12885
|
+
*/
|
|
12886
|
+
async callExisting(op, args, options = {}) {
|
|
12887
|
+
await this.ensureConnected(false);
|
|
12888
|
+
return this.request({ type: "request", op, args }, options.timeoutMs ?? CALL_TIMEOUT_MS);
|
|
12889
|
+
}
|
|
12890
|
+
ping() {
|
|
12891
|
+
return this.call("ping", {}, { timeoutMs: 3e3 });
|
|
12892
|
+
}
|
|
12893
|
+
async subscribe(listener) {
|
|
12894
|
+
if (!this.eventListeners.has(listener) && this.eventListeners.size >= MAX_EVENT_LISTENERS) {
|
|
12895
|
+
throw new Error(`Session Catalog listener limit reached (${MAX_EVENT_LISTENERS})`);
|
|
12896
|
+
}
|
|
12897
|
+
this.explicitlyClosed = false;
|
|
12898
|
+
this.eventListeners.add(listener);
|
|
12899
|
+
try {
|
|
12900
|
+
await this.call("subscribe", {});
|
|
12901
|
+
} catch (error) {
|
|
12902
|
+
this.eventListeners.delete(listener);
|
|
12903
|
+
throw error;
|
|
12904
|
+
}
|
|
12905
|
+
return async () => {
|
|
12906
|
+
this.eventListeners.delete(listener);
|
|
12907
|
+
if (this.eventListeners.size === 0)
|
|
12908
|
+
await this.callExisting("unsubscribe", {}).catch(() => void 0);
|
|
12909
|
+
};
|
|
12910
|
+
}
|
|
12911
|
+
async shutdown(reason) {
|
|
12912
|
+
try {
|
|
12913
|
+
await this.ensureConnected(false);
|
|
12914
|
+
} catch {
|
|
12915
|
+
return { stopped: false };
|
|
12916
|
+
}
|
|
12917
|
+
const result = await this.request({ type: "shutdown", ...reason !== void 0 ? { reason } : {} }, 5e3).catch(
|
|
12918
|
+
() => ({ stopped: false })
|
|
12919
|
+
);
|
|
12920
|
+
if (result.stopped) {
|
|
12921
|
+
const metadataPath = sessionCatalogProjectServerMetadataPath(this.options.projectDir);
|
|
12922
|
+
const deadline = Date.now() + 5e3;
|
|
12923
|
+
while (fs3.existsSync(metadataPath) && Date.now() < deadline) await delay(20);
|
|
12924
|
+
if (result.pid && result.pid !== process.pid) {
|
|
12925
|
+
while (Date.now() < deadline) {
|
|
12926
|
+
try {
|
|
12927
|
+
process.kill(result.pid, 0);
|
|
12928
|
+
await delay(20);
|
|
12929
|
+
} catch {
|
|
12930
|
+
break;
|
|
12931
|
+
}
|
|
12932
|
+
}
|
|
12933
|
+
}
|
|
12934
|
+
}
|
|
12935
|
+
return result;
|
|
12936
|
+
}
|
|
12937
|
+
async close() {
|
|
12938
|
+
this.explicitlyClosed = true;
|
|
12939
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
12940
|
+
this.reconnectTimer = void 0;
|
|
12941
|
+
const socket = this.socket;
|
|
12942
|
+
this.socket = null;
|
|
12943
|
+
this.info = null;
|
|
12944
|
+
if (socket && !socket.destroyed)
|
|
12945
|
+
await new Promise((resolve16) => {
|
|
12946
|
+
socket.once("close", resolve16);
|
|
12947
|
+
socket.end();
|
|
12948
|
+
});
|
|
12949
|
+
}
|
|
12950
|
+
async ensureConnected(spawnIfMissing) {
|
|
12951
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
12952
|
+
if (this.connecting) return this.connecting;
|
|
12953
|
+
this.connecting = this.connectWithElection(spawnIfMissing).finally(() => {
|
|
12954
|
+
this.connecting = null;
|
|
12955
|
+
});
|
|
12956
|
+
return this.connecting;
|
|
12957
|
+
}
|
|
12958
|
+
async connectWithElection(spawnIfMissing) {
|
|
12959
|
+
const deadline = Date.now() + (spawnIfMissing ? START_TIMEOUT_MS : CONNECT_TIMEOUT_MS);
|
|
12960
|
+
let spawned = false;
|
|
12961
|
+
let lastError = new Error("Session Catalog project server unavailable");
|
|
12962
|
+
while (Date.now() < deadline) {
|
|
12963
|
+
try {
|
|
12964
|
+
await this.connectOnce();
|
|
12965
|
+
return;
|
|
12966
|
+
} catch (error) {
|
|
12967
|
+
lastError = error;
|
|
12968
|
+
}
|
|
12969
|
+
if (!spawnIfMissing) break;
|
|
12970
|
+
if (!spawned) {
|
|
12971
|
+
this.spawnDetached();
|
|
12972
|
+
spawned = true;
|
|
12973
|
+
}
|
|
12974
|
+
await delay(75);
|
|
12975
|
+
}
|
|
12976
|
+
throw lastError;
|
|
12977
|
+
}
|
|
12978
|
+
connectOnce() {
|
|
12979
|
+
this.socket?.destroy();
|
|
12980
|
+
this.socket = null;
|
|
12981
|
+
this.info = null;
|
|
12982
|
+
this.authToken = void 0;
|
|
12983
|
+
this.buffer = "";
|
|
12984
|
+
return new Promise((resolve16, reject) => {
|
|
12985
|
+
const socket = net.createConnection(this.endpoint);
|
|
12986
|
+
this.socket = socket;
|
|
12987
|
+
socket.setEncoding("utf8");
|
|
12988
|
+
const timer = setTimeout(() => {
|
|
12989
|
+
reject(new Error("Session Catalog handshake timed out"));
|
|
12990
|
+
socket.destroy();
|
|
12991
|
+
}, CONNECT_TIMEOUT_MS);
|
|
12992
|
+
timer.unref?.();
|
|
12993
|
+
this.connectResolve = () => {
|
|
12994
|
+
clearTimeout(timer);
|
|
12995
|
+
this.connectResolve = null;
|
|
12996
|
+
this.connectReject = null;
|
|
12997
|
+
resolve16();
|
|
12998
|
+
};
|
|
12999
|
+
this.connectReject = (error) => {
|
|
13000
|
+
clearTimeout(timer);
|
|
13001
|
+
this.connectResolve = null;
|
|
13002
|
+
this.connectReject = null;
|
|
13003
|
+
reject(error);
|
|
13004
|
+
};
|
|
13005
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
13006
|
+
socket.on("error", (error) => {
|
|
13007
|
+
if (!this.info) this.connectReject?.(error);
|
|
13008
|
+
});
|
|
13009
|
+
socket.on("close", () => this.onClose(socket));
|
|
13010
|
+
});
|
|
13011
|
+
}
|
|
13012
|
+
currentAuthToken() {
|
|
13013
|
+
if (this.authToken === void 0) {
|
|
13014
|
+
try {
|
|
13015
|
+
const parsed = JSON.parse(
|
|
13016
|
+
fs3.readFileSync(sessionCatalogProjectServerMetadataPath(this.options.projectDir), "utf8")
|
|
13017
|
+
);
|
|
13018
|
+
if (typeof parsed.authToken === "string" && parsed.authToken)
|
|
13019
|
+
this.authToken = parsed.authToken;
|
|
13020
|
+
} catch {
|
|
13021
|
+
}
|
|
13022
|
+
}
|
|
13023
|
+
return this.authToken;
|
|
13024
|
+
}
|
|
13025
|
+
request(message, timeoutMs) {
|
|
13026
|
+
const socket = this.socket;
|
|
13027
|
+
if (!socket || socket.destroyed)
|
|
13028
|
+
return Promise.reject(new Error("Session Catalog connection is unavailable"));
|
|
13029
|
+
const id = this.nextId++;
|
|
13030
|
+
if (this.pending.size >= MAX_PENDING_REQUESTS) {
|
|
13031
|
+
return Promise.reject(
|
|
13032
|
+
new Error(`Session Catalog pending request limit reached (${MAX_PENDING_REQUESTS})`)
|
|
13033
|
+
);
|
|
13034
|
+
}
|
|
13035
|
+
const encoded = encodeSessionCatalogMessage({
|
|
13036
|
+
...message,
|
|
13037
|
+
id,
|
|
13038
|
+
authToken: this.currentAuthToken()
|
|
13039
|
+
});
|
|
13040
|
+
if (encoded.length > SESSION_CATALOG_MAX_FRAME_CHARS)
|
|
13041
|
+
return Promise.reject(new Error("Session Catalog request exceeded frame limit"));
|
|
13042
|
+
return new Promise((resolve16, reject) => {
|
|
13043
|
+
const timer = setTimeout(() => {
|
|
13044
|
+
const pending = this.pending.get(id);
|
|
13045
|
+
if (!pending) return;
|
|
13046
|
+
this.pending.delete(id);
|
|
13047
|
+
pending.reject(
|
|
13048
|
+
new Error(
|
|
13049
|
+
`Session Catalog ${message.type === "request" ? message.op : message.type} timed out`
|
|
13050
|
+
)
|
|
13051
|
+
);
|
|
13052
|
+
}, timeoutMs);
|
|
13053
|
+
timer.unref?.();
|
|
13054
|
+
this.pending.set(id, { resolve: resolve16, reject, timer });
|
|
13055
|
+
socket.write(encoded);
|
|
13056
|
+
});
|
|
13057
|
+
}
|
|
13058
|
+
onData(socket, chunk) {
|
|
13059
|
+
if (socket !== this.socket) return;
|
|
13060
|
+
this.buffer += chunk;
|
|
13061
|
+
if (this.buffer.length > SESSION_CATALOG_MAX_FRAME_CHARS) {
|
|
13062
|
+
socket.destroy(new Error("Session Catalog response exceeded frame limit"));
|
|
13063
|
+
return;
|
|
13064
|
+
}
|
|
13065
|
+
while (true) {
|
|
13066
|
+
const newline = this.buffer.indexOf("\n");
|
|
13067
|
+
if (newline < 0) return;
|
|
13068
|
+
const line = this.buffer.slice(0, newline);
|
|
13069
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
13070
|
+
if (!line) continue;
|
|
13071
|
+
try {
|
|
13072
|
+
this.onMessage(JSON.parse(line));
|
|
13073
|
+
} catch {
|
|
13074
|
+
socket.destroy(new Error("Invalid Session Catalog response"));
|
|
13075
|
+
return;
|
|
13076
|
+
}
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
onMessage(message) {
|
|
13080
|
+
if (message.type === "hello") {
|
|
13081
|
+
if (message.protocolVersion !== SESSION_CATALOG_PROTOCOL_VERSION) {
|
|
13082
|
+
this.connectReject?.(
|
|
13083
|
+
new Error(
|
|
13084
|
+
`Session Catalog protocol mismatch: client=${SESSION_CATALOG_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
13085
|
+
)
|
|
13086
|
+
);
|
|
13087
|
+
this.socket?.destroy();
|
|
13088
|
+
return;
|
|
13089
|
+
}
|
|
13090
|
+
if (normalize2(message.projectDir) !== normalize2(this.options.projectDir) || normalize2(message.projectRoot) !== normalize2(this.options.projectRoot)) {
|
|
13091
|
+
this.connectReject?.(new Error("Session Catalog project identity mismatch"));
|
|
13092
|
+
this.socket?.destroy();
|
|
13093
|
+
return;
|
|
13094
|
+
}
|
|
13095
|
+
this.info = message;
|
|
13096
|
+
this.connectResolve?.();
|
|
13097
|
+
return;
|
|
13098
|
+
}
|
|
13099
|
+
if (message.type === "event") {
|
|
13100
|
+
for (const listener of this.eventListeners) {
|
|
13101
|
+
try {
|
|
13102
|
+
listener(message.event);
|
|
13103
|
+
} catch {
|
|
13104
|
+
}
|
|
13105
|
+
}
|
|
13106
|
+
return;
|
|
13107
|
+
}
|
|
13108
|
+
const pending = this.pending.get(message.id);
|
|
13109
|
+
if (!pending) return;
|
|
13110
|
+
this.pending.delete(message.id);
|
|
13111
|
+
clearTimeout(pending.timer);
|
|
13112
|
+
if (message.ok) pending.resolve(message.result);
|
|
13113
|
+
else {
|
|
13114
|
+
const error = new Error(message.error);
|
|
13115
|
+
if (message.errorName) error.name = message.errorName;
|
|
13116
|
+
pending.reject(error);
|
|
13117
|
+
}
|
|
13118
|
+
}
|
|
13119
|
+
onClose(socket) {
|
|
13120
|
+
if (socket !== this.socket) return;
|
|
13121
|
+
this.socket = null;
|
|
13122
|
+
this.info = null;
|
|
13123
|
+
this.authToken = void 0;
|
|
13124
|
+
const error = new Error("Session Catalog connection closed");
|
|
13125
|
+
this.connectReject?.(error);
|
|
13126
|
+
this.connectResolve = null;
|
|
13127
|
+
this.connectReject = null;
|
|
13128
|
+
for (const pending of this.pending.values()) {
|
|
13129
|
+
clearTimeout(pending.timer);
|
|
13130
|
+
pending.reject(error);
|
|
13131
|
+
}
|
|
13132
|
+
this.pending.clear();
|
|
13133
|
+
this.scheduleSubscriptionReconnect();
|
|
13134
|
+
}
|
|
13135
|
+
scheduleSubscriptionReconnect() {
|
|
13136
|
+
if (this.explicitlyClosed || this.eventListeners.size === 0 || this.reconnectTimer) return;
|
|
13137
|
+
this.reconnectTimer = setTimeout(() => {
|
|
13138
|
+
this.reconnectTimer = void 0;
|
|
13139
|
+
void this.call("subscribe", {}).catch(() => this.scheduleSubscriptionReconnect());
|
|
13140
|
+
}, 250);
|
|
13141
|
+
this.reconnectTimer.unref?.();
|
|
13142
|
+
}
|
|
13143
|
+
spawnDetached() {
|
|
13144
|
+
const url = resolveSessionCatalogProjectServerUrl();
|
|
13145
|
+
if (!url) throw new Error("Built Session Catalog project server is unavailable");
|
|
13146
|
+
const child = spawn2(
|
|
13147
|
+
process.execPath,
|
|
13148
|
+
[
|
|
13149
|
+
fileURLToPath3(url),
|
|
13150
|
+
"--project-dir",
|
|
13151
|
+
this.options.projectDir,
|
|
13152
|
+
"--project-root",
|
|
13153
|
+
this.options.projectRoot
|
|
13154
|
+
],
|
|
13155
|
+
{ detached: true, stdio: "ignore", windowsHide: true, env: process.env }
|
|
13156
|
+
);
|
|
13157
|
+
child.unref();
|
|
13158
|
+
}
|
|
13159
|
+
};
|
|
12777
13160
|
|
|
12778
13161
|
// src/utils/message-invariants.ts
|
|
12779
13162
|
function repairToolUseAdjacency(messages) {
|
|
@@ -12910,14 +13293,14 @@ function ulid(seedTime = Date.now()) {
|
|
|
12910
13293
|
}
|
|
12911
13294
|
|
|
12912
13295
|
// src/utils/session-scoped-path.ts
|
|
12913
|
-
import * as
|
|
13296
|
+
import * as path16 from "node:path";
|
|
12914
13297
|
function sessionScopedPath(dir, sessionId, suffix) {
|
|
12915
13298
|
if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
|
|
12916
13299
|
throw invalid(sessionId);
|
|
12917
13300
|
}
|
|
12918
|
-
const resolved =
|
|
12919
|
-
const rel =
|
|
12920
|
-
if (rel.startsWith("..") ||
|
|
13301
|
+
const resolved = path16.resolve(dir, `${sessionId}${suffix}`);
|
|
13302
|
+
const rel = path16.relative(path16.resolve(dir), resolved);
|
|
13303
|
+
if (rel.startsWith("..") || path16.isAbsolute(rel)) {
|
|
12921
13304
|
throw invalid(sessionId);
|
|
12922
13305
|
}
|
|
12923
13306
|
return resolved;
|
|
@@ -12939,7 +13322,7 @@ function truncate3(s, max) {
|
|
|
12939
13322
|
// src/storage/file-session-writer.ts
|
|
12940
13323
|
import { closeSync, createReadStream as createReadStream2, fsyncSync, openSync, writeSync } from "node:fs";
|
|
12941
13324
|
import * as fsp7 from "node:fs/promises";
|
|
12942
|
-
import * as
|
|
13325
|
+
import * as path17 from "node:path";
|
|
12943
13326
|
import { createInterface as createInterface2 } from "node:readline";
|
|
12944
13327
|
|
|
12945
13328
|
// src/storage/session-workspace-checkpoints.ts
|
|
@@ -13205,7 +13588,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
13205
13588
|
this.meta = meta;
|
|
13206
13589
|
this.events = events;
|
|
13207
13590
|
this.resumed = opts.resumed ?? false;
|
|
13208
|
-
this.manifestFile = opts.dir ?
|
|
13591
|
+
this.manifestFile = opts.dir ? path17.join(opts.dir, `${path17.basename(id)}.summary.json`) : "";
|
|
13209
13592
|
this.filePath = opts.filePath ?? "";
|
|
13210
13593
|
this.secretScrubber = opts.secretScrubber;
|
|
13211
13594
|
this.checkpointCas = opts.checkpointCas;
|
|
@@ -14094,10 +14477,10 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
14094
14477
|
};
|
|
14095
14478
|
|
|
14096
14479
|
// src/storage/session-checkpoint-cas.ts
|
|
14097
|
-
import { spawn as
|
|
14098
|
-
import { createHash as
|
|
14480
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
14481
|
+
import { createHash as createHash3, randomUUID as randomUUID11 } from "node:crypto";
|
|
14099
14482
|
import * as fsp8 from "node:fs/promises";
|
|
14100
|
-
import * as
|
|
14483
|
+
import * as path18 from "node:path";
|
|
14101
14484
|
|
|
14102
14485
|
// src/storage/storage-concurrency.ts
|
|
14103
14486
|
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
@@ -14125,16 +14508,16 @@ var MAX_GIT_OUTPUT = 16 * 1024 * 1024;
|
|
|
14125
14508
|
var MAX_BLOB_BYTES = 64 * 1024 * 1024;
|
|
14126
14509
|
var MAX_CHECKPOINT_BYTES = 512 * 1024 * 1024;
|
|
14127
14510
|
function sha256(content) {
|
|
14128
|
-
return
|
|
14511
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
14129
14512
|
}
|
|
14130
14513
|
function isInside(root, target) {
|
|
14131
|
-
const relative4 =
|
|
14132
|
-
return relative4 === "" || !relative4.startsWith("..") && !
|
|
14514
|
+
const relative4 = path18.relative(root, target);
|
|
14515
|
+
return relative4 === "" || !relative4.startsWith("..") && !path18.isAbsolute(relative4);
|
|
14133
14516
|
}
|
|
14134
14517
|
function normalizeRelative(input) {
|
|
14135
|
-
if (!input ||
|
|
14518
|
+
if (!input || path18.isAbsolute(input)) return null;
|
|
14136
14519
|
const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
14137
|
-
const resolved =
|
|
14520
|
+
const resolved = path18.posix.normalize(normalized);
|
|
14138
14521
|
if (!resolved || resolved === "." || resolved === ".." || resolved.startsWith("../")) return null;
|
|
14139
14522
|
return resolved;
|
|
14140
14523
|
}
|
|
@@ -14149,8 +14532,8 @@ var SessionCheckpointCas = class {
|
|
|
14149
14532
|
projectRoot;
|
|
14150
14533
|
runGit;
|
|
14151
14534
|
constructor(opts) {
|
|
14152
|
-
this.rootDir =
|
|
14153
|
-
this.projectRoot =
|
|
14535
|
+
this.rootDir = path18.resolve(opts.rootDir);
|
|
14536
|
+
this.projectRoot = path18.resolve(opts.projectRoot);
|
|
14154
14537
|
this.runGit = opts.runGit ?? defaultRunGit;
|
|
14155
14538
|
}
|
|
14156
14539
|
async capture(_sessionId, _promptIndex) {
|
|
@@ -14175,7 +14558,7 @@ var SessionCheckpointCas = class {
|
|
|
14175
14558
|
relativePaths,
|
|
14176
14559
|
CAPTURE_CONCURRENCY,
|
|
14177
14560
|
async (relative4) => {
|
|
14178
|
-
const absolute =
|
|
14561
|
+
const absolute = path18.resolve(this.projectRoot, ...relative4.split("/"));
|
|
14179
14562
|
if (!isInside(this.projectRoot, absolute)) {
|
|
14180
14563
|
unresolved.push({ path: relative4, reason: "path escapes project root" });
|
|
14181
14564
|
return null;
|
|
@@ -14184,8 +14567,8 @@ var SessionCheckpointCas = class {
|
|
|
14184
14567
|
const stat10 = await fsp8.lstat(absolute);
|
|
14185
14568
|
if (stat10.isSymbolicLink()) {
|
|
14186
14569
|
const linkTarget = await fsp8.readlink(absolute);
|
|
14187
|
-
const resolvedLink =
|
|
14188
|
-
if (
|
|
14570
|
+
const resolvedLink = path18.resolve(path18.dirname(absolute), linkTarget);
|
|
14571
|
+
if (path18.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
|
|
14189
14572
|
unresolved.push({
|
|
14190
14573
|
path: relative4,
|
|
14191
14574
|
reason: "symlink target escapes project root"
|
|
@@ -14247,7 +14630,7 @@ var SessionCheckpointCas = class {
|
|
|
14247
14630
|
};
|
|
14248
14631
|
}
|
|
14249
14632
|
async materialize(checkpoint, targetRoot) {
|
|
14250
|
-
const target =
|
|
14633
|
+
const target = path18.resolve(targetRoot);
|
|
14251
14634
|
if (target === this.projectRoot) {
|
|
14252
14635
|
throw new Error("Refusing to materialize a workspace checkpoint over the parent project root");
|
|
14253
14636
|
}
|
|
@@ -14287,10 +14670,10 @@ var SessionCheckpointCas = class {
|
|
|
14287
14670
|
try {
|
|
14288
14671
|
const output = await this.safeOutputPath(target, realTarget, entry.path);
|
|
14289
14672
|
if (entry.state === "symlink") {
|
|
14290
|
-
if (
|
|
14673
|
+
if (path18.isAbsolute(entry.linkTarget)) {
|
|
14291
14674
|
throw new Error("absolute symlink target refused");
|
|
14292
14675
|
}
|
|
14293
|
-
const resolvedLink =
|
|
14676
|
+
const resolvedLink = path18.resolve(path18.dirname(output), entry.linkTarget);
|
|
14294
14677
|
if (!isInside(target, resolvedLink)) throw new Error("symlink target escapes checkpoint root");
|
|
14295
14678
|
}
|
|
14296
14679
|
prepared.push({
|
|
@@ -14318,7 +14701,7 @@ var SessionCheckpointCas = class {
|
|
|
14318
14701
|
await fsp8.unlink(output).catch((err) => {
|
|
14319
14702
|
if (err.code !== "ENOENT") throw err;
|
|
14320
14703
|
});
|
|
14321
|
-
await fsp8.mkdir(
|
|
14704
|
+
await fsp8.mkdir(path18.dirname(output), { recursive: true });
|
|
14322
14705
|
await fsp8.symlink(entry.linkTarget, output);
|
|
14323
14706
|
writtenFiles.push(entry.path);
|
|
14324
14707
|
continue;
|
|
@@ -14335,15 +14718,15 @@ var SessionCheckpointCas = class {
|
|
|
14335
14718
|
}
|
|
14336
14719
|
objectPath(hash) {
|
|
14337
14720
|
if (!HASH_RE.test(hash)) throw new Error(`Invalid CAS object hash: ${hash}`);
|
|
14338
|
-
return
|
|
14721
|
+
return path18.join(this.rootDir, "objects", hash.slice(0, 2), hash.slice(2));
|
|
14339
14722
|
}
|
|
14340
14723
|
manifestPath(hash) {
|
|
14341
14724
|
if (!HASH_RE.test(hash)) throw new Error(`Invalid checkpoint manifest hash: ${hash}`);
|
|
14342
|
-
return
|
|
14725
|
+
return path18.join(this.rootDir, "manifests", `${hash}.json`);
|
|
14343
14726
|
}
|
|
14344
14727
|
async putBlob(hash, content) {
|
|
14345
14728
|
const target = this.objectPath(hash);
|
|
14346
|
-
await fsp8.mkdir(
|
|
14729
|
+
await fsp8.mkdir(path18.dirname(target), { recursive: true });
|
|
14347
14730
|
try {
|
|
14348
14731
|
const existing = await fsp8.readFile(target);
|
|
14349
14732
|
if (sha256(existing) !== hash) throw new Error(`Corrupt CAS object collision: ${hash}`);
|
|
@@ -14351,9 +14734,9 @@ var SessionCheckpointCas = class {
|
|
|
14351
14734
|
} catch (err) {
|
|
14352
14735
|
if (err.code !== "ENOENT") throw err;
|
|
14353
14736
|
}
|
|
14354
|
-
const temp =
|
|
14355
|
-
|
|
14356
|
-
`.${
|
|
14737
|
+
const temp = path18.join(
|
|
14738
|
+
path18.dirname(target),
|
|
14739
|
+
`.${path18.basename(target)}.${process.pid}.${randomUUID11()}.tmp`
|
|
14357
14740
|
);
|
|
14358
14741
|
let handle;
|
|
14359
14742
|
try {
|
|
@@ -14414,7 +14797,7 @@ var SessionCheckpointCas = class {
|
|
|
14414
14797
|
async safeOutputPath(target, realTarget, relative4) {
|
|
14415
14798
|
const normalized = normalizeRelative(relative4);
|
|
14416
14799
|
if (!normalized) throw new Error("invalid relative path");
|
|
14417
|
-
const output =
|
|
14800
|
+
const output = path18.resolve(target, ...normalized.split("/"));
|
|
14418
14801
|
if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
|
|
14419
14802
|
let probe = output;
|
|
14420
14803
|
for (; ; ) {
|
|
@@ -14424,7 +14807,7 @@ var SessionCheckpointCas = class {
|
|
|
14424
14807
|
return output;
|
|
14425
14808
|
} catch (err) {
|
|
14426
14809
|
if (err.code !== "ENOENT") throw err;
|
|
14427
|
-
const parent =
|
|
14810
|
+
const parent = path18.dirname(probe);
|
|
14428
14811
|
if (parent === probe) throw err;
|
|
14429
14812
|
probe = parent;
|
|
14430
14813
|
}
|
|
@@ -14432,14 +14815,14 @@ var SessionCheckpointCas = class {
|
|
|
14432
14815
|
}
|
|
14433
14816
|
};
|
|
14434
14817
|
function defaultRunGit(args, cwd) {
|
|
14435
|
-
return new Promise((
|
|
14818
|
+
return new Promise((resolve16) => {
|
|
14436
14819
|
const stdoutChunks = [];
|
|
14437
14820
|
const stderrChunks = [];
|
|
14438
14821
|
let stdoutBytes = 0;
|
|
14439
14822
|
let stderrBytes = 0;
|
|
14440
14823
|
let stdoutTruncated = false;
|
|
14441
14824
|
let stderrTruncated = false;
|
|
14442
|
-
const child =
|
|
14825
|
+
const child = spawn3("git", args, {
|
|
14443
14826
|
cwd,
|
|
14444
14827
|
env: buildChildEnv(),
|
|
14445
14828
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14475,8 +14858,8 @@ function defaultRunGit(args, cwd) {
|
|
|
14475
14858
|
stdoutTruncated,
|
|
14476
14859
|
stderrTruncated
|
|
14477
14860
|
});
|
|
14478
|
-
child.on("error", (err) =>
|
|
14479
|
-
child.on("close", (code) =>
|
|
14861
|
+
child.on("error", (err) => resolve16(result(1, err.message)));
|
|
14862
|
+
child.on("close", (code) => resolve16(result(code ?? 1)));
|
|
14480
14863
|
});
|
|
14481
14864
|
}
|
|
14482
14865
|
|
|
@@ -14488,13 +14871,13 @@ function generateSessionId(startedAt, _model) {
|
|
|
14488
14871
|
}
|
|
14489
14872
|
|
|
14490
14873
|
// src/storage/session-id-resolver.ts
|
|
14491
|
-
import * as
|
|
14874
|
+
import * as path19 from "node:path";
|
|
14492
14875
|
function resolveSessionId(query, candidateIds) {
|
|
14493
14876
|
const normalized = query.trim();
|
|
14494
14877
|
if (!normalized) return { status: "missing", query: normalized };
|
|
14495
14878
|
const uniqueIds = [...new Set(candidateIds)];
|
|
14496
14879
|
if (uniqueIds.includes(normalized)) return { status: "resolved", id: normalized };
|
|
14497
|
-
const leaf = (id) =>
|
|
14880
|
+
const leaf = (id) => path19.posix.basename(id.replace(/\\/g, "/"));
|
|
14498
14881
|
const exactLeafMatches = uniqueIds.filter((id) => leaf(id) === normalized);
|
|
14499
14882
|
if (exactLeafMatches.length === 1) {
|
|
14500
14883
|
return { status: "resolved", id: exactLeafMatches[0] };
|
|
@@ -14543,15 +14926,15 @@ function scrubPersistedSessionSummary(summary, scrubber2) {
|
|
|
14543
14926
|
}
|
|
14544
14927
|
|
|
14545
14928
|
// src/storage/session-resume-validation.ts
|
|
14546
|
-
import { createHash as
|
|
14929
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
14547
14930
|
import * as fsp9 from "node:fs/promises";
|
|
14548
|
-
import * as
|
|
14931
|
+
import * as path20 from "node:path";
|
|
14549
14932
|
var MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;
|
|
14550
14933
|
var VALIDATION_CONCURRENCY = 8;
|
|
14551
14934
|
var NOTICE_PATH_LIMIT = 20;
|
|
14552
14935
|
function isInside2(root, target) {
|
|
14553
|
-
const relative4 =
|
|
14554
|
-
return relative4 === "" || !relative4.startsWith("..") && !
|
|
14936
|
+
const relative4 = path20.relative(root, target);
|
|
14937
|
+
return relative4 === "" || !relative4.startsWith("..") && !path20.isAbsolute(relative4);
|
|
14555
14938
|
}
|
|
14556
14939
|
function errno(err) {
|
|
14557
14940
|
return err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
|
|
@@ -14562,7 +14945,7 @@ function latestObservations(events, projectRoot) {
|
|
|
14562
14945
|
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)) {
|
|
14563
14946
|
continue;
|
|
14564
14947
|
}
|
|
14565
|
-
const normalized =
|
|
14948
|
+
const normalized = path20.resolve(projectRoot, event.path);
|
|
14566
14949
|
latest.set(normalized, {
|
|
14567
14950
|
path: normalized,
|
|
14568
14951
|
hash: event.hash.toLowerCase(),
|
|
@@ -14611,7 +14994,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
|
|
|
14611
14994
|
};
|
|
14612
14995
|
}
|
|
14613
14996
|
const content = await fsp9.readFile(realFile, "utf8");
|
|
14614
|
-
const actualHash =
|
|
14997
|
+
const actualHash = createHash4("sha256").update(content, "utf8").digest("hex");
|
|
14615
14998
|
if (actualHash === observation.hash) return null;
|
|
14616
14999
|
return { ...base, status: "modified", actualHash };
|
|
14617
15000
|
} catch (err) {
|
|
@@ -14620,7 +15003,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
|
|
|
14620
15003
|
}
|
|
14621
15004
|
}
|
|
14622
15005
|
async function validateResumeFileObservations(events, projectRoot) {
|
|
14623
|
-
const lexicalRoot =
|
|
15006
|
+
const lexicalRoot = path20.resolve(projectRoot);
|
|
14624
15007
|
const realRoot = await fsp9.realpath(lexicalRoot).catch(() => lexicalRoot);
|
|
14625
15008
|
const observations = latestObservations(events, lexicalRoot);
|
|
14626
15009
|
const results = await mapWithConcurrency(
|
|
@@ -14636,9 +15019,9 @@ async function validateResumeFileObservations(events, projectRoot) {
|
|
|
14636
15019
|
}
|
|
14637
15020
|
function formatResumeValidationNotice(validation, projectRoot) {
|
|
14638
15021
|
if (validation.staleFiles.length === 0) return null;
|
|
14639
|
-
const root =
|
|
15022
|
+
const root = path20.resolve(projectRoot);
|
|
14640
15023
|
const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
|
|
14641
|
-
const relative4 =
|
|
15024
|
+
const relative4 = path20.relative(root, entry.path);
|
|
14642
15025
|
const display = isInside2(root, entry.path) ? relative4 || "." : entry.path;
|
|
14643
15026
|
return `- ${JSON.stringify(display)} [${entry.status}]`;
|
|
14644
15027
|
});
|
|
@@ -14663,22 +15046,22 @@ function formatInterruptedToolNotice(pendingToolUseCount) {
|
|
|
14663
15046
|
|
|
14664
15047
|
// src/storage/session-store/delete-session-artifacts.ts
|
|
14665
15048
|
import * as fsp10 from "node:fs/promises";
|
|
14666
|
-
import * as
|
|
15049
|
+
import * as path22 from "node:path";
|
|
14667
15050
|
|
|
14668
15051
|
// src/storage/session-store/paths.ts
|
|
14669
|
-
import * as
|
|
15052
|
+
import * as path21 from "node:path";
|
|
14670
15053
|
function sessionPath(storeDir, id, ext) {
|
|
14671
15054
|
return sessionScopedPath(storeDir, id, ext);
|
|
14672
15055
|
}
|
|
14673
15056
|
function shardManifestPath(storeDir, shardKey) {
|
|
14674
|
-
return shardKey ?
|
|
15057
|
+
return shardKey ? path21.join(storeDir, shardKey, "_manifest.json") : path21.join(storeDir, "_manifest.json");
|
|
14675
15058
|
}
|
|
14676
15059
|
function shardKeyForSessionId(id) {
|
|
14677
|
-
const dirName =
|
|
15060
|
+
const dirName = path21.dirname(id);
|
|
14678
15061
|
return dirName === "." ? "" : dirName;
|
|
14679
15062
|
}
|
|
14680
15063
|
async function ensureShardDir(storeDir, id) {
|
|
14681
|
-
const dirPath =
|
|
15064
|
+
const dirPath = path21.dirname(sessionScopedPath(storeDir, id, ""));
|
|
14682
15065
|
await ensureDir(dirPath);
|
|
14683
15066
|
return dirPath;
|
|
14684
15067
|
}
|
|
@@ -14689,9 +15072,9 @@ async function deleteSessionArtifacts({
|
|
|
14689
15072
|
id,
|
|
14690
15073
|
jsonlPath
|
|
14691
15074
|
}) {
|
|
14692
|
-
const shardDir =
|
|
14693
|
-
const base =
|
|
14694
|
-
const sessDir =
|
|
15075
|
+
const shardDir = path22.dirname(jsonlPath);
|
|
15076
|
+
const base = path22.basename(id);
|
|
15077
|
+
const sessDir = path22.join(shardDir, base);
|
|
14695
15078
|
const deletions = [
|
|
14696
15079
|
fsp10.unlink(jsonlPath),
|
|
14697
15080
|
fsp10.unlink(sessionPath(rootDir, id, ".summary.json")),
|
|
@@ -14750,7 +15133,7 @@ function isSessionJsonlFileName(name) {
|
|
|
14750
15133
|
|
|
14751
15134
|
// src/storage/session-store/directory-session-files.ts
|
|
14752
15135
|
import * as fsp11 from "node:fs/promises";
|
|
14753
|
-
import * as
|
|
15136
|
+
import * as path23 from "node:path";
|
|
14754
15137
|
function sessionIdForFile(prefix, name) {
|
|
14755
15138
|
const base = name.replace(/\.jsonl$/, "");
|
|
14756
15139
|
return prefix ? `${prefix}/${base}` : base;
|
|
@@ -14772,12 +15155,12 @@ async function collectSessionFiles(dir, prefix = "", depth = 0) {
|
|
|
14772
15155
|
if (entry.isDirectory()) {
|
|
14773
15156
|
dirEntries.push(entry);
|
|
14774
15157
|
} else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {
|
|
14775
|
-
files.push({ id: sessionIdForFile(prefix, entry.name), filePath:
|
|
15158
|
+
files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path23.join(dir, entry.name) });
|
|
14776
15159
|
}
|
|
14777
15160
|
}
|
|
14778
15161
|
const childFileArrays = await Promise.all(
|
|
14779
15162
|
dirEntries.map(
|
|
14780
|
-
(entry) => collectSessionFiles(
|
|
15163
|
+
(entry) => collectSessionFiles(path23.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
|
|
14781
15164
|
)
|
|
14782
15165
|
);
|
|
14783
15166
|
return [...childFileArrays.flat(), ...files];
|
|
@@ -14801,7 +15184,7 @@ async function collectSessionIds(dir, prefix = "", depth = 0) {
|
|
|
14801
15184
|
}
|
|
14802
15185
|
const childIdArrays = await Promise.all(
|
|
14803
15186
|
dirEntries.map(
|
|
14804
|
-
(entry) => collectSessionIds(
|
|
15187
|
+
(entry) => collectSessionIds(path23.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
|
|
14805
15188
|
)
|
|
14806
15189
|
);
|
|
14807
15190
|
return [...childIdArrays.flat(), ...fileIds];
|
|
@@ -14843,7 +15226,7 @@ function emitSessionStoreError(events, sessionId, filePath, operation, error, re
|
|
|
14843
15226
|
}
|
|
14844
15227
|
|
|
14845
15228
|
// src/storage/session-store/fork-session.ts
|
|
14846
|
-
import { createHash as
|
|
15229
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
14847
15230
|
|
|
14848
15231
|
// src/storage/session-store/replay.ts
|
|
14849
15232
|
function isReplayableMessage(value) {
|
|
@@ -14915,7 +15298,7 @@ async function forkSession(host, id, opts = {}) {
|
|
|
14915
15298
|
}
|
|
14916
15299
|
const parentPrefix = parent.events.slice(0, boundary + 1);
|
|
14917
15300
|
const workspaceCheckpoint = targetCheckpoint?.workspaceCheckpoint;
|
|
14918
|
-
const checkpointHash =
|
|
15301
|
+
const checkpointHash = createHash5("sha256").update(parentPrefix.map((event) => JSON.stringify(event)).join("\n") + "\n", "utf8").digest("hex");
|
|
14919
15302
|
const inherited = parentPrefix.filter(inheritsIntoFork);
|
|
14920
15303
|
const writer = await host.create({
|
|
14921
15304
|
id: "",
|
|
@@ -15258,6 +15641,16 @@ function replaySessionEvent(params) {
|
|
|
15258
15641
|
} else {
|
|
15259
15642
|
emitDamaged(params, "Ignored malformed messages_replaced event");
|
|
15260
15643
|
}
|
|
15644
|
+
} else if (ev.type === "messages_dropped" && ev.version === 1) {
|
|
15645
|
+
if (exactJournalActive && Number.isInteger(ev.count) && ev.count > 0) {
|
|
15646
|
+
messages.splice(0, Math.min(ev.count, messages.length));
|
|
15647
|
+
openToolUses.clear();
|
|
15648
|
+
for (const current of messages) trackMessageToolState(current, openToolUses);
|
|
15649
|
+
} else if (!exactJournalActive) {
|
|
15650
|
+
emitDamaged(params, "Ignored messages_dropped event outside the exact journal");
|
|
15651
|
+
} else {
|
|
15652
|
+
emitDamaged(params, `Ignored malformed messages_dropped event (count ${String(ev.count)})`);
|
|
15653
|
+
}
|
|
15261
15654
|
} else if (ev.type === "context_snapshot") {
|
|
15262
15655
|
if (!applyContextSnapshot(messages, openToolUses, ev.messages)) {
|
|
15263
15656
|
emitDamaged(params, "Ignored malformed context_snapshot event");
|
|
@@ -15312,7 +15705,7 @@ function emitDamaged(params, detail) {
|
|
|
15312
15705
|
|
|
15313
15706
|
// src/storage/session-store/prune-helpers.ts
|
|
15314
15707
|
import * as fsp13 from "node:fs/promises";
|
|
15315
|
-
import * as
|
|
15708
|
+
import * as path24 from "node:path";
|
|
15316
15709
|
function isPrunableSessionJsonl(name) {
|
|
15317
15710
|
return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
15318
15711
|
}
|
|
@@ -15320,7 +15713,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
|
|
|
15320
15713
|
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
15321
15714
|
let deleted = 0;
|
|
15322
15715
|
const pruneFile = async (dir, name, prefix) => {
|
|
15323
|
-
const jsonlPath =
|
|
15716
|
+
const jsonlPath = path24.join(dir, name);
|
|
15324
15717
|
try {
|
|
15325
15718
|
const stat10 = await fsp13.stat(jsonlPath);
|
|
15326
15719
|
if (stat10.mtimeMs >= cutoff) return;
|
|
@@ -15339,7 +15732,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
|
|
|
15339
15732
|
continue;
|
|
15340
15733
|
}
|
|
15341
15734
|
if (!entry.isDirectory()) continue;
|
|
15342
|
-
const dateDir =
|
|
15735
|
+
const dateDir = path24.join(storeDir, entry.name);
|
|
15343
15736
|
const files = await fsp13.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
15344
15737
|
for (const file of files) {
|
|
15345
15738
|
if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
|
|
@@ -15348,7 +15741,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
|
|
|
15348
15741
|
}
|
|
15349
15742
|
for (const entry of entries) {
|
|
15350
15743
|
if (!entry.isDirectory()) continue;
|
|
15351
|
-
const dateDir =
|
|
15744
|
+
const dateDir = path24.join(storeDir, entry.name);
|
|
15352
15745
|
try {
|
|
15353
15746
|
const remaining = await fsp13.readdir(dateDir);
|
|
15354
15747
|
if (remaining.length === 0) {
|
|
@@ -15681,6 +16074,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15681
16074
|
logger;
|
|
15682
16075
|
onAppend;
|
|
15683
16076
|
onAppendBatch;
|
|
16077
|
+
/** Present in built production output; source-only tests retain the local compatibility path. */
|
|
16078
|
+
catalogClient;
|
|
16079
|
+
maintenanceHolderId = randomUUID12();
|
|
15684
16080
|
/**
|
|
15685
16081
|
* In-memory cache for load() results, keyed by session ID. The cache is
|
|
15686
16082
|
* invalidated when the file's mtimeMs or size changes (indicating the
|
|
@@ -15698,9 +16094,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15698
16094
|
static LIST_SCAN_CONCURRENCY = 32;
|
|
15699
16095
|
constructor(opts) {
|
|
15700
16096
|
this.dir = opts.dir;
|
|
15701
|
-
this.projectRoot = opts.projectRoot ?
|
|
16097
|
+
this.projectRoot = opts.projectRoot ? path25.resolve(opts.projectRoot) : void 0;
|
|
15702
16098
|
this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
|
|
15703
|
-
rootDir:
|
|
16099
|
+
rootDir: path25.join(this.dir, "_cas"),
|
|
15704
16100
|
projectRoot: this.projectRoot
|
|
15705
16101
|
}) : void 0;
|
|
15706
16102
|
this.events = opts.events;
|
|
@@ -15709,6 +16105,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15709
16105
|
this.logger = opts.logger;
|
|
15710
16106
|
this.onAppend = opts.onAppend;
|
|
15711
16107
|
this.onAppendBatch = opts.onAppendBatch;
|
|
16108
|
+
const builtRuntime = import.meta.url.includes("/dist/");
|
|
16109
|
+
this.catalogClient = this.projectRoot && (builtRuntime || process.env["WRONGSTACK_SESSION_CATALOG_FORCE"] === "1") && resolveSessionCatalogProjectServerUrl() ? new SessionCatalogProjectClient({
|
|
16110
|
+
projectDir: path25.dirname(this.dir),
|
|
16111
|
+
projectRoot: this.projectRoot
|
|
16112
|
+
}) : void 0;
|
|
15712
16113
|
}
|
|
15713
16114
|
/**
|
|
15714
16115
|
* Emit a structured warning. Uses the configured Logger when available;
|
|
@@ -15731,10 +16132,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15731
16132
|
clearLoadCache(sessionId) {
|
|
15732
16133
|
this.loadCache.clear(sessionId);
|
|
15733
16134
|
}
|
|
16135
|
+
async dispose() {
|
|
16136
|
+
await this.catalogClient?.close();
|
|
16137
|
+
this.clearLoadCache();
|
|
16138
|
+
}
|
|
15734
16139
|
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
15735
16140
|
/** Absolute path to the session index file. */
|
|
15736
16141
|
get indexFile() {
|
|
15737
|
-
return
|
|
16142
|
+
return path25.join(this.dir, "_index.jsonl");
|
|
15738
16143
|
}
|
|
15739
16144
|
/** Join session ID to its absolute path within the store directory. */
|
|
15740
16145
|
sessionPath(id, ext) {
|
|
@@ -15792,8 +16197,23 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15792
16197
|
if (!current) return null;
|
|
15793
16198
|
return current.name === void 0 ? {} : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };
|
|
15794
16199
|
},
|
|
15795
|
-
onClose: (s) => this.
|
|
16200
|
+
onClose: (s) => this.persistCatalogSummary(s)
|
|
15796
16201
|
});
|
|
16202
|
+
if (this.catalogClient) {
|
|
16203
|
+
await this.catalogClient.call("upsert_summary", {
|
|
16204
|
+
summary: {
|
|
16205
|
+
id,
|
|
16206
|
+
title: meta.title ?? "",
|
|
16207
|
+
startedAt,
|
|
16208
|
+
model: meta.model ?? "",
|
|
16209
|
+
provider: meta.provider ?? "",
|
|
16210
|
+
tokenTotal: 0,
|
|
16211
|
+
lastActivityAt: startedAt
|
|
16212
|
+
},
|
|
16213
|
+
transcriptRelativePath: `${id}.jsonl`,
|
|
16214
|
+
summaryRelativePath: `${id}.summary.json`
|
|
16215
|
+
});
|
|
16216
|
+
}
|
|
15797
16217
|
emitSessionStoreWrite(this.events, id, file, "create", "success", Date.now() - t0);
|
|
15798
16218
|
return writer;
|
|
15799
16219
|
} catch (err) {
|
|
@@ -15823,6 +16243,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15823
16243
|
return materializeCheckpoint(this.checkpointCas, checkpoint, targetRoot);
|
|
15824
16244
|
}
|
|
15825
16245
|
async resolveId(query) {
|
|
16246
|
+
if (this.catalogClient) {
|
|
16247
|
+
return this.catalogClient.call("resolve_id", { query });
|
|
16248
|
+
}
|
|
15826
16249
|
const normalized = query.trim();
|
|
15827
16250
|
if (!normalized) throw new Error("Session not found: (empty query)");
|
|
15828
16251
|
if (normalized) {
|
|
@@ -15929,7 +16352,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15929
16352
|
// Shard directory (sessions/<date>/) — must match create() so the
|
|
15930
16353
|
// .summary.json sidecar lands next to the JSONL instead of the
|
|
15931
16354
|
// sessions root (where summaryFor() would never find it).
|
|
15932
|
-
dir:
|
|
16355
|
+
dir: path25.dirname(file),
|
|
15933
16356
|
filePath: file,
|
|
15934
16357
|
secretScrubber: this.secretScrubber,
|
|
15935
16358
|
checkpointCas: this.checkpointCas,
|
|
@@ -15940,7 +16363,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
15940
16363
|
if (!current) return null;
|
|
15941
16364
|
return current.name === void 0 ? {} : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };
|
|
15942
16365
|
},
|
|
15943
|
-
onClose: (s) => this.
|
|
16366
|
+
onClose: (s) => this.persistCatalogSummary(s)
|
|
15944
16367
|
}
|
|
15945
16368
|
);
|
|
15946
16369
|
emitSessionStoreWrite(this.events, canonicalId, file, "resume", "success", Date.now() - t0);
|
|
@@ -16053,6 +16476,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16053
16476
|
});
|
|
16054
16477
|
}
|
|
16055
16478
|
async list(limit = 20) {
|
|
16479
|
+
if (this.catalogClient) {
|
|
16480
|
+
const records = await this.catalogClient.call("list_catalog", { limit });
|
|
16481
|
+
return this.scrubSummaries(records);
|
|
16482
|
+
}
|
|
16056
16483
|
try {
|
|
16057
16484
|
const indexed = await this.readIndex();
|
|
16058
16485
|
if (indexed.length > 0) {
|
|
@@ -16075,6 +16502,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16075
16502
|
*/
|
|
16076
16503
|
async listFiltered(criteria) {
|
|
16077
16504
|
const limit = criteria.limit ?? 100;
|
|
16505
|
+
if (this.catalogClient) {
|
|
16506
|
+
const records = await this.catalogClient.call("list_catalog", {
|
|
16507
|
+
limit: Math.min(1e3, Math.max(limit, 100)),
|
|
16508
|
+
...criteria.titleContains ? { search: criteria.titleContains } : {}
|
|
16509
|
+
});
|
|
16510
|
+
return this.scrubSummaries(records).filter((summary) => matchesSessionFilter(summary, criteria)).slice(0, limit);
|
|
16511
|
+
}
|
|
16078
16512
|
try {
|
|
16079
16513
|
const indexed = await this.readIndex();
|
|
16080
16514
|
if (indexed.length === 0) {
|
|
@@ -16124,6 +16558,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16124
16558
|
await this.appendToIndexStrict(summary).catch(() => {
|
|
16125
16559
|
});
|
|
16126
16560
|
}
|
|
16561
|
+
/** Final summary boundary: daemon is authoritative when available. */
|
|
16562
|
+
async persistCatalogSummary(summary) {
|
|
16563
|
+
if (!this.catalogClient) {
|
|
16564
|
+
await this.appendToIndex(summary);
|
|
16565
|
+
return;
|
|
16566
|
+
}
|
|
16567
|
+
await this.catalogClient.call("upsert_summary", {
|
|
16568
|
+
summary,
|
|
16569
|
+
transcriptRelativePath: `${summary.id}.jsonl`,
|
|
16570
|
+
summaryRelativePath: `${summary.id}.summary.json`
|
|
16571
|
+
});
|
|
16572
|
+
}
|
|
16127
16573
|
/** Append a tombstone entry for a deleted session. */
|
|
16128
16574
|
async writeTombstone(id) {
|
|
16129
16575
|
try {
|
|
@@ -16233,6 +16679,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16233
16679
|
* fresh _index.jsonl. Useful after manual cleanup or index corruption.
|
|
16234
16680
|
*/
|
|
16235
16681
|
async rebuildIndex() {
|
|
16682
|
+
if (this.catalogClient) {
|
|
16683
|
+
const result = await this.catalogClient.call("rebuild_catalog", {}, { timeoutMs: 12e4 });
|
|
16684
|
+
return result.indexed;
|
|
16685
|
+
}
|
|
16236
16686
|
const ids = await this.collectSessionIds(this.dir);
|
|
16237
16687
|
const summaries = await Promise.all(
|
|
16238
16688
|
ids.map((id) => this.summaryFor(id).catch(() => null))
|
|
@@ -16302,7 +16752,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16302
16752
|
});
|
|
16303
16753
|
}
|
|
16304
16754
|
async collectSessionFilesInShard(shardKey) {
|
|
16305
|
-
const dir = shardKey ?
|
|
16755
|
+
const dir = shardKey ? path25.join(this.dir, shardKey) : this.dir;
|
|
16306
16756
|
const entries = await this.collectSessionFiles(dir, shardKey);
|
|
16307
16757
|
return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
|
|
16308
16758
|
}
|
|
@@ -16398,10 +16848,35 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16398
16848
|
await this.writeTombstone(id);
|
|
16399
16849
|
}
|
|
16400
16850
|
async delete(id) {
|
|
16851
|
+
if (this.catalogClient) {
|
|
16852
|
+
const canonical = await this.resolveId(id);
|
|
16853
|
+
const lease = await this.catalogClient.call("acquire_maintenance", {
|
|
16854
|
+
sessionId: canonical,
|
|
16855
|
+
operation: "delete",
|
|
16856
|
+
holderId: this.maintenanceHolderId
|
|
16857
|
+
});
|
|
16858
|
+
try {
|
|
16859
|
+
await this.catalogClient.call("delete", { sessionId: canonical, lease });
|
|
16860
|
+
} catch (error) {
|
|
16861
|
+
await this.catalogClient.call("release_maintenance", { lease }).catch(() => void 0);
|
|
16862
|
+
throw error;
|
|
16863
|
+
}
|
|
16864
|
+
this.clearLoadCache(canonical);
|
|
16865
|
+
return;
|
|
16866
|
+
}
|
|
16401
16867
|
await assertSessionCanBeDeleted(id, this.isSessionInUse);
|
|
16402
16868
|
await this.deleteSession(id);
|
|
16403
16869
|
}
|
|
16404
16870
|
async rename(id, name) {
|
|
16871
|
+
if (this.catalogClient) {
|
|
16872
|
+
const canonical = await this.resolveId(id);
|
|
16873
|
+
const summary = await this.catalogClient.call("rename", {
|
|
16874
|
+
sessionId: canonical,
|
|
16875
|
+
name: sessionContentText(this.secretScrubber.scrub(name))
|
|
16876
|
+
});
|
|
16877
|
+
this.clearLoadCache(canonical);
|
|
16878
|
+
return summary;
|
|
16879
|
+
}
|
|
16405
16880
|
const trimmed = sessionContentText(this.secretScrubber.scrub(name));
|
|
16406
16881
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
16407
16882
|
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
@@ -16453,6 +16928,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16453
16928
|
return updated;
|
|
16454
16929
|
}
|
|
16455
16930
|
async prune(maxAgeDays = 30) {
|
|
16931
|
+
if (this.catalogClient) {
|
|
16932
|
+
return this.catalogClient.call("prune", {
|
|
16933
|
+
maxAgeDays,
|
|
16934
|
+
holderId: this.maintenanceHolderId
|
|
16935
|
+
});
|
|
16936
|
+
}
|
|
16456
16937
|
const deleted = await pruneSessionFiles(this.dir, maxAgeDays, (id) => this.deleteSession(id));
|
|
16457
16938
|
if (deleted > 0) {
|
|
16458
16939
|
await this.compactIndex().catch(() => void 0);
|
|
@@ -16460,20 +16941,81 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
16460
16941
|
return deleted;
|
|
16461
16942
|
}
|
|
16462
16943
|
async clearHistory(id) {
|
|
16463
|
-
await this.
|
|
16464
|
-
const
|
|
16465
|
-
|
|
16944
|
+
const canonical = this.catalogClient ? await this.resolveId(id) : id;
|
|
16945
|
+
const maintenance = this.catalogClient ? await this.catalogClient.call("acquire_maintenance", {
|
|
16946
|
+
sessionId: canonical,
|
|
16947
|
+
operation: "clear",
|
|
16948
|
+
holderId: this.maintenanceHolderId
|
|
16949
|
+
}) : void 0;
|
|
16950
|
+
await this.ensureShardDir(canonical);
|
|
16951
|
+
const file = this.sessionPath(canonical, ".jsonl");
|
|
16952
|
+
const meta = this.sessionPath(canonical, ".summary.json");
|
|
16953
|
+
const backupSuffix = maintenance ? `.${maintenance.leaseId}.clear-backup` : void 0;
|
|
16954
|
+
const fileBackup = backupSuffix ? `${file}${backupSuffix}` : void 0;
|
|
16955
|
+
const metaBackup = backupSuffix ? `${meta}${backupSuffix}` : void 0;
|
|
16956
|
+
let fileStaged = false;
|
|
16957
|
+
let metaStaged = false;
|
|
16466
16958
|
const record = `${JSON.stringify({
|
|
16467
16959
|
type: "session_start",
|
|
16468
16960
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16469
|
-
id,
|
|
16961
|
+
id: canonical,
|
|
16470
16962
|
model: "unknown",
|
|
16471
16963
|
provider: "unknown"
|
|
16472
16964
|
})}
|
|
16473
16965
|
`;
|
|
16474
|
-
|
|
16475
|
-
|
|
16476
|
-
|
|
16966
|
+
try {
|
|
16967
|
+
if (fileBackup) {
|
|
16968
|
+
try {
|
|
16969
|
+
await fsp17.rename(file, fileBackup);
|
|
16970
|
+
fileStaged = true;
|
|
16971
|
+
} catch (error) {
|
|
16972
|
+
if (error.code !== "ENOENT") throw error;
|
|
16973
|
+
}
|
|
16974
|
+
}
|
|
16975
|
+
if (metaBackup) {
|
|
16976
|
+
try {
|
|
16977
|
+
await fsp17.rename(meta, metaBackup);
|
|
16978
|
+
metaStaged = true;
|
|
16979
|
+
} catch (error) {
|
|
16980
|
+
if (error.code !== "ENOENT") throw error;
|
|
16981
|
+
}
|
|
16982
|
+
}
|
|
16983
|
+
await atomicWrite(file, record);
|
|
16984
|
+
if (!metaBackup) await fsp17.unlink(meta).catch(() => void 0);
|
|
16985
|
+
if (this.catalogClient) {
|
|
16986
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16987
|
+
await this.catalogClient.call("upsert_summary", {
|
|
16988
|
+
summary: {
|
|
16989
|
+
id: canonical,
|
|
16990
|
+
title: "",
|
|
16991
|
+
startedAt: now,
|
|
16992
|
+
model: "unknown",
|
|
16993
|
+
provider: "unknown",
|
|
16994
|
+
tokenTotal: 0,
|
|
16995
|
+
lastActivityAt: now
|
|
16996
|
+
},
|
|
16997
|
+
transcriptRelativePath: `${canonical}.jsonl`,
|
|
16998
|
+
summaryRelativePath: `${canonical}.summary.json`
|
|
16999
|
+
});
|
|
17000
|
+
}
|
|
17001
|
+
if (fileStaged && fileBackup) await fsp17.unlink(fileBackup).catch(() => void 0);
|
|
17002
|
+
if (metaStaged && metaBackup) await fsp17.unlink(metaBackup).catch(() => void 0);
|
|
17003
|
+
} catch (error) {
|
|
17004
|
+
if (fileStaged && fileBackup) {
|
|
17005
|
+
await fsp17.unlink(file).catch(() => void 0);
|
|
17006
|
+
await fsp17.rename(fileBackup, file).catch(() => void 0);
|
|
17007
|
+
}
|
|
17008
|
+
if (metaStaged && metaBackup) {
|
|
17009
|
+
await fsp17.unlink(meta).catch(() => void 0);
|
|
17010
|
+
await fsp17.rename(metaBackup, meta).catch(() => void 0);
|
|
17011
|
+
}
|
|
17012
|
+
throw error;
|
|
17013
|
+
} finally {
|
|
17014
|
+
if (maintenance && this.catalogClient) {
|
|
17015
|
+
await this.catalogClient.call("release_maintenance", { lease: maintenance }).catch(() => void 0);
|
|
17016
|
+
}
|
|
17017
|
+
}
|
|
17018
|
+
this.clearLoadCache(canonical);
|
|
16477
17019
|
}
|
|
16478
17020
|
async summarize(id, mtime) {
|
|
16479
17021
|
return summarizeSessionFile({
|
|
@@ -16493,9 +17035,9 @@ function makeDirectorSessionFactory(opts) {
|
|
|
16493
17035
|
let dir;
|
|
16494
17036
|
if (opts.store) {
|
|
16495
17037
|
store = opts.store;
|
|
16496
|
-
dir = opts.sessionsRoot ?
|
|
17038
|
+
dir = opts.sessionsRoot ? path26.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
16497
17039
|
} else if (opts.sessionsRoot) {
|
|
16498
|
-
dir =
|
|
17040
|
+
dir = path26.join(opts.sessionsRoot, runId);
|
|
16499
17041
|
store = new DefaultSessionStore({ dir });
|
|
16500
17042
|
} else {
|
|
16501
17043
|
throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
|
|
@@ -16519,7 +17061,7 @@ function makeDirectorSessionFactory(opts) {
|
|
|
16519
17061
|
}
|
|
16520
17062
|
async function readDirectorSubagentSession(args) {
|
|
16521
17063
|
if (!args.sessionsRoot) return null;
|
|
16522
|
-
const filePath =
|
|
17064
|
+
const filePath = path26.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
|
|
16523
17065
|
let raw;
|
|
16524
17066
|
try {
|
|
16525
17067
|
raw = await fsp18.readFile(filePath, "utf8");
|
|
@@ -17663,7 +18205,7 @@ function formatRole(role) {
|
|
|
17663
18205
|
}
|
|
17664
18206
|
|
|
17665
18207
|
// src/coordination/fleet-spawn.ts
|
|
17666
|
-
async function
|
|
18208
|
+
async function spawn4(host, config, priceLookup) {
|
|
17667
18209
|
if (host.workCompleteFlag) {
|
|
17668
18210
|
throw new FleetSpawnBudgetError(
|
|
17669
18211
|
"max_spawns",
|
|
@@ -17947,7 +18489,7 @@ function hashStr(s) {
|
|
|
17947
18489
|
}
|
|
17948
18490
|
|
|
17949
18491
|
// src/coordination/multi-agent-coordinator.ts
|
|
17950
|
-
import { randomUUID as
|
|
18492
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
17951
18493
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
17952
18494
|
|
|
17953
18495
|
// src/coordination/coordinator/error-classifier.ts
|
|
@@ -18086,12 +18628,12 @@ async function executeSubagentWithTimeout({
|
|
|
18086
18628
|
}
|
|
18087
18629
|
return new Promise((resolveDecision) => {
|
|
18088
18630
|
let settled = false;
|
|
18089
|
-
const
|
|
18631
|
+
const resolve16 = (d) => {
|
|
18090
18632
|
if (settled) return;
|
|
18091
18633
|
settled = true;
|
|
18092
18634
|
resolveDecision(d);
|
|
18093
18635
|
};
|
|
18094
|
-
const fallback = setTimeout(() =>
|
|
18636
|
+
const fallback = setTimeout(() => resolve16("stop"), DECISION_TIMEOUT_MS);
|
|
18095
18637
|
const sessionId = currentSessionId();
|
|
18096
18638
|
budget._events?.emit("budget.threshold_reached", {
|
|
18097
18639
|
...sessionId ? { sessionId } : {},
|
|
@@ -18101,11 +18643,11 @@ async function executeSubagentWithTimeout({
|
|
|
18101
18643
|
timeoutMs: DECISION_TIMEOUT_MS,
|
|
18102
18644
|
extend: (extra) => {
|
|
18103
18645
|
clearTimeout(fallback);
|
|
18104
|
-
queueMicrotask(() =>
|
|
18646
|
+
queueMicrotask(() => resolve16({ extend: extra }));
|
|
18105
18647
|
},
|
|
18106
18648
|
deny: () => {
|
|
18107
18649
|
clearTimeout(fallback);
|
|
18108
|
-
|
|
18650
|
+
resolve16("stop");
|
|
18109
18651
|
}
|
|
18110
18652
|
});
|
|
18111
18653
|
});
|
|
@@ -18317,7 +18859,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
18317
18859
|
return { ...subagent, name: display };
|
|
18318
18860
|
}
|
|
18319
18861
|
async spawn(subagent) {
|
|
18320
|
-
const id = subagent.id ||
|
|
18862
|
+
const id = subagent.id || randomUUID13();
|
|
18321
18863
|
const cfg = this.withNickname(subagent, id);
|
|
18322
18864
|
if (this.subagents.has(id)) {
|
|
18323
18865
|
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
@@ -18492,7 +19034,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
18492
19034
|
taskIds.map((id) => {
|
|
18493
19035
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
18494
19036
|
if (cached) return cached;
|
|
18495
|
-
return new Promise((
|
|
19037
|
+
return new Promise((resolve16, reject) => {
|
|
18496
19038
|
const timeout = setTimeout(() => {
|
|
18497
19039
|
this.off("task.completed", handler);
|
|
18498
19040
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -18501,7 +19043,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
18501
19043
|
if (result.taskId === id) {
|
|
18502
19044
|
clearTimeout(timeout);
|
|
18503
19045
|
this.off("task.completed", handler);
|
|
18504
|
-
|
|
19046
|
+
resolve16(result);
|
|
18505
19047
|
}
|
|
18506
19048
|
};
|
|
18507
19049
|
this.on("task.completed", handler);
|
|
@@ -18526,13 +19068,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
18526
19068
|
const done = new Set(completed.map((r) => r.taskId));
|
|
18527
19069
|
return { completed, pending: taskIds.filter((id) => !done.has(id)) };
|
|
18528
19070
|
}
|
|
18529
|
-
return new Promise((
|
|
19071
|
+
return new Promise((resolve16) => {
|
|
18530
19072
|
let timer;
|
|
18531
19073
|
const handler = ({ result }) => {
|
|
18532
19074
|
if (!ids.has(result.taskId)) return;
|
|
18533
19075
|
if (timer) clearTimeout(timer);
|
|
18534
19076
|
this.off("task.completed", handler);
|
|
18535
|
-
|
|
19077
|
+
resolve16({
|
|
18536
19078
|
completed: [result],
|
|
18537
19079
|
pending: taskIds.filter((id) => id !== result.taskId)
|
|
18538
19080
|
});
|
|
@@ -18540,7 +19082,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
18540
19082
|
if (opts?.timeoutMs !== void 0) {
|
|
18541
19083
|
timer = setTimeout(() => {
|
|
18542
19084
|
this.off("task.completed", handler);
|
|
18543
|
-
|
|
19085
|
+
resolve16({ completed: [], pending: [...taskIds], timedOut: true });
|
|
18544
19086
|
}, opts.timeoutMs);
|
|
18545
19087
|
}
|
|
18546
19088
|
this.on("task.completed", handler);
|
|
@@ -19202,7 +19744,7 @@ var Director = class _Director {
|
|
|
19202
19744
|
sessionProvider;
|
|
19203
19745
|
sessionModel;
|
|
19204
19746
|
constructor(opts) {
|
|
19205
|
-
this.id = opts.config.coordinatorId ||
|
|
19747
|
+
this.id = opts.config.coordinatorId || randomUUID14();
|
|
19206
19748
|
this.manifestPath = opts.manifestPath;
|
|
19207
19749
|
this.roster = opts.roster;
|
|
19208
19750
|
this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
@@ -19464,7 +20006,7 @@ var Director = class _Director {
|
|
|
19464
20006
|
}
|
|
19465
20007
|
const config = { ...callerConfig };
|
|
19466
20008
|
this.resolveSpawnModel(config);
|
|
19467
|
-
const subagentId = await
|
|
20009
|
+
const subagentId = await spawn4(this, config, priceLookup);
|
|
19468
20010
|
const perSubagentIdleMs = typeof config.idleTimeoutMs === "number" && Number.isFinite(config.idleTimeoutMs) && config.idleTimeoutMs >= 0 ? config.idleTimeoutMs : this.subagentIdleTimeoutMs;
|
|
19469
20011
|
this.armSubagentIdleRetirement(subagentId, perSubagentIdleMs);
|
|
19470
20012
|
return subagentId;
|
|
@@ -19485,7 +20027,7 @@ var Director = class _Director {
|
|
|
19485
20027
|
);
|
|
19486
20028
|
}
|
|
19487
20029
|
const msg = {
|
|
19488
|
-
id:
|
|
20030
|
+
id: randomUUID14(),
|
|
19489
20031
|
type: "task",
|
|
19490
20032
|
from: this.id,
|
|
19491
20033
|
to: subagentId,
|
|
@@ -19739,9 +20281,9 @@ var Director = class _Director {
|
|
|
19739
20281
|
};
|
|
19740
20282
|
|
|
19741
20283
|
// src/coordination/fleet-manager.ts
|
|
19742
|
-
import { randomUUID as
|
|
20284
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
19743
20285
|
import * as fsp20 from "node:fs/promises";
|
|
19744
|
-
import * as
|
|
20286
|
+
import * as path27 from "node:path";
|
|
19745
20287
|
var FleetManager = class {
|
|
19746
20288
|
/** The fleet-wide event bus. */
|
|
19747
20289
|
fleet;
|
|
@@ -19805,7 +20347,7 @@ var FleetManager = class {
|
|
|
19805
20347
|
maxContext;
|
|
19806
20348
|
constructor(opts = {}) {
|
|
19807
20349
|
this.manifestPath = opts.manifestPath;
|
|
19808
|
-
this.directorRunId = opts.directorRunId ??
|
|
20350
|
+
this.directorRunId = opts.directorRunId ?? randomUUID15();
|
|
19809
20351
|
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
19810
20352
|
this.maxSpawnDepth = resolveMaxSpawnDepth(opts.maxSpawnDepth);
|
|
19811
20353
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
@@ -20074,7 +20616,7 @@ var FleetManager = class {
|
|
|
20074
20616
|
})),
|
|
20075
20617
|
usage: this.usage.snapshot()
|
|
20076
20618
|
};
|
|
20077
|
-
await fsp20.mkdir(
|
|
20619
|
+
await fsp20.mkdir(path27.dirname(this.manifestPath), { recursive: true });
|
|
20078
20620
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
20079
20621
|
return this.manifestPath;
|
|
20080
20622
|
}
|
|
@@ -20237,23 +20779,23 @@ var FleetManager = class {
|
|
|
20237
20779
|
};
|
|
20238
20780
|
|
|
20239
20781
|
// src/coordination/remote-mailbox.ts
|
|
20240
|
-
import * as
|
|
20782
|
+
import * as path31 from "node:path";
|
|
20241
20783
|
|
|
20242
20784
|
// src/coordination/mailbox-constants.ts
|
|
20243
20785
|
var HQ_MAILBOX_SNAPSHOT_MIN_INTERVAL_MS = 1e4;
|
|
20244
20786
|
var UNREAD_CHECK_MIN_INTERVAL_MS = 1e3;
|
|
20245
20787
|
|
|
20246
20788
|
// src/coordination/mailbox-project-server-client.ts
|
|
20247
|
-
import { spawn as
|
|
20248
|
-
import * as
|
|
20249
|
-
import * as
|
|
20250
|
-
import * as
|
|
20251
|
-
import { fileURLToPath as
|
|
20789
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
20790
|
+
import * as fs4 from "node:fs";
|
|
20791
|
+
import * as net2 from "node:net";
|
|
20792
|
+
import * as path29 from "node:path";
|
|
20793
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
20252
20794
|
|
|
20253
20795
|
// src/coordination/mailbox-project-server-endpoint.ts
|
|
20254
|
-
import { createHash as
|
|
20255
|
-
import * as
|
|
20256
|
-
import * as
|
|
20796
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
20797
|
+
import * as os3 from "node:os";
|
|
20798
|
+
import * as path28 from "node:path";
|
|
20257
20799
|
|
|
20258
20800
|
// src/coordination/mailbox-project-server-protocol.ts
|
|
20259
20801
|
var MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION = 4;
|
|
@@ -20286,25 +20828,25 @@ function encodeMailboxProjectServerMessage(message) {
|
|
|
20286
20828
|
// src/coordination/mailbox-project-server-endpoint.ts
|
|
20287
20829
|
var MAILBOX_PROJECT_SERVER_METADATA_FILE = ".mailbox-server.json";
|
|
20288
20830
|
function normalizeLocalPath(value) {
|
|
20289
|
-
const resolved =
|
|
20831
|
+
const resolved = path28.resolve(value);
|
|
20290
20832
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
20291
20833
|
}
|
|
20292
20834
|
function mailboxProjectServerKey(projectDir) {
|
|
20293
|
-
return
|
|
20835
|
+
return createHash6("sha256").update(normalizeLocalPath(projectDir)).digest("hex").slice(0, 24);
|
|
20294
20836
|
}
|
|
20295
20837
|
function mailboxProjectServerEndpoint(projectDir) {
|
|
20296
20838
|
const key = mailboxProjectServerKey(projectDir);
|
|
20297
20839
|
if (process.platform === "win32") {
|
|
20298
20840
|
return `\\\\.\\pipe\\wrongstack-mailbox-v${MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
20299
20841
|
}
|
|
20300
|
-
return
|
|
20301
|
-
|
|
20842
|
+
return path28.join(
|
|
20843
|
+
os3.tmpdir(),
|
|
20302
20844
|
`wsmb-v${MAILBOX_PROJECT_SERVER_PROTOCOL_VERSION}`,
|
|
20303
20845
|
`${key}.sock`
|
|
20304
20846
|
);
|
|
20305
20847
|
}
|
|
20306
20848
|
function mailboxProjectServerMetadataPath(projectDir) {
|
|
20307
|
-
return
|
|
20849
|
+
return path28.join(path28.resolve(projectDir), MAILBOX_PROJECT_SERVER_METADATA_FILE);
|
|
20308
20850
|
}
|
|
20309
20851
|
|
|
20310
20852
|
// src/coordination/mailbox-project-server-client.ts
|
|
@@ -20315,7 +20857,7 @@ var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
|
20315
20857
|
var AUTH_RETRY_DELAY_MS = 150;
|
|
20316
20858
|
var AUTH_RETRY_MAX_ATTEMPTS = 13;
|
|
20317
20859
|
function normalizePath(value) {
|
|
20318
|
-
const resolved =
|
|
20860
|
+
const resolved = path29.resolve(value);
|
|
20319
20861
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
20320
20862
|
}
|
|
20321
20863
|
function resolveProjectServerUrl() {
|
|
@@ -20325,7 +20867,7 @@ function resolveProjectServerUrl() {
|
|
|
20325
20867
|
]) {
|
|
20326
20868
|
try {
|
|
20327
20869
|
const url = new URL(relative4, import.meta.url);
|
|
20328
|
-
if (url.protocol === "file:" &&
|
|
20870
|
+
if (url.protocol === "file:" && fs4.existsSync(fileURLToPath4(url))) return url;
|
|
20329
20871
|
} catch {
|
|
20330
20872
|
}
|
|
20331
20873
|
}
|
|
@@ -20334,8 +20876,8 @@ function resolveProjectServerUrl() {
|
|
|
20334
20876
|
function isMailboxProjectServerAvailable() {
|
|
20335
20877
|
return resolveProjectServerUrl() !== null;
|
|
20336
20878
|
}
|
|
20337
|
-
function
|
|
20338
|
-
return new Promise((
|
|
20879
|
+
function delay2(ms) {
|
|
20880
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
20339
20881
|
}
|
|
20340
20882
|
function isUnauthorizedMailboxError(error) {
|
|
20341
20883
|
return error instanceof Error && error.name === "UnauthorizedMailboxRequest";
|
|
@@ -20343,7 +20885,7 @@ function isUnauthorizedMailboxError(error) {
|
|
|
20343
20885
|
var MailboxProjectServerConnection = class {
|
|
20344
20886
|
constructor(projectDir) {
|
|
20345
20887
|
this.projectDir = projectDir;
|
|
20346
|
-
this.projectDir =
|
|
20888
|
+
this.projectDir = path29.resolve(projectDir);
|
|
20347
20889
|
this.endpoint = mailboxProjectServerEndpoint(this.projectDir);
|
|
20348
20890
|
this.state = {
|
|
20349
20891
|
status: isMailboxProjectServerAvailable() ? "offline" : "unavailable",
|
|
@@ -20426,7 +20968,7 @@ var MailboxProjectServerConnection = class {
|
|
|
20426
20968
|
lastError = error;
|
|
20427
20969
|
const retriable = isUnauthorizedMailboxError(error) && attempt < AUTH_RETRY_MAX_ATTEMPTS;
|
|
20428
20970
|
if (!retriable) throw error;
|
|
20429
|
-
await
|
|
20971
|
+
await delay2(AUTH_RETRY_DELAY_MS);
|
|
20430
20972
|
}
|
|
20431
20973
|
}
|
|
20432
20974
|
throw lastError;
|
|
@@ -20498,7 +21040,7 @@ var MailboxProjectServerConnection = class {
|
|
|
20498
21040
|
this.spawnDetachedServer();
|
|
20499
21041
|
spawned = true;
|
|
20500
21042
|
}
|
|
20501
|
-
await
|
|
21043
|
+
await delay2(75);
|
|
20502
21044
|
}
|
|
20503
21045
|
throw lastError;
|
|
20504
21046
|
}
|
|
@@ -20508,8 +21050,8 @@ var MailboxProjectServerConnection = class {
|
|
|
20508
21050
|
this.info = null;
|
|
20509
21051
|
this.buffer = "";
|
|
20510
21052
|
this.authToken = void 0;
|
|
20511
|
-
return new Promise((
|
|
20512
|
-
const socket =
|
|
21053
|
+
return new Promise((resolve16, reject) => {
|
|
21054
|
+
const socket = net2.createConnection(this.endpoint);
|
|
20513
21055
|
this.socket = socket;
|
|
20514
21056
|
socket.setEncoding("utf8");
|
|
20515
21057
|
const timer = setTimeout(() => {
|
|
@@ -20523,7 +21065,7 @@ var MailboxProjectServerConnection = class {
|
|
|
20523
21065
|
this.connectReject = null;
|
|
20524
21066
|
this.startHeartbeat();
|
|
20525
21067
|
this.transition("connected");
|
|
20526
|
-
|
|
21068
|
+
resolve16();
|
|
20527
21069
|
void this.request({ type: "request", op: "ping", args: {} }, 3e3).catch(
|
|
20528
21070
|
() => void 0
|
|
20529
21071
|
);
|
|
@@ -20552,7 +21094,7 @@ var MailboxProjectServerConnection = class {
|
|
|
20552
21094
|
currentAuthToken() {
|
|
20553
21095
|
if (this.authToken === void 0) {
|
|
20554
21096
|
try {
|
|
20555
|
-
const raw =
|
|
21097
|
+
const raw = fs4.readFileSync(mailboxProjectServerMetadataPath(this.projectDir), "utf8");
|
|
20556
21098
|
const parsed = JSON.parse(raw);
|
|
20557
21099
|
if (typeof parsed.authToken === "string" && parsed.authToken.length > 0) {
|
|
20558
21100
|
this.authToken = parsed.authToken;
|
|
@@ -20576,7 +21118,7 @@ var MailboxProjectServerConnection = class {
|
|
|
20576
21118
|
if (encoded.length > MAILBOX_PROJECT_SERVER_MAX_FRAME_CHARS) {
|
|
20577
21119
|
return Promise.reject(new Error("Mailbox project server request exceeded frame limit"));
|
|
20578
21120
|
}
|
|
20579
|
-
return new Promise((
|
|
21121
|
+
return new Promise((resolve16, reject) => {
|
|
20580
21122
|
const timer = setTimeout(() => {
|
|
20581
21123
|
const pending = this.pending.get(id);
|
|
20582
21124
|
if (!pending) return;
|
|
@@ -20584,7 +21126,7 @@ var MailboxProjectServerConnection = class {
|
|
|
20584
21126
|
pending.reject(new Error(`Mailbox ${message.type} exceeded its ${timeoutMs}ms timeout`));
|
|
20585
21127
|
}, timeoutMs);
|
|
20586
21128
|
timer.unref?.();
|
|
20587
|
-
this.pending.set(id, { resolve:
|
|
21129
|
+
this.pending.set(id, { resolve: resolve16, reject, timer });
|
|
20588
21130
|
socket.write(encoded);
|
|
20589
21131
|
});
|
|
20590
21132
|
}
|
|
@@ -20699,8 +21241,8 @@ var MailboxProjectServerConnection = class {
|
|
|
20699
21241
|
spawnDetachedServer() {
|
|
20700
21242
|
const url = resolveProjectServerUrl();
|
|
20701
21243
|
if (!url) throw new Error("Mailbox project server entrypoint is unavailable");
|
|
20702
|
-
|
|
20703
|
-
const child =
|
|
21244
|
+
fs4.mkdirSync(this.projectDir, { recursive: true });
|
|
21245
|
+
const child = spawn5(process.execPath, [fileURLToPath4(url), "--project-dir", this.projectDir], {
|
|
20704
21246
|
cwd: this.projectDir,
|
|
20705
21247
|
detached: process.platform !== "win32",
|
|
20706
21248
|
stdio: "ignore",
|
|
@@ -21021,9 +21563,9 @@ var CredentialVerifyThrottle = class {
|
|
|
21021
21563
|
var credentialVerifyThrottle = new CredentialVerifyThrottle();
|
|
21022
21564
|
|
|
21023
21565
|
// src/coordination/global-mailbox-paths.ts
|
|
21024
|
-
import * as
|
|
21566
|
+
import * as path30 from "node:path";
|
|
21025
21567
|
function resolveProjectDir(projectRoot, globalRoot) {
|
|
21026
|
-
return
|
|
21568
|
+
return path30.join(globalRoot, "projects", projectSlug(projectRoot));
|
|
21027
21569
|
}
|
|
21028
21570
|
|
|
21029
21571
|
// src/coordination/sqlite-mailbox.ts
|
|
@@ -21073,8 +21615,8 @@ var RemoteMailbox = class {
|
|
|
21073
21615
|
hqPublisher,
|
|
21074
21616
|
eventEmitter
|
|
21075
21617
|
} : optionsOrProjectDir;
|
|
21076
|
-
this.projectDir =
|
|
21077
|
-
this.messagePath =
|
|
21618
|
+
this.projectDir = path31.resolve(options.projectDir);
|
|
21619
|
+
this.messagePath = path31.join(this.projectDir, SQLITE_MAILBOX_FILE);
|
|
21078
21620
|
this.registryPath = this.messagePath;
|
|
21079
21621
|
this.clientRegistryPath = this.messagePath;
|
|
21080
21622
|
this.events = options.events;
|
|
@@ -21273,7 +21815,7 @@ var RemoteMailbox = class {
|
|
|
21273
21815
|
this.hqSnapshotPendingMailboxId = mailboxId;
|
|
21274
21816
|
if (this.hqSnapshotTimer !== void 0 || this.hqSnapshotInFlight !== void 0) return;
|
|
21275
21817
|
const elapsed = Date.now() - this.hqSnapshotLastAt;
|
|
21276
|
-
const
|
|
21818
|
+
const delay3 = Math.max(0, HQ_MAILBOX_SNAPSHOT_MIN_INTERVAL_MS - elapsed);
|
|
21277
21819
|
this.hqSnapshotTimer = setTimeout(() => {
|
|
21278
21820
|
this.hqSnapshotTimer = void 0;
|
|
21279
21821
|
const pendingMailboxId = this.hqSnapshotPendingMailboxId;
|
|
@@ -21294,7 +21836,7 @@ var RemoteMailbox = class {
|
|
|
21294
21836
|
}
|
|
21295
21837
|
})();
|
|
21296
21838
|
this.hqSnapshotInFlight = inFlight;
|
|
21297
|
-
},
|
|
21839
|
+
}, delay3);
|
|
21298
21840
|
this.hqSnapshotTimer.unref?.();
|
|
21299
21841
|
}
|
|
21300
21842
|
publishHqEvent(event) {
|
|
@@ -21320,7 +21862,7 @@ var RemoteMailbox = class {
|
|
|
21320
21862
|
this.hqEventPending.clear();
|
|
21321
21863
|
return;
|
|
21322
21864
|
}
|
|
21323
|
-
const mailboxId = `${
|
|
21865
|
+
const mailboxId = `${path31.basename(this.projectDir)}:mailbox`;
|
|
21324
21866
|
const inFlight = this.query({ includeDeleted: true, limit: 100 }).then((messages) => {
|
|
21325
21867
|
const message = messages.find((candidate) => candidate.id === event.messageId);
|
|
21326
21868
|
const action = event.type === "message.sent" ? "message.sent" : "message.updated";
|
|
@@ -21343,7 +21885,7 @@ var RemoteMailbox = class {
|
|
|
21343
21885
|
if (!publisher || !event.startsWith("mailbox.agent_") && !event.startsWith("mailbox.client_")) {
|
|
21344
21886
|
return;
|
|
21345
21887
|
}
|
|
21346
|
-
const mailboxId = `${
|
|
21888
|
+
const mailboxId = `${path31.basename(this.projectDir)}:mailbox`;
|
|
21347
21889
|
const record = typeof payload === "object" && payload !== null ? payload : {};
|
|
21348
21890
|
const agentId = typeof record["agentId"] === "string" ? record["agentId"] : void 0;
|
|
21349
21891
|
const action = event === "mailbox.agent_registered" ? "agent.registered" : event === "mailbox.agent_heartbeat" ? "agent.heartbeat" : event === "mailbox.agent_deregistered" ? "agent.deregistered" : void 0;
|
|
@@ -21366,7 +21908,7 @@ function createProjectMailbox(options) {
|
|
|
21366
21908
|
return new RemoteMailbox(options);
|
|
21367
21909
|
}
|
|
21368
21910
|
function getSharedProjectMailbox(projectDir, events, hqPublisher) {
|
|
21369
|
-
const key =
|
|
21911
|
+
const key = path31.resolve(projectDir);
|
|
21370
21912
|
let projectCache = sharedRemoteMailboxes.get(key);
|
|
21371
21913
|
if (!projectCache) {
|
|
21372
21914
|
projectCache = {
|
|
@@ -21406,12 +21948,12 @@ function getSharedProjectMailbox(projectDir, events, hqPublisher) {
|
|
|
21406
21948
|
}
|
|
21407
21949
|
|
|
21408
21950
|
// src/coordination/mailbox-tool.ts
|
|
21409
|
-
import { createHash as
|
|
21951
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
21410
21952
|
function defaultResolveProjectDir(ctx) {
|
|
21411
21953
|
return resolveProjectDir(ctx.projectRoot, wstackGlobalRoot());
|
|
21412
21954
|
}
|
|
21413
21955
|
function mailboxSessionTag(sessionId) {
|
|
21414
|
-
return
|
|
21956
|
+
return createHash7("sha256").update(sessionId).digest("hex").slice(0, 8);
|
|
21415
21957
|
}
|
|
21416
21958
|
function resolveMailboxIdentity(ctx, fallbackBase = "leader") {
|
|
21417
21959
|
const fieldId = ctx.agentId && ctx.agentId !== "unknown" ? ctx.agentId : void 0;
|
|
@@ -21816,7 +22358,7 @@ function makeFleetStatusTool(opts = {}) {
|
|
|
21816
22358
|
}
|
|
21817
22359
|
|
|
21818
22360
|
// src/coordination/fleet-supervisor.ts
|
|
21819
|
-
import { randomUUID as
|
|
22361
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
21820
22362
|
var COLLAB_ID_PREFIXES2 = ["bug-hunter-", "refactor-planner-", "critic-"];
|
|
21821
22363
|
var DEFAULTS = {
|
|
21822
22364
|
intervalMs: 2e4,
|
|
@@ -22094,7 +22636,7 @@ var FleetSupervisor = class {
|
|
|
22094
22636
|
*/
|
|
22095
22637
|
async decide(question, context, options, risk) {
|
|
22096
22638
|
const request = {
|
|
22097
|
-
id: `fleetsup-${
|
|
22639
|
+
id: `fleetsup-${randomUUID16()}`,
|
|
22098
22640
|
sessionId: this.opts.sessionId?.(),
|
|
22099
22641
|
source: "system",
|
|
22100
22642
|
question,
|
|
@@ -23734,16 +24276,16 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23734
24276
|
throw validationError(`routePath must not start with '?' (got ${JSON.stringify(routePath)})`);
|
|
23735
24277
|
}
|
|
23736
24278
|
const queryIndex = url.indexOf("?");
|
|
23737
|
-
const
|
|
24279
|
+
const path36 = queryIndex === -1 ? url : url.slice(0, queryIndex);
|
|
23738
24280
|
if (actor !== void 0) {
|
|
23739
|
-
const requiredCapability = requiredCredentialCapability(method,
|
|
24281
|
+
const requiredCapability = requiredCredentialCapability(method, path36);
|
|
23740
24282
|
if (requiredCapability === void 0) {
|
|
23741
24283
|
writeJson(response, 403, {
|
|
23742
|
-
error: { code: "FORBIDDEN", message: `credential access is not permitted for ${method} ${
|
|
24284
|
+
error: { code: "FORBIDDEN", message: `credential access is not permitted for ${method} ${path36}` }
|
|
23743
24285
|
});
|
|
23744
24286
|
return;
|
|
23745
24287
|
}
|
|
23746
|
-
if (
|
|
24288
|
+
if (path36 !== "/mailbox/send" && !hasMailboxCapability(actor, requiredCapability)) {
|
|
23747
24289
|
writeJson(response, 403, {
|
|
23748
24290
|
error: {
|
|
23749
24291
|
code: "FORBIDDEN",
|
|
@@ -23753,7 +24295,7 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23753
24295
|
return;
|
|
23754
24296
|
}
|
|
23755
24297
|
}
|
|
23756
|
-
if (method === "POST" &&
|
|
24298
|
+
if (method === "POST" && path36 === "/mailbox/send") {
|
|
23757
24299
|
const input = validateSend(await readJsonBody(request, maxBodyBytes), actor?.actorId);
|
|
23758
24300
|
if (actor !== void 0) {
|
|
23759
24301
|
const requiredCapability = requiredSendCapability(input.type);
|
|
@@ -23771,7 +24313,7 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23771
24313
|
writeJson(response, 201, await mailbox.send(input));
|
|
23772
24314
|
return;
|
|
23773
24315
|
}
|
|
23774
|
-
if (method === "POST" &&
|
|
24316
|
+
if (method === "POST" && path36 === "/mailbox/query") {
|
|
23775
24317
|
const queryContext = parseSinceMs(url, defaultMaxAgeMs);
|
|
23776
24318
|
if ("error" in queryContext) {
|
|
23777
24319
|
writeJson(response, 400, { error: queryContext.error });
|
|
@@ -23791,7 +24333,7 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23791
24333
|
writeJson(response, 200, { data: projected, count: projected.length });
|
|
23792
24334
|
return;
|
|
23793
24335
|
}
|
|
23794
|
-
if (method === "POST" &&
|
|
24336
|
+
if (method === "POST" && path36 === "/mailbox/check") {
|
|
23795
24337
|
const queryContext = parseSinceMs(url, defaultMaxAgeMs);
|
|
23796
24338
|
if ("error" in queryContext) {
|
|
23797
24339
|
writeJson(response, 400, { error: queryContext.error });
|
|
@@ -23825,7 +24367,7 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23825
24367
|
writeJson(response, 200, { data: projected, count: projected.length });
|
|
23826
24368
|
return;
|
|
23827
24369
|
}
|
|
23828
|
-
if (method === "POST" &&
|
|
24370
|
+
if (method === "POST" && path36 === "/mailbox/ack") {
|
|
23829
24371
|
const input = validateAck(await readJsonBody(request, maxBodyBytes), actor?.actorId);
|
|
23830
24372
|
if (actor !== void 0) {
|
|
23831
24373
|
input.readerId = actor.actorId;
|
|
@@ -23839,7 +24381,7 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23839
24381
|
writeJson(response, 200, { updated: projectedAck });
|
|
23840
24382
|
return;
|
|
23841
24383
|
}
|
|
23842
|
-
if (method === "POST" &&
|
|
24384
|
+
if (method === "POST" && path36 === "/mailbox/ack-many") {
|
|
23843
24385
|
const input = validateAckMany(await readJsonBody(request, maxBodyBytes), actor?.actorId);
|
|
23844
24386
|
if (actor !== void 0) {
|
|
23845
24387
|
const requestedIds = new Set(input.acks.map((ack) => ack.messageId));
|
|
@@ -23857,54 +24399,54 @@ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, me
|
|
|
23857
24399
|
writeJson(response, 200, { updated: projectedMany, count: projectedMany.length });
|
|
23858
24400
|
return;
|
|
23859
24401
|
}
|
|
23860
|
-
if (method === "POST" &&
|
|
24402
|
+
if (method === "POST" && path36 === "/mailbox/unread-count") {
|
|
23861
24403
|
const body = await readJsonBody(request, maxBodyBytes);
|
|
23862
24404
|
const count = actor === void 0 ? await mailbox.unreadCount(requireString2(body, "forAgentId")) : await unreadCountForActor(mailbox, actor);
|
|
23863
24405
|
writeJson(response, 200, { count });
|
|
23864
24406
|
return;
|
|
23865
24407
|
}
|
|
23866
|
-
if (method === "POST" &&
|
|
24408
|
+
if (method === "POST" && path36 === "/mailbox/agents/register") {
|
|
23867
24409
|
const input = validateAgentRegistration(await readJsonBody(request, maxBodyBytes), actor);
|
|
23868
24410
|
await mailbox.registerAgent(input);
|
|
23869
24411
|
writeJson(response, 200, { ok: true });
|
|
23870
24412
|
return;
|
|
23871
24413
|
}
|
|
23872
|
-
if (method === "POST" &&
|
|
24414
|
+
if (method === "POST" && path36 === "/mailbox/agents/heartbeat") {
|
|
23873
24415
|
const input = validateAgentHeartbeat(await readJsonBody(request, maxBodyBytes), actor?.actorId);
|
|
23874
24416
|
if (actor !== void 0) input.agentId = actor.actorId;
|
|
23875
24417
|
await mailbox.heartbeat(input);
|
|
23876
24418
|
writeJson(response, 200, { ok: true });
|
|
23877
24419
|
return;
|
|
23878
24420
|
}
|
|
23879
|
-
if (method === "POST" &&
|
|
24421
|
+
if (method === "POST" && path36 === "/mailbox/register-client") {
|
|
23880
24422
|
await mailbox.registerClient(
|
|
23881
24423
|
validateClientRegistration(await readJsonBody(request, maxBodyBytes))
|
|
23882
24424
|
);
|
|
23883
24425
|
writeJson(response, 200, { ok: true });
|
|
23884
24426
|
return;
|
|
23885
24427
|
}
|
|
23886
|
-
if (method === "POST" &&
|
|
24428
|
+
if (method === "POST" && path36 === "/mailbox/heartbeat") {
|
|
23887
24429
|
await mailbox.clientHeartbeat(
|
|
23888
24430
|
validateClientHeartbeat(await readJsonBody(request, maxBodyBytes))
|
|
23889
24431
|
);
|
|
23890
24432
|
writeJson(response, 200, { ok: true });
|
|
23891
24433
|
return;
|
|
23892
24434
|
}
|
|
23893
|
-
if (method === "POST" &&
|
|
24435
|
+
if (method === "POST" && path36 === "/mailbox/purge-clients") {
|
|
23894
24436
|
writeJson(response, 200, { ok: true, purged: await mailbox.purgeClients() });
|
|
23895
24437
|
return;
|
|
23896
24438
|
}
|
|
23897
|
-
if (method === "GET" &&
|
|
24439
|
+
if (method === "GET" && path36 === "/mailbox/agents") {
|
|
23898
24440
|
const agents = await mailbox.getAgentStatuses();
|
|
23899
24441
|
writeJson(response, 200, { data: agents, count: agents.length });
|
|
23900
24442
|
return;
|
|
23901
24443
|
}
|
|
23902
|
-
if (method === "GET" &&
|
|
24444
|
+
if (method === "GET" && path36 === "/mailbox/agents/online") {
|
|
23903
24445
|
const agents = await mailbox.getOnlineAgents();
|
|
23904
24446
|
writeJson(response, 200, { data: agents, count: agents.length });
|
|
23905
24447
|
return;
|
|
23906
24448
|
}
|
|
23907
|
-
if (method === "GET" &&
|
|
24449
|
+
if (method === "GET" && path36 === "/mailbox/events" && eventEmitter) {
|
|
23908
24450
|
const queryContext = parseSinceMs(url, defaultMaxAgeMs);
|
|
23909
24451
|
if ("error" in queryContext) {
|
|
23910
24452
|
writeJson(response, 400, { error: queryContext.error });
|
|
@@ -24013,25 +24555,25 @@ function requiredSendCapability(type) {
|
|
|
24013
24555
|
}
|
|
24014
24556
|
return "mail.send.informational";
|
|
24015
24557
|
}
|
|
24016
|
-
function requiredCredentialCapability(method,
|
|
24017
|
-
if (method === "POST" &&
|
|
24018
|
-
if (method === "POST" && (
|
|
24558
|
+
function requiredCredentialCapability(method, path36) {
|
|
24559
|
+
if (method === "POST" && path36 === "/mailbox/send") return "mail.send.informational";
|
|
24560
|
+
if (method === "POST" && (path36 === "/mailbox/query" || path36 === "/mailbox/check")) {
|
|
24019
24561
|
return "mail.read.self";
|
|
24020
24562
|
}
|
|
24021
|
-
if (method === "POST" && (
|
|
24563
|
+
if (method === "POST" && (path36 === "/mailbox/ack" || path36 === "/mailbox/ack-many")) {
|
|
24022
24564
|
return "mail.ack.self";
|
|
24023
24565
|
}
|
|
24024
|
-
if (method === "POST" &&
|
|
24025
|
-
if (method === "POST" &&
|
|
24566
|
+
if (method === "POST" && path36 === "/mailbox/unread-count") return "mail.read.self";
|
|
24567
|
+
if (method === "POST" && path36 === "/mailbox/agents/register") {
|
|
24026
24568
|
return "mail.presence.register.self";
|
|
24027
24569
|
}
|
|
24028
|
-
if (method === "POST" &&
|
|
24570
|
+
if (method === "POST" && path36 === "/mailbox/agents/heartbeat") {
|
|
24029
24571
|
return "mail.presence.heartbeat.self";
|
|
24030
24572
|
}
|
|
24031
|
-
if (method === "GET" && (
|
|
24573
|
+
if (method === "GET" && (path36 === "/mailbox/agents" || path36 === "/mailbox/agents/online")) {
|
|
24032
24574
|
return "mail.presence.read";
|
|
24033
24575
|
}
|
|
24034
|
-
if (method === "GET" &&
|
|
24576
|
+
if (method === "GET" && path36 === "/mailbox/events") return "mail.events.self";
|
|
24035
24577
|
return void 0;
|
|
24036
24578
|
}
|
|
24037
24579
|
function extractEventTimestamp(event) {
|
|
@@ -24233,16 +24775,16 @@ async function checkMailbox(mailbox, input, minTimestampIso, includeReceiptState
|
|
|
24233
24775
|
var NULL_FLEET_BUS = new FleetBus();
|
|
24234
24776
|
|
|
24235
24777
|
// src/coordination/package-author-tracker.ts
|
|
24236
|
-
import * as
|
|
24237
|
-
import * as
|
|
24778
|
+
import * as fs5 from "node:fs/promises";
|
|
24779
|
+
import * as path32 from "node:path";
|
|
24238
24780
|
var DEFAULT_MAX_ENTRIES2 = 1e4;
|
|
24239
24781
|
var LOG_FILENAME2 = "package-authors.json";
|
|
24240
24782
|
function logPath2(storageDir) {
|
|
24241
|
-
return
|
|
24783
|
+
return path32.join(storageDir, LOG_FILENAME2);
|
|
24242
24784
|
}
|
|
24243
24785
|
async function loadLog2(storageDir, projectRoot) {
|
|
24244
24786
|
try {
|
|
24245
|
-
const raw = await
|
|
24787
|
+
const raw = await fs5.readFile(logPath2(storageDir), "utf-8");
|
|
24246
24788
|
const parsed = JSON.parse(raw);
|
|
24247
24789
|
if (!parsed.entries || !Array.isArray(parsed.entries)) {
|
|
24248
24790
|
return { projectRoot, entries: [] };
|
|
@@ -24260,7 +24802,7 @@ async function saveLog2(storageDir, log) {
|
|
|
24260
24802
|
`);
|
|
24261
24803
|
}
|
|
24262
24804
|
function detectEcosystem(manifestPath) {
|
|
24263
|
-
const name =
|
|
24805
|
+
const name = path32.win32.basename(manifestPath).toLowerCase();
|
|
24264
24806
|
if (name === "package.json") return "npm";
|
|
24265
24807
|
if (name === "go.mod") return "go";
|
|
24266
24808
|
if (name === "cargo.toml") return "cargo";
|
|
@@ -25142,13 +25684,13 @@ var RemoteMailboxCredentialStore = class {
|
|
|
25142
25684
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
25143
25685
|
import * as fsp21 from "node:fs/promises";
|
|
25144
25686
|
import { execFile } from "node:child_process";
|
|
25145
|
-
import * as
|
|
25146
|
-
import * as
|
|
25687
|
+
import * as os4 from "node:os";
|
|
25688
|
+
import * as path33 from "node:path";
|
|
25147
25689
|
var MAILBOX_BRIDGE_LOCK_FILENAME = ".mailbox-bridge.lock";
|
|
25148
25690
|
var MAILBOX_BRIDGE_TOKEN_FILENAME = ".mailbox.token";
|
|
25149
25691
|
async function acquireOrJoin(opts) {
|
|
25150
|
-
const lockPath =
|
|
25151
|
-
const tokenPath =
|
|
25692
|
+
const lockPath = path33.join(opts.projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
25693
|
+
const tokenPath = path33.join(opts.projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
|
|
25152
25694
|
const inspected = await readLockForInspection(lockPath);
|
|
25153
25695
|
if (inspected.kind === "live") {
|
|
25154
25696
|
const existing = inspected.lock;
|
|
@@ -25176,8 +25718,8 @@ async function acquireOrJoin(opts) {
|
|
|
25176
25718
|
return { kind: "acquired", lock: tentative, tokenPath };
|
|
25177
25719
|
}
|
|
25178
25720
|
async function finalize(projectDir, tentative, boundPort) {
|
|
25179
|
-
const lockPath =
|
|
25180
|
-
const tokenPath =
|
|
25721
|
+
const lockPath = path33.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
25722
|
+
const tokenPath = path33.join(projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
|
|
25181
25723
|
const finalized = {
|
|
25182
25724
|
...tentative,
|
|
25183
25725
|
port: boundPort,
|
|
@@ -25188,8 +25730,8 @@ async function finalize(projectDir, tentative, boundPort) {
|
|
|
25188
25730
|
return finalized;
|
|
25189
25731
|
}
|
|
25190
25732
|
async function release(projectDir, generation) {
|
|
25191
|
-
const lockPath =
|
|
25192
|
-
const tokenPath =
|
|
25733
|
+
const lockPath = path33.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
25734
|
+
const tokenPath = path33.join(projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
|
|
25193
25735
|
try {
|
|
25194
25736
|
const raw = await fsp21.readFile(lockPath, "utf-8");
|
|
25195
25737
|
const parsed = JSON.parse(raw);
|
|
@@ -25223,7 +25765,7 @@ async function readLockForInspection(lockPath) {
|
|
|
25223
25765
|
return { kind: "live", lock: parsed };
|
|
25224
25766
|
}
|
|
25225
25767
|
async function readLiveLock(projectDir) {
|
|
25226
|
-
const lockPath =
|
|
25768
|
+
const lockPath = path33.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
25227
25769
|
const result = await readLockForInspection(lockPath);
|
|
25228
25770
|
if (result.kind === "live") {
|
|
25229
25771
|
return { kind: "live", lock: result.lock };
|
|
@@ -25234,7 +25776,7 @@ async function readLiveLock(projectDir) {
|
|
|
25234
25776
|
return { kind: "absent" };
|
|
25235
25777
|
}
|
|
25236
25778
|
async function atomicWriteJson(targetPath, value) {
|
|
25237
|
-
const dir =
|
|
25779
|
+
const dir = path33.dirname(targetPath);
|
|
25238
25780
|
await fsp21.mkdir(dir, { recursive: true });
|
|
25239
25781
|
const tmp = `${targetPath}.tmp.${process.pid}.${randomBytes2(4).toString("hex")}`;
|
|
25240
25782
|
const body = JSON.stringify(value, null, 2) + "\n";
|
|
@@ -25249,8 +25791,8 @@ async function atomicWriteJson(targetPath, value) {
|
|
|
25249
25791
|
async function isProcessAlive(pid) {
|
|
25250
25792
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
25251
25793
|
if (pid === process.pid) return true;
|
|
25252
|
-
if (
|
|
25253
|
-
return new Promise((
|
|
25794
|
+
if (os4.platform() === "win32") {
|
|
25795
|
+
return new Promise((resolve16) => {
|
|
25254
25796
|
execFile(
|
|
25255
25797
|
"tasklist",
|
|
25256
25798
|
["/FI", `PID eq ${pid}`, "/NH", "/FO", "CSV"],
|
|
@@ -25262,11 +25804,11 @@ async function isProcessAlive(pid) {
|
|
|
25262
25804
|
},
|
|
25263
25805
|
(error, out) => {
|
|
25264
25806
|
if (error) {
|
|
25265
|
-
|
|
25807
|
+
resolve16(false);
|
|
25266
25808
|
return;
|
|
25267
25809
|
}
|
|
25268
25810
|
const match = /(?:^|\n)"[^"]*","(\d+)"/.exec(out);
|
|
25269
|
-
|
|
25811
|
+
resolve16(match !== null && match[1] === String(pid));
|
|
25270
25812
|
}
|
|
25271
25813
|
);
|
|
25272
25814
|
});
|
|
@@ -25397,8 +25939,8 @@ function extractManifestPath(msg) {
|
|
|
25397
25939
|
}
|
|
25398
25940
|
return void 0;
|
|
25399
25941
|
}
|
|
25400
|
-
function isManifestFile(
|
|
25401
|
-
const name = pathBasename(
|
|
25942
|
+
function isManifestFile(path36) {
|
|
25943
|
+
const name = pathBasename(path36).toLowerCase();
|
|
25402
25944
|
const manifests = [
|
|
25403
25945
|
"package.json",
|
|
25404
25946
|
"package-lock.json",
|
|
@@ -25691,8 +26233,8 @@ var AdaptiveConcurrencyController = class {
|
|
|
25691
26233
|
|
|
25692
26234
|
// src/coordination/agent-monitor.ts
|
|
25693
26235
|
import { createReadStream as createReadStream5 } from "node:fs";
|
|
25694
|
-
import * as
|
|
25695
|
-
import * as
|
|
26236
|
+
import * as fs6 from "node:fs/promises";
|
|
26237
|
+
import * as path34 from "node:path";
|
|
25696
26238
|
import { createInterface as createInterface5 } from "node:readline";
|
|
25697
26239
|
var AgentMonitorService = class _AgentMonitorService {
|
|
25698
26240
|
_fleetBus;
|
|
@@ -25772,7 +26314,7 @@ var AgentMonitorService = class _AgentMonitorService {
|
|
|
25772
26314
|
async loadSessionsFromDisk() {
|
|
25773
26315
|
let subagentIds;
|
|
25774
26316
|
try {
|
|
25775
|
-
const dirents = await
|
|
26317
|
+
const dirents = await fs6.readdir(this._transcriptsDir, { withFileTypes: true });
|
|
25776
26318
|
subagentIds = dirents.filter((d) => d.isDirectory()).map((d) => d.name);
|
|
25777
26319
|
} catch {
|
|
25778
26320
|
return this.getAllSessions();
|
|
@@ -25796,8 +26338,8 @@ var AgentMonitorService = class _AgentMonitorService {
|
|
|
25796
26338
|
return this.getAllSessions();
|
|
25797
26339
|
}
|
|
25798
26340
|
async _readTranscriptFile(subagentId) {
|
|
25799
|
-
const file =
|
|
25800
|
-
const accessible = await
|
|
26341
|
+
const file = path34.join(this._transcriptsDir, subagentId, "transcript.jsonl");
|
|
26342
|
+
const accessible = await fs6.access(file).then(() => true).catch(() => false);
|
|
25801
26343
|
if (!accessible) return [];
|
|
25802
26344
|
const out = [];
|
|
25803
26345
|
const input = createReadStream5(file, { encoding: "utf8" });
|
|
@@ -26137,14 +26679,14 @@ var AgentMonitorService = class _AgentMonitorService {
|
|
|
26137
26679
|
this._writeDrain = drain;
|
|
26138
26680
|
}
|
|
26139
26681
|
async _appendToFile(subagentId, line) {
|
|
26140
|
-
const dir =
|
|
26682
|
+
const dir = path34.join(this._transcriptsDir, subagentId);
|
|
26141
26683
|
if (!this._ensuredDirs.has(dir)) {
|
|
26142
|
-
await
|
|
26684
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
26143
26685
|
this._ensuredDirs.add(dir);
|
|
26144
26686
|
}
|
|
26145
|
-
const filePath =
|
|
26687
|
+
const filePath = path34.join(dir, "transcript.jsonl");
|
|
26146
26688
|
try {
|
|
26147
|
-
await
|
|
26689
|
+
await fs6.appendFile(filePath, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
26148
26690
|
} catch (error) {
|
|
26149
26691
|
this._ensuredDirs.delete(dir);
|
|
26150
26692
|
throw error;
|
|
@@ -26188,7 +26730,7 @@ function createAgentMonitorService(opts) {
|
|
|
26188
26730
|
}
|
|
26189
26731
|
|
|
26190
26732
|
// src/coordination/autonomous-brain.ts
|
|
26191
|
-
import { randomUUID as
|
|
26733
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
26192
26734
|
var AutonomousBrain = class {
|
|
26193
26735
|
graph;
|
|
26194
26736
|
// Fleet bus for emitting decisions — null-safe, no-op if not provided
|
|
@@ -26294,7 +26836,7 @@ var AutonomousBrain = class {
|
|
|
26294
26836
|
consequence: i === 0 ? `Spawn the most appropriate agent for: ${taskDescription.slice(0, 80)}` : `Spawn an alternative agent for the same task`
|
|
26295
26837
|
}));
|
|
26296
26838
|
return this.decideAuto({
|
|
26297
|
-
id:
|
|
26839
|
+
id: randomUUID17(),
|
|
26298
26840
|
source,
|
|
26299
26841
|
decisionType: "spawn",
|
|
26300
26842
|
question: `Should we spawn a subagent for this task?`,
|
|
@@ -26337,7 +26879,7 @@ var AutonomousBrain = class {
|
|
|
26337
26879
|
}
|
|
26338
26880
|
];
|
|
26339
26881
|
return this.decideAuto({
|
|
26340
|
-
id:
|
|
26882
|
+
id: randomUUID17(),
|
|
26341
26883
|
source,
|
|
26342
26884
|
decisionType: "approve_change",
|
|
26343
26885
|
question: `Should we approve the change "${change.title}"?`,
|
|
@@ -26396,7 +26938,7 @@ var AutonomousBrain = class {
|
|
|
26396
26938
|
consequence: "Break the task into smaller sub-tasks"
|
|
26397
26939
|
});
|
|
26398
26940
|
return this.decideAuto({
|
|
26399
|
-
id:
|
|
26941
|
+
id: randomUUID17(),
|
|
26400
26942
|
source,
|
|
26401
26943
|
decisionType: "escalate_task",
|
|
26402
26944
|
question: `Task failed: ${error.slice(0, 100)}. How should we proceed?`,
|
|
@@ -26530,12 +27072,12 @@ ${ctx.error}`);
|
|
|
26530
27072
|
};
|
|
26531
27073
|
|
|
26532
27074
|
// src/coordination/autonomous-coordinator.ts
|
|
26533
|
-
import { randomUUID as
|
|
27075
|
+
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
26534
27076
|
|
|
26535
27077
|
// src/coordination/knowledge-graph.ts
|
|
26536
|
-
import { randomUUID as
|
|
27078
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
26537
27079
|
import * as fsp22 from "node:fs/promises";
|
|
26538
|
-
import * as
|
|
27080
|
+
import * as path35 from "node:path";
|
|
26539
27081
|
var DEFAULT_MAX_NODES = 2e3;
|
|
26540
27082
|
var MAX_SUBSCRIPTIONS = 1e3;
|
|
26541
27083
|
var MAX_PENDING_DELIVERIES_PER_SUBSCRIPTION = 1e3;
|
|
@@ -26570,8 +27112,8 @@ var KnowledgeGraph = class _KnowledgeGraph {
|
|
|
26570
27112
|
return this.index;
|
|
26571
27113
|
}
|
|
26572
27114
|
constructor(sessionDir, maxNodes = DEFAULT_MAX_NODES, compactEveryWrites = Math.max(1e3, maxNodes * 4)) {
|
|
26573
|
-
this.filePath =
|
|
26574
|
-
this.graphFilePath =
|
|
27115
|
+
this.filePath = path35.join(sessionDir, "_knowledge_graph");
|
|
27116
|
+
this.graphFilePath = path35.join(this.filePath, "graph.jsonl");
|
|
26575
27117
|
this.maxNodes = maxNodes;
|
|
26576
27118
|
this.compactEveryWrites = Math.max(1, Math.floor(compactEveryWrites));
|
|
26577
27119
|
}
|
|
@@ -26581,7 +27123,7 @@ var KnowledgeGraph = class _KnowledgeGraph {
|
|
|
26581
27123
|
* Returns the node with its assigned id.
|
|
26582
27124
|
*/
|
|
26583
27125
|
async add(node) {
|
|
26584
|
-
const full = { id:
|
|
27126
|
+
const full = { id: randomUUID18(), ...node };
|
|
26585
27127
|
this.nodes.set(full.id, full);
|
|
26586
27128
|
this._trackSeq(full.id);
|
|
26587
27129
|
this._addToIndex(full, this._indexKeys(full));
|
|
@@ -26710,8 +27252,8 @@ var KnowledgeGraph = class _KnowledgeGraph {
|
|
|
26710
27252
|
if (this.subs.size >= MAX_SUBSCRIPTIONS) {
|
|
26711
27253
|
throw new Error(`Knowledge graph subscription limit reached (${MAX_SUBSCRIPTIONS})`);
|
|
26712
27254
|
}
|
|
26713
|
-
const channel =
|
|
26714
|
-
const sub = { id:
|
|
27255
|
+
const channel = randomUUID18();
|
|
27256
|
+
const sub = { id: randomUUID18(), agentId, filter, channel };
|
|
26715
27257
|
this.subs.set(channel, sub);
|
|
26716
27258
|
this.pendingDeliveries.set(channel, []);
|
|
26717
27259
|
return channel;
|
|
@@ -27192,7 +27734,7 @@ var TaskDAG = class {
|
|
|
27192
27734
|
};
|
|
27193
27735
|
|
|
27194
27736
|
// src/coordination/task-auctioneer.ts
|
|
27195
|
-
import { randomUUID as
|
|
27737
|
+
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
27196
27738
|
function isTerminalGoalStatus(status) {
|
|
27197
27739
|
return status === "done" || status === "failed";
|
|
27198
27740
|
}
|
|
@@ -27315,7 +27857,7 @@ var TaskAuctioneer = class {
|
|
|
27315
27857
|
const score = dispatchResult.confidence * (dispatchResult.role === agent.agentRole ? 1.2 : 1);
|
|
27316
27858
|
if (score < this.minConfidence) return false;
|
|
27317
27859
|
const bid = {
|
|
27318
|
-
id:
|
|
27860
|
+
id: randomUUID19(),
|
|
27319
27861
|
taskId,
|
|
27320
27862
|
agentId: agent.agentId,
|
|
27321
27863
|
agentName: agent.agentName,
|
|
@@ -28252,7 +28794,7 @@ var AutonomousCoordinator = class _AutonomousCoordinator {
|
|
|
28252
28794
|
break;
|
|
28253
28795
|
}
|
|
28254
28796
|
const decision = await this.brain.decideAuto({
|
|
28255
|
-
id:
|
|
28797
|
+
id: randomUUID20(),
|
|
28256
28798
|
source: "system",
|
|
28257
28799
|
decisionType: "prioritize_goals",
|
|
28258
28800
|
question: `What should we work on next? Open goals: ${dispatchable.map((g) => g.title).join(", ")}`,
|
|
@@ -28483,17 +29025,17 @@ ${input.detail}`
|
|
|
28483
29025
|
_waitForDagProgress(timeoutMs) {
|
|
28484
29026
|
const before = this._dagProgressKey();
|
|
28485
29027
|
if (this.dag.isDone()) return Promise.resolve();
|
|
28486
|
-
return new Promise((
|
|
29028
|
+
return new Promise((resolve16) => {
|
|
28487
29029
|
let off;
|
|
28488
29030
|
const timer = setTimeout(() => {
|
|
28489
29031
|
off?.();
|
|
28490
|
-
|
|
29032
|
+
resolve16();
|
|
28491
29033
|
}, timeoutMs);
|
|
28492
29034
|
off = this.dag.onEvent(() => {
|
|
28493
29035
|
if (this._dagProgressKey() === before) return;
|
|
28494
29036
|
clearTimeout(timer);
|
|
28495
29037
|
off?.();
|
|
28496
|
-
|
|
29038
|
+
resolve16();
|
|
28497
29039
|
});
|
|
28498
29040
|
});
|
|
28499
29041
|
}
|
|
@@ -28801,8 +29343,8 @@ var CollaborationBus = class _CollaborationBus {
|
|
|
28801
29343
|
if (this.isPaused()) return false;
|
|
28802
29344
|
this.pausedAtMs = Date.now();
|
|
28803
29345
|
this.pausedBy = byParticipant;
|
|
28804
|
-
this.pausePromise = new Promise((
|
|
28805
|
-
this.pauseResolve =
|
|
29346
|
+
this.pausePromise = new Promise((resolve16) => {
|
|
29347
|
+
this.pauseResolve = resolve16;
|
|
28806
29348
|
});
|
|
28807
29349
|
return true;
|
|
28808
29350
|
}
|
|
@@ -28838,8 +29380,8 @@ var CollaborationBus = class _CollaborationBus {
|
|
|
28838
29380
|
return true;
|
|
28839
29381
|
}
|
|
28840
29382
|
let timer;
|
|
28841
|
-
const timeoutPromise = new Promise((
|
|
28842
|
-
timer = setTimeout(() =>
|
|
29383
|
+
const timeoutPromise = new Promise((resolve16) => {
|
|
29384
|
+
timer = setTimeout(() => resolve16("timeout"), timeoutMs);
|
|
28843
29385
|
});
|
|
28844
29386
|
const resumedPromise = this.pausePromise.then(() => "resumed").catch(() => "resumed");
|
|
28845
29387
|
const winner = await Promise.race([resumedPromise, timeoutPromise]);
|