@wrongstack/core 0.264.0 → 0.265.1
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-bridge-D8sa1vtv.d.ts → agent-bridge-DrkBxszZ.d.ts} +1 -1
- package/dist/{agent-subagent-runner-c9DLkaas.d.ts → agent-subagent-runner-DM2pP-B6.d.ts} +113 -11
- package/dist/{brain-O1IdKPaK.d.ts → brain-BXd_61kQ.d.ts} +31 -2
- package/dist/{compactor-BBy0rCtB.d.ts → compactor-B8pOf45Y.d.ts} +1 -1
- package/dist/{config-Dz2F3H2K.d.ts → config-BMCj_XDs.d.ts} +80 -12
- package/dist/{context-BGSpZNSE.d.ts → context-MRk5PhNv.d.ts} +26 -12
- package/dist/coordination/index.d.ts +77 -21
- package/dist/coordination/index.js +557 -159
- package/dist/coordination/index.js.map +1 -1
- package/dist/{default-config-CXsDvOmP.d.ts → default-config-B0cj-Hry.d.ts} +11 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +609 -195
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +394 -155
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +2 -2
- package/dist/execution/prompt-enhancer.js +1 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-DzjFuN3p.d.ts → goal-preamble-DvHDSKSe.d.ts} +14 -10
- package/dist/{goal-store-CxWmCGbH.d.ts → goal-store-DtLMySNb.d.ts} +1 -1
- package/dist/{index-CYIQrXVF.d.ts → index-B-ch8K9C.d.ts} +8 -8
- package/dist/{index-CbLSI66_.d.ts → index-CEDeNodM.d.ts} +5 -5
- package/dist/index.d.ts +183 -52
- package/dist/index.js +1779 -673
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +12 -8
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +1 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-DzxuZnNz.d.ts → llm-selector-C0tfTCUe.d.ts} +14 -2
- package/dist/{mcp-servers-DC4QRPUI.d.ts → mcp-servers-2x4w6Jn9.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +74 -30
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-B_siPxqN.d.ts → models-registry-DmJlKuNp.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CK5Jdj9K.d.ts → multi-agent-coordinator-DyCkCZnU.d.ts} +1 -1
- package/dist/{null-fleet-bus-DgvD4SCO.d.ts → null-fleet-bus-CG9QY2aP.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-bK0JQBR_.d.ts → parallel-eternal-engine-Jw9uhEoT.d.ts} +9 -9
- package/dist/{path-resolver-BPEDlN38.d.ts → path-resolver-Dy2ej-gE.d.ts} +3 -3
- package/dist/{permission-4yvGmMRB.d.ts → permission-B9SB45lp.d.ts} +1 -1
- package/dist/{permission-policy-C6XpsBOy.d.ts → permission-policy-CkjSXabK.d.ts} +2 -2
- package/dist/{pipeline-CXCeMz8J.d.ts → pipeline-DPDxH_7m.d.ts} +3 -3
- package/dist/{plan-templates-BvzRBkJc.d.ts → plan-templates-CzD9GnAU.d.ts} +32 -8
- package/dist/{provider-runner-C5aQpDWE.d.ts → provider-runner-DMa70ODu.d.ts} +3 -3
- package/dist/{retry-policy-CFhdtRzz.d.ts → retry-policy-CN0khdlj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +274 -93
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CxiVLbt1.d.ts → secret-vault-B2yw84VT.d.ts} +43 -4
- package/dist/secret-vault-BAKpgFw_.d.ts +57 -0
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +204 -23
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-gIuhRTkN.d.ts → selector-CzHh_igB.d.ts} +1 -1
- package/dist/{session-event-bridge-DkvvrpDt.d.ts → session-event-bridge-BUI6Jf-4.d.ts} +1 -1
- package/dist/{session-reader-KdfVwkKP.d.ts → session-reader-CMgdMSRP.d.ts} +1 -1
- package/dist/storage/index.d.ts +112 -15
- package/dist/storage/index.js +419 -81
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +261 -53
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -5
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-CJjEwPXn.d.ts → wstack-paths-hOpNLmvf.d.ts} +2 -0
- package/package.json +1 -1
- package/skills/api-design/SKILL.md +1 -1
- package/skills/audit-log/SKILL.md +6 -6
- package/skills/bug-hunter/SKILL.md +5 -5
- package/skills/chimera/SKILL.md +4 -4
- package/skills/docker-deploy/SKILL.md +1 -1
- package/skills/git-flow/SKILL.md +3 -3
- package/skills/multi-agent/SKILL.md +3 -3
- package/skills/node-modern/SKILL.md +1 -0
- package/skills/observability/SKILL.md +2 -2
- package/skills/output-standards/SKILL.md +51 -28
- package/skills/refactor-planner/SKILL.md +3 -3
- package/skills/security-scanner/SKILL.md +4 -3
- package/skills/tech-stack/SKILL.md +1 -2
- package/dist/secret-vault-BJDY28ev.d.ts +0 -25
package/dist/storage/index.js
CHANGED
|
@@ -35,8 +35,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
35
35
|
}
|
|
36
36
|
let mode;
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
39
|
-
mode =
|
|
38
|
+
const stat7 = await fsp.stat(targetPath);
|
|
39
|
+
mode = stat7.mode & 511;
|
|
40
40
|
} catch {
|
|
41
41
|
mode = opts.mode;
|
|
42
42
|
}
|
|
@@ -76,8 +76,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
76
76
|
}
|
|
77
77
|
if (code !== "EEXIST") throw err;
|
|
78
78
|
try {
|
|
79
|
-
const
|
|
80
|
-
if (Date.now() -
|
|
79
|
+
const stat7 = await fsp.stat(lockPath);
|
|
80
|
+
if (Date.now() - stat7.mtimeMs > staleMs) {
|
|
81
81
|
await fsp.unlink(lockPath);
|
|
82
82
|
continue;
|
|
83
83
|
}
|
|
@@ -87,7 +87,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
87
87
|
if (Date.now() - started >= timeoutMs) {
|
|
88
88
|
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
89
89
|
}
|
|
90
|
-
await new Promise((
|
|
90
|
+
await new Promise((resolve6) => setTimeout(resolve6, 25));
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
try {
|
|
@@ -121,7 +121,7 @@ async function renameWithRetry(from, to) {
|
|
|
121
121
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
122
122
|
throw err;
|
|
123
123
|
}
|
|
124
|
-
await new Promise((
|
|
124
|
+
await new Promise((resolve6) => setTimeout(resolve6, delays[i]));
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
throw lastErr;
|
|
@@ -329,7 +329,8 @@ function resolveWstackPaths(opts) {
|
|
|
329
329
|
projectSddSession: path2.join(projectDir, "sdd-session.json"),
|
|
330
330
|
projectPlan: path2.join(projectDir, "plan.json"),
|
|
331
331
|
projectAutophase: path2.join(projectDir, "autophase"),
|
|
332
|
-
syncConfig: path2.join(globalRoot, "sync.json")
|
|
332
|
+
syncConfig: path2.join(globalRoot, "sync.json"),
|
|
333
|
+
projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
|
|
333
334
|
};
|
|
334
335
|
}
|
|
335
336
|
|
|
@@ -443,11 +444,34 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
443
444
|
dir;
|
|
444
445
|
events;
|
|
445
446
|
secretScrubber;
|
|
447
|
+
/**
|
|
448
|
+
* In-memory cache for load() results, keyed by session ID. The cache is
|
|
449
|
+
* invalidated when the file's mtimeMs or size changes (indicating the
|
|
450
|
+
* file was written to). This eliminates redundant full-file reads and
|
|
451
|
+
* JSON parses when the same session is loaded multiple times within the
|
|
452
|
+
* store's lifetime (e.g., webui session detail views, list() fallbacks).
|
|
453
|
+
*
|
|
454
|
+
* Max size is capped to prevent unbounded memory growth in long-running
|
|
455
|
+
* processes. When the limit is reached, the oldest entry is evicted.
|
|
456
|
+
*/
|
|
457
|
+
_loadCache = /* @__PURE__ */ new Map();
|
|
458
|
+
static LOAD_CACHE_MAX_ENTRIES = 50;
|
|
446
459
|
constructor(opts) {
|
|
447
460
|
this.dir = opts.dir;
|
|
448
461
|
this.events = opts.events;
|
|
449
462
|
this.secretScrubber = opts.secretScrubber;
|
|
450
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Clear the load() cache. Useful for testing or when the caller knows
|
|
466
|
+
* the file has changed externally (e.g., another process wrote to it).
|
|
467
|
+
*/
|
|
468
|
+
clearLoadCache(sessionId) {
|
|
469
|
+
if (sessionId !== void 0) {
|
|
470
|
+
this._loadCache.delete(sessionId);
|
|
471
|
+
} else {
|
|
472
|
+
this._loadCache.clear();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
451
475
|
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
452
476
|
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
453
477
|
this.events?.emit("storage.read", {
|
|
@@ -590,7 +614,20 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
590
614
|
const t0 = Date.now();
|
|
591
615
|
let outcome = "success";
|
|
592
616
|
let errorMsg;
|
|
617
|
+
let cacheHit = false;
|
|
593
618
|
try {
|
|
619
|
+
let stat7;
|
|
620
|
+
try {
|
|
621
|
+
const s = await fsp.stat(file);
|
|
622
|
+
stat7 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
623
|
+
} catch (err) {
|
|
624
|
+
throw err;
|
|
625
|
+
}
|
|
626
|
+
const cached = this._loadCache.get(id);
|
|
627
|
+
if (cached && cached.mtimeMs === stat7.mtimeMs && cached.size === stat7.size) {
|
|
628
|
+
cacheHit = true;
|
|
629
|
+
return cached.data;
|
|
630
|
+
}
|
|
594
631
|
const raw = await fsp.readFile(file, "utf8");
|
|
595
632
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
596
633
|
const events = [];
|
|
@@ -606,13 +643,30 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
606
643
|
const meta = this.metaFromEvents(id, events);
|
|
607
644
|
const { messages, usage } = this.replay(events, id);
|
|
608
645
|
const toolCallEnds = extractToolCallEnds(events);
|
|
609
|
-
|
|
646
|
+
const data = { metadata: meta, events, messages, usage, toolCallEnds };
|
|
647
|
+
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
648
|
+
const oldest = this._loadCache.keys().next().value;
|
|
649
|
+
if (oldest !== void 0) {
|
|
650
|
+
this._loadCache.delete(oldest);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
this._loadCache.set(id, { mtimeMs: stat7.mtimeMs, size: stat7.size, data });
|
|
654
|
+
return data;
|
|
610
655
|
} catch (err) {
|
|
611
656
|
outcome = "failure";
|
|
612
657
|
errorMsg = toErrorMessage(err);
|
|
613
658
|
throw err;
|
|
614
659
|
} finally {
|
|
615
660
|
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
661
|
+
if (cacheHit) {
|
|
662
|
+
this.events?.emit("storage.cache_hit", {
|
|
663
|
+
sessionId: id,
|
|
664
|
+
store: "session",
|
|
665
|
+
filePath: file,
|
|
666
|
+
operation: "load",
|
|
667
|
+
durationMs: Date.now() - t0
|
|
668
|
+
});
|
|
669
|
+
}
|
|
616
670
|
}
|
|
617
671
|
}
|
|
618
672
|
async list(limit = 20) {
|
|
@@ -780,8 +834,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
780
834
|
return JSON.parse(raw);
|
|
781
835
|
} catch {
|
|
782
836
|
const full = this.sessionPath(id, ".jsonl");
|
|
783
|
-
const
|
|
784
|
-
const summary = await this.summarize(id,
|
|
837
|
+
const stat7 = await fsp.stat(full);
|
|
838
|
+
const summary = await this.summarize(id, stat7.mtime.toISOString());
|
|
785
839
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
786
840
|
const msg = toErrorMessage(err);
|
|
787
841
|
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
@@ -863,8 +917,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
863
917
|
const pruneFile = async (dir, name, prefix) => {
|
|
864
918
|
const jsonlPath = path2.join(dir, name);
|
|
865
919
|
try {
|
|
866
|
-
const
|
|
867
|
-
if (
|
|
920
|
+
const stat7 = await fsp.stat(jsonlPath);
|
|
921
|
+
if (stat7.mtimeMs >= cutoff) return;
|
|
868
922
|
} catch {
|
|
869
923
|
return;
|
|
870
924
|
}
|
|
@@ -3137,6 +3191,9 @@ function deepFreeze(obj) {
|
|
|
3137
3191
|
}
|
|
3138
3192
|
return Object.freeze(obj);
|
|
3139
3193
|
}
|
|
3194
|
+
var KEY_BYTES = 32;
|
|
3195
|
+
var KEY_FILE_MAGIC = Buffer.from("WSKV", "ascii");
|
|
3196
|
+
KEY_FILE_MAGIC.length + 1 + KEY_BYTES;
|
|
3140
3197
|
function decryptConfigSecrets(cfg, vault, opts) {
|
|
3141
3198
|
const warn = ((msg) => console.warn(msg));
|
|
3142
3199
|
return walk(cfg, vault, (v, key) => {
|
|
@@ -3234,7 +3291,8 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
|
3234
3291
|
iterationTimeoutMs: 3e5,
|
|
3235
3292
|
sessionTimeoutMs: 18e5,
|
|
3236
3293
|
perIterationOutputCapBytes: 1e5,
|
|
3237
|
-
autoExtendLimit: true
|
|
3294
|
+
autoExtendLimit: true,
|
|
3295
|
+
restrictToProjectRoot: false
|
|
3238
3296
|
});
|
|
3239
3297
|
var DEFAULT_CONTEXT_CONFIG = Object.freeze({
|
|
3240
3298
|
preserveK: 10,
|
|
@@ -3274,7 +3332,8 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
3274
3332
|
iterationTimeoutMs: DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,
|
|
3275
3333
|
sessionTimeoutMs: DEFAULT_TOOLS_CONFIG.sessionTimeoutMs,
|
|
3276
3334
|
perIterationOutputCapBytes: DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
|
|
3277
|
-
autoExtendLimit: DEFAULT_TOOLS_CONFIG.autoExtendLimit
|
|
3335
|
+
autoExtendLimit: DEFAULT_TOOLS_CONFIG.autoExtendLimit,
|
|
3336
|
+
restrictToProjectRoot: DEFAULT_TOOLS_CONFIG.restrictToProjectRoot
|
|
3278
3337
|
},
|
|
3279
3338
|
log: { level: "info" },
|
|
3280
3339
|
features: {
|
|
@@ -4710,15 +4769,15 @@ var SessionRecovery = class {
|
|
|
4710
4769
|
async detectStale(sessionId) {
|
|
4711
4770
|
const fp = this.filePath(sessionId);
|
|
4712
4771
|
const TAIL_SIZE = 8192;
|
|
4713
|
-
let
|
|
4772
|
+
let stat7;
|
|
4714
4773
|
try {
|
|
4715
|
-
|
|
4774
|
+
stat7 = await fsp.stat(fp);
|
|
4716
4775
|
} catch (err) {
|
|
4717
4776
|
if (err.code === "ENOENT") return null;
|
|
4718
4777
|
return null;
|
|
4719
4778
|
}
|
|
4720
|
-
if (
|
|
4721
|
-
const position = Math.max(0,
|
|
4779
|
+
if (stat7.size === 0) return null;
|
|
4780
|
+
const position = Math.max(0, stat7.size - TAIL_SIZE);
|
|
4722
4781
|
const buf = Buffer.alloc(TAIL_SIZE);
|
|
4723
4782
|
let fh;
|
|
4724
4783
|
try {
|
|
@@ -5281,6 +5340,8 @@ var SessionAnalyzer = class {
|
|
|
5281
5340
|
var REGISTRY_FILE = "session-registry.json";
|
|
5282
5341
|
var HEARTBEAT_INTERVAL_MS = 5e3;
|
|
5283
5342
|
var STALE_TIMEOUT_MS = 3e4;
|
|
5343
|
+
var CLOSING_GRACE_MS = 15e3;
|
|
5344
|
+
var STALE_LOCK_MS = 1e4;
|
|
5284
5345
|
function pidAlive(pid) {
|
|
5285
5346
|
try {
|
|
5286
5347
|
process.kill(pid, 0);
|
|
@@ -5293,6 +5354,12 @@ var SessionRegistry = class {
|
|
|
5293
5354
|
filePath;
|
|
5294
5355
|
heartbeatTimer = null;
|
|
5295
5356
|
currentSessionId = null;
|
|
5357
|
+
/**
|
|
5358
|
+
* Last full entry this process registered. Kept so the heartbeat can
|
|
5359
|
+
* re-create our entry if it ever goes missing — e.g. our initial register()
|
|
5360
|
+
* write was dropped (a wedged lock), the file was reset, or we were pruned.
|
|
5361
|
+
*/
|
|
5362
|
+
lastEntry = null;
|
|
5296
5363
|
constructor(globalRoot) {
|
|
5297
5364
|
this.filePath = path2.join(globalRoot, REGISTRY_FILE);
|
|
5298
5365
|
}
|
|
@@ -5314,6 +5381,7 @@ var SessionRegistry = class {
|
|
|
5314
5381
|
agentCount: entry.agents?.length ?? 0,
|
|
5315
5382
|
agents: entry.agents ?? []
|
|
5316
5383
|
};
|
|
5384
|
+
this.lastEntry = full;
|
|
5317
5385
|
await this.atomicUpdate((registry) => {
|
|
5318
5386
|
const now = Date.now();
|
|
5319
5387
|
for (const [id, existing] of Object.entries(registry)) {
|
|
@@ -5339,16 +5407,28 @@ var SessionRegistry = class {
|
|
|
5339
5407
|
*/
|
|
5340
5408
|
async updateAgents(agents) {
|
|
5341
5409
|
if (!this.currentSessionId) return;
|
|
5410
|
+
const hasRunning = agents.some((a) => a.status === "running" || a.status === "streaming");
|
|
5411
|
+
const hasWaiting = agents.some((a) => a.status === "waiting_user");
|
|
5412
|
+
const hasError = agents.some((a) => a.status === "error");
|
|
5413
|
+
const status = hasRunning || hasWaiting || hasError ? "active" : "idle";
|
|
5414
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
5415
|
+
if (this.lastEntry) {
|
|
5416
|
+
this.lastEntry.agents = agents;
|
|
5417
|
+
this.lastEntry.agentCount = agents.length;
|
|
5418
|
+
this.lastEntry.status = status;
|
|
5419
|
+
this.lastEntry.lastHeartbeatAt = nowIso;
|
|
5420
|
+
}
|
|
5342
5421
|
await this.atomicUpdate((registry) => {
|
|
5343
|
-
|
|
5344
|
-
if (!entry)
|
|
5422
|
+
let entry = registry[this.currentSessionId];
|
|
5423
|
+
if (!entry) {
|
|
5424
|
+
if (!this.lastEntry) return;
|
|
5425
|
+
entry = { ...this.lastEntry };
|
|
5426
|
+
registry[this.currentSessionId] = entry;
|
|
5427
|
+
}
|
|
5345
5428
|
entry.agents = agents;
|
|
5346
5429
|
entry.agentCount = agents.length;
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
const hasError = agents.some((a) => a.status === "error");
|
|
5350
|
-
entry.status = hasRunning ? "active" : hasWaiting ? "active" : hasError ? "active" : "idle";
|
|
5351
|
-
entry.lastHeartbeatAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5430
|
+
entry.status = status;
|
|
5431
|
+
entry.lastHeartbeatAt = nowIso;
|
|
5352
5432
|
});
|
|
5353
5433
|
}
|
|
5354
5434
|
/**
|
|
@@ -5426,6 +5506,12 @@ var SessionRegistry = class {
|
|
|
5426
5506
|
entry.status = hasRunning ? "active" : "idle";
|
|
5427
5507
|
}
|
|
5428
5508
|
await this.writeAtomic(registry);
|
|
5509
|
+
} else if (this.lastEntry) {
|
|
5510
|
+
await this.atomicUpdate((reg) => {
|
|
5511
|
+
if (!reg[this.currentSessionId] && this.lastEntry) {
|
|
5512
|
+
reg[this.currentSessionId] = { ...this.lastEntry, lastHeartbeatAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5513
|
+
}
|
|
5514
|
+
});
|
|
5429
5515
|
}
|
|
5430
5516
|
} catch {
|
|
5431
5517
|
}
|
|
@@ -5438,6 +5524,11 @@ var SessionRegistry = class {
|
|
|
5438
5524
|
let pruned = false;
|
|
5439
5525
|
for (const [id, entry] of Object.entries(registry)) {
|
|
5440
5526
|
const heartbeatAge = now - new Date(entry.lastHeartbeatAt).getTime();
|
|
5527
|
+
if (entry.status === "closing" && heartbeatAge > CLOSING_GRACE_MS) {
|
|
5528
|
+
delete registry[id];
|
|
5529
|
+
pruned = true;
|
|
5530
|
+
continue;
|
|
5531
|
+
}
|
|
5441
5532
|
if (heartbeatAge > STALE_TIMEOUT_MS && !pidAlive(entry.pid)) {
|
|
5442
5533
|
entry.status = "stale";
|
|
5443
5534
|
const startedAge = now - new Date(entry.startedAt).getTime();
|
|
@@ -5457,17 +5548,23 @@ var SessionRegistry = class {
|
|
|
5457
5548
|
}
|
|
5458
5549
|
async atomicUpdate(fn) {
|
|
5459
5550
|
const lockPath = `${this.filePath}.lock`;
|
|
5460
|
-
const maxRetries =
|
|
5551
|
+
const maxRetries = 8;
|
|
5461
5552
|
const retryDelayMs = 20;
|
|
5462
5553
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
5463
5554
|
try {
|
|
5464
5555
|
await fsp.mkdir(path2.dirname(this.filePath), { recursive: true });
|
|
5465
|
-
|
|
5556
|
+
let lockHandle = await fsp.open(lockPath, "wx").catch(() => null);
|
|
5466
5557
|
if (!lockHandle) {
|
|
5467
|
-
|
|
5468
|
-
|
|
5558
|
+
if (await this.breakStaleLock(lockPath)) {
|
|
5559
|
+
lockHandle = await fsp.open(lockPath, "wx").catch(() => null);
|
|
5560
|
+
}
|
|
5561
|
+
if (!lockHandle) {
|
|
5562
|
+
await new Promise((r) => setTimeout(r, retryDelayMs * (attempt + 1)));
|
|
5563
|
+
continue;
|
|
5564
|
+
}
|
|
5469
5565
|
}
|
|
5470
5566
|
try {
|
|
5567
|
+
await lockHandle.writeFile(String(process.pid)).catch(() => void 0);
|
|
5471
5568
|
const raw = await fsp.readFile(this.filePath, "utf8").catch(() => "{}");
|
|
5472
5569
|
const registry = JSON.parse(raw);
|
|
5473
5570
|
fn(registry);
|
|
@@ -5482,6 +5579,31 @@ var SessionRegistry = class {
|
|
|
5482
5579
|
}
|
|
5483
5580
|
}
|
|
5484
5581
|
}
|
|
5582
|
+
/**
|
|
5583
|
+
* Break a contended lock if it is stale: the recorded owner pid is no longer
|
|
5584
|
+
* alive, or the lock is older than {@link STALE_LOCK_MS}. Returns true when the
|
|
5585
|
+
* lock was removed (caller should retry acquisition). Best-effort and
|
|
5586
|
+
* race-tolerant — a fresh lock (age ~0, live owner) is never broken, so the
|
|
5587
|
+
* common concurrent case self-heals on the next heartbeat.
|
|
5588
|
+
*/
|
|
5589
|
+
async breakStaleLock(lockPath) {
|
|
5590
|
+
try {
|
|
5591
|
+
const [stat7, content] = await Promise.all([
|
|
5592
|
+
fsp.stat(lockPath),
|
|
5593
|
+
fsp.readFile(lockPath, "utf8").catch(() => "")
|
|
5594
|
+
]);
|
|
5595
|
+
const ageMs = Date.now() - stat7.mtimeMs;
|
|
5596
|
+
const ownerPid = Number.parseInt(content.trim(), 10);
|
|
5597
|
+
const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !pidAlive(ownerPid);
|
|
5598
|
+
if (ownerDead || ageMs > STALE_LOCK_MS) {
|
|
5599
|
+
await fsp.unlink(lockPath).catch(() => void 0);
|
|
5600
|
+
return true;
|
|
5601
|
+
}
|
|
5602
|
+
return false;
|
|
5603
|
+
} catch {
|
|
5604
|
+
return true;
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5485
5607
|
async writeAtomicLocked(registry) {
|
|
5486
5608
|
const tmp = `${this.filePath}.${randomUUID().slice(0, 8)}.tmp`;
|
|
5487
5609
|
await fsp.writeFile(tmp, JSON.stringify(registry, null, 2), "utf8");
|
|
@@ -5509,6 +5631,10 @@ function hasSessionRegistry() {
|
|
|
5509
5631
|
}
|
|
5510
5632
|
|
|
5511
5633
|
// src/agent-status-tracker.ts
|
|
5634
|
+
var AGENT_REAP_MS = 3e4;
|
|
5635
|
+
var AGENT_SWEEP_INTERVAL_MS = 1e4;
|
|
5636
|
+
var PARTIAL_TEXT_CAP = 1200;
|
|
5637
|
+
var PARTIAL_FLUSH_THROTTLE_MS = 300;
|
|
5512
5638
|
var AgentStatusTracker = class {
|
|
5513
5639
|
events;
|
|
5514
5640
|
registry;
|
|
@@ -5520,11 +5646,21 @@ var AgentStatusTracker = class {
|
|
|
5520
5646
|
leaderCurrentTool;
|
|
5521
5647
|
leaderIterations = 0;
|
|
5522
5648
|
leaderToolCalls = 0;
|
|
5649
|
+
leaderCostUsd = 0;
|
|
5650
|
+
leaderTokensIn = 0;
|
|
5651
|
+
leaderTokensOut = 0;
|
|
5652
|
+
leaderCtxPct;
|
|
5653
|
+
leaderModel;
|
|
5654
|
+
leaderPartialText = "";
|
|
5523
5655
|
unsubscribers = [];
|
|
5656
|
+
onUpdate;
|
|
5657
|
+
sweepTimer = null;
|
|
5658
|
+
partialTimer = null;
|
|
5524
5659
|
constructor(opts) {
|
|
5525
5660
|
this.events = opts.events;
|
|
5526
5661
|
this.registry = opts.registry;
|
|
5527
5662
|
this.leaderName = opts.leaderName ?? "leader";
|
|
5663
|
+
this.onUpdate = opts.onUpdate;
|
|
5528
5664
|
}
|
|
5529
5665
|
start() {
|
|
5530
5666
|
this.unsubscribers.push(
|
|
@@ -5534,10 +5670,22 @@ var AgentStatusTracker = class {
|
|
|
5534
5670
|
this.flush();
|
|
5535
5671
|
})
|
|
5536
5672
|
);
|
|
5673
|
+
this.unsubscribers.push(
|
|
5674
|
+
this.events.onPattern("iteration.started", (_e, payload) => {
|
|
5675
|
+
const ctx = payload?.ctx;
|
|
5676
|
+
if (!ctx) return;
|
|
5677
|
+
if (ctx.model) this.leaderModel = ctx.model;
|
|
5678
|
+
if (typeof ctx.tokenCount === "number" && typeof ctx.maxContext === "number" && ctx.maxContext > 0) {
|
|
5679
|
+
this.leaderCtxPct = Math.round(ctx.tokenCount / ctx.maxContext * 100);
|
|
5680
|
+
}
|
|
5681
|
+
this.flush();
|
|
5682
|
+
})
|
|
5683
|
+
);
|
|
5537
5684
|
this.unsubscribers.push(
|
|
5538
5685
|
this.events.onPattern("agent.run.completed", () => {
|
|
5539
5686
|
this.leaderStatus = "idle";
|
|
5540
5687
|
this.leaderCurrentTool = void 0;
|
|
5688
|
+
this.leaderPartialText = "";
|
|
5541
5689
|
this.flush();
|
|
5542
5690
|
})
|
|
5543
5691
|
);
|
|
@@ -5545,6 +5693,7 @@ var AgentStatusTracker = class {
|
|
|
5545
5693
|
this.events.onPattern("agent.run.error", () => {
|
|
5546
5694
|
this.leaderStatus = "error";
|
|
5547
5695
|
this.leaderCurrentTool = void 0;
|
|
5696
|
+
this.leaderPartialText = "";
|
|
5548
5697
|
this.flush();
|
|
5549
5698
|
})
|
|
5550
5699
|
);
|
|
@@ -5574,74 +5723,120 @@ var AgentStatusTracker = class {
|
|
|
5574
5723
|
this.unsubscribers.push(
|
|
5575
5724
|
this.events.onPattern("llm.stream_started", () => {
|
|
5576
5725
|
this.leaderStatus = "streaming";
|
|
5726
|
+
this.leaderPartialText = "";
|
|
5577
5727
|
this.flush();
|
|
5578
5728
|
})
|
|
5579
5729
|
);
|
|
5580
5730
|
this.unsubscribers.push(
|
|
5581
|
-
this.events.onPattern("
|
|
5731
|
+
this.events.onPattern("provider.text_delta", (_e, payload) => {
|
|
5732
|
+
const text = payload?.text;
|
|
5733
|
+
if (!text) return;
|
|
5734
|
+
this.leaderStatus = "streaming";
|
|
5735
|
+
const next = this.leaderPartialText + text;
|
|
5736
|
+
this.leaderPartialText = next.length > PARTIAL_TEXT_CAP ? next.slice(next.length - PARTIAL_TEXT_CAP) : next;
|
|
5737
|
+
this.schedulePartialFlush();
|
|
5738
|
+
})
|
|
5739
|
+
);
|
|
5740
|
+
this.unsubscribers.push(
|
|
5741
|
+
this.events.onPattern("token.accounted", (_e, payload) => {
|
|
5582
5742
|
const p = payload;
|
|
5583
|
-
if (p
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
iterations: 0,
|
|
5589
|
-
toolCalls: 0,
|
|
5590
|
-
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5591
|
-
});
|
|
5592
|
-
this.flush();
|
|
5593
|
-
}
|
|
5743
|
+
if (!p) return;
|
|
5744
|
+
this.leaderTokensIn += p.usage?.input ?? 0;
|
|
5745
|
+
this.leaderTokensOut += p.usage?.output ?? 0;
|
|
5746
|
+
this.leaderCostUsd += p.cost?.total ?? 0;
|
|
5747
|
+
this.flush();
|
|
5594
5748
|
})
|
|
5595
5749
|
);
|
|
5750
|
+
const touch = (id) => {
|
|
5751
|
+
let entry = this.agents.get(id);
|
|
5752
|
+
if (!entry) {
|
|
5753
|
+
entry = { id, name: id, status: "idle", iterations: 0, toolCalls: 0, lastActivityAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5754
|
+
this.agents.set(id, entry);
|
|
5755
|
+
}
|
|
5756
|
+
entry.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5757
|
+
return entry;
|
|
5758
|
+
};
|
|
5596
5759
|
this.unsubscribers.push(
|
|
5597
|
-
this.events.onPattern("
|
|
5760
|
+
this.events.onPattern("subagent.spawned", (_e, payload) => {
|
|
5598
5761
|
const p = payload;
|
|
5599
|
-
if (p?.subagentId)
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
this.flush();
|
|
5606
|
-
}
|
|
5607
|
-
}
|
|
5762
|
+
if (!p?.subagentId) return;
|
|
5763
|
+
const entry = touch(p.subagentId);
|
|
5764
|
+
entry.name = p.name?.trim() || entry.name;
|
|
5765
|
+
if (p.model) entry.model = p.model;
|
|
5766
|
+
entry.status = "running";
|
|
5767
|
+
this.flush();
|
|
5608
5768
|
})
|
|
5609
5769
|
);
|
|
5610
5770
|
this.unsubscribers.push(
|
|
5611
|
-
this.events.onPattern("
|
|
5771
|
+
this.events.onPattern("subagent.ctx_pct", (_e, payload) => {
|
|
5612
5772
|
const p = payload;
|
|
5613
|
-
if (p?.subagentId)
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
entry.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5618
|
-
this.flush();
|
|
5619
|
-
}
|
|
5620
|
-
}
|
|
5773
|
+
if (!p?.subagentId) return;
|
|
5774
|
+
const entry = touch(p.subagentId);
|
|
5775
|
+
if (typeof p.load === "number") entry.ctxPct = Math.round(p.load * 100);
|
|
5776
|
+
this.flush();
|
|
5621
5777
|
})
|
|
5622
5778
|
);
|
|
5623
5779
|
this.unsubscribers.push(
|
|
5624
|
-
this.events.onPattern("
|
|
5780
|
+
this.events.onPattern("subagent.task_started", (_e, payload) => {
|
|
5625
5781
|
const p = payload;
|
|
5626
|
-
if (p?.subagentId)
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
this.flush();
|
|
5632
|
-
}
|
|
5633
|
-
}
|
|
5782
|
+
if (!p?.subagentId) return;
|
|
5783
|
+
const entry = touch(p.subagentId);
|
|
5784
|
+
entry.status = "running";
|
|
5785
|
+
entry.iterations++;
|
|
5786
|
+
this.flush();
|
|
5634
5787
|
})
|
|
5635
5788
|
);
|
|
5636
5789
|
this.unsubscribers.push(
|
|
5637
|
-
this.events.onPattern("
|
|
5790
|
+
this.events.onPattern("subagent.tool_executed", (_e, payload) => {
|
|
5638
5791
|
const p = payload;
|
|
5639
|
-
if (p?.subagentId)
|
|
5640
|
-
|
|
5641
|
-
|
|
5792
|
+
if (!p?.subagentId) return;
|
|
5793
|
+
const entry = touch(p.subagentId);
|
|
5794
|
+
entry.status = "running";
|
|
5795
|
+
entry.currentTool = p.name;
|
|
5796
|
+
entry.toolCalls++;
|
|
5797
|
+
this.flush();
|
|
5798
|
+
})
|
|
5799
|
+
);
|
|
5800
|
+
this.unsubscribers.push(
|
|
5801
|
+
this.events.onPattern("subagent.iteration_summary", (_e, payload) => {
|
|
5802
|
+
const p = payload;
|
|
5803
|
+
if (!p?.subagentId) return;
|
|
5804
|
+
const entry = touch(p.subagentId);
|
|
5805
|
+
entry.status = "running";
|
|
5806
|
+
if (typeof p.iteration === "number") entry.iterations = p.iteration;
|
|
5807
|
+
if (typeof p.toolCalls === "number") entry.toolCalls = p.toolCalls;
|
|
5808
|
+
if (typeof p.costUsd === "number") entry.costUsd = p.costUsd;
|
|
5809
|
+
if (p.currentTool) entry.currentTool = p.currentTool;
|
|
5810
|
+
if (typeof p.partialText === "string") {
|
|
5811
|
+
entry.partialText = p.partialText.length > PARTIAL_TEXT_CAP ? p.partialText.slice(p.partialText.length - PARTIAL_TEXT_CAP) : p.partialText;
|
|
5642
5812
|
}
|
|
5813
|
+
this.flush();
|
|
5814
|
+
})
|
|
5815
|
+
);
|
|
5816
|
+
this.unsubscribers.push(
|
|
5817
|
+
this.events.onPattern("subagent.task_completed", (_e, payload) => {
|
|
5818
|
+
const p = payload;
|
|
5819
|
+
if (!p?.subagentId) return;
|
|
5820
|
+
const entry = this.agents.get(p.subagentId);
|
|
5821
|
+
if (!entry) return;
|
|
5822
|
+
entry.status = p.status === "failed" || p.status === "timeout" ? "error" : "idle";
|
|
5823
|
+
entry.currentTool = void 0;
|
|
5824
|
+
entry.partialText = void 0;
|
|
5825
|
+
if (typeof p.iterations === "number") entry.iterations = p.iterations;
|
|
5826
|
+
if (typeof p.toolCalls === "number") entry.toolCalls = p.toolCalls;
|
|
5827
|
+
entry.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5828
|
+
this.flush();
|
|
5643
5829
|
})
|
|
5644
5830
|
);
|
|
5831
|
+
this.unsubscribers.push(
|
|
5832
|
+
this.events.onPattern("subagent.stopped", (_e, payload) => {
|
|
5833
|
+
const p = payload;
|
|
5834
|
+
if (!p?.subagentId) return;
|
|
5835
|
+
if (this.agents.delete(p.subagentId)) this.flush();
|
|
5836
|
+
})
|
|
5837
|
+
);
|
|
5838
|
+
this.sweepTimer = setInterval(() => this.sweep(), AGENT_SWEEP_INTERVAL_MS);
|
|
5839
|
+
if (typeof this.sweepTimer.unref === "function") this.sweepTimer.unref();
|
|
5645
5840
|
}
|
|
5646
5841
|
stop() {
|
|
5647
5842
|
for (const unsub of this.unsubscribers) {
|
|
@@ -5651,6 +5846,45 @@ var AgentStatusTracker = class {
|
|
|
5651
5846
|
}
|
|
5652
5847
|
}
|
|
5653
5848
|
this.unsubscribers = [];
|
|
5849
|
+
if (this.sweepTimer) {
|
|
5850
|
+
clearInterval(this.sweepTimer);
|
|
5851
|
+
this.sweepTimer = null;
|
|
5852
|
+
}
|
|
5853
|
+
if (this.partialTimer) {
|
|
5854
|
+
clearTimeout(this.partialTimer);
|
|
5855
|
+
this.partialTimer = null;
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Coalesce streamed-text flushes: at most one registry write per
|
|
5860
|
+
* {@link PARTIAL_FLUSH_THROTTLE_MS} while text streams in, so per-token
|
|
5861
|
+
* deltas never thrash the cross-process registry file.
|
|
5862
|
+
*/
|
|
5863
|
+
schedulePartialFlush() {
|
|
5864
|
+
if (this.partialTimer) return;
|
|
5865
|
+
this.partialTimer = setTimeout(() => {
|
|
5866
|
+
this.partialTimer = null;
|
|
5867
|
+
this.flush();
|
|
5868
|
+
}, PARTIAL_FLUSH_THROTTLE_MS);
|
|
5869
|
+
if (typeof this.partialTimer.unref === "function") this.partialTimer.unref();
|
|
5870
|
+
}
|
|
5871
|
+
/**
|
|
5872
|
+
* Remove subagents that have been finished (idle/error) for longer than
|
|
5873
|
+
* {@link AGENT_REAP_MS}. Running / streaming / waiting_user agents are kept
|
|
5874
|
+
* regardless of age — only *not-working* agents are reaped.
|
|
5875
|
+
*/
|
|
5876
|
+
sweep() {
|
|
5877
|
+
const now = Date.now();
|
|
5878
|
+
let removed = false;
|
|
5879
|
+
for (const [id, a] of this.agents) {
|
|
5880
|
+
const finished = a.status !== "running" && a.status !== "streaming" && a.status !== "waiting_user";
|
|
5881
|
+
const age = now - Date.parse(a.lastActivityAt);
|
|
5882
|
+
if (finished && Number.isFinite(age) && age > AGENT_REAP_MS) {
|
|
5883
|
+
this.agents.delete(id);
|
|
5884
|
+
removed = true;
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
if (removed) this.flush();
|
|
5654
5888
|
}
|
|
5655
5889
|
flush() {
|
|
5656
5890
|
const leaderEntry = {
|
|
@@ -5660,12 +5894,105 @@ var AgentStatusTracker = class {
|
|
|
5660
5894
|
currentTool: this.leaderCurrentTool,
|
|
5661
5895
|
iterations: this.leaderIterations,
|
|
5662
5896
|
toolCalls: this.leaderToolCalls,
|
|
5897
|
+
costUsd: this.leaderCostUsd,
|
|
5898
|
+
tokensIn: this.leaderTokensIn,
|
|
5899
|
+
tokensOut: this.leaderTokensOut,
|
|
5900
|
+
ctxPct: this.leaderCtxPct,
|
|
5901
|
+
model: this.leaderModel,
|
|
5902
|
+
partialText: this.leaderPartialText || void 0,
|
|
5663
5903
|
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5664
5904
|
};
|
|
5665
5905
|
const allAgents = [leaderEntry, ...this.agents.values()];
|
|
5666
|
-
this.registry.updateAgents(allAgents).
|
|
5906
|
+
this.registry.updateAgents(allAgents).then(() => {
|
|
5907
|
+
try {
|
|
5908
|
+
this.onUpdate?.();
|
|
5909
|
+
} catch {
|
|
5910
|
+
}
|
|
5911
|
+
}).catch(() => void 0);
|
|
5912
|
+
}
|
|
5913
|
+
};
|
|
5914
|
+
var INSTANCES_FILE = "webui-instances.json";
|
|
5915
|
+
var DISCOVERY_TTL_MS = 2500;
|
|
5916
|
+
var COALESCE_MS = 50;
|
|
5917
|
+
var POST_TIMEOUT_MS = 500;
|
|
5918
|
+
function pidAlive2(pid) {
|
|
5919
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
5920
|
+
try {
|
|
5921
|
+
process.kill(pid, 0);
|
|
5922
|
+
return true;
|
|
5923
|
+
} catch (err) {
|
|
5924
|
+
return err.code !== "ESRCH";
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
function normRoot(root) {
|
|
5928
|
+
const resolved = path2.resolve(root);
|
|
5929
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
5930
|
+
}
|
|
5931
|
+
var FleetNotifier = class {
|
|
5932
|
+
baseDir;
|
|
5933
|
+
projectRoot;
|
|
5934
|
+
selfPid;
|
|
5935
|
+
doPost;
|
|
5936
|
+
cache = null;
|
|
5937
|
+
timer = null;
|
|
5938
|
+
disposed = false;
|
|
5939
|
+
constructor(opts) {
|
|
5940
|
+
this.baseDir = opts.baseDir;
|
|
5941
|
+
this.projectRoot = normRoot(opts.projectRoot);
|
|
5942
|
+
this.selfPid = opts.selfPid ?? process.pid;
|
|
5943
|
+
this.doPost = opts.post ?? defaultPost;
|
|
5944
|
+
}
|
|
5945
|
+
/** Coalesced, best-effort nudge. Safe to call on every status change. */
|
|
5946
|
+
notify() {
|
|
5947
|
+
if (this.disposed || this.timer) return;
|
|
5948
|
+
this.timer = setTimeout(() => {
|
|
5949
|
+
this.timer = null;
|
|
5950
|
+
void this.flush();
|
|
5951
|
+
}, COALESCE_MS);
|
|
5952
|
+
if (typeof this.timer.unref === "function") this.timer.unref();
|
|
5953
|
+
}
|
|
5954
|
+
/** Resolve same-project WebUI ping URLs (cached briefly). Exposed for tests. */
|
|
5955
|
+
async endpoints() {
|
|
5956
|
+
const now = Date.now();
|
|
5957
|
+
if (this.cache && now - this.cache.at < DISCOVERY_TTL_MS) return this.cache.urls;
|
|
5958
|
+
const urls = await this.discover();
|
|
5959
|
+
this.cache = { at: now, urls };
|
|
5960
|
+
return urls;
|
|
5961
|
+
}
|
|
5962
|
+
dispose() {
|
|
5963
|
+
this.disposed = true;
|
|
5964
|
+
if (this.timer) {
|
|
5965
|
+
clearTimeout(this.timer);
|
|
5966
|
+
this.timer = null;
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
async flush() {
|
|
5970
|
+
const urls = await this.endpoints();
|
|
5971
|
+
await Promise.all(urls.map((u) => this.doPost(u).catch(() => void 0)));
|
|
5972
|
+
}
|
|
5973
|
+
async discover() {
|
|
5974
|
+
try {
|
|
5975
|
+
const raw = await fsp.readFile(path2.join(this.baseDir, INSTANCES_FILE), "utf8");
|
|
5976
|
+
const data = JSON.parse(raw);
|
|
5977
|
+
const list = Array.isArray(data?.instances) ? data.instances : [];
|
|
5978
|
+
return list.filter((i) => i && typeof i.httpPort === "number").filter((i) => i.pid !== this.selfPid).filter((i) => normRoot(i.projectRoot) === this.projectRoot).filter((i) => pidAlive2(i.pid)).map((i) => {
|
|
5979
|
+
const host = i.host === "0.0.0.0" || i.host === "::" || !i.host ? "127.0.0.1" : i.host;
|
|
5980
|
+
return `http://${host}:${i.httpPort}/api/fleet/ping`;
|
|
5981
|
+
});
|
|
5982
|
+
} catch {
|
|
5983
|
+
return [];
|
|
5984
|
+
}
|
|
5667
5985
|
}
|
|
5668
5986
|
};
|
|
5987
|
+
async function defaultPost(url) {
|
|
5988
|
+
const ac = new AbortController();
|
|
5989
|
+
const t = setTimeout(() => ac.abort(), POST_TIMEOUT_MS);
|
|
5990
|
+
try {
|
|
5991
|
+
await fetch(url, { method: "POST", signal: ac.signal });
|
|
5992
|
+
} finally {
|
|
5993
|
+
clearTimeout(t);
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5669
5996
|
var DefaultSessionRewinder = class {
|
|
5670
5997
|
constructor(sessionsDir, projectRoot) {
|
|
5671
5998
|
this.sessionsDir = sessionsDir;
|
|
@@ -6034,6 +6361,7 @@ async function savePlan(filePath, plan, events) {
|
|
|
6034
6361
|
outcome: "success",
|
|
6035
6362
|
durationMs: Date.now() - t0
|
|
6036
6363
|
});
|
|
6364
|
+
return true;
|
|
6037
6365
|
} catch (err) {
|
|
6038
6366
|
events?.emit("storage.error", {
|
|
6039
6367
|
sessionId: "~boot~",
|
|
@@ -6047,6 +6375,7 @@ async function savePlan(filePath, plan, events) {
|
|
|
6047
6375
|
"[plan-store] save failed:",
|
|
6048
6376
|
toErrorMessage(err)
|
|
6049
6377
|
);
|
|
6378
|
+
return false;
|
|
6050
6379
|
}
|
|
6051
6380
|
}
|
|
6052
6381
|
function emptyPlan(sessionId, title) {
|
|
@@ -6148,7 +6477,10 @@ async function mutatePlan(filePath, sessionId, fn) {
|
|
|
6148
6477
|
return withFileLock(filePath, async () => {
|
|
6149
6478
|
const plan = await loadPlan(filePath) ?? emptyPlan(sessionId);
|
|
6150
6479
|
const updated = await fn(plan);
|
|
6151
|
-
await savePlan(filePath, updated);
|
|
6480
|
+
const persisted = await savePlan(filePath, updated);
|
|
6481
|
+
if (!persisted) {
|
|
6482
|
+
throw new Error(`Failed to persist plan to ${filePath} \u2014 the change was NOT saved.`);
|
|
6483
|
+
}
|
|
6152
6484
|
return updated;
|
|
6153
6485
|
});
|
|
6154
6486
|
}
|
|
@@ -6346,6 +6678,7 @@ async function saveTasks(filePath, tasks, events, traceId) {
|
|
|
6346
6678
|
durationMs: Date.now() - t0,
|
|
6347
6679
|
...traceId !== void 0 && { traceId }
|
|
6348
6680
|
});
|
|
6681
|
+
return true;
|
|
6349
6682
|
} catch (err) {
|
|
6350
6683
|
events?.emit("storage.error", {
|
|
6351
6684
|
sessionId: traceId ?? "~boot~",
|
|
@@ -6361,13 +6694,17 @@ async function saveTasks(filePath, tasks, events, traceId) {
|
|
|
6361
6694
|
"[task-store] save failed:",
|
|
6362
6695
|
toErrorMessage(err)
|
|
6363
6696
|
);
|
|
6697
|
+
return false;
|
|
6364
6698
|
}
|
|
6365
6699
|
}
|
|
6366
6700
|
async function mutateTasks(filePath, sessionId, fn, events, traceId) {
|
|
6367
6701
|
return withFileLock(filePath, async () => {
|
|
6368
6702
|
const file = await loadTasks(filePath, events, traceId) ?? emptyTaskFile(sessionId);
|
|
6369
6703
|
const updated = await fn(file);
|
|
6370
|
-
await saveTasks(filePath, updated, events, traceId);
|
|
6704
|
+
const persisted = await saveTasks(filePath, updated, events, traceId);
|
|
6705
|
+
if (!persisted) {
|
|
6706
|
+
throw new Error(`Failed to persist tasks to ${filePath} \u2014 the change was NOT saved.`);
|
|
6707
|
+
}
|
|
6371
6708
|
return updated;
|
|
6372
6709
|
});
|
|
6373
6710
|
}
|
|
@@ -7091,8 +7428,8 @@ var CloudSync = class {
|
|
|
7091
7428
|
const localPath = this.categoryToPath(cat);
|
|
7092
7429
|
if (!localPath) continue;
|
|
7093
7430
|
try {
|
|
7094
|
-
const
|
|
7095
|
-
if (
|
|
7431
|
+
const stat7 = await fsp.stat(localPath);
|
|
7432
|
+
if (stat7.isDirectory()) {
|
|
7096
7433
|
const files = await this.walkDir(localPath, localPath);
|
|
7097
7434
|
for (const file of files) {
|
|
7098
7435
|
const content = await fsp.readFile(file, "utf8");
|
|
@@ -7117,8 +7454,8 @@ var CloudSync = class {
|
|
|
7117
7454
|
const localPath = this.categoryToPath(cat);
|
|
7118
7455
|
if (!localPath) continue;
|
|
7119
7456
|
try {
|
|
7120
|
-
const
|
|
7121
|
-
if (
|
|
7457
|
+
const stat7 = await fsp.stat(localPath);
|
|
7458
|
+
if (stat7.isDirectory()) {
|
|
7122
7459
|
const files = await this.walkDir(localPath, localPath);
|
|
7123
7460
|
for (const file of files) {
|
|
7124
7461
|
const content = await fsp.readFile(file);
|
|
@@ -7145,6 +7482,7 @@ var CloudSync = class {
|
|
|
7145
7482
|
return this.paths.globalMemory;
|
|
7146
7483
|
case "history":
|
|
7147
7484
|
return this.paths.historyFile;
|
|
7485
|
+
/* v8 ignore next -- unreachable: SyncCategory is exhaustively matched above */
|
|
7148
7486
|
default:
|
|
7149
7487
|
return null;
|
|
7150
7488
|
}
|
|
@@ -7323,6 +7661,6 @@ function resolveSessionLoggingConfig(cfg) {
|
|
|
7323
7661
|
};
|
|
7324
7662
|
}
|
|
7325
7663
|
|
|
7326
|
-
export { ALL_SYNC_CATEGORIES, AgentStatusTracker, AnnotationsStore, CORE_RECONSTRUCT_EVENTS, CloudSync, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultMemoryStore, DefaultPromptStore, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DirectorStateCheckpoint, FileMemoryBackend, GraphMemoryBackend, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, QueueStore, RecoveryLock, ReplayLogStore, STANDARD_AUDIT_EVENTS, SessionAnalyzer, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, ToolAuditLog, addPlanItem, appendJournal, attachPlanCheckpoint, attachTodosCheckpoint, clearPlan, createSessionEventBridge, deriveTodosFromPlanItem, emptyGoal, emptyPlan, emptyTaskFile, formatGoal, formatPlan, formatPlanTemplates, getPlanTemplate, getSessionRegistry, goalFilePath, hasSessionRegistry, listPlanTemplates, loadDirectorState, loadGoal, loadPlan, loadTasks, loadTodosCheckpoint, mutatePlan, mutateTasks, parseEntries, parseProgressFromText, recordProgress, removePlanItem, resolveAuditLevel, resolveSessionLoggingConfig, runConfigMigrations, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, setPlanItemStatus, setProgress, summarizeUsage };
|
|
7664
|
+
export { ALL_SYNC_CATEGORIES, AgentStatusTracker, AnnotationsStore, CORE_RECONSTRUCT_EVENTS, CloudSync, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultMemoryStore, DefaultPromptStore, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DirectorStateCheckpoint, FileMemoryBackend, FleetNotifier, GraphMemoryBackend, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, QueueStore, RecoveryLock, ReplayLogStore, STANDARD_AUDIT_EVENTS, SessionAnalyzer, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, ToolAuditLog, addPlanItem, appendJournal, attachPlanCheckpoint, attachTodosCheckpoint, clearPlan, createSessionEventBridge, deriveTodosFromPlanItem, emptyGoal, emptyPlan, emptyTaskFile, formatGoal, formatPlan, formatPlanTemplates, getPlanTemplate, getSessionRegistry, goalFilePath, hasSessionRegistry, listPlanTemplates, loadDirectorState, loadGoal, loadPlan, loadTasks, loadTodosCheckpoint, mutatePlan, mutateTasks, parseEntries, parseProgressFromText, recordProgress, removePlanItem, resolveAuditLevel, resolveSessionLoggingConfig, runConfigMigrations, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, setPlanItemStatus, setProgress, summarizeUsage };
|
|
7327
7665
|
//# sourceMappingURL=index.js.map
|
|
7328
7666
|
//# sourceMappingURL=index.js.map
|