@wrongstack/core 0.89.1 → 0.104.0
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-B8F2ORwg.d.ts → agent-bridge-mOxbpFcg.d.ts} +1 -1
- package/dist/{agent-subagent-runner-CQaLFNnA.d.ts → agent-subagent-runner-DukQLUcS.d.ts} +433 -433
- package/dist/{events-DzyLtja6.d.ts → brain-Dfv4Y82E.d.ts} +179 -92
- package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
- package/dist/{config-_DZ7dN-T.d.ts → config-BSU-6vah.d.ts} +45 -38
- package/dist/{context-ToHAp4-U.d.ts → context-CNRYfhUv.d.ts} +1 -1
- package/dist/coordination/index.d.ts +16 -17
- package/dist/coordination/index.js +128 -37
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +34 -35
- package/dist/defaults/index.js +752 -218
- package/dist/defaults/index.js.map +1 -1
- package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
- package/dist/execution/index.d.ts +65 -370
- package/dist/execution/index.js +446 -53
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +9 -10
- package/dist/extension/index.js +8 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/goal-preamble-CI8lxeY1.d.ts +378 -0
- package/dist/{goal-store-DvWLNu52.d.ts → goal-store-ht0VmR1A.d.ts} +63 -21
- package/dist/{index-Ca_lOmKl.d.ts → index-BWRN6wOb.d.ts} +13 -15
- package/dist/{index-Dx-CnrDN.d.ts → index-DIKEcfgC.d.ts} +9 -10
- package/dist/index.d.ts +188 -176
- package/dist/index.js +1508 -407
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +9 -9
- package/dist/infrastructure/index.js +4 -2
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +14 -16
- package/dist/kernel/index.js.map +1 -1
- package/dist/{logger-B72yyPc6.d.ts → logger-B63L5bTg.d.ts} +1 -1
- package/dist/{logger-C_27pj9i.d.ts → logger-B9J5puGM.d.ts} +1 -1
- package/dist/{mcp-servers-Dck3T85_.d.ts → mcp-servers-CXCsANdY.d.ts} +3 -3
- package/dist/{mode-CHo2XtHs.d.ts → mode-ARA3HrkY.d.ts} +1 -1
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +8 -6
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-Be3osGt5.d.ts → models-registry-B6_KfS65.d.ts} +1 -1
- package/dist/{models-registry-Boz639EI.d.ts → models-registry-DU64QxQa.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-Da9nd8Ic.d.ts → multi-agent-coordinator-51LvnXkD.d.ts} +2 -2
- package/dist/{null-fleet-bus-CHJOXv3t.d.ts → null-fleet-bus-D09hMzFQ.d.ts} +32 -31
- package/dist/observability/index.d.ts +3 -3
- package/dist/{observability-CoSNZdhX.d.ts → observability-D-HZN_mF.d.ts} +1 -1
- package/dist/{parallel-eternal-engine-DyMCj2Pw.d.ts → parallel-eternal-engine-CUtmM_0V.d.ts} +54 -35
- package/dist/{path-resolver-TjBlRKyH.d.ts → path-resolver-DDJiMAtX.d.ts} +3 -3
- package/dist/{permission-14CChMmO.d.ts → permission-BDv7z0mk.d.ts} +7 -2
- package/dist/{permission-policy-gW5htOo1.d.ts → permission-policy-dF74EpDp.d.ts} +2 -2
- package/dist/{system-prompt-C0rLCeyn.d.ts → pipeline-BqiA_UMr.d.ts} +73 -73
- package/dist/{plan-templates-D9UhwoGh.d.ts → plan-templates-BdDxl9cI.d.ts} +79 -11
- package/dist/{provider-runner-BEPWwkxH.d.ts → provider-runner-BUunikwY.d.ts} +5 -5
- package/dist/{retry-policy-DSu6O6rD.d.ts → retry-policy-BcmuT_V0.d.ts} +2 -2
- package/dist/sdd/index.d.ts +17 -33
- package/dist/sdd/index.js +145 -36
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-scrubber-yGBFQYju.d.ts → secret-vault-DrOhc2i5.d.ts} +7 -7
- package/dist/security/index.d.ts +4 -5
- package/dist/security/index.js +4 -2
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-11-fm95U.d.ts → selector-C7wcdqMA.d.ts} +1 -1
- package/dist/{session-event-bridge-D0u-x576.d.ts → session-event-bridge-BpJ5trO9.d.ts} +2 -2
- package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
- package/dist/{skill-BJeF2DwY.d.ts → skill-Bj6Ezqb8.d.ts} +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.js +3 -9
- package/dist/skills/index.js.map +1 -1
- package/dist/spec-TBi3Jr6T.d.ts +78 -0
- package/dist/storage/index.d.ts +145 -13
- package/dist/storage/index.js +990 -172
- package/dist/storage/index.js.map +1 -1
- package/dist/task-format-vGOIftmK.d.ts +23 -0
- package/dist/task-graph-u1q9Jkyk.d.ts +84 -0
- package/dist/types/index.d.ts +27 -28
- package/dist/types/index.js +19 -31
- package/dist/types/index.js.map +1 -1
- package/dist/utils/expect-defined.d.ts +7 -0
- package/dist/utils/expect-defined.js +11 -0
- package/dist/utils/expect-defined.js.map +1 -0
- package/dist/utils/index.d.ts +8 -20
- package/dist/utils/index.js +120 -41
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
- package/package.json +5 -1
- package/skills/tech-stack/SKILL.md +177 -0
- package/dist/memory-CEXuo7sz.d.ts +0 -16
- package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
- package/dist/task-graph-CikNdRTG.d.ts +0 -161
package/dist/defaults/index.js
CHANGED
|
@@ -5,18 +5,13 @@ import * as path3 from 'path';
|
|
|
5
5
|
import { isAbsolute, resolve } from 'path';
|
|
6
6
|
import * as fs5 from 'fs';
|
|
7
7
|
import * as os from 'os';
|
|
8
|
+
import { hostname } from 'os';
|
|
8
9
|
import { execFile } from 'child_process';
|
|
9
10
|
import { promisify } from 'util';
|
|
10
11
|
import { EventEmitter } from 'events';
|
|
11
12
|
|
|
12
13
|
var __defProp = Object.defineProperty;
|
|
13
14
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
15
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
16
|
-
}) : x)(function(x) {
|
|
17
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
18
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
19
|
-
});
|
|
20
15
|
var __esm = (fn, res) => function __init() {
|
|
21
16
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
22
17
|
};
|
|
@@ -294,16 +289,18 @@ function formatCtx(ctx) {
|
|
|
294
289
|
}
|
|
295
290
|
}
|
|
296
291
|
|
|
297
|
-
// src/
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// src/utils/message-invariants.ts
|
|
301
|
-
function expectDefined(value) {
|
|
292
|
+
// src/utils/expect-defined.ts
|
|
293
|
+
function expectDefined(value, label) {
|
|
302
294
|
if (value === null || value === void 0) {
|
|
303
295
|
throw new Error("Expected value to be defined");
|
|
304
296
|
}
|
|
305
297
|
return value;
|
|
306
298
|
}
|
|
299
|
+
|
|
300
|
+
// src/storage/session-store.ts
|
|
301
|
+
init_atomic_write();
|
|
302
|
+
|
|
303
|
+
// src/utils/message-invariants.ts
|
|
307
304
|
function repairToolUseAdjacency(messages) {
|
|
308
305
|
const removedToolUses = [];
|
|
309
306
|
const removedToolResults = [];
|
|
@@ -396,12 +393,6 @@ function isEmptyMessage(msg) {
|
|
|
396
393
|
}
|
|
397
394
|
|
|
398
395
|
// src/storage/session-store.ts
|
|
399
|
-
function expectDefined2(value) {
|
|
400
|
-
if (value === null || value === void 0) {
|
|
401
|
-
throw new Error("Expected value to be defined");
|
|
402
|
-
}
|
|
403
|
-
return value;
|
|
404
|
-
}
|
|
405
396
|
function sanitizeModel(model) {
|
|
406
397
|
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
407
398
|
}
|
|
@@ -1112,7 +1103,7 @@ var FileSessionWriter = class {
|
|
|
1112
1103
|
let targetCheckpointLine = -1;
|
|
1113
1104
|
let afterTarget = false;
|
|
1114
1105
|
for (let i = 0; i < lines.length; i++) {
|
|
1115
|
-
const line =
|
|
1106
|
+
const line = expectDefined(lines[i]);
|
|
1116
1107
|
if (!line.trim()) continue;
|
|
1117
1108
|
let event;
|
|
1118
1109
|
try {
|
|
@@ -1408,11 +1399,235 @@ function mergeAdjacentText(blocks) {
|
|
|
1408
1399
|
return out;
|
|
1409
1400
|
}
|
|
1410
1401
|
|
|
1411
|
-
// src/
|
|
1402
|
+
// src/types/memory.ts
|
|
1403
|
+
var MEMORY_TYPE_LABELS = {
|
|
1404
|
+
fact: "Fact",
|
|
1405
|
+
decision: "Decision",
|
|
1406
|
+
convention: "Convention",
|
|
1407
|
+
preference: "Preference",
|
|
1408
|
+
reference: "Reference",
|
|
1409
|
+
anti_pattern: "Anti-pattern"
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
// src/storage/memory-backend.ts
|
|
1412
1413
|
init_atomic_write();
|
|
1414
|
+
var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
|
|
1415
|
+
var TAG_RE = /#([\w-]+)/g;
|
|
1416
|
+
function formatMetadata(entry) {
|
|
1417
|
+
const parts = [];
|
|
1418
|
+
if (entry.type && entry.priority) {
|
|
1419
|
+
parts.push(`[${entry.type}|${entry.priority}]`);
|
|
1420
|
+
} else if (entry.type) {
|
|
1421
|
+
parts.push(`[${entry.type}]`);
|
|
1422
|
+
} else if (entry.priority) {
|
|
1423
|
+
parts.push(`[${entry.priority}]`);
|
|
1424
|
+
}
|
|
1425
|
+
if (entry.tags && entry.tags.length > 0) {
|
|
1426
|
+
parts.push(entry.tags.map((t) => `#${t}`).join(" "));
|
|
1427
|
+
}
|
|
1428
|
+
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
1429
|
+
}
|
|
1430
|
+
function lineToEntry(line, scope) {
|
|
1431
|
+
const trimmed = line.trim();
|
|
1432
|
+
if (!trimmed.startsWith("- [")) return null;
|
|
1433
|
+
const tsMatch = trimmed.match(/^-\s*\[([^\]]+)\]/);
|
|
1434
|
+
if (!tsMatch) return null;
|
|
1435
|
+
const ts = tsMatch[1] ?? "";
|
|
1436
|
+
let rest = trimmed.slice(tsMatch[0].length).trim();
|
|
1437
|
+
let type;
|
|
1438
|
+
let priority;
|
|
1439
|
+
const tpMatch = rest.match(TYPE_PRIORITY_RE);
|
|
1440
|
+
if (tpMatch) {
|
|
1441
|
+
const a = tpMatch[1] ?? "";
|
|
1442
|
+
const b = tpMatch[2] ?? "";
|
|
1443
|
+
if (isMemoryType(a)) {
|
|
1444
|
+
type = a;
|
|
1445
|
+
priority = isPriority(b) ? b : void 0;
|
|
1446
|
+
} else if (isPriority(a)) {
|
|
1447
|
+
priority = a;
|
|
1448
|
+
}
|
|
1449
|
+
rest = rest.slice(tpMatch[0].length).trim();
|
|
1450
|
+
}
|
|
1451
|
+
const idMatch = rest.match(/^mem_\d+_\w+\s+/);
|
|
1452
|
+
let text;
|
|
1453
|
+
if (idMatch) {
|
|
1454
|
+
text = rest.slice(idMatch[0].length).trim();
|
|
1455
|
+
} else {
|
|
1456
|
+
text = rest.trim();
|
|
1457
|
+
}
|
|
1458
|
+
const tags = [];
|
|
1459
|
+
let tagMatch;
|
|
1460
|
+
TAG_RE.lastIndex = 0;
|
|
1461
|
+
while ((tagMatch = TAG_RE.exec(text)) !== null) {
|
|
1462
|
+
tags.push(tagMatch[1] ?? "");
|
|
1463
|
+
}
|
|
1464
|
+
const cleanText = text.replace(TAG_RE, "").replace(/\s{2,}/g, " ").trim();
|
|
1465
|
+
if (!cleanText) return null;
|
|
1466
|
+
return {
|
|
1467
|
+
scope,
|
|
1468
|
+
text: cleanText,
|
|
1469
|
+
ts,
|
|
1470
|
+
type,
|
|
1471
|
+
priority,
|
|
1472
|
+
tags: tags.length > 0 ? tags : void 0
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function isMemoryType(s) {
|
|
1476
|
+
return s in MEMORY_TYPE_LABELS;
|
|
1477
|
+
}
|
|
1478
|
+
function isPriority(s) {
|
|
1479
|
+
return s === "critical" || s === "high" || s === "medium" || s === "low";
|
|
1480
|
+
}
|
|
1481
|
+
var FileMemoryBackend = class {
|
|
1482
|
+
kind = "file";
|
|
1483
|
+
files;
|
|
1484
|
+
constructor(opts) {
|
|
1485
|
+
this.files = {
|
|
1486
|
+
"project-agents": opts.paths.inProjectAgentsFile,
|
|
1487
|
+
"project-memory": opts.paths.projectMemory,
|
|
1488
|
+
"user-memory": opts.paths.globalMemory
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
resolveFile(filePath, scope) {
|
|
1492
|
+
return filePath || this.files[scope];
|
|
1493
|
+
}
|
|
1494
|
+
async remember(scope, entry, filePath) {
|
|
1495
|
+
const file = this.resolveFile(filePath, scope);
|
|
1496
|
+
await ensureDir(path3.dirname(file));
|
|
1497
|
+
let existing = "";
|
|
1498
|
+
try {
|
|
1499
|
+
existing = await fsp.readFile(file, "utf8");
|
|
1500
|
+
} catch {
|
|
1501
|
+
}
|
|
1502
|
+
const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
1503
|
+
const meta = formatMetadata(entry);
|
|
1504
|
+
const line = `
|
|
1505
|
+
- [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
|
|
1506
|
+
`;
|
|
1507
|
+
const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# WrongStack Memory
|
|
1508
|
+
${line}`;
|
|
1509
|
+
await atomicWrite(file, next);
|
|
1510
|
+
}
|
|
1511
|
+
async forget(scope, query, filePath) {
|
|
1512
|
+
const file = this.resolveFile(filePath, scope);
|
|
1513
|
+
let existing;
|
|
1514
|
+
try {
|
|
1515
|
+
existing = await fsp.readFile(file, "utf8");
|
|
1516
|
+
} catch {
|
|
1517
|
+
return 0;
|
|
1518
|
+
}
|
|
1519
|
+
const needle = query.toLowerCase();
|
|
1520
|
+
const idMatcher = /mem_\d+_\w+/;
|
|
1521
|
+
let removed = 0;
|
|
1522
|
+
const lines = existing.split("\n").filter((line) => {
|
|
1523
|
+
const trimmed = line.trim();
|
|
1524
|
+
if (!trimmed.startsWith("- ")) return true;
|
|
1525
|
+
if (idMatcher.test(query)) {
|
|
1526
|
+
const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
|
|
1527
|
+
if (entryIdMatch && entryIdMatch[0] === query) {
|
|
1528
|
+
removed++;
|
|
1529
|
+
return false;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
if (trimmed.toLowerCase().includes(needle)) {
|
|
1533
|
+
removed++;
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
return true;
|
|
1537
|
+
});
|
|
1538
|
+
if (removed > 0) {
|
|
1539
|
+
if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
|
|
1540
|
+
await atomicWrite(file, "");
|
|
1541
|
+
} else {
|
|
1542
|
+
await atomicWrite(file, lines.join("\n"));
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
return removed;
|
|
1546
|
+
}
|
|
1547
|
+
async readAll(scope, filePath) {
|
|
1548
|
+
const file = this.resolveFile(filePath, scope);
|
|
1549
|
+
try {
|
|
1550
|
+
return await fsp.readFile(file, "utf8");
|
|
1551
|
+
} catch {
|
|
1552
|
+
return "";
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
async list(scope, filePath, limit) {
|
|
1556
|
+
const raw = await this.readAll(scope, filePath);
|
|
1557
|
+
if (!raw.trim()) return [];
|
|
1558
|
+
const entries = parseEntries(raw, scope);
|
|
1559
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
1560
|
+
}
|
|
1561
|
+
async search(scope, query, filePath, limit) {
|
|
1562
|
+
const entries = await this.list(scope, filePath);
|
|
1563
|
+
const needle = query.toLowerCase().split(/\s+/);
|
|
1564
|
+
const scored = entries.map((e) => {
|
|
1565
|
+
const words = e.text.toLowerCase().split(/\s+/);
|
|
1566
|
+
let score = 0;
|
|
1567
|
+
for (const n of needle) {
|
|
1568
|
+
if (words.some((w) => w.includes(n))) score += 1;
|
|
1569
|
+
if (e.tags?.some((t) => t.toLowerCase().includes(n))) score += 2;
|
|
1570
|
+
}
|
|
1571
|
+
return { entry: e, score };
|
|
1572
|
+
});
|
|
1573
|
+
scored.sort((a, b) => b.score - a.score);
|
|
1574
|
+
const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
|
|
1575
|
+
return limit ? matched.slice(0, limit) : matched;
|
|
1576
|
+
}
|
|
1577
|
+
async clear(scope, filePath) {
|
|
1578
|
+
const file = this.resolveFile(filePath, scope);
|
|
1579
|
+
await atomicWrite(file, "");
|
|
1580
|
+
}
|
|
1581
|
+
async consolidate(scope, filePath) {
|
|
1582
|
+
const file = this.resolveFile(filePath, scope);
|
|
1583
|
+
let existing;
|
|
1584
|
+
try {
|
|
1585
|
+
existing = await fsp.readFile(file, "utf8");
|
|
1586
|
+
} catch {
|
|
1587
|
+
return 0;
|
|
1588
|
+
}
|
|
1589
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1590
|
+
let removed = 0;
|
|
1591
|
+
const lines = existing.split("\n").filter((line) => {
|
|
1592
|
+
const trimmed = line.trim();
|
|
1593
|
+
if (!trimmed.startsWith("- ")) return true;
|
|
1594
|
+
const norm = trimmed.replace(/\[[^\]]+\]/g, "").replace(/\bmem_\d+_\w+\s*/, "").replace(/#[\w-]+/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1595
|
+
if (seen.has(norm)) {
|
|
1596
|
+
removed++;
|
|
1597
|
+
return false;
|
|
1598
|
+
}
|
|
1599
|
+
seen.add(norm);
|
|
1600
|
+
return true;
|
|
1601
|
+
});
|
|
1602
|
+
const next = lines.join("\n");
|
|
1603
|
+
const backup = `${file}.bak.${Date.now()}`;
|
|
1604
|
+
try {
|
|
1605
|
+
await fsp.copyFile(file, backup);
|
|
1606
|
+
} catch {
|
|
1607
|
+
}
|
|
1608
|
+
try {
|
|
1609
|
+
await atomicWrite(file, next);
|
|
1610
|
+
} catch {
|
|
1611
|
+
return 0;
|
|
1612
|
+
}
|
|
1613
|
+
return removed;
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
function parseEntries(raw, scope = "project-memory") {
|
|
1617
|
+
const entries = [];
|
|
1618
|
+
for (const line of raw.split("\n")) {
|
|
1619
|
+
const entry = lineToEntry(line, scope);
|
|
1620
|
+
if (entry) entries.push(entry);
|
|
1621
|
+
}
|
|
1622
|
+
return entries.reverse();
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// src/storage/memory-store.ts
|
|
1413
1626
|
var MAX_BYTES_TOTAL = 32e3;
|
|
1414
1627
|
var DefaultMemoryStore = class {
|
|
1415
1628
|
files;
|
|
1629
|
+
events;
|
|
1630
|
+
backend;
|
|
1416
1631
|
/**
|
|
1417
1632
|
* Per-scope serialization queue. `remember` / `forget` / `consolidate` /
|
|
1418
1633
|
* `clear` are read-modify-write against a single file; without a lock,
|
|
@@ -1429,12 +1644,29 @@ var DefaultMemoryStore = class {
|
|
|
1429
1644
|
writeChain = /* @__PURE__ */ new Map();
|
|
1430
1645
|
/** Last write error per scope — surfaced as warnings on the next readAll(). */
|
|
1431
1646
|
writeErrors = /* @__PURE__ */ new Map();
|
|
1647
|
+
/**
|
|
1648
|
+
* When the global root is a temporary directory (opencode, CI sandboxes),
|
|
1649
|
+
* memory files are also mirrored to the project tree so they survive
|
|
1650
|
+
* session cleanup. The primary path stays in the temp root for isolation;
|
|
1651
|
+
* this backup ensures memory is never lost.
|
|
1652
|
+
*/
|
|
1653
|
+
persistBackup;
|
|
1654
|
+
backupDir;
|
|
1432
1655
|
constructor(opts) {
|
|
1433
1656
|
this.files = {
|
|
1434
1657
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
1435
1658
|
"project-memory": opts.paths.projectMemory,
|
|
1436
1659
|
"user-memory": opts.paths.globalMemory
|
|
1437
1660
|
};
|
|
1661
|
+
this.events = opts.events;
|
|
1662
|
+
this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
|
|
1663
|
+
const root = opts.paths.globalRoot.toLowerCase();
|
|
1664
|
+
this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
|
|
1665
|
+
this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
|
|
1666
|
+
}
|
|
1667
|
+
/** Expose the backend for plugin introspection and advanced queries. */
|
|
1668
|
+
getBackend() {
|
|
1669
|
+
return this.backend;
|
|
1438
1670
|
}
|
|
1439
1671
|
async runSerialized(scope, work) {
|
|
1440
1672
|
const prior = this.writeChain.get(scope) ?? Promise.resolve();
|
|
@@ -1461,7 +1693,7 @@ var DefaultMemoryStore = class {
|
|
|
1461
1693
|
if (writeErr2) {
|
|
1462
1694
|
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
|
|
1463
1695
|
}
|
|
1464
|
-
const body = await this.
|
|
1696
|
+
const body = await this.backend.readAll(scope, this.files[scope]);
|
|
1465
1697
|
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
1466
1698
|
|
|
1467
1699
|
${body.trim()}`);
|
|
@@ -1469,117 +1701,214 @@ ${body.trim()}`);
|
|
|
1469
1701
|
return parts.join("\n\n");
|
|
1470
1702
|
}
|
|
1471
1703
|
async read(scope) {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1704
|
+
return this.backend.readAll(scope, this.files[scope]);
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* List entries from a scope, newest first. Delegates to the backend
|
|
1708
|
+
* so graph/semantic backends can return enriched or filtered results.
|
|
1709
|
+
*/
|
|
1710
|
+
async list(scope = "project-memory", limit) {
|
|
1711
|
+
return this.backend.list(scope, this.files[scope], limit);
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Find memories related to the given text via graph traversal.
|
|
1715
|
+
* Falls back to content search when no graph backend is available.
|
|
1716
|
+
*/
|
|
1717
|
+
async findRelated(text, scope = "project-memory", limit = 5) {
|
|
1718
|
+
if (this.backend.findRelated) {
|
|
1719
|
+
return this.backend.findRelated(scope, this.files[scope], text, limit);
|
|
1476
1720
|
}
|
|
1721
|
+
return this.search(text, scope, limit);
|
|
1722
|
+
}
|
|
1723
|
+
async search(query, scope = "project-memory", limit) {
|
|
1724
|
+
return this.backend.search(scope, query, this.files[scope], limit);
|
|
1477
1725
|
}
|
|
1478
|
-
async remember(text, scope = "project-memory") {
|
|
1726
|
+
async remember(text, scope = "project-memory", metadata) {
|
|
1727
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1479
1728
|
return this.runSerialized(scope, async () => {
|
|
1480
|
-
const
|
|
1481
|
-
await
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
`;
|
|
1492
|
-
const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
|
|
1493
|
-
${entry}`;
|
|
1494
|
-
await atomicWrite(file, next);
|
|
1495
|
-
const buf = Buffer.byteLength(next, "utf8");
|
|
1496
|
-
if (buf > MAX_BYTES_TOTAL) {
|
|
1497
|
-
await this.consolidateUnsafe(scope);
|
|
1729
|
+
const entry = { scope, text, ts, ...metadata };
|
|
1730
|
+
await this.backend.remember(scope, entry, this.files[scope]);
|
|
1731
|
+
const raw = await this.backend.readAll(scope, this.files[scope]);
|
|
1732
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
|
|
1733
|
+
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
1734
|
+
if (removed > 0) {
|
|
1735
|
+
this.events?.emit("memory.consolidated", {
|
|
1736
|
+
scope,
|
|
1737
|
+
removed
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1498
1740
|
}
|
|
1741
|
+
await this.mirrorBackup(scope);
|
|
1742
|
+
this.events?.emit("memory.remembered", {
|
|
1743
|
+
scope,
|
|
1744
|
+
text,
|
|
1745
|
+
ts,
|
|
1746
|
+
type: entry.type,
|
|
1747
|
+
tags: entry.tags,
|
|
1748
|
+
priority: entry.priority
|
|
1749
|
+
});
|
|
1499
1750
|
});
|
|
1500
1751
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
const
|
|
1513
|
-
const
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
const
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
if (
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1752
|
+
/**
|
|
1753
|
+
* Score and rank memories by relevance to the current context.
|
|
1754
|
+
* Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
|
|
1755
|
+
*/
|
|
1756
|
+
async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
|
|
1757
|
+
const all = await this.list(scope);
|
|
1758
|
+
if (all.length === 0) return [];
|
|
1759
|
+
const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
1760
|
+
const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
|
|
1761
|
+
const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
|
|
1762
|
+
const now = Date.now();
|
|
1763
|
+
const scored = [];
|
|
1764
|
+
for (const entry of all) {
|
|
1765
|
+
let score = 0;
|
|
1766
|
+
const reasons = [];
|
|
1767
|
+
const textLower = entry.text.toLowerCase();
|
|
1768
|
+
const tagsLower = (entry.tags ?? []).map((t) => t.toLowerCase());
|
|
1769
|
+
let taskHits = 0;
|
|
1770
|
+
for (const w of taskWords) {
|
|
1771
|
+
if (textLower.includes(w)) {
|
|
1772
|
+
taskHits++;
|
|
1773
|
+
score += 2;
|
|
1774
|
+
}
|
|
1775
|
+
if (tagsLower.some((t) => t.includes(w))) {
|
|
1776
|
+
taskHits++;
|
|
1777
|
+
score += 3;
|
|
1527
1778
|
}
|
|
1528
1779
|
}
|
|
1529
|
-
if (
|
|
1530
|
-
|
|
1531
|
-
|
|
1780
|
+
if (taskHits > 0) reasons.push(`task match (${taskHits})`);
|
|
1781
|
+
let skillHits = 0;
|
|
1782
|
+
for (const w of skillWords) {
|
|
1783
|
+
if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
|
|
1784
|
+
skillHits++;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
score += skillHits;
|
|
1788
|
+
if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
|
|
1789
|
+
for (const w of toolWords) {
|
|
1790
|
+
if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
|
|
1791
|
+
score += 1;
|
|
1792
|
+
reasons.push(`tool mention: ${w}`);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
switch (entry.priority) {
|
|
1796
|
+
case "critical":
|
|
1797
|
+
score += 5;
|
|
1798
|
+
reasons.push("critical");
|
|
1799
|
+
break;
|
|
1800
|
+
case "high":
|
|
1801
|
+
score += 3;
|
|
1802
|
+
reasons.push("high priority");
|
|
1803
|
+
break;
|
|
1804
|
+
case "medium":
|
|
1805
|
+
score += 1;
|
|
1806
|
+
break;
|
|
1807
|
+
case "low":
|
|
1808
|
+
score -= 2;
|
|
1809
|
+
reasons.push("low priority");
|
|
1810
|
+
break;
|
|
1811
|
+
}
|
|
1812
|
+
switch (entry.type) {
|
|
1813
|
+
case "decision":
|
|
1814
|
+
score += 2;
|
|
1815
|
+
reasons.push("decision");
|
|
1816
|
+
break;
|
|
1817
|
+
case "convention":
|
|
1818
|
+
score += 2;
|
|
1819
|
+
reasons.push("convention");
|
|
1820
|
+
break;
|
|
1821
|
+
case "anti_pattern":
|
|
1822
|
+
score += 3;
|
|
1823
|
+
reasons.push("anti-pattern");
|
|
1824
|
+
break;
|
|
1825
|
+
case "preference":
|
|
1826
|
+
score += 1;
|
|
1827
|
+
reasons.push("preference");
|
|
1828
|
+
break;
|
|
1829
|
+
}
|
|
1830
|
+
const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
|
|
1831
|
+
if (ageDays < 1) score += 1;
|
|
1832
|
+
else if (ageDays > 30) score -= 1;
|
|
1833
|
+
if (entry.confidence !== void 0 && entry.confidence < 0.5) {
|
|
1834
|
+
score -= 2;
|
|
1835
|
+
reasons.push("low confidence");
|
|
1836
|
+
}
|
|
1837
|
+
if (entry.lastAccessed) {
|
|
1838
|
+
const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
|
|
1839
|
+
if (hoursSinceAccess < 1) score -= 1;
|
|
1840
|
+
}
|
|
1841
|
+
if (score > 0) {
|
|
1842
|
+
scored.push({
|
|
1843
|
+
...entry,
|
|
1844
|
+
score,
|
|
1845
|
+
matchReason: reasons.join(", ") || "keyword match"
|
|
1846
|
+
});
|
|
1532
1847
|
}
|
|
1533
|
-
return true;
|
|
1534
|
-
});
|
|
1535
|
-
if (removed > 0) {
|
|
1536
|
-
await atomicWrite(file, lines.join("\n"));
|
|
1537
1848
|
}
|
|
1538
|
-
|
|
1849
|
+
scored.sort((a, b) => b.score - a.score);
|
|
1850
|
+
const threshold = 2;
|
|
1851
|
+
const relevant = scored.filter(
|
|
1852
|
+
(s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
|
|
1853
|
+
);
|
|
1854
|
+
return relevant.slice(0, Math.min(limit, 15));
|
|
1539
1855
|
}
|
|
1540
|
-
async
|
|
1541
|
-
return this.runSerialized(scope, async () =>
|
|
1856
|
+
async forget(query, scope = "project-memory") {
|
|
1857
|
+
return this.runSerialized(scope, async () => {
|
|
1858
|
+
const removed = await this.backend.forget(scope, query, this.files[scope]);
|
|
1859
|
+
if (removed > 0) {
|
|
1860
|
+
this.events?.emit("memory.forgotten", {
|
|
1861
|
+
scope,
|
|
1862
|
+
query,
|
|
1863
|
+
removed
|
|
1864
|
+
});
|
|
1865
|
+
await this.mirrorBackup(scope);
|
|
1866
|
+
}
|
|
1867
|
+
return removed;
|
|
1868
|
+
});
|
|
1542
1869
|
}
|
|
1543
|
-
async
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
const trimmed = line.trim();
|
|
1554
|
-
if (!trimmed.startsWith("- ")) return true;
|
|
1555
|
-
const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
|
|
1556
|
-
if (seen.has(norm)) return false;
|
|
1557
|
-
seen.add(norm);
|
|
1558
|
-
return true;
|
|
1870
|
+
async consolidate(scope) {
|
|
1871
|
+
return this.runSerialized(scope, async () => {
|
|
1872
|
+
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
1873
|
+
if (removed > 0) {
|
|
1874
|
+
this.events?.emit("memory.consolidated", {
|
|
1875
|
+
scope,
|
|
1876
|
+
removed
|
|
1877
|
+
});
|
|
1878
|
+
await this.mirrorBackup(scope);
|
|
1879
|
+
}
|
|
1559
1880
|
});
|
|
1560
|
-
const next = lines.join("\n");
|
|
1561
|
-
const backup = `${file}.bak.${Date.now()}`;
|
|
1562
|
-
try {
|
|
1563
|
-
await fsp.copyFile(file, backup);
|
|
1564
|
-
} catch {
|
|
1565
|
-
}
|
|
1566
|
-
try {
|
|
1567
|
-
await atomicWrite(file, next);
|
|
1568
|
-
} catch {
|
|
1569
|
-
return;
|
|
1570
|
-
}
|
|
1571
1881
|
}
|
|
1572
1882
|
async clear(scope) {
|
|
1573
1883
|
if (scope) {
|
|
1574
|
-
await this.runSerialized(scope, async () =>
|
|
1884
|
+
await this.runSerialized(scope, async () => {
|
|
1885
|
+
await this.backend.clear(scope, this.files[scope]);
|
|
1886
|
+
this.events?.emit("memory.cleared", { scope });
|
|
1887
|
+
await this.mirrorBackup(scope);
|
|
1888
|
+
});
|
|
1575
1889
|
return;
|
|
1576
1890
|
}
|
|
1577
1891
|
await Promise.all(
|
|
1578
1892
|
["project-agents", "project-memory", "user-memory"].map(
|
|
1579
|
-
(s) => this.runSerialized(s, async () =>
|
|
1893
|
+
(s) => this.runSerialized(s, async () => {
|
|
1894
|
+
await this.backend.clear(s, this.files[s]);
|
|
1895
|
+
this.events?.emit("memory.cleared", { scope: s });
|
|
1896
|
+
await this.mirrorBackup(s);
|
|
1897
|
+
})
|
|
1580
1898
|
)
|
|
1581
1899
|
);
|
|
1582
1900
|
}
|
|
1901
|
+
/** Mirror current memory content to the persistent backup directory. */
|
|
1902
|
+
async mirrorBackup(scope) {
|
|
1903
|
+
if (!this.persistBackup || scope === "project-agents") return;
|
|
1904
|
+
try {
|
|
1905
|
+
const content = await this.backend.readAll(scope, this.files[scope]);
|
|
1906
|
+
const { writeFile: writeFile4, mkdir: mkdir6 } = await import('fs/promises');
|
|
1907
|
+
await mkdir6(this.backupDir, { recursive: true });
|
|
1908
|
+
await writeFile4(`${this.backupDir}/${scope}.md`, content, "utf8");
|
|
1909
|
+
} catch {
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1583
1912
|
};
|
|
1584
1913
|
function labelOf(scope) {
|
|
1585
1914
|
switch (scope) {
|
|
@@ -2295,12 +2624,6 @@ function compileUserRegex(pattern, flags) {
|
|
|
2295
2624
|
}
|
|
2296
2625
|
|
|
2297
2626
|
// src/storage/session-reader.ts
|
|
2298
|
-
function expectDefined3(value) {
|
|
2299
|
-
if (value === null || value === void 0) {
|
|
2300
|
-
throw new Error("Expected value to be defined");
|
|
2301
|
-
}
|
|
2302
|
-
return value;
|
|
2303
|
-
}
|
|
2304
2627
|
var DefaultSessionReader = class {
|
|
2305
2628
|
store;
|
|
2306
2629
|
constructor(opts) {
|
|
@@ -2362,7 +2685,7 @@ var DefaultSessionReader = class {
|
|
|
2362
2685
|
continue;
|
|
2363
2686
|
}
|
|
2364
2687
|
for (let i = 0; i < data.events.length; i++) {
|
|
2365
|
-
const ev =
|
|
2688
|
+
const ev = expectDefined(data.events[i]);
|
|
2366
2689
|
if (allowedTypes && !allowedTypes.has(ev.type)) continue;
|
|
2367
2690
|
const text = eventText(ev);
|
|
2368
2691
|
if (text === null) continue;
|
|
@@ -2811,7 +3134,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
|
2811
3134
|
let pending = null;
|
|
2812
3135
|
let writeChain = Promise.resolve();
|
|
2813
3136
|
const enqueueWrite = (todos) => {
|
|
2814
|
-
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos))
|
|
3137
|
+
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
|
|
3138
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3139
|
+
console.error(`[TodosCheckpoint] save failed for session ${sessionId}: ${msg}`);
|
|
3140
|
+
});
|
|
2815
3141
|
return writeChain;
|
|
2816
3142
|
};
|
|
2817
3143
|
const flush = () => {
|
|
@@ -3115,7 +3441,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
|
3115
3441
|
}
|
|
3116
3442
|
const lock = {
|
|
3117
3443
|
pid: processId,
|
|
3118
|
-
hostname:
|
|
3444
|
+
hostname: hostname(),
|
|
3119
3445
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3120
3446
|
};
|
|
3121
3447
|
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
@@ -3641,12 +3967,6 @@ function getDangerousCapabilities(toolOrCaps) {
|
|
|
3641
3967
|
init_atomic_write();
|
|
3642
3968
|
|
|
3643
3969
|
// src/utils/glob-match.ts
|
|
3644
|
-
function expectDefined4(value) {
|
|
3645
|
-
if (value === null || value === void 0) {
|
|
3646
|
-
throw new Error("Expected value to be defined");
|
|
3647
|
-
}
|
|
3648
|
-
return value;
|
|
3649
|
-
}
|
|
3650
3970
|
function escapeRegex(s) {
|
|
3651
3971
|
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
3652
3972
|
}
|
|
@@ -3658,7 +3978,7 @@ function getCachedGlob(pattern) {
|
|
|
3658
3978
|
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
3659
3979
|
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
3660
3980
|
for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
|
|
3661
|
-
COMPILED_GLOB_CACHE.delete(
|
|
3981
|
+
COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
|
|
3662
3982
|
}
|
|
3663
3983
|
}
|
|
3664
3984
|
const re = compileGlob(pattern);
|
|
@@ -4523,12 +4843,6 @@ function parseDescription(raw) {
|
|
|
4523
4843
|
}
|
|
4524
4844
|
|
|
4525
4845
|
// src/utils/json-repair.ts
|
|
4526
|
-
function expectDefined5(value) {
|
|
4527
|
-
if (value === null || value === void 0) {
|
|
4528
|
-
throw new Error("Expected value to be defined");
|
|
4529
|
-
}
|
|
4530
|
-
return value;
|
|
4531
|
-
}
|
|
4532
4846
|
function completePartialObject(s) {
|
|
4533
4847
|
if (!s.trim().startsWith("{")) return s;
|
|
4534
4848
|
if (tryParse(s).ok) return s;
|
|
@@ -4540,7 +4854,7 @@ function completePartialObject(s) {
|
|
|
4540
4854
|
let contentEnd = 0;
|
|
4541
4855
|
let stringBraceDepth = 0;
|
|
4542
4856
|
for (let i = 0; i < s.length; i++) {
|
|
4543
|
-
const ch =
|
|
4857
|
+
const ch = expectDefined(s[i]);
|
|
4544
4858
|
if (inString) {
|
|
4545
4859
|
contentEnd = i + 1;
|
|
4546
4860
|
if (escaped) {
|
|
@@ -4926,12 +5240,6 @@ var DefaultProviderRunner = class {
|
|
|
4926
5240
|
};
|
|
4927
5241
|
|
|
4928
5242
|
// src/utils/token-estimate.ts
|
|
4929
|
-
function expectDefined6(value) {
|
|
4930
|
-
if (value === null || value === void 0) {
|
|
4931
|
-
throw new Error("Expected value to be defined");
|
|
4932
|
-
}
|
|
4933
|
-
return value;
|
|
4934
|
-
}
|
|
4935
5243
|
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
4936
5244
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
4937
5245
|
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
@@ -4941,7 +5249,7 @@ function getCachedEstimate(key, compute) {
|
|
|
4941
5249
|
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
4942
5250
|
const keys = [...ESTIMATE_CACHE.keys()];
|
|
4943
5251
|
for (let i = 0; i < Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4); i++) {
|
|
4944
|
-
ESTIMATE_CACHE.delete(
|
|
5252
|
+
ESTIMATE_CACHE.delete(expectDefined(keys[i]));
|
|
4945
5253
|
}
|
|
4946
5254
|
}
|
|
4947
5255
|
const estimate = compute();
|
|
@@ -5434,12 +5742,6 @@ var IntelligentCompactor = class {
|
|
|
5434
5742
|
};
|
|
5435
5743
|
|
|
5436
5744
|
// src/models/llm-selector.ts
|
|
5437
|
-
function expectDefined7(value) {
|
|
5438
|
-
if (value === null || value === void 0) {
|
|
5439
|
-
throw new Error("Expected value to be defined");
|
|
5440
|
-
}
|
|
5441
|
-
return value;
|
|
5442
|
-
}
|
|
5443
5745
|
var DEFAULT_SYSTEM_PROMPT = `You are a context pruning assistant. Given a conversation history and a token budget, decide which message ranges are worth keeping verbatim and which should be collapsed into summaries.
|
|
5444
5746
|
|
|
5445
5747
|
Output a JSON object with this structure:
|
|
@@ -5480,7 +5782,7 @@ function formatMessages(messages, maxChars = 8e3) {
|
|
|
5480
5782
|
const lines = [];
|
|
5481
5783
|
let used = 0;
|
|
5482
5784
|
for (let i = 0; i < messages.length; i++) {
|
|
5483
|
-
const m =
|
|
5785
|
+
const m = expectDefined(messages[i]);
|
|
5484
5786
|
const role = m.role.padEnd(10, " ");
|
|
5485
5787
|
let text;
|
|
5486
5788
|
if (typeof m.content === "string") {
|
|
@@ -5545,7 +5847,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
5545
5847
|
let tokenCount = 0;
|
|
5546
5848
|
let startIdx = 0;
|
|
5547
5849
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
5548
|
-
const m =
|
|
5850
|
+
const m = expectDefined(messages[i]);
|
|
5549
5851
|
const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
|
|
5550
5852
|
(acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
|
|
5551
5853
|
0
|
|
@@ -6152,12 +6454,6 @@ function createToolOutputSerializer(opts = {}) {
|
|
|
6152
6454
|
}
|
|
6153
6455
|
|
|
6154
6456
|
// src/execution/tool-executor.ts
|
|
6155
|
-
function expectDefined8(value) {
|
|
6156
|
-
if (value === null || value === void 0) {
|
|
6157
|
-
throw new Error("Expected value to be defined");
|
|
6158
|
-
}
|
|
6159
|
-
return value;
|
|
6160
|
-
}
|
|
6161
6457
|
var ToolExecutor = class {
|
|
6162
6458
|
constructor(registry, opts) {
|
|
6163
6459
|
this.registry = registry;
|
|
@@ -6564,7 +6860,7 @@ function hasMalformedArguments(input) {
|
|
|
6564
6860
|
function extractMalformedRaw(input) {
|
|
6565
6861
|
if (!hasMalformedArguments(input)) return void 0;
|
|
6566
6862
|
const obj = input;
|
|
6567
|
-
const value = obj[
|
|
6863
|
+
const value = obj[expectDefined(Object.keys(obj)[0])];
|
|
6568
6864
|
if (value === void 0 || value === null) return void 0;
|
|
6569
6865
|
if (typeof value === "string") return value;
|
|
6570
6866
|
try {
|
|
@@ -6788,12 +7084,58 @@ function appendJournal(goal, entry) {
|
|
|
6788
7084
|
journal: trimmed
|
|
6789
7085
|
};
|
|
6790
7086
|
}
|
|
7087
|
+
function parseProgressFromText(text) {
|
|
7088
|
+
const re = /\[progress:\s*(\d{1,3})%\]\s*(?:[—\-]\s*(.+))?/i;
|
|
7089
|
+
const m = text.match(re);
|
|
7090
|
+
if (!m) return null;
|
|
7091
|
+
const progress = Math.min(100, Math.max(0, Number.parseInt(m[1], 10)));
|
|
7092
|
+
const note = m[2]?.trim() || void 0;
|
|
7093
|
+
return { progress, note };
|
|
7094
|
+
}
|
|
7095
|
+
function recordProgress(goal, progress, note) {
|
|
7096
|
+
const clamped = Math.min(100, Math.max(0, progress));
|
|
7097
|
+
const history = [...goal.progressHistory ?? [], { at: (/* @__PURE__ */ new Date()).toISOString(), progress: clamped, note }];
|
|
7098
|
+
const trimmed = history.length > 200 ? history.slice(-200) : history;
|
|
7099
|
+
return {
|
|
7100
|
+
...goal,
|
|
7101
|
+
progress: clamped,
|
|
7102
|
+
progressNote: note ?? `${clamped}% complete`,
|
|
7103
|
+
progressHistory: trimmed,
|
|
7104
|
+
progressTrend: computeTrend(trimmed)
|
|
7105
|
+
};
|
|
7106
|
+
}
|
|
7107
|
+
function computeTrend(history) {
|
|
7108
|
+
if (history.length < 3) return void 0;
|
|
7109
|
+
const recent = history.slice(-5);
|
|
7110
|
+
const deltas = [];
|
|
7111
|
+
for (let i = 1; i < recent.length; i++) {
|
|
7112
|
+
deltas.push(recent[i].progress - recent[i - 1].progress);
|
|
7113
|
+
}
|
|
7114
|
+
if (deltas.length < 2) return void 0;
|
|
7115
|
+
const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;
|
|
7116
|
+
if (avgDelta > 2) return "accelerating";
|
|
7117
|
+
if (avgDelta < -1) return "stalling";
|
|
7118
|
+
return "steady";
|
|
7119
|
+
}
|
|
6791
7120
|
|
|
6792
7121
|
// src/utils/sleep.ts
|
|
6793
7122
|
function sleep(ms) {
|
|
6794
7123
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
6795
7124
|
}
|
|
6796
7125
|
|
|
7126
|
+
// src/execution/autonomy-brain.ts
|
|
7127
|
+
function formatDecisionSummary(decision, request) {
|
|
7128
|
+
const question = request.question.length > 80 ? request.question.slice(0, 77) + "\u2026" : request.question;
|
|
7129
|
+
if (decision.type === "deny") {
|
|
7130
|
+
return `\u{1F9E0} Brain: DENIED \u2014 "${question}" \u2192 ${decision.reason}`;
|
|
7131
|
+
}
|
|
7132
|
+
if (decision.type === "answer") {
|
|
7133
|
+
const action = decision.optionId ? `chose [${decision.optionId}]` : decision.text.length > 60 ? decision.text.slice(0, 57) + "\u2026" : decision.text;
|
|
7134
|
+
return `\u{1F9E0} Brain: DECIDED \u2014 "${question}" \u2192 ${action}`;
|
|
7135
|
+
}
|
|
7136
|
+
return `\u{1F9E0} Brain: ASKED HUMAN \u2014 "${question}"`;
|
|
7137
|
+
}
|
|
7138
|
+
|
|
6797
7139
|
// src/execution/eternal-autonomy.ts
|
|
6798
7140
|
var execFileP = promisify(execFile);
|
|
6799
7141
|
var BRAINSTORM_DONE = /* @__PURE__ */ Symbol("brainstorm-done");
|
|
@@ -6863,7 +7205,7 @@ var EternalAutonomyEngine = class {
|
|
|
6863
7205
|
this.consecutiveFailures = 0;
|
|
6864
7206
|
}
|
|
6865
7207
|
if (this.stopRequested) break;
|
|
6866
|
-
await sleep(this.opts.cycleGapMs ??
|
|
7208
|
+
await sleep(this.opts.cycleGapMs ?? 0);
|
|
6867
7209
|
}
|
|
6868
7210
|
} finally {
|
|
6869
7211
|
this.state = "stopped";
|
|
@@ -7033,6 +7375,18 @@ var EternalAutonomyEngine = class {
|
|
|
7033
7375
|
this.stopRequested = true;
|
|
7034
7376
|
return true;
|
|
7035
7377
|
}
|
|
7378
|
+
const parsed = parseProgressFromText(finalText);
|
|
7379
|
+
if (parsed) {
|
|
7380
|
+
await this.updateProgress(parsed.progress, parsed.note);
|
|
7381
|
+
if (parsed.progress >= 100) {
|
|
7382
|
+
await this.markGoalCompleted(
|
|
7383
|
+
{ source: action.source, task: action.task, directive: action.directive },
|
|
7384
|
+
`progress reached 100%: ${parsed.note ?? "goal complete"}`
|
|
7385
|
+
);
|
|
7386
|
+
this.stopRequested = true;
|
|
7387
|
+
return true;
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
7036
7390
|
this.iterationsSinceCompact++;
|
|
7037
7391
|
await this.maybeCompact().catch((err) => {
|
|
7038
7392
|
this.opts.onError?.(
|
|
@@ -7119,11 +7473,16 @@ var EternalAutonomyEngine = class {
|
|
|
7119
7473
|
this.consecutiveBrainstormDone++;
|
|
7120
7474
|
const threshold = this.opts.brainstormDoneStopThreshold ?? 3;
|
|
7121
7475
|
if (this.consecutiveBrainstormDone >= threshold) {
|
|
7122
|
-
await this.
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7476
|
+
const shouldStop = await this.consultBrainForDone(goal);
|
|
7477
|
+
if (shouldStop) {
|
|
7478
|
+
await this.markGoalCompleted(
|
|
7479
|
+
{ source: "brainstorm", task: "no further work", directive: "" },
|
|
7480
|
+
`brainstorm returned DONE ${this.consecutiveBrainstormDone}x in a row${this.opts.brain ? ", brain confirmed" : ""}`
|
|
7481
|
+
);
|
|
7482
|
+
this.stopRequested = true;
|
|
7483
|
+
} else {
|
|
7484
|
+
this.consecutiveBrainstormDone = 0;
|
|
7485
|
+
}
|
|
7127
7486
|
}
|
|
7128
7487
|
return null;
|
|
7129
7488
|
}
|
|
@@ -7357,6 +7716,69 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
7357
7716
|
async appendFailure(task, note) {
|
|
7358
7717
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
7359
7718
|
}
|
|
7719
|
+
/**
|
|
7720
|
+
* Consult the brain on whether the goal is truly complete.
|
|
7721
|
+
* Without a brain, always returns true (use heuristic: DONE threshold met = done).
|
|
7722
|
+
*/
|
|
7723
|
+
async consultBrainForDone(goal) {
|
|
7724
|
+
if (!this.opts.brain) return true;
|
|
7725
|
+
const deliverablesStatus = goal.deliverables?.length ? `
|
|
7726
|
+
Deliverables: ${goal.deliverables.length} total, progress ${goal.progress ?? "unknown"}%` : "";
|
|
7727
|
+
const recentJournal = goal.journal.slice(-5).map(
|
|
7728
|
+
(e) => ` #${e.iteration} [${e.status}] ${e.task}`
|
|
7729
|
+
).join("\n");
|
|
7730
|
+
try {
|
|
7731
|
+
const decision = await this.opts.brain.decide({
|
|
7732
|
+
id: `goal-done-${goal.iterations}`,
|
|
7733
|
+
source: "system",
|
|
7734
|
+
question: `Brainstorm returned DONE ${this.consecutiveBrainstormDone}x. Is the goal truly complete?`,
|
|
7735
|
+
context: [
|
|
7736
|
+
`Goal: ${goal.goal}`,
|
|
7737
|
+
`Iterations: ${goal.iterations}`,
|
|
7738
|
+
`Progress: ${goal.progress ?? "unknown"}%`,
|
|
7739
|
+
deliverablesStatus,
|
|
7740
|
+
recentJournal ? `
|
|
7741
|
+
Recent work:
|
|
7742
|
+
${recentJournal}` : ""
|
|
7743
|
+
].join("\n"),
|
|
7744
|
+
risk: "high",
|
|
7745
|
+
fallback: "continue"
|
|
7746
|
+
});
|
|
7747
|
+
const summary = formatDecisionSummary(decision, {
|
|
7748
|
+
id: `goal-done-${goal.iterations}`,
|
|
7749
|
+
source: "system",
|
|
7750
|
+
question: "Is the goal complete?",
|
|
7751
|
+
risk: "high",
|
|
7752
|
+
fallback: "continue"
|
|
7753
|
+
});
|
|
7754
|
+
const rationale = "rationale" in decision ? decision.rationale : void 0;
|
|
7755
|
+
await this.appendIterationEntry({
|
|
7756
|
+
source: "brainstorm",
|
|
7757
|
+
task: summary,
|
|
7758
|
+
status: "success",
|
|
7759
|
+
note: rationale
|
|
7760
|
+
});
|
|
7761
|
+
if (decision.type === "deny") {
|
|
7762
|
+
return false;
|
|
7763
|
+
}
|
|
7764
|
+
if (decision.type === "answer") {
|
|
7765
|
+
const text = decision.text.toLowerCase();
|
|
7766
|
+
return text.includes("complete") || text.includes("done") || text.includes("yes");
|
|
7767
|
+
}
|
|
7768
|
+
return true;
|
|
7769
|
+
} catch {
|
|
7770
|
+
return true;
|
|
7771
|
+
}
|
|
7772
|
+
}
|
|
7773
|
+
/**
|
|
7774
|
+
* Persist a progress update from the agent's [PROGRESS: N%] output.
|
|
7775
|
+
*/
|
|
7776
|
+
async updateProgress(progress, note) {
|
|
7777
|
+
const current = await loadGoal(this.goalPath);
|
|
7778
|
+
if (!current) return;
|
|
7779
|
+
const updated = recordProgress(current, progress, note);
|
|
7780
|
+
await saveGoal(this.goalPath, updated);
|
|
7781
|
+
}
|
|
7360
7782
|
async persistEngineState(state) {
|
|
7361
7783
|
const current = await loadGoal(this.goalPath);
|
|
7362
7784
|
if (!current) return;
|
|
@@ -7722,15 +8144,7 @@ function makeAgentSubagentRunner(opts) {
|
|
|
7722
8144
|
let budgetError = null;
|
|
7723
8145
|
const onBudgetError = (err) => {
|
|
7724
8146
|
if (err instanceof BudgetThresholdSignal) {
|
|
7725
|
-
|
|
7726
|
-
if (decision === "stop") {
|
|
7727
|
-
budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
|
|
7728
|
-
aborter.abort();
|
|
7729
|
-
}
|
|
7730
|
-
}).catch(() => {
|
|
7731
|
-
budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
|
|
7732
|
-
aborter.abort();
|
|
7733
|
-
});
|
|
8147
|
+
budgetError = err;
|
|
7734
8148
|
return;
|
|
7735
8149
|
}
|
|
7736
8150
|
aborter.abort();
|
|
@@ -7838,15 +8252,23 @@ function makeAgentSubagentRunner(opts) {
|
|
|
7838
8252
|
}
|
|
7839
8253
|
if (budgetError) {
|
|
7840
8254
|
if ("decision" in budgetError) {
|
|
7841
|
-
|
|
7842
|
-
|
|
8255
|
+
try {
|
|
8256
|
+
const decision = await budgetError.decision;
|
|
8257
|
+
if (decision === "stop") {
|
|
8258
|
+
budgetError = new BudgetExceededError(
|
|
8259
|
+
budgetError.kind,
|
|
8260
|
+
budgetError.limit,
|
|
8261
|
+
budgetError.used
|
|
8262
|
+
);
|
|
8263
|
+
} else {
|
|
8264
|
+
budgetError = null;
|
|
8265
|
+
}
|
|
8266
|
+
} catch {
|
|
7843
8267
|
budgetError = new BudgetExceededError(
|
|
7844
8268
|
budgetError.kind,
|
|
7845
8269
|
budgetError.limit,
|
|
7846
8270
|
budgetError.used
|
|
7847
8271
|
);
|
|
7848
|
-
} else {
|
|
7849
|
-
budgetError = null;
|
|
7850
8272
|
}
|
|
7851
8273
|
}
|
|
7852
8274
|
if (budgetError) throw budgetError;
|
|
@@ -10010,6 +10432,109 @@ Working rules:
|
|
|
10010
10432
|
"cloud cost"
|
|
10011
10433
|
]
|
|
10012
10434
|
}
|
|
10435
|
+
},
|
|
10436
|
+
{
|
|
10437
|
+
config: {
|
|
10438
|
+
id: "tech-stack",
|
|
10439
|
+
name: "Tech Stack Validator",
|
|
10440
|
+
role: "tech-stack",
|
|
10441
|
+
tools: ["search", "fetch", "read", "grep", "glob", "outdated", "audit", "json"],
|
|
10442
|
+
prompt: `You are the Tech Stack Validator \u2014 a single-shot validation agent that fires
|
|
10443
|
+
before any package, library, or framework choice is committed.
|
|
10444
|
+
|
|
10445
|
+
Your ONLY job: verify that a technology choice is current, real, and not obsolete.
|
|
10446
|
+
You are the "this isn't code, this is 10-year-old technology" agent. Intervene
|
|
10447
|
+
hard when the LLM hallucinates a version number or suggests dead tech.
|
|
10448
|
+
|
|
10449
|
+
## Critical rules
|
|
10450
|
+
|
|
10451
|
+
1. **Verify existence.** Search npm registry (fetch https://registry.npmjs.org/<pkg>/latest)
|
|
10452
|
+
or web search. A package that doesn't exist = hallucination.
|
|
10453
|
+
|
|
10454
|
+
2. **Check latest version.** Never trust any version number from the model. Always
|
|
10455
|
+
fetch the actual latest stable version from npm or the project's release page.
|
|
10456
|
+
|
|
10457
|
+
3. **Reject dead packages.** No release in >2 years + unresolved critical issues =
|
|
10458
|
+
dead. Suggest a maintained replacement.
|
|
10459
|
+
|
|
10460
|
+
4. **Reject prehistoric tech.** Any package/pattern superseded \u22655 years ago is
|
|
10461
|
+
REJECTED. Key blocklist:
|
|
10462
|
+
- axios / node-fetch / got / request \u2192 native fetch (Node 18+)
|
|
10463
|
+
- moment \u2192 date-fns / luxon / Temporal
|
|
10464
|
+
- jQuery (new projects) \u2192 vanilla DOM / React
|
|
10465
|
+
- Gulp / Grunt \u2192 tsup / esbuild / vite
|
|
10466
|
+
- CoffeeScript / Flow \u2192 TypeScript
|
|
10467
|
+
- Bluebird \u2192 native Promises
|
|
10468
|
+
- crypto-js \u2192 node:crypto / Web Crypto
|
|
10469
|
+
- Bower \u2192 npm/pnpm
|
|
10470
|
+
- underscore \u2192 lodash or native ES2020+
|
|
10471
|
+
|
|
10472
|
+
5. **The intervention phrase.** When rejecting on age grounds, you MUST output
|
|
10473
|
+
exactly: "This isn't code, this is X-year-old technology." where X =
|
|
10474
|
+
current year \u2212 the year the technology was made obsolete. Follow with
|
|
10475
|
+
what replaced it and a one-step migration path.
|
|
10476
|
+
|
|
10477
|
+
6. **Prefer built-in over third-party.** Check Node 22+ native APIs first:
|
|
10478
|
+
node:test, node:sqlite, fetch, WebSocket, Web Crypto \u2014 all built-in.
|
|
10479
|
+
|
|
10480
|
+
## Workflow (single-shot \u2014 do NOT loop)
|
|
10481
|
+
|
|
10482
|
+
1. Receive the proposed package + version
|
|
10483
|
+
2. Search npm registry or web for the latest version
|
|
10484
|
+
3. Check age, maintenance status, deprecation
|
|
10485
|
+
4. Output verdict: APPROVED (with exact version) or REJECTED (with replacement)
|
|
10486
|
+
|
|
10487
|
+
## Output format
|
|
10488
|
+
|
|
10489
|
+
### Tech Stack Validation \u2014 <package>
|
|
10490
|
+
|
|
10491
|
+
**Status**: APPROVED | REJECTED
|
|
10492
|
+
|
|
10493
|
+
**Package**: <name>@<version>
|
|
10494
|
+
**Source**: <URL you checked \u2014 npm registry, GitHub, web search>
|
|
10495
|
+
**Age**: <first release> \u2014 <last release date>
|
|
10496
|
+
**Verdict**: 1\u20132 sentence explanation.
|
|
10497
|
+
|
|
10498
|
+
When REJECTED on age:
|
|
10499
|
+
**"This isn't code, this is X-year-old technology."**
|
|
10500
|
+
**Replaced by**: <modern alternative>
|
|
10501
|
+
**Migration**: <one concrete step>
|
|
10502
|
+
|
|
10503
|
+
When APPROVED:
|
|
10504
|
+
**Install**: pnpm add <name>@^<major>.<minor>.0`
|
|
10505
|
+
},
|
|
10506
|
+
budget: {
|
|
10507
|
+
timeoutMs: 6e4,
|
|
10508
|
+
maxIterations: 5,
|
|
10509
|
+
maxToolCalls: 20,
|
|
10510
|
+
maxTokens: 4e4,
|
|
10511
|
+
maxCostUsd: 0.1
|
|
10512
|
+
},
|
|
10513
|
+
capability: {
|
|
10514
|
+
phase: "meta",
|
|
10515
|
+
summary: "Single-shot tech stack validator: checks npm for latest versions, rejects dead/obsolete packages, enforces modern alternatives.",
|
|
10516
|
+
keywords: [
|
|
10517
|
+
"tech stack",
|
|
10518
|
+
"version",
|
|
10519
|
+
"package",
|
|
10520
|
+
"library",
|
|
10521
|
+
"framework",
|
|
10522
|
+
"dependency",
|
|
10523
|
+
"install",
|
|
10524
|
+
"upgrade",
|
|
10525
|
+
"latest",
|
|
10526
|
+
"npm",
|
|
10527
|
+
"pnpm add",
|
|
10528
|
+
"outdated",
|
|
10529
|
+
"obsolete",
|
|
10530
|
+
"deprecated",
|
|
10531
|
+
"what version",
|
|
10532
|
+
"which package",
|
|
10533
|
+
"check version",
|
|
10534
|
+
"verify version",
|
|
10535
|
+
"is this current"
|
|
10536
|
+
]
|
|
10537
|
+
}
|
|
10013
10538
|
}
|
|
10014
10539
|
];
|
|
10015
10540
|
|
|
@@ -11342,12 +11867,6 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
11342
11867
|
};
|
|
11343
11868
|
|
|
11344
11869
|
// src/execution/parallel-eternal-engine.ts
|
|
11345
|
-
function expectDefined9(value) {
|
|
11346
|
-
if (value === null || value === void 0) {
|
|
11347
|
-
throw new Error("Expected value to be defined");
|
|
11348
|
-
}
|
|
11349
|
-
return value;
|
|
11350
|
-
}
|
|
11351
11870
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
11352
11871
|
var ParallelEternalEngine = class {
|
|
11353
11872
|
constructor(opts) {
|
|
@@ -11503,7 +12022,7 @@ var ParallelEternalEngine = class {
|
|
|
11503
12022
|
// Fan-out
|
|
11504
12023
|
// -------------------------------------------------------------------------
|
|
11505
12024
|
async fanOut(goal, tasks) {
|
|
11506
|
-
const coordinator =
|
|
12025
|
+
const coordinator = expectDefined(this.coordinator);
|
|
11507
12026
|
const slotCount = Math.min(this.slots, tasks.length);
|
|
11508
12027
|
const routes = this.dispatchEnabled ? await Promise.all(
|
|
11509
12028
|
tasks.slice(0, slotCount).map(
|
|
@@ -11535,7 +12054,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
11535
12054
|
const routeInfo = [];
|
|
11536
12055
|
const spawnPromises = [];
|
|
11537
12056
|
for (let i = 0; i < slotCount; i++) {
|
|
11538
|
-
const task =
|
|
12057
|
+
const task = expectDefined(tasks[i]);
|
|
11539
12058
|
const route = routes[i] ?? null;
|
|
11540
12059
|
const subagentId = `parallel-${this.iterations}-${i}`;
|
|
11541
12060
|
const taskId = randomUUID();
|
|
@@ -11808,7 +12327,17 @@ ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first ite
|
|
|
11808
12327
|
}
|
|
11809
12328
|
|
|
11810
12329
|
// src/execution/goal-preamble.ts
|
|
11811
|
-
function buildGoalPreamble(goal) {
|
|
12330
|
+
function buildGoalPreamble(goal, deliverables) {
|
|
12331
|
+
const deliverableBlock = deliverables && deliverables.length > 0 ? [
|
|
12332
|
+
"",
|
|
12333
|
+
"CONCRETE DELIVERABLES (check these off as you go):",
|
|
12334
|
+
...deliverables.map((d, i) => ` ${i + 1}. ${d}`),
|
|
12335
|
+
"",
|
|
12336
|
+
"After EACH iteration, estimate your completion percentage (0\u2013100)",
|
|
12337
|
+
"against this deliverable list. Output it as:",
|
|
12338
|
+
" [PROGRESS: N%] \u2014 <1-sentence status>",
|
|
12339
|
+
"The eternal engine reads this to update the progress bar."
|
|
12340
|
+
].join("\n") : "";
|
|
11812
12341
|
return [
|
|
11813
12342
|
"[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
|
|
11814
12343
|
"The user granted you full autonomy. Read these constraints once, then act.",
|
|
@@ -11817,6 +12346,7 @@ function buildGoalPreamble(goal) {
|
|
|
11817
12346
|
"---",
|
|
11818
12347
|
goal,
|
|
11819
12348
|
"---",
|
|
12349
|
+
deliverableBlock,
|
|
11820
12350
|
"",
|
|
11821
12351
|
"AUTHORITY YOU HAVE:",
|
|
11822
12352
|
"- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
|
|
@@ -11838,6 +12368,7 @@ function buildGoalPreamble(goal) {
|
|
|
11838
12368
|
"- You can tell the user HOW to verify it themselves in 10 seconds.",
|
|
11839
12369
|
'- You have NOT hedged. None of: "looks like it should work", "I',
|
|
11840
12370
|
' believe this fixes it", "the changes appear correct".',
|
|
12371
|
+
`- Progress bar shows 100%. All deliverables checked off.`,
|
|
11841
12372
|
"",
|
|
11842
12373
|
"WHAT IS NOT DONE \u2014 never report any of these as completion:",
|
|
11843
12374
|
"- An error message you didn't recover from.",
|
|
@@ -11852,6 +12383,13 @@ function buildGoalPreamble(goal) {
|
|
|
11852
12383
|
" respond to with a fresh attempt (different role, different model,",
|
|
11853
12384
|
" tighter prompt).",
|
|
11854
12385
|
"",
|
|
12386
|
+
"PROGRESS REPORTING \u2014 MANDATORY every iteration:",
|
|
12387
|
+
"- End every response with exactly: [PROGRESS: N%] \u2014 <status note>",
|
|
12388
|
+
" where N is your honest estimate (0-100) of how much of the goal",
|
|
12389
|
+
" is done. The engine tracks this and shows a live progress bar.",
|
|
12390
|
+
"- Example: [PROGRESS: 45%] \u2014 Auth module refactored, 3/5 tests pass,",
|
|
12391
|
+
" remaining: test coverage + docs update.",
|
|
12392
|
+
"",
|
|
11855
12393
|
"PERSISTENCE PROTOCOL:",
|
|
11856
12394
|
"- If blocked, try at least 3 different angles before reporting the",
|
|
11857
12395
|
" problem to the user. Different tool inputs, different subagent",
|
|
@@ -12026,12 +12564,6 @@ function createMessage(type, from, payload, to) {
|
|
|
12026
12564
|
priority: "normal"
|
|
12027
12565
|
};
|
|
12028
12566
|
}
|
|
12029
|
-
function expectDefined10(value) {
|
|
12030
|
-
if (value === null || value === void 0) {
|
|
12031
|
-
throw new Error("Expected value to be defined");
|
|
12032
|
-
}
|
|
12033
|
-
return value;
|
|
12034
|
-
}
|
|
12035
12567
|
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
12036
12568
|
var IS_WINDOWS = process.platform === "win32";
|
|
12037
12569
|
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
@@ -12045,7 +12577,7 @@ function globToRegex(pat) {
|
|
|
12045
12577
|
let i = 0;
|
|
12046
12578
|
let re = "^";
|
|
12047
12579
|
while (i < pat.length) {
|
|
12048
|
-
const c =
|
|
12580
|
+
const c = expectDefined(pat[i]);
|
|
12049
12581
|
if (c === "*") {
|
|
12050
12582
|
if (pat[i + 1] === "*") {
|
|
12051
12583
|
re += ".*";
|
|
@@ -12084,7 +12616,7 @@ function globToRegex(pat) {
|
|
|
12084
12616
|
}
|
|
12085
12617
|
function baseDir(pat) {
|
|
12086
12618
|
let i = pat.length - 1;
|
|
12087
|
-
while (i >= 0 && !GLOB_CHARS.has(
|
|
12619
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
12088
12620
|
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
12089
12621
|
return cut < 0 ? "." : pat.slice(0, cut);
|
|
12090
12622
|
}
|
|
@@ -12165,6 +12697,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
12165
12697
|
settled = false;
|
|
12166
12698
|
timeoutMs;
|
|
12167
12699
|
cancelled = false;
|
|
12700
|
+
_raceResolved = false;
|
|
12168
12701
|
alerts = [];
|
|
12169
12702
|
snapshotWarnings = [];
|
|
12170
12703
|
/** Tracks tool call counts per subagent for progress-based timeout decisions. */
|
|
@@ -12261,7 +12794,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
12261
12794
|
* Safe to call multiple times (idempotent after first call).
|
|
12262
12795
|
*/
|
|
12263
12796
|
cancel(reason = "Director cancelled collab session") {
|
|
12264
|
-
if (this.
|
|
12797
|
+
if (this.cancelled) return;
|
|
12265
12798
|
this.cancelled = true;
|
|
12266
12799
|
if (this._timeoutTimer) {
|
|
12267
12800
|
clearTimeout(this._timeoutTimer);
|
|
@@ -12299,6 +12832,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
12299
12832
|
this.subagentIds.set("critic", criticId);
|
|
12300
12833
|
const timeout = new Promise((_, reject) => {
|
|
12301
12834
|
this._timeoutTimer = setTimeout(() => {
|
|
12835
|
+
if (this._raceResolved) return;
|
|
12302
12836
|
this.cancel("Session-level timeout reached");
|
|
12303
12837
|
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
12304
12838
|
}, this.timeoutMs);
|
|
@@ -12314,6 +12848,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
12314
12848
|
timeout
|
|
12315
12849
|
]);
|
|
12316
12850
|
} catch (err) {
|
|
12851
|
+
this._raceResolved = true;
|
|
12317
12852
|
if (this._timeoutTimer) {
|
|
12318
12853
|
clearTimeout(this._timeoutTimer);
|
|
12319
12854
|
this._timeoutTimer = void 0;
|
|
@@ -12328,6 +12863,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
12328
12863
|
}
|
|
12329
12864
|
this.snapshotWarnings = await this.checkSnapshotFreshness();
|
|
12330
12865
|
const report = this.assembleReport();
|
|
12866
|
+
this._raceResolved = true;
|
|
12331
12867
|
this.cleanup();
|
|
12332
12868
|
this.emit("session.done", report);
|
|
12333
12869
|
return report;
|
|
@@ -16552,7 +17088,7 @@ var TaskTracker = class {
|
|
|
16552
17088
|
const blockers = this.getBlockers(taskId);
|
|
16553
17089
|
return blockers.every((id) => {
|
|
16554
17090
|
const node = this.graph?.nodes.get(id);
|
|
16555
|
-
return node?.status === "completed";
|
|
17091
|
+
return node?.status === "completed" || node?.status === "failed";
|
|
16556
17092
|
});
|
|
16557
17093
|
}
|
|
16558
17094
|
getProgress() {
|
|
@@ -16584,7 +17120,7 @@ var TaskTracker = class {
|
|
|
16584
17120
|
const remainingBlockers = this.getBlockers(depId);
|
|
16585
17121
|
const allUnblocked = remainingBlockers.every((id) => {
|
|
16586
17122
|
const blocker = this.graph?.nodes.get(id);
|
|
16587
|
-
return blocker?.status === "completed";
|
|
17123
|
+
return blocker?.status === "completed" || blocker?.status === "failed";
|
|
16588
17124
|
});
|
|
16589
17125
|
if (allUnblocked) {
|
|
16590
17126
|
dep.status = "pending";
|
|
@@ -16598,7 +17134,7 @@ var TaskTracker = class {
|
|
|
16598
17134
|
const blockers = this.getBlockers(taskId);
|
|
16599
17135
|
const someBlocked = blockers.some((id) => {
|
|
16600
17136
|
const blocker = this.graph?.nodes.get(id);
|
|
16601
|
-
return blocker?.status !== "completed";
|
|
17137
|
+
return blocker?.status !== "completed" && blocker?.status !== "failed";
|
|
16602
17138
|
});
|
|
16603
17139
|
if (someBlocked) {
|
|
16604
17140
|
const node = this.graph.nodes.get(taskId);
|
|
@@ -17035,12 +17571,6 @@ var TaskGraphStore = class {
|
|
|
17035
17571
|
};
|
|
17036
17572
|
|
|
17037
17573
|
// src/sdd/spec-builder.ts
|
|
17038
|
-
function expectDefined11(value) {
|
|
17039
|
-
if (value === null || value === void 0) {
|
|
17040
|
-
throw new Error("Expected value to be defined");
|
|
17041
|
-
}
|
|
17042
|
-
return value;
|
|
17043
|
-
}
|
|
17044
17574
|
function buildQuestioningPrompt(session, min, max) {
|
|
17045
17575
|
const answered = session.answers.length;
|
|
17046
17576
|
const remaining = Math.max(0, min - answered);
|
|
@@ -17086,7 +17616,7 @@ function buildQuestioningPrompt(session, min, max) {
|
|
|
17086
17616
|
if (answered > 0) {
|
|
17087
17617
|
lines.push("", "**Conversation so far:**");
|
|
17088
17618
|
for (let i = 0; i < answered; i++) {
|
|
17089
|
-
const a =
|
|
17619
|
+
const a = expectDefined(session.answers[i]);
|
|
17090
17620
|
lines.push(``, `Q${i + 1}: ${a.question}`, `A${i + 1}: ${a.answer}`);
|
|
17091
17621
|
}
|
|
17092
17622
|
}
|
|
@@ -17828,12 +18358,6 @@ function truncate2(str, maxLen) {
|
|
|
17828
18358
|
}
|
|
17829
18359
|
|
|
17830
18360
|
// src/sdd/critical-path.ts
|
|
17831
|
-
function expectDefined12(value) {
|
|
17832
|
-
if (value === null || value === void 0) {
|
|
17833
|
-
throw new Error("Expected value to be defined");
|
|
17834
|
-
}
|
|
17835
|
-
return value;
|
|
17836
|
-
}
|
|
17837
18361
|
function analyzeCriticalPath(graph) {
|
|
17838
18362
|
const nodes = Array.from(graph.nodes.values());
|
|
17839
18363
|
const topoOrder = topologicalSort(graph);
|
|
@@ -17909,7 +18433,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
|
|
|
17909
18433
|
const visited = /* @__PURE__ */ new Set();
|
|
17910
18434
|
const queue = [taskId];
|
|
17911
18435
|
while (queue.length > 0) {
|
|
17912
|
-
const current =
|
|
18436
|
+
const current = expectDefined(queue.shift());
|
|
17913
18437
|
const blocked = blocksMap.get(current);
|
|
17914
18438
|
if (!blocked) continue;
|
|
17915
18439
|
for (const id of blocked) {
|
|
@@ -17955,7 +18479,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
17955
18479
|
if (!changed) break;
|
|
17956
18480
|
}
|
|
17957
18481
|
let maxDist = 0;
|
|
17958
|
-
let maxId =
|
|
18482
|
+
let maxId = expectDefined(allIds[0]);
|
|
17959
18483
|
for (const id of allIds) {
|
|
17960
18484
|
const d = dist.get(id) ?? 0;
|
|
17961
18485
|
if (d > maxDist) {
|
|
@@ -18424,25 +18948,22 @@ var SddTaskDecomposer = class {
|
|
|
18424
18948
|
return nodes.some((n) => n.status === "blocked");
|
|
18425
18949
|
}
|
|
18426
18950
|
};
|
|
18427
|
-
function expectDefined13(value) {
|
|
18428
|
-
if (value === null || value === void 0) {
|
|
18429
|
-
throw new Error("Expected value to be defined");
|
|
18430
|
-
}
|
|
18431
|
-
return value;
|
|
18432
|
-
}
|
|
18433
18951
|
var SddParallelRun = class {
|
|
18434
18952
|
constructor(opts) {
|
|
18435
18953
|
this.opts = opts;
|
|
18436
18954
|
this.slots = Math.min(16, Math.max(1, opts.parallelSlots ?? 4));
|
|
18437
18955
|
this.timeoutMs = opts.taskTimeoutMs ?? 3e5;
|
|
18956
|
+
this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
|
|
18438
18957
|
this.decomposer = new SddTaskDecomposer(opts.tracker, opts.graph, { parallelSlots: this.slots });
|
|
18439
18958
|
}
|
|
18440
18959
|
opts;
|
|
18441
18960
|
slots;
|
|
18442
18961
|
timeoutMs;
|
|
18962
|
+
maxRetries;
|
|
18443
18963
|
decomposer;
|
|
18444
18964
|
coordinator = null;
|
|
18445
18965
|
stopRequested = false;
|
|
18966
|
+
retryMap = /* @__PURE__ */ new Map();
|
|
18446
18967
|
// -------------------------------------------------------------------
|
|
18447
18968
|
// Public API
|
|
18448
18969
|
// -------------------------------------------------------------------
|
|
@@ -18454,6 +18975,7 @@ var SddParallelRun = class {
|
|
|
18454
18975
|
/** Execute all waves until completion or deadlock. Returns final summary. */
|
|
18455
18976
|
async run() {
|
|
18456
18977
|
this.stopRequested = false;
|
|
18978
|
+
this.retryMap.clear();
|
|
18457
18979
|
const startTime = Date.now();
|
|
18458
18980
|
let totalCompleted = 0;
|
|
18459
18981
|
let totalFailed = 0;
|
|
@@ -18578,13 +19100,24 @@ var SddParallelRun = class {
|
|
|
18578
19100
|
const successCount = results.filter((r) => r.status === "success").length;
|
|
18579
19101
|
const failCount = results.length - successCount;
|
|
18580
19102
|
for (let i = 0; i < results.length; i++) {
|
|
18581
|
-
const result =
|
|
18582
|
-
const taskId =
|
|
19103
|
+
const result = expectDefined(results[i]);
|
|
19104
|
+
const taskId = expectDefined(taskIds[i]);
|
|
18583
19105
|
if (result.status === "success") {
|
|
18584
19106
|
this.opts.tracker.updateNodeStatus(taskId, "completed");
|
|
19107
|
+
this.retryMap.delete(taskId);
|
|
18585
19108
|
} else {
|
|
18586
19109
|
const errMsg = result.error?.kind ? `${result.error.kind}: ${result.error.message}` : result.error?.message ?? "unknown error";
|
|
18587
|
-
this.
|
|
19110
|
+
const currentRetries = this.retryMap.get(taskId) ?? 0;
|
|
19111
|
+
if (currentRetries < this.maxRetries) {
|
|
19112
|
+
this.retryMap.set(taskId, currentRetries + 1);
|
|
19113
|
+
this.opts.tracker.updateNodeStatus(
|
|
19114
|
+
taskId,
|
|
19115
|
+
"pending",
|
|
19116
|
+
`Retry ${currentRetries + 1}/${this.maxRetries}: ${errMsg}`
|
|
19117
|
+
);
|
|
19118
|
+
} else {
|
|
19119
|
+
this.opts.tracker.updateNodeStatus(taskId, "failed", errMsg);
|
|
19120
|
+
}
|
|
18588
19121
|
}
|
|
18589
19122
|
}
|
|
18590
19123
|
return {
|
|
@@ -18599,6 +19132,7 @@ var SddParallelRun = class {
|
|
|
18599
19132
|
}
|
|
18600
19133
|
buildProgress() {
|
|
18601
19134
|
const gp = this.opts.tracker.getProgress();
|
|
19135
|
+
const isDeadlocked = !this.decomposer.isDone() && this.decomposer.nextBatch().deadlocked;
|
|
18602
19136
|
return {
|
|
18603
19137
|
wave: this.decomposer.getWaveCount(),
|
|
18604
19138
|
total: gp.total,
|
|
@@ -18608,7 +19142,7 @@ var SddParallelRun = class {
|
|
|
18608
19142
|
blocked: gp.blocked,
|
|
18609
19143
|
pending: gp.pending,
|
|
18610
19144
|
percent: gp.percentComplete,
|
|
18611
|
-
deadlocked:
|
|
19145
|
+
deadlocked: isDeadlocked
|
|
18612
19146
|
};
|
|
18613
19147
|
}
|
|
18614
19148
|
};
|