@wrongstack/core 0.272.0 → 0.272.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-jVSZiygR.d.ts → agent-bridge-DFQYEeXf.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DOLIwBRo.d.ts → agent-subagent-runner-BZa_IEcd.d.ts} +4 -4
- package/dist/{brain-CdbbJWi3.d.ts → brain-etbcbRwV.d.ts} +24 -1
- package/dist/{config-D2DGoGSQ.d.ts → config-rRS8yorV.d.ts} +43 -1
- package/dist/coordination/index.d.ts +74 -14
- package/dist/coordination/index.js +285 -97
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +16 -16
- package/dist/defaults/index.js +209 -140
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +9 -9
- package/dist/extension/index.d.ts +4 -4
- package/dist/{global-mailbox-CQj_C9Dp.d.ts → global-mailbox-DJ4EoRr0.d.ts} +7 -3
- package/dist/{goal-preamble-ZXDjjR1y.d.ts → goal-preamble-hM8BH7TK.d.ts} +5 -5
- package/dist/{goal-store-CcJBd-g1.d.ts → goal-store-CWlbT0TO.d.ts} +1 -1
- package/dist/hq/index.d.ts +6 -4
- package/dist/hq/index.js +14 -6
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-Qo4kTzgw.d.ts → index-DWm_PE9L.d.ts} +3 -3
- package/dist/{index-BL7BAx0p.d.ts → index-DqW4o62H.d.ts} +4 -4
- package/dist/index.d.ts +27 -27
- package/dist/index.js +571 -215
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +4 -4
- package/dist/kernel/index.d.ts +5 -5
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DS-YUXvF.d.ts → mcp-servers-BpWHTKlE.d.ts} +1 -1
- package/dist/models/index.d.ts +3 -3
- package/dist/{models-registry-DP6pGHet.d.ts → models-registry-CXQFUn5t.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-BvbdNQ14.d.ts → multi-agent-coordinator-jyimfo7D.d.ts} +1 -1
- package/dist/{null-fleet-bus-BxTfXBKo.d.ts → null-fleet-bus-DOGQcvrY.d.ts} +5 -5
- package/dist/observability/index.d.ts +1 -1
- package/dist/{parallel-eternal-engine-Cf-GTegR.d.ts → parallel-eternal-engine-rItJBYp9.d.ts} +6 -6
- package/dist/{path-resolver-DztfnFcv.d.ts → path-resolver-DrpF5MGK.d.ts} +2 -2
- package/dist/{pipeline-sNIkhXeB.d.ts → pipeline-Ckkn3AOA.d.ts} +1 -1
- package/dist/{plan-templates-DYiKFmEb.d.ts → plan-templates-BvHw5Znw.d.ts} +24 -6
- package/dist/{provider-model-resolve-dYAbTs_i.d.ts → provider-model-resolve-nZqnCeaR.d.ts} +1 -1
- package/dist/{provider-runner-Dw8x0F7u.d.ts → provider-runner-zVOn1p67.d.ts} +1 -1
- package/dist/sdd/index.d.ts +5 -5
- package/dist/storage/index.d.ts +12 -7
- package/dist/storage/index.js +250 -111
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/types/index.d.ts +13 -13
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/storage/index.js
CHANGED
|
@@ -80,7 +80,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
80
80
|
if (Date.now() - started >= timeoutMs) {
|
|
81
81
|
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
82
82
|
}
|
|
83
|
-
await new Promise((
|
|
83
|
+
await new Promise((resolve7) => setTimeout(resolve7, 25));
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
try {
|
|
@@ -114,7 +114,7 @@ async function renameWithRetry(from, to) {
|
|
|
114
114
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
115
115
|
throw err;
|
|
116
116
|
}
|
|
117
|
-
await new Promise((
|
|
117
|
+
await new Promise((resolve7) => setTimeout(resolve7, delays[i]));
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
throw lastErr;
|
|
@@ -477,7 +477,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
477
477
|
this._loadCache.clear();
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
|
-
//
|
|
480
|
+
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
481
481
|
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
482
482
|
this.events?.emit("storage.read", {
|
|
483
483
|
sessionId,
|
|
@@ -592,7 +592,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
592
592
|
this.events,
|
|
593
593
|
{
|
|
594
594
|
resumed: true,
|
|
595
|
-
// Shard directory (sessions/<date>/)
|
|
595
|
+
// Shard directory (sessions/<date>/) — must match create() so the
|
|
596
596
|
// .summary.json sidecar lands next to the JSONL instead of the
|
|
597
597
|
// sessions root (where summaryFor() would never find it).
|
|
598
598
|
dir: path2.dirname(file),
|
|
@@ -633,19 +633,93 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
633
633
|
const raw = await fsp.readFile(file, "utf8");
|
|
634
634
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
635
635
|
const events = [];
|
|
636
|
+
let sessionStartEvent;
|
|
637
|
+
let sessionEndEvent;
|
|
638
|
+
let sessionModel;
|
|
639
|
+
let sessionProvider;
|
|
640
|
+
let sessionPendingToolUses;
|
|
641
|
+
const messages = [];
|
|
642
|
+
const openToolUses = /* @__PURE__ */ new Set();
|
|
643
|
+
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
636
644
|
for (const line of lines) {
|
|
637
645
|
try {
|
|
638
646
|
const parsed = JSON.parse(line);
|
|
639
647
|
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
640
|
-
|
|
648
|
+
const ev = parsed;
|
|
649
|
+
events.push(ev);
|
|
650
|
+
if (ev.type === "session_start" && !sessionStartEvent) {
|
|
651
|
+
sessionStartEvent = ev;
|
|
652
|
+
sessionModel = ev.model;
|
|
653
|
+
sessionProvider = ev.provider;
|
|
654
|
+
}
|
|
655
|
+
if (ev.type === "session_end") {
|
|
656
|
+
sessionEndEvent = ev;
|
|
657
|
+
sessionPendingToolUses = ev.pendingToolUses;
|
|
658
|
+
}
|
|
659
|
+
if (ev.type === "user_input") {
|
|
660
|
+
openToolUses.clear();
|
|
661
|
+
messages.push({ role: "user", content: ev.content, ts: ev.ts });
|
|
662
|
+
} else if (ev.type === "llm_response") {
|
|
663
|
+
messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
|
|
664
|
+
for (const b of ev.content) {
|
|
665
|
+
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
666
|
+
}
|
|
667
|
+
usage = {
|
|
668
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
669
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
670
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
671
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
672
|
+
};
|
|
673
|
+
} else if (ev.type === "tool_result") {
|
|
674
|
+
if (!openToolUses.has(ev.id)) {
|
|
675
|
+
this.events?.emit("session.damaged", {
|
|
676
|
+
sessionId: id,
|
|
677
|
+
detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
|
|
678
|
+
});
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
openToolUses.delete(ev.id);
|
|
682
|
+
const resultBlock = {
|
|
683
|
+
type: "tool_result",
|
|
684
|
+
tool_use_id: ev.id,
|
|
685
|
+
content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
|
|
686
|
+
is_error: ev.isError
|
|
687
|
+
};
|
|
688
|
+
const last = messages[messages.length - 1];
|
|
689
|
+
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
690
|
+
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
691
|
+
last.content.push(resultBlock);
|
|
692
|
+
} else {
|
|
693
|
+
messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
|
|
694
|
+
}
|
|
695
|
+
}
|
|
641
696
|
}
|
|
642
697
|
} catch {
|
|
643
698
|
}
|
|
644
699
|
}
|
|
645
|
-
|
|
646
|
-
|
|
700
|
+
if (openToolUses.size > 0) {
|
|
701
|
+
this.events?.emit("session.damaged", {
|
|
702
|
+
sessionId: id,
|
|
703
|
+
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
const repaired = repairToolUseAdjacency(messages);
|
|
707
|
+
if (repaired.report.changed) {
|
|
708
|
+
this.events?.emit("session.damaged", {
|
|
709
|
+
sessionId: id,
|
|
710
|
+
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
const meta = {
|
|
714
|
+
id,
|
|
715
|
+
startedAt: sessionStartEvent?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
716
|
+
endedAt: sessionEndEvent?.ts,
|
|
717
|
+
model: sessionModel,
|
|
718
|
+
provider: sessionProvider,
|
|
719
|
+
pendingToolUses: sessionPendingToolUses
|
|
720
|
+
};
|
|
647
721
|
const toolCallEnds = extractToolCallEnds(events);
|
|
648
|
-
const data = { metadata: meta, events, messages, usage, toolCallEnds };
|
|
722
|
+
const data = { metadata: meta, events, messages: repaired.messages, usage, toolCallEnds };
|
|
649
723
|
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
650
724
|
const oldest = this._loadCache.keys().next().value;
|
|
651
725
|
if (oldest !== void 0) {
|
|
@@ -688,7 +762,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
688
762
|
return [];
|
|
689
763
|
}
|
|
690
764
|
}
|
|
691
|
-
//
|
|
765
|
+
// ── Session index (_index.jsonl) ─────────────────────────────────────────
|
|
692
766
|
//
|
|
693
767
|
// One JSON line per closed session, appended atomically on close().
|
|
694
768
|
// When a session is deleted, a tombstone {action:"delete",id:"..."} is
|
|
@@ -864,7 +938,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
864
938
|
return [...childFileArrays.flat(), ...files];
|
|
865
939
|
}
|
|
866
940
|
/** Recursively collect session IDs from date-shard subdirectories.
|
|
867
|
-
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_
|
|
941
|
+
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
|
|
868
942
|
* Skips `.jsonl`/`.summary.json` root files, dot-files, and
|
|
869
943
|
* sub-directories that belong to fleet/subagent sessions. */
|
|
870
944
|
async collectSessionIds(dir, prefix = "", depth = 0) {
|
|
@@ -919,7 +993,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
919
993
|
}));
|
|
920
994
|
});
|
|
921
995
|
outcome = "failure";
|
|
922
|
-
errorMsg = "summary fallback \
|
|
996
|
+
errorMsg = "summary fallback \xE2\u20AC\u201D manifest rebuilt";
|
|
923
997
|
this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
|
|
924
998
|
return summary;
|
|
925
999
|
} catch (err) {
|
|
@@ -1211,76 +1285,6 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1211
1285
|
stream.destroy();
|
|
1212
1286
|
}
|
|
1213
1287
|
}
|
|
1214
|
-
metaFromEvents(id, events) {
|
|
1215
|
-
const start = events.find((e) => e.type === "session_start");
|
|
1216
|
-
const end = events.findLast((e) => e.type === "session_end");
|
|
1217
|
-
return {
|
|
1218
|
-
id,
|
|
1219
|
-
startedAt: start?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1220
|
-
endedAt: end?.ts,
|
|
1221
|
-
model: start?.model,
|
|
1222
|
-
provider: start?.provider,
|
|
1223
|
-
pendingToolUses: end?.pendingToolUses
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
replay(events, sessionId = "unknown") {
|
|
1227
|
-
const messages = [];
|
|
1228
|
-
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
1229
|
-
const openToolUses = /* @__PURE__ */ new Set();
|
|
1230
|
-
for (const e of events) {
|
|
1231
|
-
if (e.type === "user_input") {
|
|
1232
|
-
openToolUses.clear();
|
|
1233
|
-
messages.push({ role: "user", content: e.content, ts: e.ts });
|
|
1234
|
-
} else if (e.type === "llm_response") {
|
|
1235
|
-
messages.push({ role: "assistant", content: e.content, ts: e.ts });
|
|
1236
|
-
for (const b of e.content) {
|
|
1237
|
-
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
1238
|
-
}
|
|
1239
|
-
usage = {
|
|
1240
|
-
input: usage.input + (e.usage.input ?? 0),
|
|
1241
|
-
output: usage.output + (e.usage.output ?? 0),
|
|
1242
|
-
cacheRead: (usage.cacheRead ?? 0) + (e.usage.cacheRead ?? 0),
|
|
1243
|
-
cacheWrite: (usage.cacheWrite ?? 0) + (e.usage.cacheWrite ?? 0)
|
|
1244
|
-
};
|
|
1245
|
-
} else if (e.type === "tool_result") {
|
|
1246
|
-
if (!openToolUses.has(e.id)) {
|
|
1247
|
-
this.events?.emit("session.damaged", {
|
|
1248
|
-
sessionId,
|
|
1249
|
-
detail: `Orphan tool_result "${e.id}" has no matching tool_use`
|
|
1250
|
-
});
|
|
1251
|
-
continue;
|
|
1252
|
-
}
|
|
1253
|
-
openToolUses.delete(e.id);
|
|
1254
|
-
const resultBlock = {
|
|
1255
|
-
type: "tool_result",
|
|
1256
|
-
tool_use_id: e.id,
|
|
1257
|
-
content: typeof e.content === "string" ? e.content : JSON.stringify(e.content),
|
|
1258
|
-
is_error: e.isError
|
|
1259
|
-
};
|
|
1260
|
-
const last = messages[messages.length - 1];
|
|
1261
|
-
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
1262
|
-
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
1263
|
-
last.content.push(resultBlock);
|
|
1264
|
-
} else {
|
|
1265
|
-
messages.push({ role: "user", content: [resultBlock], ts: e.ts });
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
if (openToolUses.size > 0) {
|
|
1270
|
-
this.events?.emit("session.damaged", {
|
|
1271
|
-
sessionId,
|
|
1272
|
-
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
const repaired = repairToolUseAdjacency(messages);
|
|
1276
|
-
if (repaired.report.changed) {
|
|
1277
|
-
this.events?.emit("session.damaged", {
|
|
1278
|
-
sessionId,
|
|
1279
|
-
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
return { messages: repaired.messages, usage };
|
|
1283
|
-
}
|
|
1284
1288
|
};
|
|
1285
1289
|
function extractToolCallEnds(events) {
|
|
1286
1290
|
const result = [];
|
|
@@ -1339,7 +1343,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1339
1343
|
/**
|
|
1340
1344
|
* Lazy session_start/session_resumed init, shared by all appenders.
|
|
1341
1345
|
* A single promise (not a boolean) so a second append racing the first
|
|
1342
|
-
* can't push its event into the buffer BEFORE the first append's event
|
|
1346
|
+
* can't push its event into the buffer BEFORE the first append's event —
|
|
1343
1347
|
* every appender awaits the same init and resumes in FIFO call order.
|
|
1344
1348
|
*/
|
|
1345
1349
|
initPromise = null;
|
|
@@ -1352,24 +1356,24 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1352
1356
|
lastAppendWarnAt = 0;
|
|
1353
1357
|
secretScrubber;
|
|
1354
1358
|
onCloseCb;
|
|
1355
|
-
/** Implements SessionWriter.traceId
|
|
1359
|
+
/** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
|
|
1356
1360
|
traceId;
|
|
1357
|
-
//
|
|
1361
|
+
// ── Write buffer — batches events to reduce per-event disk I/O ─────────
|
|
1358
1362
|
//
|
|
1359
1363
|
// Every append() pushes the scrubbed event into an in-memory buffer instead
|
|
1360
1364
|
// of calling handle.appendFile() synchronously. The buffer flushes to disk
|
|
1361
1365
|
// when it reaches FLUSH_SIZE events OR after FLUSH_INTERVAL_MS of inactivity.
|
|
1362
1366
|
// This cuts the number of disk writes by ~95% without changing the on-disk
|
|
1363
|
-
// format
|
|
1367
|
+
// format — the JSONL is still one JSON object per line.
|
|
1364
1368
|
writeBuffer = [];
|
|
1365
1369
|
flushTimer = null;
|
|
1366
1370
|
static FLUSH_INTERVAL_MS = 500;
|
|
1367
1371
|
static FLUSH_SIZE = 50;
|
|
1368
|
-
//
|
|
1372
|
+
// ── Write serialization ─────────────────────────────────────────────────
|
|
1369
1373
|
//
|
|
1370
1374
|
// All disk writes are funneled through a FIFO promise chain. Without it,
|
|
1371
1375
|
// a timer-driven flush racing an explicit flush()/close() issues two
|
|
1372
|
-
// concurrent appendFile() calls on the shared O_APPEND handle
|
|
1376
|
+
// concurrent appendFile() calls on the shared O_APPEND handle — the kernel
|
|
1373
1377
|
// may complete them out of order (chronology breaks) or, for large
|
|
1374
1378
|
// batches, interleave partial writes (torn JSONL lines). The chain keeps
|
|
1375
1379
|
// exactly one write in flight; failures don't break the chain.
|
|
@@ -1383,7 +1387,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1383
1387
|
);
|
|
1384
1388
|
return write;
|
|
1385
1389
|
}
|
|
1386
|
-
//
|
|
1390
|
+
// ── Enriched summary tracking ──────────────────────────────────────────
|
|
1387
1391
|
iterationCount = 0;
|
|
1388
1392
|
toolCallCount = 0;
|
|
1389
1393
|
toolErrorCount = 0;
|
|
@@ -1475,7 +1479,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1475
1479
|
* (user_input, llm_response) call this so they survive SIGKILL/crash
|
|
1476
1480
|
* instead of sitting in the in-memory buffer for up to 500ms.
|
|
1477
1481
|
*
|
|
1478
|
-
* Idempotent
|
|
1482
|
+
* Idempotent — cancels any pending timer and writes whatever has
|
|
1479
1483
|
* accumulated in the buffer. Safe to call even when the buffer
|
|
1480
1484
|
* is empty (no-op).
|
|
1481
1485
|
*/
|
|
@@ -1498,7 +1502,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1498
1502
|
/**
|
|
1499
1503
|
* Flush all buffered events to disk as a single appendFile call.
|
|
1500
1504
|
* Errors use the same throttled-warning pattern the old per-event
|
|
1501
|
-
* append path used
|
|
1505
|
+
* append path used — one warning every 5s with a suppressed count.
|
|
1502
1506
|
* On failure the buffer is cleared (events are best-effort, same as
|
|
1503
1507
|
* the old per-event path where a failed write was silently dropped).
|
|
1504
1508
|
*/
|
|
@@ -1681,7 +1685,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1681
1685
|
/**
|
|
1682
1686
|
* Truncate the session file to the checkpoint with the given promptIndex,
|
|
1683
1687
|
* removing all events that follow it. Uses a single-pass byte-offset scan
|
|
1684
|
-
* so post-checkpoint content is never read or parsed
|
|
1688
|
+
* so post-checkpoint content is never read or parsed — O(1) memory instead
|
|
1685
1689
|
* of O(N) JSON.parse calls over the full file.
|
|
1686
1690
|
*/
|
|
1687
1691
|
async truncateToCheckpoint(targetPromptIndex) {
|
|
@@ -1838,7 +1842,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1838
1842
|
await fsp.writeFile(this.filePath, record, "utf8");
|
|
1839
1843
|
}
|
|
1840
1844
|
/**
|
|
1841
|
-
* Idea #1
|
|
1845
|
+
* Idea #1 — write an in-flight marker. The agent loop should call
|
|
1842
1846
|
* this at the start of each long-running operation; a matching
|
|
1843
1847
|
* `clearInFlightMarker` follows on clean exit. A stale marker
|
|
1844
1848
|
* (no end) is what `SessionRecovery.detectStale` looks for.
|
|
@@ -1855,9 +1859,9 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1855
1859
|
this.events?.emit("in_flight.started", { context, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1856
1860
|
}
|
|
1857
1861
|
/**
|
|
1858
|
-
* Idea #1
|
|
1862
|
+
* Idea #1 — close the in-flight marker. Idempotent in spirit
|
|
1859
1863
|
* (you can call it after a successful iteration even if you
|
|
1860
|
-
* didn't open one this round)
|
|
1864
|
+
* didn't open one this round) — but the session log records
|
|
1861
1865
|
* every call so postmortem tooling can see "the agent finished
|
|
1862
1866
|
* cleanly X times, then died without finishing Y".
|
|
1863
1867
|
*/
|
|
@@ -2475,6 +2479,20 @@ var DefaultMemoryStore = class {
|
|
|
2475
2479
|
*/
|
|
2476
2480
|
persistBackup;
|
|
2477
2481
|
backupDir;
|
|
2482
|
+
/**
|
|
2483
|
+
* Per-scope tracked byte sizes — incremented on `remember()`, decremented on
|
|
2484
|
+
* `forget()`, recalculated after `consolidate()`. Eliminates the redundant
|
|
2485
|
+
* readAll() call that previously checked the file size after every write.
|
|
2486
|
+
*/
|
|
2487
|
+
_trackedByteSizes = {};
|
|
2488
|
+
/** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
|
|
2489
|
+
_scoreCache = /* @__PURE__ */ new Map();
|
|
2490
|
+
/**
|
|
2491
|
+
* Per-entry cached lowercase strings — computed once per scoreRelevant() call,
|
|
2492
|
+
* stored here so repeated scoring of the same entries avoids re-computation.
|
|
2493
|
+
* Cleared on every mutation (remember/forget/consolidate/clear).
|
|
2494
|
+
*/
|
|
2495
|
+
_cachedLower = null;
|
|
2478
2496
|
constructor(opts) {
|
|
2479
2497
|
this.files = {
|
|
2480
2498
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
@@ -2508,6 +2526,20 @@ var DefaultMemoryStore = class {
|
|
|
2508
2526
|
}
|
|
2509
2527
|
}
|
|
2510
2528
|
}
|
|
2529
|
+
/**
|
|
2530
|
+
* Recalculate the tracked byte size for a scope by re-reading the file and
|
|
2531
|
+
* summing the serialized byte length of each line. Called after consolidate()
|
|
2532
|
+
* (which modifies the file) to keep the tracker accurate.
|
|
2533
|
+
*/
|
|
2534
|
+
async _recalcTrackedByteSize(scope) {
|
|
2535
|
+
const raw = await this.backend.readAll(scope, this.files[scope]);
|
|
2536
|
+
let total = 0;
|
|
2537
|
+
for (const line of raw.split("\n")) {
|
|
2538
|
+
if (line.trim()) total += Buffer.byteLength(line, "utf8");
|
|
2539
|
+
}
|
|
2540
|
+
this._trackedByteSizes[scope] = total;
|
|
2541
|
+
return total;
|
|
2542
|
+
}
|
|
2511
2543
|
async readAll() {
|
|
2512
2544
|
const parts = [];
|
|
2513
2545
|
for (const scope of ["project-agents", "project-memory", "user-memory"]) {
|
|
@@ -2608,6 +2640,7 @@ ${body.trim()}`);
|
|
|
2608
2640
|
const t0 = Date.now();
|
|
2609
2641
|
try {
|
|
2610
2642
|
await this.backend.remember(scope, entry, filePath);
|
|
2643
|
+
this._scoreCache.clear();
|
|
2611
2644
|
const dur = Date.now() - t0;
|
|
2612
2645
|
this.events?.emit("storage.write", {
|
|
2613
2646
|
sessionId: "~memory~",
|
|
@@ -2632,16 +2665,21 @@ ${body.trim()}`);
|
|
|
2632
2665
|
});
|
|
2633
2666
|
throw err;
|
|
2634
2667
|
}
|
|
2635
|
-
|
|
2636
|
-
if (
|
|
2668
|
+
let trackedSize = this._trackedByteSizes[scope];
|
|
2669
|
+
if (trackedSize === void 0) {
|
|
2670
|
+
trackedSize = await this._recalcTrackedByteSize(scope);
|
|
2671
|
+
}
|
|
2672
|
+
if (trackedSize > MAX_BYTES_TOTAL) {
|
|
2637
2673
|
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
2638
2674
|
if (removed > 0) {
|
|
2639
2675
|
this.events?.emit("memory.consolidated", {
|
|
2640
2676
|
scope,
|
|
2641
2677
|
removed
|
|
2642
2678
|
});
|
|
2679
|
+
await this._recalcTrackedByteSize(scope);
|
|
2643
2680
|
}
|
|
2644
2681
|
}
|
|
2682
|
+
this._trackedByteSizes[scope] = (this._trackedByteSizes[scope] ?? 0) + Buffer.byteLength(JSON.stringify(entry), "utf8");
|
|
2645
2683
|
await this.mirrorBackup(scope);
|
|
2646
2684
|
this.events?.emit("memory.remembered", {
|
|
2647
2685
|
scope,
|
|
@@ -2660,16 +2698,30 @@ ${body.trim()}`);
|
|
|
2660
2698
|
async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
|
|
2661
2699
|
const all = await this.list(scope);
|
|
2662
2700
|
if (all.length === 0) return [];
|
|
2701
|
+
const ctxHash = `${scope}|${ctx.currentTask}|${(ctx.activeSkills ?? []).join(",")}|${(ctx.toolNames ?? []).join(",")}`;
|
|
2702
|
+
const now = Date.now();
|
|
2703
|
+
const TTL_MS = 3e4;
|
|
2704
|
+
const cached = this._scoreCache.get(ctxHash);
|
|
2705
|
+
if (cached && cached.expiresAt > now && cached.entries === all) {
|
|
2706
|
+
return cached.scored.slice(0, Math.min(limit, 15));
|
|
2707
|
+
}
|
|
2663
2708
|
const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
2664
2709
|
const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
|
|
2665
2710
|
const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
|
|
2666
|
-
|
|
2711
|
+
this._cachedLower = /* @__PURE__ */ new WeakMap();
|
|
2667
2712
|
const scored = [];
|
|
2668
2713
|
for (const entry of all) {
|
|
2669
2714
|
let score = 0;
|
|
2670
2715
|
const reasons = [];
|
|
2671
|
-
|
|
2672
|
-
|
|
2716
|
+
let cachedLower = this._cachedLower.get(entry);
|
|
2717
|
+
if (!cachedLower) {
|
|
2718
|
+
cachedLower = {
|
|
2719
|
+
textLower: entry.text.toLowerCase(),
|
|
2720
|
+
tagsLower: (entry.tags ?? []).map((t) => t.toLowerCase())
|
|
2721
|
+
};
|
|
2722
|
+
this._cachedLower.set(entry, cachedLower);
|
|
2723
|
+
}
|
|
2724
|
+
const { textLower, tagsLower } = cachedLower;
|
|
2673
2725
|
let taskHits = 0;
|
|
2674
2726
|
for (const w of taskWords) {
|
|
2675
2727
|
if (textLower.includes(w)) {
|
|
@@ -2755,6 +2807,7 @@ ${body.trim()}`);
|
|
|
2755
2807
|
const relevant = scored.filter(
|
|
2756
2808
|
(s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
|
|
2757
2809
|
);
|
|
2810
|
+
this._scoreCache.set(ctxHash, { entries: all, scored: relevant, expiresAt: now + TTL_MS });
|
|
2758
2811
|
return relevant.slice(0, Math.min(limit, 15));
|
|
2759
2812
|
}
|
|
2760
2813
|
async forget(query, scope = "project-memory") {
|
|
@@ -2764,6 +2817,7 @@ ${body.trim()}`);
|
|
|
2764
2817
|
let removed = 0;
|
|
2765
2818
|
try {
|
|
2766
2819
|
removed = await this.backend.forget(scope, query, filePath);
|
|
2820
|
+
this._scoreCache.clear();
|
|
2767
2821
|
const dur = Date.now() - t0;
|
|
2768
2822
|
this.events?.emit("storage.write", {
|
|
2769
2823
|
sessionId: "~memory~",
|
|
@@ -2795,6 +2849,7 @@ ${body.trim()}`);
|
|
|
2795
2849
|
removed
|
|
2796
2850
|
});
|
|
2797
2851
|
await this.mirrorBackup(scope);
|
|
2852
|
+
await this._recalcTrackedByteSize(scope);
|
|
2798
2853
|
}
|
|
2799
2854
|
return removed;
|
|
2800
2855
|
});
|
|
@@ -2806,6 +2861,7 @@ ${body.trim()}`);
|
|
|
2806
2861
|
let removed = 0;
|
|
2807
2862
|
try {
|
|
2808
2863
|
removed = await this.backend.consolidate(scope, filePath);
|
|
2864
|
+
this._scoreCache.clear();
|
|
2809
2865
|
const dur = Date.now() - t0;
|
|
2810
2866
|
this.events?.emit("storage.write", {
|
|
2811
2867
|
sessionId: "~memory~",
|
|
@@ -2836,6 +2892,7 @@ ${body.trim()}`);
|
|
|
2836
2892
|
removed
|
|
2837
2893
|
});
|
|
2838
2894
|
await this.mirrorBackup(scope);
|
|
2895
|
+
await this._recalcTrackedByteSize(scope);
|
|
2839
2896
|
}
|
|
2840
2897
|
});
|
|
2841
2898
|
}
|
|
@@ -2846,6 +2903,7 @@ ${body.trim()}`);
|
|
|
2846
2903
|
const t0 = Date.now();
|
|
2847
2904
|
try {
|
|
2848
2905
|
await this.backend.clear(scope, filePath);
|
|
2906
|
+
this._scoreCache.clear();
|
|
2849
2907
|
const dur = Date.now() - t0;
|
|
2850
2908
|
this.events?.emit("storage.write", {
|
|
2851
2909
|
sessionId: "~memory~",
|
|
@@ -2872,6 +2930,7 @@ ${body.trim()}`);
|
|
|
2872
2930
|
}
|
|
2873
2931
|
this.events?.emit("memory.cleared", { scope });
|
|
2874
2932
|
await this.mirrorBackup(scope);
|
|
2933
|
+
this._trackedByteSizes[scope] = 0;
|
|
2875
2934
|
});
|
|
2876
2935
|
return;
|
|
2877
2936
|
}
|
|
@@ -3763,6 +3822,15 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
|
|
|
3763
3822
|
}
|
|
3764
3823
|
return out;
|
|
3765
3824
|
}
|
|
3825
|
+
function samePath(a, b) {
|
|
3826
|
+
let ra = path2.resolve(a);
|
|
3827
|
+
let rb = path2.resolve(b);
|
|
3828
|
+
if (process.platform === "win32" || process.platform === "darwin") {
|
|
3829
|
+
ra = ra.toLowerCase();
|
|
3830
|
+
rb = rb.toLowerCase();
|
|
3831
|
+
}
|
|
3832
|
+
return ra === rb;
|
|
3833
|
+
}
|
|
3766
3834
|
function deepMerge2(base, patch) {
|
|
3767
3835
|
const opts = { arrayMode: "concat-primitives" };
|
|
3768
3836
|
if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {
|
|
@@ -3791,10 +3859,11 @@ var DefaultConfigLoader = class {
|
|
|
3791
3859
|
}
|
|
3792
3860
|
async load(opts = {}) {
|
|
3793
3861
|
let cfg = { ...BEHAVIOR_DEFAULTS };
|
|
3862
|
+
const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
|
|
3794
3863
|
const [global, local, inProject] = await Promise.all([
|
|
3795
3864
|
this.readJson(this.paths.globalConfig),
|
|
3796
3865
|
this.readJson(this.paths.projectLocalConfig),
|
|
3797
|
-
this.readJson(this.paths.inProjectConfig)
|
|
3866
|
+
inProjectCollides ? Promise.resolve({}) : this.readJson(this.paths.inProjectConfig)
|
|
3798
3867
|
]);
|
|
3799
3868
|
cfg = deepMerge2(cfg, global);
|
|
3800
3869
|
cfg = deepMerge2(cfg, local);
|
|
@@ -6077,6 +6146,7 @@ var AgentStatusTracker = class {
|
|
|
6077
6146
|
leaderCtxPct;
|
|
6078
6147
|
leaderModel;
|
|
6079
6148
|
leaderPartialText = "";
|
|
6149
|
+
leaderStartedAt;
|
|
6080
6150
|
unsubscribers = [];
|
|
6081
6151
|
onUpdate;
|
|
6082
6152
|
sweepTimer = null;
|
|
@@ -6093,7 +6163,11 @@ var AgentStatusTracker = class {
|
|
|
6093
6163
|
}
|
|
6094
6164
|
start() {
|
|
6095
6165
|
this.unsubscribers.push(
|
|
6096
|
-
this.events.onPattern("agent.run.started", () => {
|
|
6166
|
+
this.events.onPattern("agent.run.started", (_event, payload) => {
|
|
6167
|
+
const p = payload;
|
|
6168
|
+
this.markLeaderStarted(p?.at);
|
|
6169
|
+
this.captureLeaderContext(p?.ctx);
|
|
6170
|
+
if (p?.model) this.leaderModel = p.model;
|
|
6097
6171
|
this.leaderStatus = "running";
|
|
6098
6172
|
this.leaderIterations++;
|
|
6099
6173
|
this.flush();
|
|
@@ -6101,25 +6175,36 @@ var AgentStatusTracker = class {
|
|
|
6101
6175
|
);
|
|
6102
6176
|
this.unsubscribers.push(
|
|
6103
6177
|
this.events.onPattern("iteration.started", (_e, payload) => {
|
|
6104
|
-
const
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6178
|
+
const p = payload;
|
|
6179
|
+
const ctx = p?.ctx;
|
|
6180
|
+
this.markLeaderStarted();
|
|
6181
|
+
this.leaderStatus = "running";
|
|
6182
|
+
if (typeof p?.index === "number") {
|
|
6183
|
+
this.leaderIterations = Math.max(this.leaderIterations, p.index + 1);
|
|
6109
6184
|
}
|
|
6185
|
+
if (!ctx) {
|
|
6186
|
+
this.flush();
|
|
6187
|
+
return;
|
|
6188
|
+
}
|
|
6189
|
+
this.captureLeaderContext(ctx);
|
|
6110
6190
|
this.flush();
|
|
6111
6191
|
})
|
|
6112
6192
|
);
|
|
6113
6193
|
this.unsubscribers.push(
|
|
6114
|
-
this.events.onPattern("agent.run.completed", () => {
|
|
6115
|
-
|
|
6194
|
+
this.events.onPattern("agent.run.completed", (_event, payload) => {
|
|
6195
|
+
const p = payload;
|
|
6196
|
+
this.captureLeaderContext(p?.ctx);
|
|
6197
|
+
this.leaderStatus = p?.status === "failed" ? "error" : "idle";
|
|
6116
6198
|
this.leaderCurrentTool = void 0;
|
|
6117
6199
|
this.leaderPartialText = "";
|
|
6200
|
+
if (this.leaderStatus === "idle") this.leaderStartedAt = void 0;
|
|
6118
6201
|
this.flush();
|
|
6119
6202
|
})
|
|
6120
6203
|
);
|
|
6121
6204
|
this.unsubscribers.push(
|
|
6122
|
-
this.events.onPattern("agent.run.error", () => {
|
|
6205
|
+
this.events.onPattern("agent.run.error", (_event, payload) => {
|
|
6206
|
+
const p = payload;
|
|
6207
|
+
this.captureLeaderContext(p?.ctx);
|
|
6123
6208
|
this.leaderStatus = "error";
|
|
6124
6209
|
this.leaderCurrentTool = void 0;
|
|
6125
6210
|
this.leaderPartialText = "";
|
|
@@ -6130,6 +6215,7 @@ var AgentStatusTracker = class {
|
|
|
6130
6215
|
this.events.onPattern("tool.started", (_event, payload) => {
|
|
6131
6216
|
const p = payload;
|
|
6132
6217
|
if (p?.name) {
|
|
6218
|
+
this.markLeaderStarted();
|
|
6133
6219
|
this.leaderCurrentTool = p.name;
|
|
6134
6220
|
this.leaderToolCalls++;
|
|
6135
6221
|
}
|
|
@@ -6145,12 +6231,14 @@ var AgentStatusTracker = class {
|
|
|
6145
6231
|
);
|
|
6146
6232
|
this.unsubscribers.push(
|
|
6147
6233
|
this.events.onPattern("brain.ask_human", () => {
|
|
6234
|
+
this.markLeaderStarted();
|
|
6148
6235
|
this.leaderStatus = "waiting_user";
|
|
6149
6236
|
this.flush();
|
|
6150
6237
|
})
|
|
6151
6238
|
);
|
|
6152
6239
|
this.unsubscribers.push(
|
|
6153
6240
|
this.events.onPattern("llm.stream_started", () => {
|
|
6241
|
+
this.markLeaderStarted();
|
|
6154
6242
|
this.leaderStatus = "streaming";
|
|
6155
6243
|
this.leaderPartialText = "";
|
|
6156
6244
|
this.flush();
|
|
@@ -6158,14 +6246,42 @@ var AgentStatusTracker = class {
|
|
|
6158
6246
|
);
|
|
6159
6247
|
this.unsubscribers.push(
|
|
6160
6248
|
this.events.onPattern("provider.text_delta", (_e, payload) => {
|
|
6161
|
-
const
|
|
6249
|
+
const p = payload;
|
|
6250
|
+
const text = p?.text;
|
|
6162
6251
|
if (!text) return;
|
|
6252
|
+
this.markLeaderStarted();
|
|
6253
|
+
this.captureLeaderContext(p?.ctx);
|
|
6163
6254
|
this.leaderStatus = "streaming";
|
|
6164
6255
|
const next = this.leaderPartialText + text;
|
|
6165
6256
|
this.leaderPartialText = next.length > PARTIAL_TEXT_CAP ? next.slice(next.length - PARTIAL_TEXT_CAP) : next;
|
|
6166
6257
|
this.schedulePartialFlush();
|
|
6167
6258
|
})
|
|
6168
6259
|
);
|
|
6260
|
+
this.unsubscribers.push(
|
|
6261
|
+
this.events.onPattern("provider.response", (_e, payload) => {
|
|
6262
|
+
const p = payload;
|
|
6263
|
+
this.captureLeaderContext(p?.ctx);
|
|
6264
|
+
this.flush();
|
|
6265
|
+
})
|
|
6266
|
+
);
|
|
6267
|
+
this.unsubscribers.push(
|
|
6268
|
+
this.events.onPattern("provider.fallback", (_e, payload) => {
|
|
6269
|
+
const p = payload;
|
|
6270
|
+
if (p?.to?.model) {
|
|
6271
|
+
this.leaderModel = p.to.providerId ? `${p.to.providerId}/${p.to.model}` : p.to.model;
|
|
6272
|
+
this.flush();
|
|
6273
|
+
}
|
|
6274
|
+
})
|
|
6275
|
+
);
|
|
6276
|
+
this.unsubscribers.push(
|
|
6277
|
+
this.events.onPattern("ctx.pct", (_e, payload) => {
|
|
6278
|
+
const p = payload;
|
|
6279
|
+
if (typeof p?.load === "number" && Number.isFinite(p.load)) {
|
|
6280
|
+
this.leaderCtxPct = Math.round(p.load * 100);
|
|
6281
|
+
this.flush();
|
|
6282
|
+
}
|
|
6283
|
+
})
|
|
6284
|
+
);
|
|
6169
6285
|
this.unsubscribers.push(
|
|
6170
6286
|
this.events.onPattern("token.accounted", (_e, payload) => {
|
|
6171
6287
|
const p = payload;
|
|
@@ -6179,7 +6295,8 @@ var AgentStatusTracker = class {
|
|
|
6179
6295
|
const touch = (id) => {
|
|
6180
6296
|
let entry = this.agents.get(id);
|
|
6181
6297
|
if (!entry) {
|
|
6182
|
-
|
|
6298
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6299
|
+
entry = { id, name: id, status: "idle", iterations: 0, toolCalls: 0, startedAt: now, lastActivityAt: now };
|
|
6183
6300
|
this.agents.set(id, entry);
|
|
6184
6301
|
}
|
|
6185
6302
|
entry.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6192,6 +6309,7 @@ var AgentStatusTracker = class {
|
|
|
6192
6309
|
const entry = touch(p.subagentId);
|
|
6193
6310
|
entry.name = p.name?.trim() || entry.name;
|
|
6194
6311
|
if (p.model) entry.model = p.model;
|
|
6312
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6195
6313
|
entry.status = "running";
|
|
6196
6314
|
this.flush();
|
|
6197
6315
|
})
|
|
@@ -6211,6 +6329,7 @@ var AgentStatusTracker = class {
|
|
|
6211
6329
|
if (!p?.subagentId) return;
|
|
6212
6330
|
const entry = touch(p.subagentId);
|
|
6213
6331
|
entry.status = "running";
|
|
6332
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6214
6333
|
entry.iterations++;
|
|
6215
6334
|
this.flush();
|
|
6216
6335
|
})
|
|
@@ -6221,6 +6340,7 @@ var AgentStatusTracker = class {
|
|
|
6221
6340
|
if (!p?.subagentId) return;
|
|
6222
6341
|
const entry = touch(p.subagentId);
|
|
6223
6342
|
entry.status = "running";
|
|
6343
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6224
6344
|
entry.currentTool = p.name;
|
|
6225
6345
|
entry.toolCalls++;
|
|
6226
6346
|
this.flush();
|
|
@@ -6232,6 +6352,7 @@ var AgentStatusTracker = class {
|
|
|
6232
6352
|
if (!p?.subagentId) return;
|
|
6233
6353
|
const entry = touch(p.subagentId);
|
|
6234
6354
|
entry.status = "running";
|
|
6355
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6235
6356
|
if (typeof p.iteration === "number") entry.iterations = p.iteration;
|
|
6236
6357
|
if (typeof p.toolCalls === "number") entry.toolCalls = p.toolCalls;
|
|
6237
6358
|
if (typeof p.costUsd === "number") entry.costUsd = p.costUsd;
|
|
@@ -6319,6 +6440,7 @@ var AgentStatusTracker = class {
|
|
|
6319
6440
|
const leaderEntry = {
|
|
6320
6441
|
id: "leader",
|
|
6321
6442
|
name: this.leaderName,
|
|
6443
|
+
startedAt: this.leaderStartedAt,
|
|
6322
6444
|
status: this.leaderStatus,
|
|
6323
6445
|
currentTool: this.leaderCurrentTool,
|
|
6324
6446
|
iterations: this.leaderIterations,
|
|
@@ -6344,6 +6466,23 @@ var AgentStatusTracker = class {
|
|
|
6344
6466
|
}
|
|
6345
6467
|
}).catch(() => void 0);
|
|
6346
6468
|
}
|
|
6469
|
+
markLeaderStarted(startedAt) {
|
|
6470
|
+
if (this.leaderStartedAt && (this.leaderStatus === "running" || this.leaderStatus === "streaming" || this.leaderStatus === "waiting_user")) {
|
|
6471
|
+
return;
|
|
6472
|
+
}
|
|
6473
|
+
this.leaderStartedAt = startedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
6474
|
+
}
|
|
6475
|
+
captureLeaderContext(ctx) {
|
|
6476
|
+
if (typeof ctx !== "object" || ctx === null) return;
|
|
6477
|
+
const c = ctx;
|
|
6478
|
+
if (typeof c.model === "string" && c.model.length > 0) this.leaderModel = c.model;
|
|
6479
|
+
const metaLimit = c.meta?.["effectiveMaxContext"];
|
|
6480
|
+
const providerMax = c.provider?.capabilities?.maxContext;
|
|
6481
|
+
const maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : void 0;
|
|
6482
|
+
if (typeof c.lastRequestTokens === "number" && c.lastRequestTokens > 0 && maxContext !== void 0) {
|
|
6483
|
+
this.leaderCtxPct = Math.round(c.lastRequestTokens / maxContext * 100);
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6347
6486
|
};
|
|
6348
6487
|
var INSTANCES_FILE = "webui-instances.json";
|
|
6349
6488
|
var DISCOVERY_TTL_MS = 2500;
|