@wrongstack/core 0.87.0 → 0.89.3
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-C9P_HPez.d.ts → agent-bridge-DbVe1dHT.d.ts} +1 -1
- package/dist/{agent-subagent-runner-2Aq0jOSj.d.ts → agent-subagent-runner-C6eqID1t.d.ts} +477 -476
- package/dist/{events-DnRqXaZ3.d.ts → brain-s9DyD_Vf.d.ts} +185 -92
- package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
- package/dist/{config-_DZ7dN-T.d.ts → config-84VaZpH6.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 +208 -93
- package/dist/coordination/index.js.map +1 -1
- package/dist/{default-config-DEXI4jsl.d.ts → default-config-CXsDvOmP.d.ts} +3 -1
- package/dist/defaults/index.d.ts +32 -34
- package/dist/defaults/index.js +744 -274
- 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 +18 -19
- package/dist/execution/index.js +79 -64
- 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/{index-BNOLadHw.d.ts → index-CqrroYcU.d.ts} +13 -15
- package/dist/{index-N0_c4bHQ.d.ts → index-D3Nax3YU.d.ts} +11 -10
- package/dist/index.d.ts +191 -178
- package/dist/index.js +1419 -526
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +9 -9
- package/dist/infrastructure/index.js +16 -5
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +14 -16
- package/dist/kernel/index.js +17 -0
- 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-D3E5ixAR.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-DllpCVkF.d.ts → multi-agent-coordinator-DHNrjPaA.d.ts} +2 -2
- package/dist/{null-fleet-bus-BY0AN-sr.d.ts → null-fleet-bus-BzzciAc4.d.ts} +39 -32
- 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-D402RASp.d.ts → parallel-eternal-engine-CTpcrb9O.d.ts} +34 -33
- package/dist/{path-resolver-UPFTsDyD.d.ts → path-resolver-B7hgMAVe.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-C1Ad9OjI.d.ts} +73 -73
- package/dist/{plan-templates-DRvPgkfZ.d.ts → plan-templates-CNphsz0n.d.ts} +79 -11
- package/dist/{provider-runner-COAJM8tC.d.ts → provider-runner-cqvlB_oS.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 +12 -33
- package/dist/sdd/index.js +110 -50
- 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 +22 -4
- 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-IVzs2GpE.d.ts} +1 -1
- package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
- package/dist/{skill-BJeF2DwY.d.ts → skill-BJxzIyyN.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/storage/index.d.ts +134 -14
- package/dist/storage/index.js +982 -248
- package/dist/storage/index.js.map +1 -1
- package/dist/{task-graph-CikNdRTG.d.ts → task-graph-DJBqO0EY.d.ts} +1 -1
- package/dist/types/index.d.ts +26 -28
- package/dist/types/index.js +62 -37
- 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 +11 -20
- package/dist/utils/index.js +61 -43
- 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/dist/memory-CEXuo7sz.d.ts +0 -16
- package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as path6 from 'path';
|
|
|
6
6
|
import { join, extname, relative, isAbsolute, resolve, sep } from 'path';
|
|
7
7
|
import * as fs2 from 'fs';
|
|
8
8
|
import * as os7 from 'os';
|
|
9
|
+
import { hostname } from 'os';
|
|
9
10
|
import { execFile, spawn } from 'child_process';
|
|
10
11
|
import { promisify } from 'util';
|
|
11
12
|
import { EventEmitter } from 'events';
|
|
@@ -15,12 +16,6 @@ import { pipeline } from 'stream/promises';
|
|
|
15
16
|
|
|
16
17
|
var __defProp = Object.defineProperty;
|
|
17
18
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
18
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
19
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
20
|
-
}) : x)(function(x) {
|
|
21
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
22
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
23
|
-
});
|
|
24
19
|
var __esm = (fn, res) => function __init() {
|
|
25
20
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
26
21
|
};
|
|
@@ -33,7 +28,8 @@ var __export = (target, all) => {
|
|
|
33
28
|
var atomic_write_exports = {};
|
|
34
29
|
__export(atomic_write_exports, {
|
|
35
30
|
atomicWrite: () => atomicWrite,
|
|
36
|
-
ensureDir: () => ensureDir
|
|
31
|
+
ensureDir: () => ensureDir,
|
|
32
|
+
withFileLock: () => withFileLock
|
|
37
33
|
});
|
|
38
34
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
39
35
|
const dir = path6.dirname(targetPath);
|
|
@@ -56,8 +52,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
56
52
|
}
|
|
57
53
|
let mode;
|
|
58
54
|
try {
|
|
59
|
-
const
|
|
60
|
-
mode =
|
|
55
|
+
const stat11 = await fsp3.stat(targetPath);
|
|
56
|
+
mode = stat11.mode & 511;
|
|
61
57
|
} catch {
|
|
62
58
|
mode = opts.mode;
|
|
63
59
|
}
|
|
@@ -76,6 +72,49 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
76
72
|
async function ensureDir(dir) {
|
|
77
73
|
await fsp3.mkdir(dir, { recursive: true });
|
|
78
74
|
}
|
|
75
|
+
async function withFileLock(targetPath, fn, opts = {}) {
|
|
76
|
+
const dir = path6.dirname(targetPath);
|
|
77
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
78
|
+
const lockPath = path6.join(dir, `.${path6.basename(targetPath)}.lock`);
|
|
79
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
80
|
+
const staleMs = opts.staleMs ?? 3e4;
|
|
81
|
+
const started = Date.now();
|
|
82
|
+
let handle;
|
|
83
|
+
for (; ; ) {
|
|
84
|
+
try {
|
|
85
|
+
handle = await fsp3.open(lockPath, "wx");
|
|
86
|
+
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
87
|
+
break;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (err.code !== "EEXIST") throw err;
|
|
90
|
+
try {
|
|
91
|
+
const stat11 = await fsp3.stat(lockPath);
|
|
92
|
+
if (Date.now() - stat11.mtimeMs > staleMs) {
|
|
93
|
+
await fsp3.unlink(lockPath);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (Date.now() - started >= timeoutMs) {
|
|
100
|
+
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
101
|
+
}
|
|
102
|
+
await new Promise((resolve13) => setTimeout(resolve13, 25));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
return await fn();
|
|
107
|
+
} finally {
|
|
108
|
+
try {
|
|
109
|
+
await handle?.close();
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
await fsp3.unlink(lockPath);
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
79
118
|
async function renameWithRetry(from, to) {
|
|
80
119
|
if (process.platform !== "win32") {
|
|
81
120
|
await fsp3.rename(from, to);
|
|
@@ -703,6 +742,23 @@ var EventBus = class {
|
|
|
703
742
|
}
|
|
704
743
|
}
|
|
705
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* Emit a plugin-defined event that is intentionally outside EventMap.
|
|
747
|
+
* Custom events are delivered to wildcard/pattern listeners only; typed
|
|
748
|
+
* listeners remain reserved for core EventMap keys.
|
|
749
|
+
*/
|
|
750
|
+
emitCustom(event, payload) {
|
|
751
|
+
if (this.wildcards.length === 0) return;
|
|
752
|
+
const snapshot = this.wildcards.slice();
|
|
753
|
+
for (const { match, fn } of snapshot) {
|
|
754
|
+
if (!match(event)) continue;
|
|
755
|
+
try {
|
|
756
|
+
fn(event, payload);
|
|
757
|
+
} catch (err) {
|
|
758
|
+
this.logger?.error(`EventBus wildcard listener for "${event}" threw`, err);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
706
762
|
clear() {
|
|
707
763
|
this.listeners.clear();
|
|
708
764
|
this.wildcards.length = 0;
|
|
@@ -1093,6 +1149,7 @@ var DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({
|
|
|
1093
1149
|
}
|
|
1094
1150
|
}
|
|
1095
1151
|
});
|
|
1152
|
+
var DEFAULT_SESSION_PRUNE_DAYS = 30;
|
|
1096
1153
|
|
|
1097
1154
|
// src/types/secret-vault.ts
|
|
1098
1155
|
var ENCRYPTED_PREFIX = "enc:v1:";
|
|
@@ -1254,9 +1311,18 @@ async function restrictFilePermissions(filePath, opts) {
|
|
|
1254
1311
|
const { execFile: execFile2 } = await import('child_process');
|
|
1255
1312
|
const { promisify: promisify2 } = await import('util');
|
|
1256
1313
|
const execFileAsync = promisify2(execFile2);
|
|
1257
|
-
|
|
1314
|
+
const user = windowsAccountName();
|
|
1315
|
+
if (!user) {
|
|
1316
|
+
warn(
|
|
1317
|
+
`[secret-vault] Could not determine the current Windows user for ${filePath}; skipping icacls hardening.`
|
|
1318
|
+
);
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${user}:(F)`]);
|
|
1258
1322
|
} catch {
|
|
1259
|
-
warn(
|
|
1323
|
+
warn(
|
|
1324
|
+
`[secret-vault] Could not restrict permissions on ${filePath} \u2014 config file may be readable by other users on this system.`
|
|
1325
|
+
);
|
|
1260
1326
|
}
|
|
1261
1327
|
} else {
|
|
1262
1328
|
try {
|
|
@@ -1265,6 +1331,13 @@ async function restrictFilePermissions(filePath, opts) {
|
|
|
1265
1331
|
}
|
|
1266
1332
|
}
|
|
1267
1333
|
}
|
|
1334
|
+
function windowsAccountName() {
|
|
1335
|
+
const username = process.env.USERNAME || process.env.USER;
|
|
1336
|
+
if (!username || username.includes("\0")) return void 0;
|
|
1337
|
+
const domain = process.env.USERDOMAIN;
|
|
1338
|
+
if (domain && !domain.includes("\0")) return `${domain}\\${username}`;
|
|
1339
|
+
return username;
|
|
1340
|
+
}
|
|
1268
1341
|
function walkCount(node, vault, counter) {
|
|
1269
1342
|
if (node === null || node === void 0) return node;
|
|
1270
1343
|
if (typeof node !== "object") return node;
|
|
@@ -1371,10 +1444,15 @@ function writeErr(s, stream = process.stderr) {
|
|
|
1371
1444
|
|
|
1372
1445
|
// src/utils/color.ts
|
|
1373
1446
|
var isColorTty = () => {
|
|
1374
|
-
if (process.env.NO_COLOR) return false;
|
|
1375
|
-
if (process.env.FORCE_COLOR) return true;
|
|
1447
|
+
if (envFlag(process.env.NO_COLOR)) return false;
|
|
1448
|
+
if (envFlag(process.env.FORCE_COLOR)) return true;
|
|
1376
1449
|
return isStdoutTTY();
|
|
1377
1450
|
};
|
|
1451
|
+
function envFlag(value) {
|
|
1452
|
+
if (value === void 0) return false;
|
|
1453
|
+
if (value.trim() === "") return false;
|
|
1454
|
+
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
1455
|
+
}
|
|
1378
1456
|
var COLOR = isColorTty();
|
|
1379
1457
|
var wrap = (open5, close) => (s) => COLOR ? `\x1B[${open5}m${s}\x1B[${close}m` : s;
|
|
1380
1458
|
var color = {
|
|
@@ -1418,6 +1496,7 @@ var COLORS = {
|
|
|
1418
1496
|
debug: color.gray,
|
|
1419
1497
|
trace: color.dim
|
|
1420
1498
|
};
|
|
1499
|
+
var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
|
|
1421
1500
|
var DefaultLogger = class _DefaultLogger {
|
|
1422
1501
|
level;
|
|
1423
1502
|
file;
|
|
@@ -1425,7 +1504,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
1425
1504
|
pretty;
|
|
1426
1505
|
stderr;
|
|
1427
1506
|
constructor(opts = {}) {
|
|
1428
|
-
this.level = opts.level ?? process.env.WRONGSTACK_LOG_LEVEL
|
|
1507
|
+
this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);
|
|
1429
1508
|
this.file = opts.file;
|
|
1430
1509
|
this.bindings = opts.bindings ?? {};
|
|
1431
1510
|
this.pretty = opts.pretty ?? true;
|
|
@@ -1490,6 +1569,9 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
1490
1569
|
}
|
|
1491
1570
|
}
|
|
1492
1571
|
};
|
|
1572
|
+
function parseLogLevel(raw) {
|
|
1573
|
+
return raw && LOG_LEVELS.has(raw) ? raw : "info";
|
|
1574
|
+
}
|
|
1493
1575
|
function formatCtx(ctx) {
|
|
1494
1576
|
if (ctx instanceof Error) return color.dim(ctx.message);
|
|
1495
1577
|
if (typeof ctx === "string") return color.dim(ctx);
|
|
@@ -1626,13 +1708,25 @@ function round4(n) {
|
|
|
1626
1708
|
return Math.round(n * 1e4) / 1e4;
|
|
1627
1709
|
}
|
|
1628
1710
|
|
|
1629
|
-
// src/
|
|
1630
|
-
|
|
1711
|
+
// src/types/memory.ts
|
|
1712
|
+
var MEMORY_TYPE_LABELS = {
|
|
1713
|
+
fact: "Fact",
|
|
1714
|
+
decision: "Decision",
|
|
1715
|
+
convention: "Convention",
|
|
1716
|
+
preference: "Preference",
|
|
1717
|
+
reference: "Reference",
|
|
1718
|
+
anti_pattern: "Anti-pattern"
|
|
1719
|
+
};
|
|
1720
|
+
|
|
1721
|
+
// src/utils/expect-defined.ts
|
|
1722
|
+
function expectDefined(value, label) {
|
|
1631
1723
|
if (value === null || value === void 0) {
|
|
1632
|
-
throw new Error("Expected value to be defined");
|
|
1724
|
+
throw new Error(label ? `Expected ${label} to be defined` : "Expected value to be defined");
|
|
1633
1725
|
}
|
|
1634
1726
|
return value;
|
|
1635
1727
|
}
|
|
1728
|
+
|
|
1729
|
+
// src/utils/token-estimate.ts
|
|
1636
1730
|
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
1637
1731
|
var _cal = {
|
|
1638
1732
|
ratio: 1,
|
|
@@ -1766,12 +1860,6 @@ function resetCalibration() {
|
|
|
1766
1860
|
}
|
|
1767
1861
|
|
|
1768
1862
|
// src/utils/message-invariants.ts
|
|
1769
|
-
function expectDefined2(value) {
|
|
1770
|
-
if (value === null || value === void 0) {
|
|
1771
|
-
throw new Error("Expected value to be defined");
|
|
1772
|
-
}
|
|
1773
|
-
return value;
|
|
1774
|
-
}
|
|
1775
1863
|
function repairToolUseAdjacency(messages) {
|
|
1776
1864
|
const removedToolUses = [];
|
|
1777
1865
|
const removedToolResults = [];
|
|
@@ -1779,7 +1867,7 @@ function repairToolUseAdjacency(messages) {
|
|
|
1779
1867
|
let changed = false;
|
|
1780
1868
|
const out = [];
|
|
1781
1869
|
for (let i = 0; i < messages.length; i++) {
|
|
1782
|
-
const original =
|
|
1870
|
+
const original = expectDefined(messages[i]);
|
|
1783
1871
|
let msg = original;
|
|
1784
1872
|
if (hasToolUse(msg)) {
|
|
1785
1873
|
const nextIds = toolResultIds(messages[i + 1]);
|
|
@@ -2937,12 +3025,6 @@ Remember: your job is to make the user a better developer, not just to complete
|
|
|
2937
3025
|
];
|
|
2938
3026
|
|
|
2939
3027
|
// src/types/context-window.ts
|
|
2940
|
-
function expectDefined3(value) {
|
|
2941
|
-
if (value === null || value === void 0) {
|
|
2942
|
-
throw new Error("Expected value to be defined");
|
|
2943
|
-
}
|
|
2944
|
-
return value;
|
|
2945
|
-
}
|
|
2946
3028
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
2947
3029
|
var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
2948
3030
|
{
|
|
@@ -2999,7 +3081,7 @@ function isContextWindowModeId(id) {
|
|
|
2999
3081
|
}
|
|
3000
3082
|
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
3001
3083
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
3002
|
-
const mode = getContextWindowMode(requested) ??
|
|
3084
|
+
const mode = getContextWindowMode(requested) ?? expectDefined(getContextWindowMode(DEFAULT_CONTEXT_WINDOW_MODE_ID));
|
|
3003
3085
|
if (mode.id !== DEFAULT_CONTEXT_WINDOW_MODE_ID) {
|
|
3004
3086
|
return mode;
|
|
3005
3087
|
}
|
|
@@ -3479,17 +3561,12 @@ function sizeSignals(toolName, content) {
|
|
|
3479
3561
|
}
|
|
3480
3562
|
|
|
3481
3563
|
// src/execution/tool-executor.ts
|
|
3482
|
-
function expectDefined4(value) {
|
|
3483
|
-
if (value === null || value === void 0) {
|
|
3484
|
-
throw new Error("Expected value to be defined");
|
|
3485
|
-
}
|
|
3486
|
-
return value;
|
|
3487
|
-
}
|
|
3488
3564
|
var ToolExecutor = class {
|
|
3489
3565
|
constructor(registry, opts) {
|
|
3490
3566
|
this.registry = registry;
|
|
3491
3567
|
this.opts = opts;
|
|
3492
3568
|
this.iterationTimeoutMs = opts.iterationTimeoutMs ?? 3e5;
|
|
3569
|
+
this.maxToolTimeoutMs = opts.maxToolTimeoutMs ?? 3e5;
|
|
3493
3570
|
this.serializer = createToolOutputSerializer({
|
|
3494
3571
|
perIterationOutputCapBytes: opts.perIterationOutputCapBytes ?? 1e5
|
|
3495
3572
|
});
|
|
@@ -3498,6 +3575,7 @@ var ToolExecutor = class {
|
|
|
3498
3575
|
opts;
|
|
3499
3576
|
serializer;
|
|
3500
3577
|
iterationTimeoutMs;
|
|
3578
|
+
maxToolTimeoutMs;
|
|
3501
3579
|
/**
|
|
3502
3580
|
* Clear the interactive confirm awaiter so the executor returns
|
|
3503
3581
|
* `ToolConfirmPendingResult` instead of blocking on stdin. Used by
|
|
@@ -3735,7 +3813,10 @@ ${post.additionalContext}` };
|
|
|
3735
3813
|
if (parentSignal.reason instanceof Error) throw parentSignal.reason;
|
|
3736
3814
|
throw new Error(typeof parentSignal.reason === "string" ? parentSignal.reason : "aborted");
|
|
3737
3815
|
}
|
|
3738
|
-
const timeoutMs =
|
|
3816
|
+
const timeoutMs = clampTimeoutMs(
|
|
3817
|
+
tool.timeoutMs ?? this.iterationTimeoutMs,
|
|
3818
|
+
this.maxToolTimeoutMs
|
|
3819
|
+
);
|
|
3739
3820
|
const ctrl = new AbortController();
|
|
3740
3821
|
const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
|
|
3741
3822
|
const combined = AbortSignal.any([parentSignal, ctrl.signal]);
|
|
@@ -3870,6 +3951,12 @@ ${excerpt}`;
|
|
|
3870
3951
|
return void 0;
|
|
3871
3952
|
}
|
|
3872
3953
|
};
|
|
3954
|
+
function clampTimeoutMs(timeoutMs, maxTimeoutMs) {
|
|
3955
|
+
const fallback = 3e5;
|
|
3956
|
+
const finiteTimeout = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : fallback;
|
|
3957
|
+
const finiteMax = Number.isFinite(maxTimeoutMs) && maxTimeoutMs > 0 ? maxTimeoutMs : fallback;
|
|
3958
|
+
return Math.max(1, Math.min(finiteTimeout, finiteMax));
|
|
3959
|
+
}
|
|
3873
3960
|
var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
|
|
3874
3961
|
function hasMalformedArguments(input) {
|
|
3875
3962
|
if (!input || typeof input !== "object" || Array.isArray(input)) return false;
|
|
@@ -3880,7 +3967,7 @@ function hasMalformedArguments(input) {
|
|
|
3880
3967
|
function extractMalformedRaw(input) {
|
|
3881
3968
|
if (!hasMalformedArguments(input)) return void 0;
|
|
3882
3969
|
const obj = input;
|
|
3883
|
-
const value = obj[
|
|
3970
|
+
const value = obj[expectDefined(Object.keys(obj)[0])];
|
|
3884
3971
|
if (value === void 0 || value === null) return void 0;
|
|
3885
3972
|
if (typeof value === "string") return value;
|
|
3886
3973
|
try {
|
|
@@ -3927,12 +4014,6 @@ function compileUserRegex(pattern, flags) {
|
|
|
3927
4014
|
}
|
|
3928
4015
|
|
|
3929
4016
|
// src/storage/session-reader.ts
|
|
3930
|
-
function expectDefined5(value) {
|
|
3931
|
-
if (value === null || value === void 0) {
|
|
3932
|
-
throw new Error("Expected value to be defined");
|
|
3933
|
-
}
|
|
3934
|
-
return value;
|
|
3935
|
-
}
|
|
3936
4017
|
var DefaultSessionReader = class {
|
|
3937
4018
|
store;
|
|
3938
4019
|
constructor(opts) {
|
|
@@ -3994,7 +4075,7 @@ var DefaultSessionReader = class {
|
|
|
3994
4075
|
continue;
|
|
3995
4076
|
}
|
|
3996
4077
|
for (let i = 0; i < data.events.length; i++) {
|
|
3997
|
-
const ev =
|
|
4078
|
+
const ev = expectDefined(data.events[i]);
|
|
3998
4079
|
if (allowedTypes && !allowedTypes.has(ev.type)) continue;
|
|
3999
4080
|
const text = eventText(ev);
|
|
4000
4081
|
if (text === null) continue;
|
|
@@ -4362,12 +4443,6 @@ function formatTodosList(todos) {
|
|
|
4362
4443
|
}
|
|
4363
4444
|
|
|
4364
4445
|
// src/utils/glob-match.ts
|
|
4365
|
-
function expectDefined6(value) {
|
|
4366
|
-
if (value === null || value === void 0) {
|
|
4367
|
-
throw new Error("Expected value to be defined");
|
|
4368
|
-
}
|
|
4369
|
-
return value;
|
|
4370
|
-
}
|
|
4371
4446
|
function escapeRegex(s) {
|
|
4372
4447
|
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
4373
4448
|
}
|
|
@@ -4379,7 +4454,7 @@ function getCachedGlob(pattern) {
|
|
|
4379
4454
|
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
4380
4455
|
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
4381
4456
|
for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
|
|
4382
|
-
COMPILED_GLOB_CACHE.delete(
|
|
4457
|
+
COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
|
|
4383
4458
|
}
|
|
4384
4459
|
}
|
|
4385
4460
|
const re = compileGlob(pattern);
|
|
@@ -4739,20 +4814,6 @@ function buildChildEnv(optsOrSessionId) {
|
|
|
4739
4814
|
function sleep(ms) {
|
|
4740
4815
|
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
4741
4816
|
}
|
|
4742
|
-
|
|
4743
|
-
// src/utils/expect-defined.ts
|
|
4744
|
-
function expectDefined7(value) {
|
|
4745
|
-
if (value === null || value === void 0) {
|
|
4746
|
-
throw new Error("Expected value to be defined");
|
|
4747
|
-
}
|
|
4748
|
-
return value;
|
|
4749
|
-
}
|
|
4750
|
-
function expectDefined8(value) {
|
|
4751
|
-
if (value === null || value === void 0) {
|
|
4752
|
-
throw new Error("Expected value to be defined");
|
|
4753
|
-
}
|
|
4754
|
-
return value;
|
|
4755
|
-
}
|
|
4756
4817
|
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
4757
4818
|
var IS_WINDOWS = process.platform === "win32";
|
|
4758
4819
|
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
@@ -4766,7 +4827,7 @@ function globToRegex(pat) {
|
|
|
4766
4827
|
let i = 0;
|
|
4767
4828
|
let re = "^";
|
|
4768
4829
|
while (i < pat.length) {
|
|
4769
|
-
const c =
|
|
4830
|
+
const c = expectDefined(pat[i]);
|
|
4770
4831
|
if (c === "*") {
|
|
4771
4832
|
if (pat[i + 1] === "*") {
|
|
4772
4833
|
re += ".*";
|
|
@@ -4805,7 +4866,7 @@ function globToRegex(pat) {
|
|
|
4805
4866
|
}
|
|
4806
4867
|
function baseDir(pat) {
|
|
4807
4868
|
let i = pat.length - 1;
|
|
4808
|
-
while (i >= 0 && !GLOB_CHARS.has(
|
|
4869
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
4809
4870
|
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
4810
4871
|
return cut < 0 ? "." : pat.slice(0, cut);
|
|
4811
4872
|
}
|
|
@@ -4840,8 +4901,8 @@ async function expandGlob(pattern) {
|
|
|
4840
4901
|
for (const e of entries) {
|
|
4841
4902
|
const full = `${dir}${SEP}${e}`;
|
|
4842
4903
|
try {
|
|
4843
|
-
const
|
|
4844
|
-
if (
|
|
4904
|
+
const stat11 = await fsp3.stat(full);
|
|
4905
|
+
if (stat11.isDirectory()) await walk4(full, rest);
|
|
4845
4906
|
} catch {
|
|
4846
4907
|
}
|
|
4847
4908
|
}
|
|
@@ -4858,8 +4919,8 @@ async function expandGlob(pattern) {
|
|
|
4858
4919
|
if (entries.includes(seg)) {
|
|
4859
4920
|
const full = `${dir}${SEP}${seg}`;
|
|
4860
4921
|
try {
|
|
4861
|
-
const
|
|
4862
|
-
if (
|
|
4922
|
+
const stat11 = await fsp3.stat(full);
|
|
4923
|
+
if (stat11.isDirectory()) await walk4(full, rest);
|
|
4863
4924
|
} catch {
|
|
4864
4925
|
}
|
|
4865
4926
|
}
|
|
@@ -4870,12 +4931,6 @@ async function expandGlob(pattern) {
|
|
|
4870
4931
|
}
|
|
4871
4932
|
|
|
4872
4933
|
// src/utils/json-repair.ts
|
|
4873
|
-
function expectDefined9(value) {
|
|
4874
|
-
if (value === null || value === void 0) {
|
|
4875
|
-
throw new Error("Expected value to be defined");
|
|
4876
|
-
}
|
|
4877
|
-
return value;
|
|
4878
|
-
}
|
|
4879
4934
|
function completePartialObject(s) {
|
|
4880
4935
|
if (!s.trim().startsWith("{")) return s;
|
|
4881
4936
|
if (tryParse(s).ok) return s;
|
|
@@ -4887,7 +4942,7 @@ function completePartialObject(s) {
|
|
|
4887
4942
|
let contentEnd = 0;
|
|
4888
4943
|
let stringBraceDepth = 0;
|
|
4889
4944
|
for (let i = 0; i < s.length; i++) {
|
|
4890
|
-
const ch =
|
|
4945
|
+
const ch = expectDefined(s[i]);
|
|
4891
4946
|
if (inString) {
|
|
4892
4947
|
contentEnd = i + 1;
|
|
4893
4948
|
if (escaped) {
|
|
@@ -4983,12 +5038,6 @@ function mergeCustomModelDefs(providerCustomModels, configModels) {
|
|
|
4983
5038
|
|
|
4984
5039
|
// src/storage/session-store.ts
|
|
4985
5040
|
init_atomic_write();
|
|
4986
|
-
function expectDefined10(value) {
|
|
4987
|
-
if (value === null || value === void 0) {
|
|
4988
|
-
throw new Error("Expected value to be defined");
|
|
4989
|
-
}
|
|
4990
|
-
return value;
|
|
4991
|
-
}
|
|
4992
5041
|
function sanitizeModel(model) {
|
|
4993
5042
|
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
4994
5043
|
}
|
|
@@ -5254,8 +5303,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
5254
5303
|
return JSON.parse(raw);
|
|
5255
5304
|
} catch {
|
|
5256
5305
|
const full = this.sessionPath(id, ".jsonl");
|
|
5257
|
-
const
|
|
5258
|
-
const summary = await this.summarize(id,
|
|
5306
|
+
const stat11 = await fsp3.stat(full);
|
|
5307
|
+
const summary = await this.summarize(id, stat11.mtime.toISOString());
|
|
5259
5308
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
5260
5309
|
console.warn(
|
|
5261
5310
|
`[session-store] Failed to write manifest for "${id}":`,
|
|
@@ -5303,8 +5352,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
5303
5352
|
if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
|
|
5304
5353
|
const jsonlPath = path6.join(dateDir, file.name);
|
|
5305
5354
|
try {
|
|
5306
|
-
const
|
|
5307
|
-
if (
|
|
5355
|
+
const stat11 = await fsp3.stat(jsonlPath);
|
|
5356
|
+
if (stat11.mtimeMs >= cutoff) continue;
|
|
5308
5357
|
} catch {
|
|
5309
5358
|
continue;
|
|
5310
5359
|
}
|
|
@@ -5699,7 +5748,7 @@ var FileSessionWriter = class {
|
|
|
5699
5748
|
let targetCheckpointLine = -1;
|
|
5700
5749
|
let afterTarget = false;
|
|
5701
5750
|
for (let i = 0; i < lines.length; i++) {
|
|
5702
|
-
const line =
|
|
5751
|
+
const line = expectDefined(lines[i]);
|
|
5703
5752
|
if (!line.trim()) continue;
|
|
5704
5753
|
let event;
|
|
5705
5754
|
try {
|
|
@@ -5994,12 +6043,224 @@ function mergeAdjacentText(blocks) {
|
|
|
5994
6043
|
}
|
|
5995
6044
|
return out;
|
|
5996
6045
|
}
|
|
6046
|
+
init_atomic_write();
|
|
6047
|
+
var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
|
|
6048
|
+
var TAG_RE = /#([\w-]+)/g;
|
|
6049
|
+
function formatMetadata(entry) {
|
|
6050
|
+
const parts = [];
|
|
6051
|
+
if (entry.type && entry.priority) {
|
|
6052
|
+
parts.push(`[${entry.type}|${entry.priority}]`);
|
|
6053
|
+
} else if (entry.type) {
|
|
6054
|
+
parts.push(`[${entry.type}]`);
|
|
6055
|
+
} else if (entry.priority) {
|
|
6056
|
+
parts.push(`[${entry.priority}]`);
|
|
6057
|
+
}
|
|
6058
|
+
if (entry.tags && entry.tags.length > 0) {
|
|
6059
|
+
parts.push(entry.tags.map((t2) => `#${t2}`).join(" "));
|
|
6060
|
+
}
|
|
6061
|
+
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
6062
|
+
}
|
|
6063
|
+
function lineToEntry(line, scope) {
|
|
6064
|
+
const trimmed = line.trim();
|
|
6065
|
+
if (!trimmed.startsWith("- [")) return null;
|
|
6066
|
+
const tsMatch = trimmed.match(/^-\s*\[([^\]]+)\]/);
|
|
6067
|
+
if (!tsMatch) return null;
|
|
6068
|
+
const ts = tsMatch[1] ?? "";
|
|
6069
|
+
let rest = trimmed.slice(tsMatch[0].length).trim();
|
|
6070
|
+
let type;
|
|
6071
|
+
let priority;
|
|
6072
|
+
const tpMatch = rest.match(TYPE_PRIORITY_RE);
|
|
6073
|
+
if (tpMatch) {
|
|
6074
|
+
const a = tpMatch[1] ?? "";
|
|
6075
|
+
const b = tpMatch[2] ?? "";
|
|
6076
|
+
if (isMemoryType(a)) {
|
|
6077
|
+
type = a;
|
|
6078
|
+
priority = isPriority(b) ? b : void 0;
|
|
6079
|
+
} else if (isPriority(a)) {
|
|
6080
|
+
priority = a;
|
|
6081
|
+
}
|
|
6082
|
+
rest = rest.slice(tpMatch[0].length).trim();
|
|
6083
|
+
}
|
|
6084
|
+
const idMatch = rest.match(/^mem_\d+_\w+\s+/);
|
|
6085
|
+
let text;
|
|
6086
|
+
if (idMatch) {
|
|
6087
|
+
text = rest.slice(idMatch[0].length).trim();
|
|
6088
|
+
} else {
|
|
6089
|
+
text = rest.trim();
|
|
6090
|
+
}
|
|
6091
|
+
const tags = [];
|
|
6092
|
+
let tagMatch;
|
|
6093
|
+
TAG_RE.lastIndex = 0;
|
|
6094
|
+
while ((tagMatch = TAG_RE.exec(text)) !== null) {
|
|
6095
|
+
tags.push(tagMatch[1] ?? "");
|
|
6096
|
+
}
|
|
6097
|
+
const cleanText = text.replace(TAG_RE, "").replace(/\s{2,}/g, " ").trim();
|
|
6098
|
+
if (!cleanText) return null;
|
|
6099
|
+
return {
|
|
6100
|
+
scope,
|
|
6101
|
+
text: cleanText,
|
|
6102
|
+
ts,
|
|
6103
|
+
type,
|
|
6104
|
+
priority,
|
|
6105
|
+
tags: tags.length > 0 ? tags : void 0
|
|
6106
|
+
};
|
|
6107
|
+
}
|
|
6108
|
+
function isMemoryType(s) {
|
|
6109
|
+
return s in MEMORY_TYPE_LABELS;
|
|
6110
|
+
}
|
|
6111
|
+
function isPriority(s) {
|
|
6112
|
+
return s === "critical" || s === "high" || s === "medium" || s === "low";
|
|
6113
|
+
}
|
|
6114
|
+
var FileMemoryBackend = class {
|
|
6115
|
+
kind = "file";
|
|
6116
|
+
files;
|
|
6117
|
+
constructor(opts) {
|
|
6118
|
+
this.files = {
|
|
6119
|
+
"project-agents": opts.paths.inProjectAgentsFile,
|
|
6120
|
+
"project-memory": opts.paths.projectMemory,
|
|
6121
|
+
"user-memory": opts.paths.globalMemory
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
6124
|
+
resolveFile(filePath, scope) {
|
|
6125
|
+
return filePath || this.files[scope];
|
|
6126
|
+
}
|
|
6127
|
+
async remember(scope, entry, filePath) {
|
|
6128
|
+
const file = this.resolveFile(filePath, scope);
|
|
6129
|
+
await ensureDir(path6.dirname(file));
|
|
6130
|
+
let existing = "";
|
|
6131
|
+
try {
|
|
6132
|
+
existing = await fsp3.readFile(file, "utf8");
|
|
6133
|
+
} catch {
|
|
6134
|
+
}
|
|
6135
|
+
const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
6136
|
+
const meta = formatMetadata(entry);
|
|
6137
|
+
const line = `
|
|
6138
|
+
- [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
|
|
6139
|
+
`;
|
|
6140
|
+
const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# WrongStack Memory
|
|
6141
|
+
${line}`;
|
|
6142
|
+
await atomicWrite(file, next);
|
|
6143
|
+
}
|
|
6144
|
+
async forget(scope, query, filePath) {
|
|
6145
|
+
const file = this.resolveFile(filePath, scope);
|
|
6146
|
+
let existing;
|
|
6147
|
+
try {
|
|
6148
|
+
existing = await fsp3.readFile(file, "utf8");
|
|
6149
|
+
} catch {
|
|
6150
|
+
return 0;
|
|
6151
|
+
}
|
|
6152
|
+
const needle = query.toLowerCase();
|
|
6153
|
+
const idMatcher = /mem_\d+_\w+/;
|
|
6154
|
+
let removed = 0;
|
|
6155
|
+
const lines = existing.split("\n").filter((line) => {
|
|
6156
|
+
const trimmed = line.trim();
|
|
6157
|
+
if (!trimmed.startsWith("- ")) return true;
|
|
6158
|
+
if (idMatcher.test(query)) {
|
|
6159
|
+
const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
|
|
6160
|
+
if (entryIdMatch && entryIdMatch[0] === query) {
|
|
6161
|
+
removed++;
|
|
6162
|
+
return false;
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
if (trimmed.toLowerCase().includes(needle)) {
|
|
6166
|
+
removed++;
|
|
6167
|
+
return false;
|
|
6168
|
+
}
|
|
6169
|
+
return true;
|
|
6170
|
+
});
|
|
6171
|
+
if (removed > 0) {
|
|
6172
|
+
if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
|
|
6173
|
+
await atomicWrite(file, "");
|
|
6174
|
+
} else {
|
|
6175
|
+
await atomicWrite(file, lines.join("\n"));
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
return removed;
|
|
6179
|
+
}
|
|
6180
|
+
async readAll(scope, filePath) {
|
|
6181
|
+
const file = this.resolveFile(filePath, scope);
|
|
6182
|
+
try {
|
|
6183
|
+
return await fsp3.readFile(file, "utf8");
|
|
6184
|
+
} catch {
|
|
6185
|
+
return "";
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6188
|
+
async list(scope, filePath, limit) {
|
|
6189
|
+
const raw = await this.readAll(scope, filePath);
|
|
6190
|
+
if (!raw.trim()) return [];
|
|
6191
|
+
const entries = parseEntries(raw, scope);
|
|
6192
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6193
|
+
}
|
|
6194
|
+
async search(scope, query, filePath, limit) {
|
|
6195
|
+
const entries = await this.list(scope, filePath);
|
|
6196
|
+
const needle = query.toLowerCase().split(/\s+/);
|
|
6197
|
+
const scored = entries.map((e) => {
|
|
6198
|
+
const words = e.text.toLowerCase().split(/\s+/);
|
|
6199
|
+
let score = 0;
|
|
6200
|
+
for (const n of needle) {
|
|
6201
|
+
if (words.some((w) => w.includes(n))) score += 1;
|
|
6202
|
+
if (e.tags?.some((t2) => t2.toLowerCase().includes(n))) score += 2;
|
|
6203
|
+
}
|
|
6204
|
+
return { entry: e, score };
|
|
6205
|
+
});
|
|
6206
|
+
scored.sort((a, b) => b.score - a.score);
|
|
6207
|
+
const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
|
|
6208
|
+
return limit ? matched.slice(0, limit) : matched;
|
|
6209
|
+
}
|
|
6210
|
+
async clear(scope, filePath) {
|
|
6211
|
+
const file = this.resolveFile(filePath, scope);
|
|
6212
|
+
await atomicWrite(file, "");
|
|
6213
|
+
}
|
|
6214
|
+
async consolidate(scope, filePath) {
|
|
6215
|
+
const file = this.resolveFile(filePath, scope);
|
|
6216
|
+
let existing;
|
|
6217
|
+
try {
|
|
6218
|
+
existing = await fsp3.readFile(file, "utf8");
|
|
6219
|
+
} catch {
|
|
6220
|
+
return 0;
|
|
6221
|
+
}
|
|
6222
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6223
|
+
let removed = 0;
|
|
6224
|
+
const lines = existing.split("\n").filter((line) => {
|
|
6225
|
+
const trimmed = line.trim();
|
|
6226
|
+
if (!trimmed.startsWith("- ")) return true;
|
|
6227
|
+
const norm = trimmed.replace(/\[[^\]]+\]/g, "").replace(/\bmem_\d+_\w+\s*/, "").replace(/#[\w-]+/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
6228
|
+
if (seen.has(norm)) {
|
|
6229
|
+
removed++;
|
|
6230
|
+
return false;
|
|
6231
|
+
}
|
|
6232
|
+
seen.add(norm);
|
|
6233
|
+
return true;
|
|
6234
|
+
});
|
|
6235
|
+
const next = lines.join("\n");
|
|
6236
|
+
const backup = `${file}.bak.${Date.now()}`;
|
|
6237
|
+
try {
|
|
6238
|
+
await fsp3.copyFile(file, backup);
|
|
6239
|
+
} catch {
|
|
6240
|
+
}
|
|
6241
|
+
try {
|
|
6242
|
+
await atomicWrite(file, next);
|
|
6243
|
+
} catch {
|
|
6244
|
+
return 0;
|
|
6245
|
+
}
|
|
6246
|
+
return removed;
|
|
6247
|
+
}
|
|
6248
|
+
};
|
|
6249
|
+
function parseEntries(raw, scope = "project-memory") {
|
|
6250
|
+
const entries = [];
|
|
6251
|
+
for (const line of raw.split("\n")) {
|
|
6252
|
+
const entry = lineToEntry(line, scope);
|
|
6253
|
+
if (entry) entries.push(entry);
|
|
6254
|
+
}
|
|
6255
|
+
return entries.reverse();
|
|
6256
|
+
}
|
|
5997
6257
|
|
|
5998
6258
|
// src/storage/memory-store.ts
|
|
5999
|
-
init_atomic_write();
|
|
6000
6259
|
var MAX_BYTES_TOTAL = 32e3;
|
|
6001
6260
|
var DefaultMemoryStore = class {
|
|
6002
6261
|
files;
|
|
6262
|
+
events;
|
|
6263
|
+
backend;
|
|
6003
6264
|
/**
|
|
6004
6265
|
* Per-scope serialization queue. `remember` / `forget` / `consolidate` /
|
|
6005
6266
|
* `clear` are read-modify-write against a single file; without a lock,
|
|
@@ -6016,12 +6277,29 @@ var DefaultMemoryStore = class {
|
|
|
6016
6277
|
writeChain = /* @__PURE__ */ new Map();
|
|
6017
6278
|
/** Last write error per scope — surfaced as warnings on the next readAll(). */
|
|
6018
6279
|
writeErrors = /* @__PURE__ */ new Map();
|
|
6280
|
+
/**
|
|
6281
|
+
* When the global root is a temporary directory (opencode, CI sandboxes),
|
|
6282
|
+
* memory files are also mirrored to the project tree so they survive
|
|
6283
|
+
* session cleanup. The primary path stays in the temp root for isolation;
|
|
6284
|
+
* this backup ensures memory is never lost.
|
|
6285
|
+
*/
|
|
6286
|
+
persistBackup;
|
|
6287
|
+
backupDir;
|
|
6019
6288
|
constructor(opts) {
|
|
6020
6289
|
this.files = {
|
|
6021
6290
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
6022
6291
|
"project-memory": opts.paths.projectMemory,
|
|
6023
6292
|
"user-memory": opts.paths.globalMemory
|
|
6024
6293
|
};
|
|
6294
|
+
this.events = opts.events;
|
|
6295
|
+
this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
|
|
6296
|
+
const root = opts.paths.globalRoot.toLowerCase();
|
|
6297
|
+
this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
|
|
6298
|
+
this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
|
|
6299
|
+
}
|
|
6300
|
+
/** Expose the backend for plugin introspection and advanced queries. */
|
|
6301
|
+
getBackend() {
|
|
6302
|
+
return this.backend;
|
|
6025
6303
|
}
|
|
6026
6304
|
async runSerialized(scope, work) {
|
|
6027
6305
|
const prior = this.writeChain.get(scope) ?? Promise.resolve();
|
|
@@ -6048,7 +6326,7 @@ var DefaultMemoryStore = class {
|
|
|
6048
6326
|
if (writeErr2) {
|
|
6049
6327
|
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
|
|
6050
6328
|
}
|
|
6051
|
-
const body = await this.
|
|
6329
|
+
const body = await this.backend.readAll(scope, this.files[scope]);
|
|
6052
6330
|
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
6053
6331
|
|
|
6054
6332
|
${body.trim()}`);
|
|
@@ -6056,117 +6334,214 @@ ${body.trim()}`);
|
|
|
6056
6334
|
return parts.join("\n\n");
|
|
6057
6335
|
}
|
|
6058
6336
|
async read(scope) {
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6337
|
+
return this.backend.readAll(scope, this.files[scope]);
|
|
6338
|
+
}
|
|
6339
|
+
/**
|
|
6340
|
+
* List entries from a scope, newest first. Delegates to the backend
|
|
6341
|
+
* so graph/semantic backends can return enriched or filtered results.
|
|
6342
|
+
*/
|
|
6343
|
+
async list(scope = "project-memory", limit) {
|
|
6344
|
+
return this.backend.list(scope, this.files[scope], limit);
|
|
6345
|
+
}
|
|
6346
|
+
/**
|
|
6347
|
+
* Find memories related to the given text via graph traversal.
|
|
6348
|
+
* Falls back to content search when no graph backend is available.
|
|
6349
|
+
*/
|
|
6350
|
+
async findRelated(text, scope = "project-memory", limit = 5) {
|
|
6351
|
+
if (this.backend.findRelated) {
|
|
6352
|
+
return this.backend.findRelated(scope, this.files[scope], text, limit);
|
|
6063
6353
|
}
|
|
6354
|
+
return this.search(text, scope, limit);
|
|
6355
|
+
}
|
|
6356
|
+
async search(query, scope = "project-memory", limit) {
|
|
6357
|
+
return this.backend.search(scope, query, this.files[scope], limit);
|
|
6064
6358
|
}
|
|
6065
|
-
async remember(text, scope = "project-memory") {
|
|
6359
|
+
async remember(text, scope = "project-memory", metadata) {
|
|
6360
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
6066
6361
|
return this.runSerialized(scope, async () => {
|
|
6067
|
-
const
|
|
6068
|
-
await
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
`;
|
|
6079
|
-
const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
|
|
6080
|
-
${entry}`;
|
|
6081
|
-
await atomicWrite(file, next);
|
|
6082
|
-
const buf = Buffer.byteLength(next, "utf8");
|
|
6083
|
-
if (buf > MAX_BYTES_TOTAL) {
|
|
6084
|
-
await this.consolidateUnsafe(scope);
|
|
6362
|
+
const entry = { scope, text, ts, ...metadata };
|
|
6363
|
+
await this.backend.remember(scope, entry, this.files[scope]);
|
|
6364
|
+
const raw = await this.backend.readAll(scope, this.files[scope]);
|
|
6365
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
|
|
6366
|
+
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
6367
|
+
if (removed > 0) {
|
|
6368
|
+
this.events?.emit("memory.consolidated", {
|
|
6369
|
+
scope,
|
|
6370
|
+
removed
|
|
6371
|
+
});
|
|
6372
|
+
}
|
|
6085
6373
|
}
|
|
6374
|
+
await this.mirrorBackup(scope);
|
|
6375
|
+
this.events?.emit("memory.remembered", {
|
|
6376
|
+
scope,
|
|
6377
|
+
text,
|
|
6378
|
+
ts,
|
|
6379
|
+
type: entry.type,
|
|
6380
|
+
tags: entry.tags,
|
|
6381
|
+
priority: entry.priority
|
|
6382
|
+
});
|
|
6086
6383
|
});
|
|
6087
6384
|
}
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
const
|
|
6100
|
-
const
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
const
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
if (
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6385
|
+
/**
|
|
6386
|
+
* Score and rank memories by relevance to the current context.
|
|
6387
|
+
* Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
|
|
6388
|
+
*/
|
|
6389
|
+
async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
|
|
6390
|
+
const all = await this.list(scope);
|
|
6391
|
+
if (all.length === 0) return [];
|
|
6392
|
+
const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
6393
|
+
const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
|
|
6394
|
+
const toolWords = (ctx.toolNames ?? []).flatMap((t2) => t2.toLowerCase().split("_"));
|
|
6395
|
+
const now = Date.now();
|
|
6396
|
+
const scored = [];
|
|
6397
|
+
for (const entry of all) {
|
|
6398
|
+
let score = 0;
|
|
6399
|
+
const reasons = [];
|
|
6400
|
+
const textLower = entry.text.toLowerCase();
|
|
6401
|
+
const tagsLower = (entry.tags ?? []).map((t2) => t2.toLowerCase());
|
|
6402
|
+
let taskHits = 0;
|
|
6403
|
+
for (const w of taskWords) {
|
|
6404
|
+
if (textLower.includes(w)) {
|
|
6405
|
+
taskHits++;
|
|
6406
|
+
score += 2;
|
|
6407
|
+
}
|
|
6408
|
+
if (tagsLower.some((t2) => t2.includes(w))) {
|
|
6409
|
+
taskHits++;
|
|
6410
|
+
score += 3;
|
|
6114
6411
|
}
|
|
6115
6412
|
}
|
|
6116
|
-
if (
|
|
6117
|
-
|
|
6118
|
-
|
|
6413
|
+
if (taskHits > 0) reasons.push(`task match (${taskHits})`);
|
|
6414
|
+
let skillHits = 0;
|
|
6415
|
+
for (const w of skillWords) {
|
|
6416
|
+
if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t2) => t2.includes(w)))) {
|
|
6417
|
+
skillHits++;
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6420
|
+
score += skillHits;
|
|
6421
|
+
if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
|
|
6422
|
+
for (const w of toolWords) {
|
|
6423
|
+
if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t2) => t2.includes(w)))) {
|
|
6424
|
+
score += 1;
|
|
6425
|
+
reasons.push(`tool mention: ${w}`);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
switch (entry.priority) {
|
|
6429
|
+
case "critical":
|
|
6430
|
+
score += 5;
|
|
6431
|
+
reasons.push("critical");
|
|
6432
|
+
break;
|
|
6433
|
+
case "high":
|
|
6434
|
+
score += 3;
|
|
6435
|
+
reasons.push("high priority");
|
|
6436
|
+
break;
|
|
6437
|
+
case "medium":
|
|
6438
|
+
score += 1;
|
|
6439
|
+
break;
|
|
6440
|
+
case "low":
|
|
6441
|
+
score -= 2;
|
|
6442
|
+
reasons.push("low priority");
|
|
6443
|
+
break;
|
|
6444
|
+
}
|
|
6445
|
+
switch (entry.type) {
|
|
6446
|
+
case "decision":
|
|
6447
|
+
score += 2;
|
|
6448
|
+
reasons.push("decision");
|
|
6449
|
+
break;
|
|
6450
|
+
case "convention":
|
|
6451
|
+
score += 2;
|
|
6452
|
+
reasons.push("convention");
|
|
6453
|
+
break;
|
|
6454
|
+
case "anti_pattern":
|
|
6455
|
+
score += 3;
|
|
6456
|
+
reasons.push("anti-pattern");
|
|
6457
|
+
break;
|
|
6458
|
+
case "preference":
|
|
6459
|
+
score += 1;
|
|
6460
|
+
reasons.push("preference");
|
|
6461
|
+
break;
|
|
6462
|
+
}
|
|
6463
|
+
const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
|
|
6464
|
+
if (ageDays < 1) score += 1;
|
|
6465
|
+
else if (ageDays > 30) score -= 1;
|
|
6466
|
+
if (entry.confidence !== void 0 && entry.confidence < 0.5) {
|
|
6467
|
+
score -= 2;
|
|
6468
|
+
reasons.push("low confidence");
|
|
6469
|
+
}
|
|
6470
|
+
if (entry.lastAccessed) {
|
|
6471
|
+
const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
|
|
6472
|
+
if (hoursSinceAccess < 1) score -= 1;
|
|
6473
|
+
}
|
|
6474
|
+
if (score > 0) {
|
|
6475
|
+
scored.push({
|
|
6476
|
+
...entry,
|
|
6477
|
+
score,
|
|
6478
|
+
matchReason: reasons.join(", ") || "keyword match"
|
|
6479
|
+
});
|
|
6119
6480
|
}
|
|
6120
|
-
return true;
|
|
6121
|
-
});
|
|
6122
|
-
if (removed > 0) {
|
|
6123
|
-
await atomicWrite(file, lines.join("\n"));
|
|
6124
6481
|
}
|
|
6125
|
-
|
|
6482
|
+
scored.sort((a, b) => b.score - a.score);
|
|
6483
|
+
const threshold = 2;
|
|
6484
|
+
const relevant = scored.filter(
|
|
6485
|
+
(s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
|
|
6486
|
+
);
|
|
6487
|
+
return relevant.slice(0, Math.min(limit, 15));
|
|
6126
6488
|
}
|
|
6127
|
-
async
|
|
6128
|
-
return this.runSerialized(scope, async () =>
|
|
6489
|
+
async forget(query, scope = "project-memory") {
|
|
6490
|
+
return this.runSerialized(scope, async () => {
|
|
6491
|
+
const removed = await this.backend.forget(scope, query, this.files[scope]);
|
|
6492
|
+
if (removed > 0) {
|
|
6493
|
+
this.events?.emit("memory.forgotten", {
|
|
6494
|
+
scope,
|
|
6495
|
+
query,
|
|
6496
|
+
removed
|
|
6497
|
+
});
|
|
6498
|
+
await this.mirrorBackup(scope);
|
|
6499
|
+
}
|
|
6500
|
+
return removed;
|
|
6501
|
+
});
|
|
6129
6502
|
}
|
|
6130
|
-
async
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
const trimmed = line.trim();
|
|
6141
|
-
if (!trimmed.startsWith("- ")) return true;
|
|
6142
|
-
const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
|
|
6143
|
-
if (seen.has(norm)) return false;
|
|
6144
|
-
seen.add(norm);
|
|
6145
|
-
return true;
|
|
6503
|
+
async consolidate(scope) {
|
|
6504
|
+
return this.runSerialized(scope, async () => {
|
|
6505
|
+
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
6506
|
+
if (removed > 0) {
|
|
6507
|
+
this.events?.emit("memory.consolidated", {
|
|
6508
|
+
scope,
|
|
6509
|
+
removed
|
|
6510
|
+
});
|
|
6511
|
+
await this.mirrorBackup(scope);
|
|
6512
|
+
}
|
|
6146
6513
|
});
|
|
6147
|
-
const next = lines.join("\n");
|
|
6148
|
-
const backup = `${file}.bak.${Date.now()}`;
|
|
6149
|
-
try {
|
|
6150
|
-
await fsp3.copyFile(file, backup);
|
|
6151
|
-
} catch {
|
|
6152
|
-
}
|
|
6153
|
-
try {
|
|
6154
|
-
await atomicWrite(file, next);
|
|
6155
|
-
} catch {
|
|
6156
|
-
return;
|
|
6157
|
-
}
|
|
6158
6514
|
}
|
|
6159
6515
|
async clear(scope) {
|
|
6160
6516
|
if (scope) {
|
|
6161
|
-
await this.runSerialized(scope, async () =>
|
|
6517
|
+
await this.runSerialized(scope, async () => {
|
|
6518
|
+
await this.backend.clear(scope, this.files[scope]);
|
|
6519
|
+
this.events?.emit("memory.cleared", { scope });
|
|
6520
|
+
await this.mirrorBackup(scope);
|
|
6521
|
+
});
|
|
6162
6522
|
return;
|
|
6163
6523
|
}
|
|
6164
6524
|
await Promise.all(
|
|
6165
6525
|
["project-agents", "project-memory", "user-memory"].map(
|
|
6166
|
-
(s) => this.runSerialized(s, async () =>
|
|
6526
|
+
(s) => this.runSerialized(s, async () => {
|
|
6527
|
+
await this.backend.clear(s, this.files[s]);
|
|
6528
|
+
this.events?.emit("memory.cleared", { scope: s });
|
|
6529
|
+
await this.mirrorBackup(s);
|
|
6530
|
+
})
|
|
6167
6531
|
)
|
|
6168
6532
|
);
|
|
6169
6533
|
}
|
|
6534
|
+
/** Mirror current memory content to the persistent backup directory. */
|
|
6535
|
+
async mirrorBackup(scope) {
|
|
6536
|
+
if (!this.persistBackup || scope === "project-agents") return;
|
|
6537
|
+
try {
|
|
6538
|
+
const content = await this.backend.readAll(scope, this.files[scope]);
|
|
6539
|
+
const { writeFile: writeFile11, mkdir: mkdir17 } = await import('fs/promises');
|
|
6540
|
+
await mkdir17(this.backupDir, { recursive: true });
|
|
6541
|
+
await writeFile11(`${this.backupDir}/${scope}.md`, content, "utf8");
|
|
6542
|
+
} catch {
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6170
6545
|
};
|
|
6171
6546
|
function labelOf(scope) {
|
|
6172
6547
|
switch (scope) {
|
|
@@ -6321,6 +6696,13 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
6321
6696
|
function envBool(v) {
|
|
6322
6697
|
return !/^(0|false|no|off)$/i.test(v.trim());
|
|
6323
6698
|
}
|
|
6699
|
+
function envBoolOptional(v) {
|
|
6700
|
+
return v !== void 0 && envBool(v);
|
|
6701
|
+
}
|
|
6702
|
+
var LOG_LEVELS2 = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
|
|
6703
|
+
function envLogLevel(v) {
|
|
6704
|
+
return LOG_LEVELS2.has(v) ? v : "info";
|
|
6705
|
+
}
|
|
6324
6706
|
var ENV_MAP = {
|
|
6325
6707
|
WRONGSTACK_PROVIDER: (c, v) => {
|
|
6326
6708
|
c.provider = v;
|
|
@@ -6344,7 +6726,7 @@ var ENV_MAP = {
|
|
|
6344
6726
|
},
|
|
6345
6727
|
WRONGSTACK_LOG_LEVEL: (c, v) => {
|
|
6346
6728
|
if (!c.log) c.log = { level: "info" };
|
|
6347
|
-
c.log.level = v;
|
|
6729
|
+
c.log.level = envLogLevel(v);
|
|
6348
6730
|
},
|
|
6349
6731
|
WRONGSTACK_INDEX_ON_START: (c, v) => {
|
|
6350
6732
|
c.indexing = { ...defaultIndexing, ...c.indexing, onSessionStart: envBool(v) };
|
|
@@ -6386,7 +6768,7 @@ function deepMerge2(base, patch) {
|
|
|
6386
6768
|
out[k] = [.../* @__PURE__ */ new Set([...existing, ...v])];
|
|
6387
6769
|
} else {
|
|
6388
6770
|
out[k] = v;
|
|
6389
|
-
if (process.env.WRONGSTACK_DEBUG_CONFIG) {
|
|
6771
|
+
if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {
|
|
6390
6772
|
console.warn(
|
|
6391
6773
|
`[config] Non-primitive array for "${k}" replaced (global + local config merge). Global entries: ${existing?.length ?? 0}, local entries: ${v.length}.`
|
|
6392
6774
|
);
|
|
@@ -6980,7 +7362,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
|
6980
7362
|
let pending = null;
|
|
6981
7363
|
let writeChain = Promise.resolve();
|
|
6982
7364
|
const enqueueWrite = (todos) => {
|
|
6983
|
-
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos))
|
|
7365
|
+
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
|
|
7366
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7367
|
+
console.error(`[TodosCheckpoint] save failed for session ${sessionId}: ${msg}`);
|
|
7368
|
+
});
|
|
6984
7369
|
return writeChain;
|
|
6985
7370
|
};
|
|
6986
7371
|
const flush = () => {
|
|
@@ -7284,7 +7669,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
|
7284
7669
|
}
|
|
7285
7670
|
const lock = {
|
|
7286
7671
|
pid: processId,
|
|
7287
|
-
hostname:
|
|
7672
|
+
hostname: hostname(),
|
|
7288
7673
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7289
7674
|
};
|
|
7290
7675
|
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
@@ -7954,6 +8339,7 @@ function parseDescription(raw) {
|
|
|
7954
8339
|
}
|
|
7955
8340
|
return { trigger, scope };
|
|
7956
8341
|
}
|
|
8342
|
+
var STREAM_DRAIN_TIMEOUT_MS = 500;
|
|
7957
8343
|
function buildResponse(state) {
|
|
7958
8344
|
const content = [];
|
|
7959
8345
|
for (const b of state.blockOrder) {
|
|
@@ -8166,7 +8552,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
8166
8552
|
await Promise.race([
|
|
8167
8553
|
Promise.resolve(iter.return?.()),
|
|
8168
8554
|
new Promise((resolve13) => {
|
|
8169
|
-
drainTimer = setTimeout(resolve13,
|
|
8555
|
+
drainTimer = setTimeout(resolve13, STREAM_DRAIN_TIMEOUT_MS);
|
|
8170
8556
|
})
|
|
8171
8557
|
]);
|
|
8172
8558
|
} finally {
|
|
@@ -8505,12 +8891,6 @@ var IntelligentCompactor = class {
|
|
|
8505
8891
|
};
|
|
8506
8892
|
|
|
8507
8893
|
// src/models/llm-selector.ts
|
|
8508
|
-
function expectDefined11(value) {
|
|
8509
|
-
if (value === null || value === void 0) {
|
|
8510
|
-
throw new Error("Expected value to be defined");
|
|
8511
|
-
}
|
|
8512
|
-
return value;
|
|
8513
|
-
}
|
|
8514
8894
|
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.
|
|
8515
8895
|
|
|
8516
8896
|
Output a JSON object with this structure:
|
|
@@ -8551,7 +8931,7 @@ function formatMessages(messages, maxChars = 8e3) {
|
|
|
8551
8931
|
const lines = [];
|
|
8552
8932
|
let used = 0;
|
|
8553
8933
|
for (let i = 0; i < messages.length; i++) {
|
|
8554
|
-
const m =
|
|
8934
|
+
const m = expectDefined(messages[i]);
|
|
8555
8935
|
const role = m.role.padEnd(10, " ");
|
|
8556
8936
|
let text;
|
|
8557
8937
|
if (typeof m.content === "string") {
|
|
@@ -8616,7 +8996,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
8616
8996
|
let tokenCount = 0;
|
|
8617
8997
|
let startIdx = 0;
|
|
8618
8998
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
8619
|
-
const m =
|
|
8999
|
+
const m = expectDefined(messages[i]);
|
|
8620
9000
|
const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
|
|
8621
9001
|
(acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
|
|
8622
9002
|
0
|
|
@@ -9904,8 +10284,8 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
9904
10284
|
await saveGoal(this.goalPath, abandoned);
|
|
9905
10285
|
}
|
|
9906
10286
|
try {
|
|
9907
|
-
const { unlink:
|
|
9908
|
-
await
|
|
10287
|
+
const { unlink: unlink14 } = await import('fs/promises');
|
|
10288
|
+
await unlink14(this.goalPath);
|
|
9909
10289
|
} catch {
|
|
9910
10290
|
}
|
|
9911
10291
|
this.opts.onEternalStop?.();
|
|
@@ -9928,6 +10308,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
9928
10308
|
};
|
|
9929
10309
|
|
|
9930
10310
|
// src/coordination/subagent-budget.ts
|
|
10311
|
+
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
9931
10312
|
var BudgetExceededError = class extends Error {
|
|
9932
10313
|
kind;
|
|
9933
10314
|
limit;
|
|
@@ -10283,15 +10664,7 @@ function makeAgentSubagentRunner(opts) {
|
|
|
10283
10664
|
let budgetError = null;
|
|
10284
10665
|
const onBudgetError = (err) => {
|
|
10285
10666
|
if (err instanceof BudgetThresholdSignal) {
|
|
10286
|
-
|
|
10287
|
-
if (decision === "stop") {
|
|
10288
|
-
budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
|
|
10289
|
-
aborter.abort();
|
|
10290
|
-
}
|
|
10291
|
-
}).catch(() => {
|
|
10292
|
-
budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
|
|
10293
|
-
aborter.abort();
|
|
10294
|
-
});
|
|
10667
|
+
budgetError = err;
|
|
10295
10668
|
return;
|
|
10296
10669
|
}
|
|
10297
10670
|
aborter.abort();
|
|
@@ -10399,15 +10772,23 @@ function makeAgentSubagentRunner(opts) {
|
|
|
10399
10772
|
}
|
|
10400
10773
|
if (budgetError) {
|
|
10401
10774
|
if ("decision" in budgetError) {
|
|
10402
|
-
|
|
10403
|
-
|
|
10775
|
+
try {
|
|
10776
|
+
const decision = await budgetError.decision;
|
|
10777
|
+
if (decision === "stop") {
|
|
10778
|
+
budgetError = new BudgetExceededError(
|
|
10779
|
+
budgetError.kind,
|
|
10780
|
+
budgetError.limit,
|
|
10781
|
+
budgetError.used
|
|
10782
|
+
);
|
|
10783
|
+
} else {
|
|
10784
|
+
budgetError = null;
|
|
10785
|
+
}
|
|
10786
|
+
} catch {
|
|
10404
10787
|
budgetError = new BudgetExceededError(
|
|
10405
10788
|
budgetError.kind,
|
|
10406
10789
|
budgetError.limit,
|
|
10407
10790
|
budgetError.used
|
|
10408
10791
|
);
|
|
10409
|
-
} else {
|
|
10410
|
-
budgetError = null;
|
|
10411
10792
|
}
|
|
10412
10793
|
}
|
|
10413
10794
|
if (budgetError) throw budgetError;
|
|
@@ -12729,16 +13110,17 @@ Do not add prose, markdown, or code fences.`;
|
|
|
12729
13110
|
const raw = (await complete(prompt)).trim();
|
|
12730
13111
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
12731
13112
|
if (!match) return null;
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
13113
|
+
const parsed = safeParse(
|
|
13114
|
+
match[0]
|
|
13115
|
+
);
|
|
13116
|
+
if (!parsed.ok || !parsed.value || typeof parsed.value.role !== "string") return null;
|
|
13117
|
+
const role = parsed.value.role.trim();
|
|
13118
|
+
const valid = candidates.some((c) => c.role === role);
|
|
13119
|
+
if (!valid) return null;
|
|
13120
|
+
return {
|
|
13121
|
+
role,
|
|
13122
|
+
reason: typeof parsed.value.reason === "string" ? parsed.value.reason : void 0
|
|
13123
|
+
};
|
|
12742
13124
|
};
|
|
12743
13125
|
}
|
|
12744
13126
|
|
|
@@ -13693,15 +14075,38 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13693
14075
|
}
|
|
13694
14076
|
const start = Date.now();
|
|
13695
14077
|
let timer = null;
|
|
14078
|
+
let preemptedForLimit = null;
|
|
13696
14079
|
const timeoutPromise = new Promise((_, reject) => {
|
|
13697
14080
|
const armFor = (ms) => {
|
|
13698
14081
|
if (timer) clearTimeout(timer);
|
|
13699
14082
|
timer = setTimeout(onTick, Math.max(0, ms));
|
|
13700
14083
|
};
|
|
13701
14084
|
const scheduleNext = () => {
|
|
13702
|
-
const
|
|
14085
|
+
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
14086
|
+
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
13703
14087
|
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
13704
|
-
|
|
14088
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedForLimit === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * TIMEOUT_PREEMPT_FRACTION - (Date.now() - start);
|
|
14089
|
+
armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
|
|
14090
|
+
};
|
|
14091
|
+
const negotiateTimeout = async (used, limit) => {
|
|
14092
|
+
const handler = budget.onThreshold;
|
|
14093
|
+
if (!handler) return "stop";
|
|
14094
|
+
const result = handler({
|
|
14095
|
+
kind: "timeout",
|
|
14096
|
+
used,
|
|
14097
|
+
limit,
|
|
14098
|
+
requestDecision: () => new Promise((resolveDecision) => {
|
|
14099
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
14100
|
+
kind: "timeout",
|
|
14101
|
+
used,
|
|
14102
|
+
limit,
|
|
14103
|
+
timeoutMs: 6e4,
|
|
14104
|
+
extend: (extra) => resolveDecision({ extend: extra }),
|
|
14105
|
+
deny: () => resolveDecision("stop")
|
|
14106
|
+
});
|
|
14107
|
+
})
|
|
14108
|
+
});
|
|
14109
|
+
return typeof result === "string" ? result : await result;
|
|
13705
14110
|
};
|
|
13706
14111
|
const onTick = async () => {
|
|
13707
14112
|
const elapsed = Date.now() - start;
|
|
@@ -13714,6 +14119,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13714
14119
|
reject(new BudgetExceededError("timeout", idleLimit ?? 0, budget.idleMs()));
|
|
13715
14120
|
return;
|
|
13716
14121
|
}
|
|
14122
|
+
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptedForLimit !== wallLimit && elapsed >= wallLimit * TIMEOUT_PREEMPT_FRACTION) {
|
|
14123
|
+
try {
|
|
14124
|
+
const decision = await negotiateTimeout(elapsed, wallLimit);
|
|
14125
|
+
if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
|
|
14126
|
+
budget.limits.timeoutMs = decision.extend.timeoutMs;
|
|
14127
|
+
preemptedForLimit = null;
|
|
14128
|
+
} else {
|
|
14129
|
+
preemptedForLimit = wallLimit;
|
|
14130
|
+
}
|
|
14131
|
+
} catch {
|
|
14132
|
+
preemptedForLimit = wallLimit;
|
|
14133
|
+
}
|
|
14134
|
+
scheduleNext();
|
|
14135
|
+
return;
|
|
14136
|
+
}
|
|
13717
14137
|
if (!wallExceeded) {
|
|
13718
14138
|
scheduleNext();
|
|
13719
14139
|
return;
|
|
@@ -13725,28 +14145,15 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13725
14145
|
return;
|
|
13726
14146
|
}
|
|
13727
14147
|
try {
|
|
13728
|
-
const
|
|
13729
|
-
kind: "timeout",
|
|
13730
|
-
used: elapsed,
|
|
13731
|
-
limit,
|
|
13732
|
-
requestDecision: () => new Promise((resolveDecision) => {
|
|
13733
|
-
budget._events?.emit("budget.threshold_reached", {
|
|
13734
|
-
kind: "timeout",
|
|
13735
|
-
used: elapsed,
|
|
13736
|
-
limit,
|
|
13737
|
-
timeoutMs: 6e4,
|
|
13738
|
-
extend: (extra) => resolveDecision({ extend: extra }),
|
|
13739
|
-
deny: () => resolveDecision("stop")
|
|
13740
|
-
});
|
|
13741
|
-
})
|
|
13742
|
-
});
|
|
13743
|
-
const decision = typeof result === "string" ? result : await result;
|
|
14148
|
+
const decision = await negotiateTimeout(elapsed, limit);
|
|
13744
14149
|
if (decision === "continue" || decision === "throw" || decision === "stop") {
|
|
14150
|
+
preemptedForLimit = null;
|
|
13745
14151
|
armFor(Math.max(1e3, limit));
|
|
13746
14152
|
return;
|
|
13747
14153
|
}
|
|
13748
14154
|
if (decision.extend.timeoutMs !== void 0) {
|
|
13749
14155
|
budget.limits.timeoutMs = decision.extend.timeoutMs;
|
|
14156
|
+
preemptedForLimit = null;
|
|
13750
14157
|
scheduleNext();
|
|
13751
14158
|
return;
|
|
13752
14159
|
}
|
|
@@ -13877,12 +14284,6 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13877
14284
|
};
|
|
13878
14285
|
|
|
13879
14286
|
// src/execution/parallel-eternal-engine.ts
|
|
13880
|
-
function expectDefined12(value) {
|
|
13881
|
-
if (value === null || value === void 0) {
|
|
13882
|
-
throw new Error("Expected value to be defined");
|
|
13883
|
-
}
|
|
13884
|
-
return value;
|
|
13885
|
-
}
|
|
13886
14287
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
13887
14288
|
var ParallelEternalEngine = class {
|
|
13888
14289
|
constructor(opts) {
|
|
@@ -14038,7 +14439,7 @@ var ParallelEternalEngine = class {
|
|
|
14038
14439
|
// Fan-out
|
|
14039
14440
|
// -------------------------------------------------------------------------
|
|
14040
14441
|
async fanOut(goal, tasks) {
|
|
14041
|
-
const coordinator =
|
|
14442
|
+
const coordinator = expectDefined(this.coordinator);
|
|
14042
14443
|
const slotCount = Math.min(this.slots, tasks.length);
|
|
14043
14444
|
const routes = this.dispatchEnabled ? await Promise.all(
|
|
14044
14445
|
tasks.slice(0, slotCount).map(
|
|
@@ -14070,7 +14471,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
14070
14471
|
const routeInfo = [];
|
|
14071
14472
|
const spawnPromises = [];
|
|
14072
14473
|
for (let i = 0; i < slotCount; i++) {
|
|
14073
|
-
const task =
|
|
14474
|
+
const task = expectDefined(tasks[i]);
|
|
14074
14475
|
const route = routes[i] ?? null;
|
|
14075
14476
|
const subagentId = `parallel-${this.iterations}-${i}`;
|
|
14076
14477
|
const taskId = randomUUID();
|
|
@@ -14429,7 +14830,9 @@ var CollabSession = class extends EventEmitter {
|
|
|
14429
14830
|
settled = false;
|
|
14430
14831
|
timeoutMs;
|
|
14431
14832
|
cancelled = false;
|
|
14833
|
+
_raceResolved = false;
|
|
14432
14834
|
alerts = [];
|
|
14835
|
+
snapshotWarnings = [];
|
|
14433
14836
|
/** Tracks tool call counts per subagent for progress-based timeout decisions. */
|
|
14434
14837
|
progressBySubagent = /* @__PURE__ */ new Map();
|
|
14435
14838
|
/** Last tool call count when a timeout warning was handled. */
|
|
@@ -14499,10 +14902,19 @@ var CollabSession = class extends EventEmitter {
|
|
|
14499
14902
|
}
|
|
14500
14903
|
for (const filePath of allFiles) {
|
|
14501
14904
|
try {
|
|
14502
|
-
const content = await
|
|
14905
|
+
const [content, stat11] = await Promise.all([
|
|
14906
|
+
fsp3.readFile(filePath, "utf8"),
|
|
14907
|
+
fsp3.stat(filePath)
|
|
14908
|
+
]);
|
|
14503
14909
|
const ext = filePath.split(".").pop() ?? "";
|
|
14504
14910
|
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
14505
|
-
this.snapshot.files.push({
|
|
14911
|
+
this.snapshot.files.push({
|
|
14912
|
+
path: filePath,
|
|
14913
|
+
content,
|
|
14914
|
+
language,
|
|
14915
|
+
snapshotMtimeMs: stat11.mtimeMs,
|
|
14916
|
+
snapshotSizeBytes: stat11.size
|
|
14917
|
+
});
|
|
14506
14918
|
} catch {
|
|
14507
14919
|
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
14508
14920
|
}
|
|
@@ -14515,7 +14927,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
14515
14927
|
* Safe to call multiple times (idempotent after first call).
|
|
14516
14928
|
*/
|
|
14517
14929
|
cancel(reason = "Director cancelled collab session") {
|
|
14518
|
-
if (this.
|
|
14930
|
+
if (this.cancelled) return;
|
|
14519
14931
|
this.cancelled = true;
|
|
14520
14932
|
if (this._timeoutTimer) {
|
|
14521
14933
|
clearTimeout(this._timeoutTimer);
|
|
@@ -14525,7 +14937,11 @@ var CollabSession = class extends EventEmitter {
|
|
|
14525
14937
|
subagentId: this.director.id,
|
|
14526
14938
|
ts: Date.now(),
|
|
14527
14939
|
type: "director.cancel_collab",
|
|
14528
|
-
payload: {
|
|
14940
|
+
payload: {
|
|
14941
|
+
sessionId: this.sessionId,
|
|
14942
|
+
reason,
|
|
14943
|
+
cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14944
|
+
}
|
|
14529
14945
|
});
|
|
14530
14946
|
this.fleetBus.emit({
|
|
14531
14947
|
subagentId: this.director.id,
|
|
@@ -14549,6 +14965,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
14549
14965
|
this.subagentIds.set("critic", criticId);
|
|
14550
14966
|
const timeout = new Promise((_, reject) => {
|
|
14551
14967
|
this._timeoutTimer = setTimeout(() => {
|
|
14968
|
+
if (this._raceResolved) return;
|
|
14552
14969
|
this.cancel("Session-level timeout reached");
|
|
14553
14970
|
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
14554
14971
|
}, this.timeoutMs);
|
|
@@ -14564,6 +14981,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
14564
14981
|
timeout
|
|
14565
14982
|
]);
|
|
14566
14983
|
} catch (err) {
|
|
14984
|
+
this._raceResolved = true;
|
|
14567
14985
|
if (this._timeoutTimer) {
|
|
14568
14986
|
clearTimeout(this._timeoutTimer);
|
|
14569
14987
|
this._timeoutTimer = void 0;
|
|
@@ -14576,7 +14994,9 @@ var CollabSession = class extends EventEmitter {
|
|
|
14576
14994
|
for (const result of results?.flat() ?? []) {
|
|
14577
14995
|
await this.parseAndEmit(result);
|
|
14578
14996
|
}
|
|
14997
|
+
this.snapshotWarnings = await this.checkSnapshotFreshness();
|
|
14579
14998
|
const report = this.assembleReport();
|
|
14999
|
+
this._raceResolved = true;
|
|
14580
15000
|
this.cleanup();
|
|
14581
15001
|
this.emit("session.done", report);
|
|
14582
15002
|
return report;
|
|
@@ -14639,7 +15059,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
14639
15059
|
const defaults = {
|
|
14640
15060
|
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
14641
15061
|
"refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
|
|
14642
|
-
|
|
15062
|
+
critic: { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
|
|
14643
15063
|
};
|
|
14644
15064
|
return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
|
|
14645
15065
|
}
|
|
@@ -14719,7 +15139,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14719
15139
|
}
|
|
14720
15140
|
wireFleetBus() {
|
|
14721
15141
|
const dTool = this.fleetBus.filter("tool.executed", (e) => {
|
|
14722
|
-
this.progressBySubagent.set(
|
|
15142
|
+
this.progressBySubagent.set(
|
|
15143
|
+
e.subagentId,
|
|
15144
|
+
(this.progressBySubagent.get(e.subagentId) ?? 0) + 1
|
|
15145
|
+
);
|
|
14723
15146
|
});
|
|
14724
15147
|
this.disposers.push(dTool);
|
|
14725
15148
|
const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
|
|
@@ -14758,7 +15181,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14758
15181
|
return;
|
|
14759
15182
|
}
|
|
14760
15183
|
this.lastTimeoutProgress.set(e.subagentId, progress);
|
|
14761
|
-
const newLimit = Math.min(
|
|
15184
|
+
const newLimit = Math.min(
|
|
15185
|
+
Math.ceil((payload.timeoutMs ?? payload.limit) * 2),
|
|
15186
|
+
24 * 60 * 6e4
|
|
15187
|
+
);
|
|
14762
15188
|
setImmediate(() => {
|
|
14763
15189
|
payload.extend({ timeoutMs: newLimit });
|
|
14764
15190
|
});
|
|
@@ -14868,15 +15294,18 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14868
15294
|
needs_revision: 1,
|
|
14869
15295
|
reject: 2
|
|
14870
15296
|
};
|
|
14871
|
-
const overallVerdict = evalList.reduce(
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
15297
|
+
const overallVerdict = evalList.reduce((worst, eval_) => {
|
|
15298
|
+
const w = verdictOrder[worst];
|
|
15299
|
+
const c = verdictOrder[eval_.verdict];
|
|
15300
|
+
return c > w ? eval_.verdict : worst;
|
|
15301
|
+
}, "approve");
|
|
15302
|
+
const summary = this.buildMarkdownSummary(
|
|
15303
|
+
bugList,
|
|
15304
|
+
planList,
|
|
15305
|
+
evalList,
|
|
15306
|
+
overallVerdict,
|
|
15307
|
+
disposition
|
|
14878
15308
|
);
|
|
14879
|
-
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
|
|
14880
15309
|
return {
|
|
14881
15310
|
sessionId: this.sessionId,
|
|
14882
15311
|
startedAt: this.snapshot.createdAt,
|
|
@@ -14887,10 +15316,28 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14887
15316
|
refactorPlans: planList,
|
|
14888
15317
|
evaluations: evalList,
|
|
14889
15318
|
alerts: [...this.alerts],
|
|
15319
|
+
...this.snapshotWarnings.length > 0 ? { snapshotWarnings: this.snapshotWarnings } : {},
|
|
14890
15320
|
overallVerdict,
|
|
14891
15321
|
summary
|
|
14892
15322
|
};
|
|
14893
15323
|
}
|
|
15324
|
+
async checkSnapshotFreshness() {
|
|
15325
|
+
const warnings = [];
|
|
15326
|
+
for (const file of this.snapshot.files) {
|
|
15327
|
+
if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
|
|
15328
|
+
try {
|
|
15329
|
+
const stat11 = await fsp3.stat(file.path);
|
|
15330
|
+
const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat11.mtimeMs > file.snapshotMtimeMs + 1;
|
|
15331
|
+
const sizeChanged = file.snapshotSizeBytes !== void 0 && stat11.size !== file.snapshotSizeBytes;
|
|
15332
|
+
if (mtimeChanged || sizeChanged) {
|
|
15333
|
+
warnings.push(`${file.path} changed after the collab snapshot was captured.`);
|
|
15334
|
+
}
|
|
15335
|
+
} catch {
|
|
15336
|
+
warnings.push(`${file.path} could not be checked after the collab snapshot was captured.`);
|
|
15337
|
+
}
|
|
15338
|
+
}
|
|
15339
|
+
return warnings;
|
|
15340
|
+
}
|
|
14894
15341
|
buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
|
|
14895
15342
|
const lines = [
|
|
14896
15343
|
`## Collaborative Debugging Report \u2014 ${this.sessionId}`,
|
|
@@ -14907,10 +15354,19 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14907
15354
|
}
|
|
14908
15355
|
lines.push("");
|
|
14909
15356
|
}
|
|
15357
|
+
if (this.snapshotWarnings.length > 0) {
|
|
15358
|
+
lines.push("### Snapshot Warnings", "");
|
|
15359
|
+
for (const warning of this.snapshotWarnings) {
|
|
15360
|
+
lines.push(`- ${warning}`);
|
|
15361
|
+
}
|
|
15362
|
+
lines.push("");
|
|
15363
|
+
}
|
|
14910
15364
|
if (bugs.length > 0) {
|
|
14911
15365
|
lines.push("### Bugs Found", "");
|
|
14912
15366
|
for (const b of bugs) {
|
|
14913
|
-
lines.push(
|
|
15367
|
+
lines.push(
|
|
15368
|
+
`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
|
|
15369
|
+
);
|
|
14914
15370
|
}
|
|
14915
15371
|
lines.push("");
|
|
14916
15372
|
}
|
|
@@ -15513,6 +15969,7 @@ var FleetBus = class {
|
|
|
15513
15969
|
set.add(handler);
|
|
15514
15970
|
return () => {
|
|
15515
15971
|
set.delete(handler);
|
|
15972
|
+
if (set.size === 0) this.byId.delete(subagentId);
|
|
15516
15973
|
};
|
|
15517
15974
|
}
|
|
15518
15975
|
/** Subscribe to one event type across all subagents. */
|
|
@@ -15525,6 +15982,7 @@ var FleetBus = class {
|
|
|
15525
15982
|
set.add(handler);
|
|
15526
15983
|
return () => {
|
|
15527
15984
|
set.delete(handler);
|
|
15985
|
+
if (set.size === 0) this.byType.delete(type);
|
|
15528
15986
|
};
|
|
15529
15987
|
}
|
|
15530
15988
|
/** Subscribe to literally everything. The fleet roll-up uses this. */
|
|
@@ -15558,6 +16016,11 @@ var FleetBus = class {
|
|
|
15558
16016
|
}
|
|
15559
16017
|
}
|
|
15560
16018
|
}
|
|
16019
|
+
clear() {
|
|
16020
|
+
this.byId.clear();
|
|
16021
|
+
this.byType.clear();
|
|
16022
|
+
this.any.clear();
|
|
16023
|
+
}
|
|
15561
16024
|
};
|
|
15562
16025
|
var FleetUsageAggregator = class {
|
|
15563
16026
|
constructor(bus, priceLookup, metaLookup) {
|
|
@@ -16802,7 +17265,9 @@ var Director = class _Director {
|
|
|
16802
17265
|
let toolUses = 0;
|
|
16803
17266
|
for (const line of targetLines) {
|
|
16804
17267
|
try {
|
|
16805
|
-
const
|
|
17268
|
+
const parsed = safeParse(line);
|
|
17269
|
+
if (!parsed.ok || !parsed.value) continue;
|
|
17270
|
+
const ev = parsed.value;
|
|
16806
17271
|
if (ev.type === "assistant" && typeof ev.text === "string") {
|
|
16807
17272
|
lastAssistantText = ev.text;
|
|
16808
17273
|
} else if (ev.type === "stop" && ev.stopReason) {
|
|
@@ -17116,7 +17581,7 @@ function createDelegateTool(opts) {
|
|
|
17116
17581
|
const offIter = dir.fleet.filter("iteration.started", bump);
|
|
17117
17582
|
const offProgress = dir.fleet.filter("tool.progress", bump);
|
|
17118
17583
|
arm();
|
|
17119
|
-
dir.awaitTasks([taskId]).then((r) => finish(r[0] ?? {
|
|
17584
|
+
dir.awaitTasks([taskId]).then((r) => finish(r[0] ?? { __emptyResult: true })).catch(() => finish({ __timeout: true }));
|
|
17120
17585
|
});
|
|
17121
17586
|
if ("__timeout" in result) {
|
|
17122
17587
|
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
@@ -17141,6 +17606,29 @@ function createDelegateTool(opts) {
|
|
|
17141
17606
|
partial: partial2
|
|
17142
17607
|
};
|
|
17143
17608
|
}
|
|
17609
|
+
if ("__emptyResult" in result) {
|
|
17610
|
+
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
17611
|
+
opts.events?.emit("delegate.completed", {
|
|
17612
|
+
target,
|
|
17613
|
+
task: i.task,
|
|
17614
|
+
ok: false,
|
|
17615
|
+
status: "empty_result",
|
|
17616
|
+
summary: `[${target}] completed without a task result`,
|
|
17617
|
+
durationMs: 0,
|
|
17618
|
+
iterations: partial2?.events ?? 0,
|
|
17619
|
+
toolCalls: partial2?.toolUsesObserved ?? 0,
|
|
17620
|
+
subagentId
|
|
17621
|
+
});
|
|
17622
|
+
return {
|
|
17623
|
+
ok: false,
|
|
17624
|
+
stopReason: "error",
|
|
17625
|
+
error: "Director returned no task result for the delegated task.",
|
|
17626
|
+
hint: "Check fleet state with /fleet status, then retry or reassign the task.",
|
|
17627
|
+
subagentId,
|
|
17628
|
+
taskId,
|
|
17629
|
+
partial: partial2
|
|
17630
|
+
};
|
|
17631
|
+
}
|
|
17144
17632
|
const baseStopReason = result.status === "success" ? "end_turn" : result.status === "timeout" ? "subagent_timeout" : result.status === "stopped" ? "aborted" : "budget_exhausted";
|
|
17145
17633
|
const partial = result.status === "success" ? void 0 : await readSubagentPartial(opts, subagentId);
|
|
17146
17634
|
const errorKind = result.error?.kind;
|
|
@@ -17313,7 +17801,9 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
17313
17801
|
let toolUses = 0;
|
|
17314
17802
|
for (const line of lines) {
|
|
17315
17803
|
try {
|
|
17316
|
-
const
|
|
17804
|
+
const parsed = safeParse(line);
|
|
17805
|
+
if (!parsed.ok || !parsed.value) continue;
|
|
17806
|
+
const ev = parsed.value;
|
|
17317
17807
|
if (ev.type === "tool_use") toolUses += 1;
|
|
17318
17808
|
if (ev.type === "llm_response") {
|
|
17319
17809
|
if (typeof ev.stopReason === "string") lastStopReason = ev.stopReason;
|
|
@@ -17501,8 +17991,8 @@ async function loadProjectModes(modesDir) {
|
|
|
17501
17991
|
for (const entry of entries) {
|
|
17502
17992
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
17503
17993
|
const filePath = path6.join(modesDir, entry);
|
|
17504
|
-
const
|
|
17505
|
-
if (!
|
|
17994
|
+
const stat11 = await fsp3.stat(filePath);
|
|
17995
|
+
if (!stat11.isFile()) continue;
|
|
17506
17996
|
const content = await fsp3.readFile(filePath, "utf8");
|
|
17507
17997
|
const id = path6.basename(entry, path6.extname(entry));
|
|
17508
17998
|
modes.push({
|
|
@@ -18572,12 +19062,6 @@ var TaskGraphStore = class {
|
|
|
18572
19062
|
};
|
|
18573
19063
|
|
|
18574
19064
|
// src/sdd/spec-builder.ts
|
|
18575
|
-
function expectDefined13(value) {
|
|
18576
|
-
if (value === null || value === void 0) {
|
|
18577
|
-
throw new Error("Expected value to be defined");
|
|
18578
|
-
}
|
|
18579
|
-
return value;
|
|
18580
|
-
}
|
|
18581
19065
|
function buildQuestioningPrompt(session, min, max) {
|
|
18582
19066
|
const answered = session.answers.length;
|
|
18583
19067
|
const remaining = Math.max(0, min - answered);
|
|
@@ -18623,7 +19107,7 @@ function buildQuestioningPrompt(session, min, max) {
|
|
|
18623
19107
|
if (answered > 0) {
|
|
18624
19108
|
lines.push("", "**Conversation so far:**");
|
|
18625
19109
|
for (let i = 0; i < answered; i++) {
|
|
18626
|
-
const a =
|
|
19110
|
+
const a = expectDefined(session.answers[i]);
|
|
18627
19111
|
lines.push(``, `Q${i + 1}: ${a.question}`, `A${i + 1}: ${a.answer}`);
|
|
18628
19112
|
}
|
|
18629
19113
|
}
|
|
@@ -19365,12 +19849,6 @@ function truncate2(str, maxLen) {
|
|
|
19365
19849
|
}
|
|
19366
19850
|
|
|
19367
19851
|
// src/sdd/critical-path.ts
|
|
19368
|
-
function expectDefined14(value) {
|
|
19369
|
-
if (value === null || value === void 0) {
|
|
19370
|
-
throw new Error("Expected value to be defined");
|
|
19371
|
-
}
|
|
19372
|
-
return value;
|
|
19373
|
-
}
|
|
19374
19852
|
function analyzeCriticalPath(graph) {
|
|
19375
19853
|
const nodes = Array.from(graph.nodes.values());
|
|
19376
19854
|
const topoOrder = topologicalSort(graph);
|
|
@@ -19446,7 +19924,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
|
|
|
19446
19924
|
const visited = /* @__PURE__ */ new Set();
|
|
19447
19925
|
const queue = [taskId];
|
|
19448
19926
|
while (queue.length > 0) {
|
|
19449
|
-
const current =
|
|
19927
|
+
const current = expectDefined(queue.shift());
|
|
19450
19928
|
const blocked = blocksMap.get(current);
|
|
19451
19929
|
if (!blocked) continue;
|
|
19452
19930
|
for (const id of blocked) {
|
|
@@ -19492,7 +19970,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
19492
19970
|
if (!changed) break;
|
|
19493
19971
|
}
|
|
19494
19972
|
let maxDist = 0;
|
|
19495
|
-
let maxId =
|
|
19973
|
+
let maxId = expectDefined(allIds[0]);
|
|
19496
19974
|
for (const id of allIds) {
|
|
19497
19975
|
const d = dist.get(id) ?? 0;
|
|
19498
19976
|
if (d > maxDist) {
|
|
@@ -19961,12 +20439,6 @@ var SddTaskDecomposer = class {
|
|
|
19961
20439
|
return nodes.some((n) => n.status === "blocked");
|
|
19962
20440
|
}
|
|
19963
20441
|
};
|
|
19964
|
-
function expectDefined15(value) {
|
|
19965
|
-
if (value === null || value === void 0) {
|
|
19966
|
-
throw new Error("Expected value to be defined");
|
|
19967
|
-
}
|
|
19968
|
-
return value;
|
|
19969
|
-
}
|
|
19970
20442
|
var SddParallelRun = class {
|
|
19971
20443
|
constructor(opts) {
|
|
19972
20444
|
this.opts = opts;
|
|
@@ -20115,8 +20587,8 @@ var SddParallelRun = class {
|
|
|
20115
20587
|
const successCount = results.filter((r) => r.status === "success").length;
|
|
20116
20588
|
const failCount = results.length - successCount;
|
|
20117
20589
|
for (let i = 0; i < results.length; i++) {
|
|
20118
|
-
const result =
|
|
20119
|
-
const taskId =
|
|
20590
|
+
const result = expectDefined(results[i]);
|
|
20591
|
+
const taskId = expectDefined(taskIds[i]);
|
|
20120
20592
|
if (result.status === "success") {
|
|
20121
20593
|
this.opts.tracker.updateNodeStatus(taskId, "completed");
|
|
20122
20594
|
} else {
|
|
@@ -21223,12 +21695,6 @@ var allServers = () => ({
|
|
|
21223
21695
|
"zai-vision": { ...zaiVisionServer(), enabled: false },
|
|
21224
21696
|
"minimax-vision": { ...miniMaxVisionServer(), enabled: false }
|
|
21225
21697
|
});
|
|
21226
|
-
function expectDefined16(value) {
|
|
21227
|
-
if (value === null || value === void 0) {
|
|
21228
|
-
throw new Error("Expected value to be defined");
|
|
21229
|
-
}
|
|
21230
|
-
return value;
|
|
21231
|
-
}
|
|
21232
21698
|
function parseSkillRef(input) {
|
|
21233
21699
|
const trimmed = input.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
21234
21700
|
const atIdx = trimmed.indexOf("@");
|
|
@@ -21245,7 +21711,7 @@ function parseSkillRef(input) {
|
|
|
21245
21711
|
if (parts.length < 2) {
|
|
21246
21712
|
throw new Error(`Invalid skill reference "${input}". Expected format: user/repo or user/repo@ref`);
|
|
21247
21713
|
}
|
|
21248
|
-
return { owner:
|
|
21714
|
+
return { owner: expectDefined(parts[0]), repo: expectDefined(parts[1]), ref };
|
|
21249
21715
|
}
|
|
21250
21716
|
var MAX_TARBALL_SIZE = 50 * 1024 * 1024;
|
|
21251
21717
|
async function downloadGitHubTarball(parsed) {
|
|
@@ -21414,12 +21880,6 @@ var SkillManifestStore = class {
|
|
|
21414
21880
|
};
|
|
21415
21881
|
|
|
21416
21882
|
// src/skills/skill-installer.ts
|
|
21417
|
-
function expectDefined17(value) {
|
|
21418
|
-
if (value === null || value === void 0) {
|
|
21419
|
-
throw new Error("Expected value to be defined");
|
|
21420
|
-
}
|
|
21421
|
-
return value;
|
|
21422
|
-
}
|
|
21423
21883
|
var MAX_SKILL_FILE_SIZE = 100 * 1024;
|
|
21424
21884
|
var SkillInstaller = class {
|
|
21425
21885
|
opts;
|
|
@@ -21464,10 +21924,10 @@ var SkillInstaller = class {
|
|
|
21464
21924
|
if (!resolved.startsWith(path6.resolve(destDir))) {
|
|
21465
21925
|
throw new Error(`Path traversal detected in skill file: ${file}`);
|
|
21466
21926
|
}
|
|
21467
|
-
const
|
|
21468
|
-
if (
|
|
21927
|
+
const stat11 = await fsp3.stat(srcPath);
|
|
21928
|
+
if (stat11.size > MAX_SKILL_FILE_SIZE) {
|
|
21469
21929
|
throw new Error(
|
|
21470
|
-
`Skill file "${file}" is too large (${(
|
|
21930
|
+
`Skill file "${file}" is too large (${(stat11.size / 1024).toFixed(1)}KB). Max: ${MAX_SKILL_FILE_SIZE / 1024}KB`
|
|
21471
21931
|
);
|
|
21472
21932
|
}
|
|
21473
21933
|
await fsp3.mkdir(path6.dirname(destPath), { recursive: true });
|
|
@@ -21544,7 +22004,7 @@ var SkillInstaller = class {
|
|
|
21544
22004
|
bySource.get(key)?.push(entry);
|
|
21545
22005
|
}
|
|
21546
22006
|
for (const [, entries] of bySource) {
|
|
21547
|
-
const first =
|
|
22007
|
+
const first = expectDefined(entries[0]);
|
|
21548
22008
|
const scope = first.scope;
|
|
21549
22009
|
const isGlobal = scope === "user";
|
|
21550
22010
|
try {
|
|
@@ -21711,15 +22171,387 @@ async function collectFiles(dir, baseDir2) {
|
|
|
21711
22171
|
}
|
|
21712
22172
|
return results;
|
|
21713
22173
|
}
|
|
22174
|
+
var GraphMemoryBackend = class {
|
|
22175
|
+
kind = "graph";
|
|
22176
|
+
file;
|
|
22177
|
+
graphFile;
|
|
22178
|
+
// In-memory graph state — lazily loaded, saved on mutation.
|
|
22179
|
+
nodes = /* @__PURE__ */ new Map();
|
|
22180
|
+
edges = [];
|
|
22181
|
+
loadedScope = null;
|
|
22182
|
+
loaded = false;
|
|
22183
|
+
constructor(opts) {
|
|
22184
|
+
this.file = new FileMemoryBackend({ paths: opts.paths });
|
|
22185
|
+
this.graphFile = opts.graphPath ?? `${opts.paths.projectDir}/memory-graph.json`;
|
|
22186
|
+
}
|
|
22187
|
+
// ── Backend interface ──────────────────────────────────────────────
|
|
22188
|
+
async remember(scope, entry, filePath) {
|
|
22189
|
+
await this.file.remember(scope, entry, filePath);
|
|
22190
|
+
await this.loadGraph(scope);
|
|
22191
|
+
const nodeId = this.nodeId(entry);
|
|
22192
|
+
const existing = this.nodes.get(nodeId);
|
|
22193
|
+
if (existing) {
|
|
22194
|
+
existing.count++;
|
|
22195
|
+
existing.entry = entry;
|
|
22196
|
+
existing.type = entry.type;
|
|
22197
|
+
existing.tags = entry.tags;
|
|
22198
|
+
existing.priority = entry.priority;
|
|
22199
|
+
} else {
|
|
22200
|
+
this.nodes.set(nodeId, {
|
|
22201
|
+
id: nodeId,
|
|
22202
|
+
entry,
|
|
22203
|
+
firstSeen: entry.ts,
|
|
22204
|
+
count: 1,
|
|
22205
|
+
type: entry.type,
|
|
22206
|
+
tags: entry.tags,
|
|
22207
|
+
priority: entry.priority
|
|
22208
|
+
});
|
|
22209
|
+
for (const [, other] of this.nodes) {
|
|
22210
|
+
if (other.id === nodeId) continue;
|
|
22211
|
+
const sim = wordOverlap(entry.text, other.entry.text);
|
|
22212
|
+
const tagSim = sharedTags(entry.tags ?? [], other.tags ?? []);
|
|
22213
|
+
const weight = Math.max(sim, tagSim * 0.5);
|
|
22214
|
+
if (weight > 0.15) {
|
|
22215
|
+
this.edges.push({
|
|
22216
|
+
from: nodeId,
|
|
22217
|
+
to: other.id,
|
|
22218
|
+
relation: sim >= tagSim ? "similar" : "same_turn",
|
|
22219
|
+
weight,
|
|
22220
|
+
ts: entry.ts
|
|
22221
|
+
});
|
|
22222
|
+
}
|
|
22223
|
+
}
|
|
22224
|
+
}
|
|
22225
|
+
await this.saveGraph(scope);
|
|
22226
|
+
}
|
|
22227
|
+
async forget(scope, query, filePath) {
|
|
22228
|
+
const removed = await this.file.forget(scope, query, filePath);
|
|
22229
|
+
if (removed > 0) {
|
|
22230
|
+
await this.loadGraph(scope);
|
|
22231
|
+
const n = query.toLowerCase();
|
|
22232
|
+
const toRemove = [];
|
|
22233
|
+
for (const [id, node] of this.nodes) {
|
|
22234
|
+
if (node.entry.text.toLowerCase().includes(n)) {
|
|
22235
|
+
toRemove.push(id);
|
|
22236
|
+
}
|
|
22237
|
+
}
|
|
22238
|
+
for (const id of toRemove) this.nodes.delete(id);
|
|
22239
|
+
this.edges = this.edges.filter((e) => !toRemove.includes(e.from) && !toRemove.includes(e.to));
|
|
22240
|
+
await this.saveGraph(scope);
|
|
22241
|
+
}
|
|
22242
|
+
return removed;
|
|
22243
|
+
}
|
|
22244
|
+
async readAll(scope, filePath) {
|
|
22245
|
+
return this.file.readAll(scope, filePath);
|
|
22246
|
+
}
|
|
22247
|
+
async list(scope, filePath, limit) {
|
|
22248
|
+
await this.loadGraph(scope);
|
|
22249
|
+
const fileEntries = await this.file.list(scope, filePath);
|
|
22250
|
+
const nodeMap = new Map(this.nodes.entries());
|
|
22251
|
+
const enriched = fileEntries.map((fe) => {
|
|
22252
|
+
const nodeId = this.nodeId(fe);
|
|
22253
|
+
const node = nodeMap.get(nodeId);
|
|
22254
|
+
if (node) {
|
|
22255
|
+
return {
|
|
22256
|
+
...fe,
|
|
22257
|
+
type: node.type ?? fe.type,
|
|
22258
|
+
tags: node.tags ?? fe.tags,
|
|
22259
|
+
priority: node.priority ?? fe.priority
|
|
22260
|
+
};
|
|
22261
|
+
}
|
|
22262
|
+
return fe;
|
|
22263
|
+
});
|
|
22264
|
+
const fileIds = new Set(fileEntries.map((e) => this.nodeId(e)));
|
|
22265
|
+
for (const [id, node] of this.nodes) {
|
|
22266
|
+
if (!fileIds.has(id)) {
|
|
22267
|
+
enriched.push(node.entry);
|
|
22268
|
+
}
|
|
22269
|
+
}
|
|
22270
|
+
enriched.sort((a, b) => b.ts.localeCompare(a.ts));
|
|
22271
|
+
return limit ? enriched.slice(0, limit) : enriched;
|
|
22272
|
+
}
|
|
22273
|
+
async search(scope, query, filePath, limit) {
|
|
22274
|
+
await this.loadGraph(scope);
|
|
22275
|
+
const needle = query.toLowerCase().split(/\s+/);
|
|
22276
|
+
const all = await this.list(scope, filePath);
|
|
22277
|
+
const scored = all.map((entry) => {
|
|
22278
|
+
const words = entry.text.toLowerCase().split(/\s+/);
|
|
22279
|
+
let score = 0;
|
|
22280
|
+
for (const n of needle) {
|
|
22281
|
+
if (words.some((w) => w.includes(n))) score += 1;
|
|
22282
|
+
if (entry.tags?.some((t2) => t2.toLowerCase().includes(n))) score += 2;
|
|
22283
|
+
}
|
|
22284
|
+
const node = this.nodes.get(this.nodeId(entry));
|
|
22285
|
+
if (node) {
|
|
22286
|
+
if (node.priority === "critical") score += 3;
|
|
22287
|
+
else if (node.priority === "high") score += 2;
|
|
22288
|
+
score += node.count * 0.5;
|
|
22289
|
+
}
|
|
22290
|
+
return { entry, score };
|
|
22291
|
+
});
|
|
22292
|
+
scored.sort((a, b) => b.score - a.score);
|
|
22293
|
+
const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
|
|
22294
|
+
return limit ? matched.slice(0, limit) : matched;
|
|
22295
|
+
}
|
|
22296
|
+
async clear(scope, filePath) {
|
|
22297
|
+
await this.file.clear(scope, filePath);
|
|
22298
|
+
this.nodes.clear();
|
|
22299
|
+
this.edges = [];
|
|
22300
|
+
this.loadedScope = scope;
|
|
22301
|
+
this.loaded = true;
|
|
22302
|
+
try {
|
|
22303
|
+
await fsp3.unlink(this.graphFile);
|
|
22304
|
+
} catch {
|
|
22305
|
+
}
|
|
22306
|
+
}
|
|
22307
|
+
async consolidate(scope, filePath) {
|
|
22308
|
+
return this.file.consolidate(scope, filePath);
|
|
22309
|
+
}
|
|
22310
|
+
// ── Graph-specific queries ─────────────────────────────────────────
|
|
22311
|
+
/**
|
|
22312
|
+
* Find memories related to the given entry, ordered by edge weight.
|
|
22313
|
+
*/
|
|
22314
|
+
async findRelated(scope, _filePath, entryText, limit = 5) {
|
|
22315
|
+
await this.loadGraph(scope);
|
|
22316
|
+
const targetId = this.nodeId({ scope, text: entryText, ts: "" });
|
|
22317
|
+
const related = this.edges.filter((e) => e.from === targetId || e.to === targetId).sort((a, b) => b.weight - a.weight).slice(0, limit);
|
|
22318
|
+
const result = [];
|
|
22319
|
+
for (const edge of related) {
|
|
22320
|
+
const otherId = edge.from === targetId ? edge.to : edge.from;
|
|
22321
|
+
const node = this.nodes.get(otherId);
|
|
22322
|
+
if (node) result.push(node.entry);
|
|
22323
|
+
}
|
|
22324
|
+
return result;
|
|
22325
|
+
}
|
|
22326
|
+
/**
|
|
22327
|
+
* Get all edges for visualization or traversal.
|
|
22328
|
+
*/
|
|
22329
|
+
getGraph() {
|
|
22330
|
+
return {
|
|
22331
|
+
nodes: [...this.nodes.values()],
|
|
22332
|
+
edges: [...this.edges]
|
|
22333
|
+
};
|
|
22334
|
+
}
|
|
22335
|
+
// ── Persistence ────────────────────────────────────────────────────
|
|
22336
|
+
nodeId(entry) {
|
|
22337
|
+
const norm = entry.text.toLowerCase().trim().replace(/\s+/g, " ");
|
|
22338
|
+
return `${entry.scope ?? "mem"}::${simpleHash(norm)}`;
|
|
22339
|
+
}
|
|
22340
|
+
async loadGraph(scope) {
|
|
22341
|
+
if (this.loaded && this.loadedScope === scope) return;
|
|
22342
|
+
try {
|
|
22343
|
+
const raw = await fsp3.readFile(this.graphFile, "utf8");
|
|
22344
|
+
const data = JSON.parse(raw);
|
|
22345
|
+
this.nodes = new Map(data.nodes);
|
|
22346
|
+
this.edges = data.edges;
|
|
22347
|
+
} catch {
|
|
22348
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
22349
|
+
this.edges = [];
|
|
22350
|
+
}
|
|
22351
|
+
this.loadedScope = scope;
|
|
22352
|
+
this.loaded = true;
|
|
22353
|
+
}
|
|
22354
|
+
async saveGraph(scope) {
|
|
22355
|
+
this.loadedScope = scope;
|
|
22356
|
+
this.loaded = true;
|
|
22357
|
+
try {
|
|
22358
|
+
const data = {
|
|
22359
|
+
nodes: [...this.nodes.entries()],
|
|
22360
|
+
edges: this.edges
|
|
22361
|
+
};
|
|
22362
|
+
await fsp3.mkdir(
|
|
22363
|
+
this.graphFile.substring(0, this.graphFile.lastIndexOf("/")),
|
|
22364
|
+
{ recursive: true }
|
|
22365
|
+
);
|
|
22366
|
+
const tmp = `${this.graphFile}.tmp`;
|
|
22367
|
+
await fsp3.writeFile(tmp, JSON.stringify(data));
|
|
22368
|
+
await fsp3.rename(tmp, this.graphFile);
|
|
22369
|
+
} catch {
|
|
22370
|
+
}
|
|
22371
|
+
}
|
|
22372
|
+
};
|
|
22373
|
+
function wordOverlap(a, b) {
|
|
22374
|
+
const wordsA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
22375
|
+
const wordsB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
22376
|
+
if (wordsA.size === 0 || wordsB.size === 0) return 0;
|
|
22377
|
+
let intersection = 0;
|
|
22378
|
+
for (const w of wordsA) {
|
|
22379
|
+
if (wordsB.has(w)) intersection++;
|
|
22380
|
+
}
|
|
22381
|
+
return intersection / Math.max(wordsA.size, wordsB.size);
|
|
22382
|
+
}
|
|
22383
|
+
function sharedTags(a, b) {
|
|
22384
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
22385
|
+
const setB = new Set(b);
|
|
22386
|
+
let shared = 0;
|
|
22387
|
+
for (const t2 of a) if (setB.has(t2)) shared++;
|
|
22388
|
+
return shared / Math.max(a.length, b.length);
|
|
22389
|
+
}
|
|
22390
|
+
function simpleHash(s) {
|
|
22391
|
+
let h = 0;
|
|
22392
|
+
for (let i = 0; i < s.length; i++) {
|
|
22393
|
+
h = (h << 5) - h + s.charCodeAt(i) | 0;
|
|
22394
|
+
}
|
|
22395
|
+
return Math.abs(h).toString(36);
|
|
22396
|
+
}
|
|
22397
|
+
|
|
22398
|
+
// src/storage/memory-consolidator.ts
|
|
22399
|
+
function buildConsolidationPrompt(finalText, iterations, existingEntries) {
|
|
22400
|
+
const existingBlock = existingEntries.length > 0 ? `
|
|
22401
|
+
|
|
22402
|
+
Existing memory entries:
|
|
22403
|
+
${existingEntries.map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`).join("\n")}` : "";
|
|
22404
|
+
return `You are a memory consolidator. Review the following session summary and decide what key facts, conventions, decisions, or learnings should be persisted to long-term memory.
|
|
22405
|
+
|
|
22406
|
+
Session summary (${iterations} iterations):
|
|
22407
|
+
${finalText.slice(0, 3e3)}${existingBlock}
|
|
22408
|
+
|
|
22409
|
+
Return a JSON object with an "operations" array. Each operation must have an "action" field:
|
|
22410
|
+
- "add": create a new memory entry. Include "text", and optionally "type", "tags", "priority".
|
|
22411
|
+
- "edit": replace an existing entry. Include "query" (to match) and "text" (replacement).
|
|
22412
|
+
- "delete": remove an entry. Include "query" (to match).
|
|
22413
|
+
|
|
22414
|
+
Memory types:
|
|
22415
|
+
- "fact": Objective truth about the project (e.g. "uses pnpm workspaces")
|
|
22416
|
+
- "decision": A choice that was made (e.g. "decided to use biome over eslint")
|
|
22417
|
+
- "convention": A recurring pattern or standard (e.g. "commit messages use conventional format")
|
|
22418
|
+
- "preference": User or team preference (e.g. "prefers short variable names")
|
|
22419
|
+
- "reference": Pointer to a file or location (e.g. "auth logic in packages/core/src/auth/")
|
|
22420
|
+
- "anti_pattern": Something to avoid (e.g. "never use any in TypeScript")
|
|
22421
|
+
|
|
22422
|
+
Priority levels:
|
|
22423
|
+
- "critical": Must always be known (e.g. security constraints)
|
|
22424
|
+
- "high": Important for most tasks
|
|
22425
|
+
- "medium": Useful context
|
|
22426
|
+
- "low": Nice to know
|
|
22427
|
+
|
|
22428
|
+
Rules:
|
|
22429
|
+
- Only persist facts likely useful across multiple future sessions.
|
|
22430
|
+
- Do NOT persist task progress, temporary state, or one-off observations.
|
|
22431
|
+
- Prefer "add" over "edit" unless the existing entry is clearly outdated.
|
|
22432
|
+
- Assign a type and priority to every "add" operation.
|
|
22433
|
+
- Use 1-3 hashtag tags for each entry (e.g. #typescript #build).
|
|
22434
|
+
- Be concise \u2014 each memory entry should be one clear sentence.
|
|
22435
|
+
|
|
22436
|
+
Return ONLY valid JSON, no markdown, no explanation:
|
|
22437
|
+
{
|
|
22438
|
+
"operations": [
|
|
22439
|
+
{
|
|
22440
|
+
"action": "add",
|
|
22441
|
+
"text": "Project uses pnpm workspaces with TypeScript strict mode",
|
|
22442
|
+
"type": "convention",
|
|
22443
|
+
"priority": "high",
|
|
22444
|
+
"tags": ["pnpm", "typescript", "build"]
|
|
22445
|
+
},
|
|
22446
|
+
{
|
|
22447
|
+
"action": "edit",
|
|
22448
|
+
"query": "pnpm",
|
|
22449
|
+
"text": "Project uses pnpm v9+ with ESM-only modules",
|
|
22450
|
+
"type": "fact",
|
|
22451
|
+
"priority": "medium"
|
|
22452
|
+
},
|
|
22453
|
+
{ "action": "delete", "query": "outdated convention" }
|
|
22454
|
+
]
|
|
22455
|
+
}`;
|
|
22456
|
+
}
|
|
22457
|
+
var SessionMemoryConsolidator = class {
|
|
22458
|
+
name = "session-memory-consolidator";
|
|
22459
|
+
owner = "core";
|
|
22460
|
+
memoryStore;
|
|
22461
|
+
provider;
|
|
22462
|
+
model;
|
|
22463
|
+
minIterations;
|
|
22464
|
+
maxExistingEntries;
|
|
22465
|
+
constructor(opts) {
|
|
22466
|
+
this.memoryStore = opts.memoryStore;
|
|
22467
|
+
this.provider = opts.provider;
|
|
22468
|
+
this.model = opts.model;
|
|
22469
|
+
this.minIterations = opts.minIterations ?? 2;
|
|
22470
|
+
this.maxExistingEntries = opts.maxExistingEntries ?? 15;
|
|
22471
|
+
}
|
|
22472
|
+
afterRun = async (ctx, result) => {
|
|
22473
|
+
if (result.status !== "done") return;
|
|
22474
|
+
if (!result.finalText || result.finalText.trim().length < 20) return;
|
|
22475
|
+
if (result.iterations < this.minIterations) return;
|
|
22476
|
+
const provider = this.provider ?? ctx.provider;
|
|
22477
|
+
if (!provider || !provider.complete) return;
|
|
22478
|
+
try {
|
|
22479
|
+
const existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
|
|
22480
|
+
const prompt = buildConsolidationPrompt(
|
|
22481
|
+
result.finalText,
|
|
22482
|
+
result.iterations,
|
|
22483
|
+
existingEntries
|
|
22484
|
+
);
|
|
22485
|
+
const signal = AbortSignal.timeout(15e3);
|
|
22486
|
+
const response = await provider.complete(
|
|
22487
|
+
{
|
|
22488
|
+
model: this.model ?? ctx.model,
|
|
22489
|
+
system: [{ type: "text", text: prompt }],
|
|
22490
|
+
messages: [
|
|
22491
|
+
{ role: "user", content: "Review the session and return memory operations as JSON." }
|
|
22492
|
+
],
|
|
22493
|
+
maxTokens: 500
|
|
22494
|
+
},
|
|
22495
|
+
{ signal }
|
|
22496
|
+
);
|
|
22497
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
|
|
22498
|
+
if (!text) return;
|
|
22499
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
22500
|
+
if (!jsonMatch) return;
|
|
22501
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
22502
|
+
if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
|
|
22503
|
+
let added = 0;
|
|
22504
|
+
let edited = 0;
|
|
22505
|
+
let deleted = 0;
|
|
22506
|
+
for (const op of parsed.operations) {
|
|
22507
|
+
switch (op.action) {
|
|
22508
|
+
case "add": {
|
|
22509
|
+
if (op.text && op.text.trim()) {
|
|
22510
|
+
await this.memoryStore.remember(op.text.trim(), void 0, {
|
|
22511
|
+
type: op.type,
|
|
22512
|
+
tags: op.tags,
|
|
22513
|
+
priority: op.priority
|
|
22514
|
+
});
|
|
22515
|
+
added++;
|
|
22516
|
+
}
|
|
22517
|
+
break;
|
|
22518
|
+
}
|
|
22519
|
+
case "edit": {
|
|
22520
|
+
if (op.query && op.text && op.text.trim()) {
|
|
22521
|
+
await this.memoryStore.forget(op.query);
|
|
22522
|
+
await this.memoryStore.remember(op.text.trim(), void 0, {
|
|
22523
|
+
type: op.type,
|
|
22524
|
+
tags: op.tags,
|
|
22525
|
+
priority: op.priority
|
|
22526
|
+
});
|
|
22527
|
+
edited++;
|
|
22528
|
+
}
|
|
22529
|
+
break;
|
|
22530
|
+
}
|
|
22531
|
+
case "delete": {
|
|
22532
|
+
if (op.query) {
|
|
22533
|
+
const n = await this.memoryStore.forget(op.query);
|
|
22534
|
+
deleted += n;
|
|
22535
|
+
}
|
|
22536
|
+
break;
|
|
22537
|
+
}
|
|
22538
|
+
}
|
|
22539
|
+
}
|
|
22540
|
+
if (added > 0 || edited > 0 || deleted > 0) {
|
|
22541
|
+
const parts = [];
|
|
22542
|
+
if (added) parts.push(`${added} added`);
|
|
22543
|
+
if (edited) parts.push(`${edited} edited`);
|
|
22544
|
+
if (deleted) parts.push(`${deleted} deleted`);
|
|
22545
|
+
process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
|
|
22546
|
+
`);
|
|
22547
|
+
}
|
|
22548
|
+
} catch {
|
|
22549
|
+
}
|
|
22550
|
+
};
|
|
22551
|
+
};
|
|
21714
22552
|
|
|
21715
22553
|
// src/storage/annotations-store.ts
|
|
21716
22554
|
init_atomic_write();
|
|
21717
|
-
function expectDefined18(value) {
|
|
21718
|
-
if (value === null || value === void 0) {
|
|
21719
|
-
throw new Error("Expected value to be defined");
|
|
21720
|
-
}
|
|
21721
|
-
return value;
|
|
21722
|
-
}
|
|
21723
22555
|
var FILE_VERSION = 1;
|
|
21724
22556
|
var MAX_TEXT_LENGTH = 2e3;
|
|
21725
22557
|
var MAX_ANNOTATIONS = 1e3;
|
|
@@ -21760,9 +22592,7 @@ var AnnotationsStore = class {
|
|
|
21760
22592
|
throw new Error("Annotation text must be non-empty");
|
|
21761
22593
|
}
|
|
21762
22594
|
if (text.length > MAX_TEXT_LENGTH) {
|
|
21763
|
-
throw new Error(
|
|
21764
|
-
`Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`
|
|
21765
|
-
);
|
|
22595
|
+
throw new Error(`Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`);
|
|
21766
22596
|
}
|
|
21767
22597
|
if (!Number.isInteger(input.atEventIndex) || input.atEventIndex < 0) {
|
|
21768
22598
|
throw new Error("atEventIndex must be a non-negative integer");
|
|
@@ -21778,20 +22608,22 @@ var AnnotationsStore = class {
|
|
|
21778
22608
|
resolved: false
|
|
21779
22609
|
};
|
|
21780
22610
|
await this.enqueue(input.sessionId, async () => {
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
|
|
21786
|
-
|
|
21787
|
-
|
|
21788
|
-
|
|
21789
|
-
|
|
21790
|
-
|
|
21791
|
-
|
|
21792
|
-
|
|
21793
|
-
|
|
21794
|
-
|
|
22611
|
+
await withFileLock(this.filePath(input.sessionId), async () => {
|
|
22612
|
+
const all = await this.list(input.sessionId);
|
|
22613
|
+
all.push(annotation);
|
|
22614
|
+
if (all.length > MAX_ANNOTATIONS) {
|
|
22615
|
+
const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
|
|
22616
|
+
if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
|
|
22617
|
+
return x.a.createdAt.localeCompare(y.a.createdAt);
|
|
22618
|
+
});
|
|
22619
|
+
const evictCount = all.length - MAX_ANNOTATIONS;
|
|
22620
|
+
const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
|
|
22621
|
+
const kept = all.filter((a) => !toEvict.has(a.id));
|
|
22622
|
+
await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
|
|
22623
|
+
} else {
|
|
22624
|
+
await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
|
|
22625
|
+
}
|
|
22626
|
+
});
|
|
21795
22627
|
});
|
|
21796
22628
|
return annotation;
|
|
21797
22629
|
}
|
|
@@ -21804,21 +22636,23 @@ var AnnotationsStore = class {
|
|
|
21804
22636
|
async resolve(input) {
|
|
21805
22637
|
let updated = null;
|
|
21806
22638
|
await this.enqueue(input.sessionId, async () => {
|
|
21807
|
-
|
|
21808
|
-
|
|
21809
|
-
|
|
21810
|
-
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21814
|
-
|
|
21815
|
-
|
|
21816
|
-
|
|
21817
|
-
|
|
21818
|
-
|
|
21819
|
-
|
|
21820
|
-
|
|
21821
|
-
|
|
22639
|
+
await withFileLock(this.filePath(input.sessionId), async () => {
|
|
22640
|
+
const all = await this.list(input.sessionId);
|
|
22641
|
+
const idx = all.findIndex((a) => a.id === input.annotationId);
|
|
22642
|
+
if (idx === -1) {
|
|
22643
|
+
updated = null;
|
|
22644
|
+
return;
|
|
22645
|
+
}
|
|
22646
|
+
const next = {
|
|
22647
|
+
...expectDefined(all[idx]),
|
|
22648
|
+
resolved: true,
|
|
22649
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22650
|
+
resolvedBy: input.resolvedBy
|
|
22651
|
+
};
|
|
22652
|
+
all[idx] = next;
|
|
22653
|
+
await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
|
|
22654
|
+
updated = next;
|
|
22655
|
+
});
|
|
21822
22656
|
});
|
|
21823
22657
|
return updated;
|
|
21824
22658
|
}
|
|
@@ -21919,22 +22753,22 @@ var ReplayLogStore = class {
|
|
|
21919
22753
|
async record(input) {
|
|
21920
22754
|
const hash = hashRequest(input.request);
|
|
21921
22755
|
await this.enqueue(input.sessionId, async () => {
|
|
21922
|
-
|
|
21923
|
-
|
|
21924
|
-
|
|
21925
|
-
|
|
21926
|
-
|
|
21927
|
-
|
|
21928
|
-
|
|
21929
|
-
|
|
21930
|
-
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
this.
|
|
21937
|
-
}
|
|
22756
|
+
await withFileLock(this.filePath(input.sessionId), async () => {
|
|
22757
|
+
const entries = await this.readAll(input.sessionId);
|
|
22758
|
+
if (entries.some((entry2) => entry2.hash === hash)) return;
|
|
22759
|
+
const entry = {
|
|
22760
|
+
hash,
|
|
22761
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22762
|
+
request: input.request,
|
|
22763
|
+
response: input.response
|
|
22764
|
+
};
|
|
22765
|
+
entries.push(entry);
|
|
22766
|
+
const keep = entries.slice(-this.maxEntries);
|
|
22767
|
+
const cache = /* @__PURE__ */ new Map();
|
|
22768
|
+
for (const e of keep) cache.set(e.hash, e);
|
|
22769
|
+
this.cache.set(input.sessionId, cache);
|
|
22770
|
+
await this.rewriteCache(input.sessionId, cache);
|
|
22771
|
+
});
|
|
21938
22772
|
});
|
|
21939
22773
|
return hash;
|
|
21940
22774
|
}
|
|
@@ -21943,7 +22777,7 @@ var ReplayLogStore = class {
|
|
|
21943
22777
|
* Called when entry count exceeds the cap. Rewrites the entire file
|
|
21944
22778
|
* but only happens O(n / maxEntries) times per session.
|
|
21945
22779
|
*/
|
|
21946
|
-
async
|
|
22780
|
+
async rewriteCache(sessionId, cache) {
|
|
21947
22781
|
const all = [...cache.values()];
|
|
21948
22782
|
const keep = all.slice(-this.maxEntries);
|
|
21949
22783
|
await this.writeAll(sessionId, keep);
|
|
@@ -22007,11 +22841,12 @@ var ReplayLogStore = class {
|
|
|
22007
22841
|
for (const line of raw.split("\n")) {
|
|
22008
22842
|
if (!line.trim()) continue;
|
|
22009
22843
|
try {
|
|
22010
|
-
const parsed =
|
|
22011
|
-
if (
|
|
22012
|
-
|
|
22844
|
+
const parsed = safeParse(line);
|
|
22845
|
+
if (!parsed.ok || !parsed.value) continue;
|
|
22846
|
+
if ("entry" in parsed.value && parsed.value.entry) {
|
|
22847
|
+
out.push(parsed.value.entry);
|
|
22013
22848
|
} else {
|
|
22014
|
-
out.push(parsed);
|
|
22849
|
+
out.push(parsed.value);
|
|
22015
22850
|
}
|
|
22016
22851
|
} catch {
|
|
22017
22852
|
}
|
|
@@ -22047,12 +22882,6 @@ var ReplayLogStore = class {
|
|
|
22047
22882
|
return next;
|
|
22048
22883
|
}
|
|
22049
22884
|
};
|
|
22050
|
-
function expectDefined19(value) {
|
|
22051
|
-
if (value === null || value === void 0) {
|
|
22052
|
-
throw new Error("Expected value to be defined");
|
|
22053
|
-
}
|
|
22054
|
-
return value;
|
|
22055
|
-
}
|
|
22056
22885
|
var SessionRecovery = class {
|
|
22057
22886
|
constructor(dir) {
|
|
22058
22887
|
this.dir = dir;
|
|
@@ -22071,15 +22900,15 @@ var SessionRecovery = class {
|
|
|
22071
22900
|
async detectStale(sessionId) {
|
|
22072
22901
|
const fp = this.filePath(sessionId);
|
|
22073
22902
|
const TAIL_SIZE = 8192;
|
|
22074
|
-
let
|
|
22903
|
+
let stat11;
|
|
22075
22904
|
try {
|
|
22076
|
-
|
|
22905
|
+
stat11 = await fsp3.stat(fp);
|
|
22077
22906
|
} catch (err) {
|
|
22078
22907
|
if (err.code === "ENOENT") return null;
|
|
22079
22908
|
return null;
|
|
22080
22909
|
}
|
|
22081
|
-
if (
|
|
22082
|
-
const position = Math.max(0,
|
|
22910
|
+
if (stat11.size === 0) return null;
|
|
22911
|
+
const position = Math.max(0, stat11.size - TAIL_SIZE);
|
|
22083
22912
|
const buf = Buffer.alloc(TAIL_SIZE);
|
|
22084
22913
|
let fh;
|
|
22085
22914
|
try {
|
|
@@ -22093,7 +22922,7 @@ var SessionRecovery = class {
|
|
|
22093
22922
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
22094
22923
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
22095
22924
|
try {
|
|
22096
|
-
const ev = JSON.parse(
|
|
22925
|
+
const ev = JSON.parse(expectDefined(lines[i]));
|
|
22097
22926
|
if (ev.type === "in_flight_start") {
|
|
22098
22927
|
return {
|
|
22099
22928
|
sessionId,
|
|
@@ -22146,12 +22975,12 @@ var SessionRecovery = class {
|
|
|
22146
22975
|
let lastCheckpointIdx = -1;
|
|
22147
22976
|
for (let i = 0; i < events.length; i++) {
|
|
22148
22977
|
if (events[i]?.type === "checkpoint") {
|
|
22149
|
-
lastCheckpoint =
|
|
22978
|
+
lastCheckpoint = expectDefined(events[i]);
|
|
22150
22979
|
lastCheckpointIdx = i;
|
|
22151
22980
|
}
|
|
22152
22981
|
}
|
|
22153
22982
|
const pendingEvents = lastCheckpointIdx >= 0 ? events.slice(lastCheckpointIdx + 1) : events;
|
|
22154
|
-
const lastEv =
|
|
22983
|
+
const lastEv = expectDefined(events[events.length - 1]);
|
|
22155
22984
|
const inFlightStart = lastEv.type === "in_flight_start" ? lastEv : null;
|
|
22156
22985
|
const context = inFlightStart && inFlightStart.type === "in_flight_start" ? inFlightStart.context : null;
|
|
22157
22986
|
return {
|
|
@@ -22194,12 +23023,9 @@ var SessionRecovery = class {
|
|
|
22194
23023
|
return path6.join(this.dir, `${sessionId}.jsonl`);
|
|
22195
23024
|
}
|
|
22196
23025
|
};
|
|
22197
|
-
|
|
22198
|
-
|
|
22199
|
-
|
|
22200
|
-
}
|
|
22201
|
-
return value;
|
|
22202
|
-
}
|
|
23026
|
+
|
|
23027
|
+
// src/storage/tool-audit-log.ts
|
|
23028
|
+
init_atomic_write();
|
|
22203
23029
|
var GENESIS_PREV = "0".repeat(64);
|
|
22204
23030
|
var DEFAULT_FSYNC_EVERY = 100;
|
|
22205
23031
|
var ToolAuditLog = class {
|
|
@@ -22225,37 +23051,42 @@ var ToolAuditLog = class {
|
|
|
22225
23051
|
async record(input) {
|
|
22226
23052
|
let entry = null;
|
|
22227
23053
|
await this.enqueue(input.sessionId, async () => {
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
id
|
|
22234
|
-
ts
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
22256
|
-
|
|
22257
|
-
|
|
22258
|
-
|
|
23054
|
+
await withFileLock(this.filePath(input.sessionId), async () => {
|
|
23055
|
+
const entries = await this.readAll(input.sessionId);
|
|
23056
|
+
const prev = entries.at(-1);
|
|
23057
|
+
const prevHash = prev?.hash ?? GENESIS_PREV;
|
|
23058
|
+
const index = prev ? prev.index + 1 : 0;
|
|
23059
|
+
const id = randomUUID();
|
|
23060
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
23061
|
+
const content = {
|
|
23062
|
+
id,
|
|
23063
|
+
ts,
|
|
23064
|
+
prevHash,
|
|
23065
|
+
toolName: input.toolName,
|
|
23066
|
+
toolUseId: input.toolUseId,
|
|
23067
|
+
input: input.input,
|
|
23068
|
+
output: input.output,
|
|
23069
|
+
isError: input.isError,
|
|
23070
|
+
index
|
|
23071
|
+
};
|
|
23072
|
+
const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
|
|
23073
|
+
entry = {
|
|
23074
|
+
id,
|
|
23075
|
+
ts,
|
|
23076
|
+
prevHash,
|
|
23077
|
+
hash,
|
|
23078
|
+
toolName: input.toolName,
|
|
23079
|
+
toolUseId: input.toolUseId,
|
|
23080
|
+
input: input.input,
|
|
23081
|
+
output: input.output,
|
|
23082
|
+
isError: input.isError,
|
|
23083
|
+
index
|
|
23084
|
+
};
|
|
23085
|
+
entries.push(entry);
|
|
23086
|
+
await this.writeAll(input.sessionId, entries);
|
|
23087
|
+
this.tailHash.set(input.sessionId, hash);
|
|
23088
|
+
this.tailIndex.set(input.sessionId, index + 1);
|
|
23089
|
+
});
|
|
22259
23090
|
});
|
|
22260
23091
|
return entry;
|
|
22261
23092
|
}
|
|
@@ -22275,7 +23106,7 @@ var ToolAuditLog = class {
|
|
|
22275
23106
|
}
|
|
22276
23107
|
let prevHash = GENESIS_PREV;
|
|
22277
23108
|
for (let i = 0; i < entries.length; i++) {
|
|
22278
|
-
const e =
|
|
23109
|
+
const e = expectDefined(entries[i]);
|
|
22279
23110
|
if (e.prevHash !== prevHash) {
|
|
22280
23111
|
return {
|
|
22281
23112
|
ok: false,
|
|
@@ -22325,7 +23156,8 @@ var ToolAuditLog = class {
|
|
|
22325
23156
|
for (const line of raw.split("\n")) {
|
|
22326
23157
|
if (!line.trim()) continue;
|
|
22327
23158
|
try {
|
|
22328
|
-
|
|
23159
|
+
const parsed = safeParse(line);
|
|
23160
|
+
if (parsed.ok && parsed.value) out.push(parsed.value);
|
|
22329
23161
|
} catch {
|
|
22330
23162
|
}
|
|
22331
23163
|
}
|
|
@@ -22335,10 +23167,10 @@ var ToolAuditLog = class {
|
|
|
22335
23167
|
return [];
|
|
22336
23168
|
}
|
|
22337
23169
|
}
|
|
22338
|
-
async
|
|
23170
|
+
async writeAll(sessionId, entries) {
|
|
22339
23171
|
const fp = this.filePath(sessionId);
|
|
22340
|
-
const line = JSON.stringify(
|
|
22341
|
-
await
|
|
23172
|
+
const line = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length ? "\n" : "");
|
|
23173
|
+
await atomicWrite(fp, line, { mode: 384 });
|
|
22342
23174
|
const count = (this.unSyncedWrites.get(sessionId) ?? 0) + 1;
|
|
22343
23175
|
this.unSyncedWrites.set(sessionId, count);
|
|
22344
23176
|
if (this.fsyncEvery !== Number.POSITIVE_INFINITY && count % this.fsyncEvery === 0) {
|
|
@@ -22394,12 +23226,6 @@ function sortKeys2(value) {
|
|
|
22394
23226
|
|
|
22395
23227
|
// src/storage/session-rewinder.ts
|
|
22396
23228
|
init_atomic_write();
|
|
22397
|
-
function expectDefined21(value) {
|
|
22398
|
-
if (value === null || value === void 0) {
|
|
22399
|
-
throw new Error("Expected value to be defined");
|
|
22400
|
-
}
|
|
22401
|
-
return value;
|
|
22402
|
-
}
|
|
22403
23229
|
var DefaultSessionRewinder = class {
|
|
22404
23230
|
constructor(sessionsDir, projectRoot) {
|
|
22405
23231
|
this.sessionsDir = sessionsDir;
|
|
@@ -22438,7 +23264,7 @@ var DefaultSessionRewinder = class {
|
|
|
22438
23264
|
const events = parseEvents(raw);
|
|
22439
23265
|
let targetIdx = -1;
|
|
22440
23266
|
for (let i = 0; i < events.length; i++) {
|
|
22441
|
-
const event =
|
|
23267
|
+
const event = expectDefined(events[i]);
|
|
22442
23268
|
if (event.type === "checkpoint") {
|
|
22443
23269
|
const checkpointEvent = event;
|
|
22444
23270
|
if (checkpointEvent.promptIndex === checkpointIndex) {
|
|
@@ -22452,7 +23278,7 @@ var DefaultSessionRewinder = class {
|
|
|
22452
23278
|
}
|
|
22453
23279
|
const snapshotsToRevert = [];
|
|
22454
23280
|
for (let i = targetIdx + 1; i < events.length; i++) {
|
|
22455
|
-
const event =
|
|
23281
|
+
const event = expectDefined(events[i]);
|
|
22456
23282
|
if (event.type === "checkpoint") {
|
|
22457
23283
|
break;
|
|
22458
23284
|
}
|
|
@@ -22634,12 +23460,6 @@ var DefaultPromptStore = class {
|
|
|
22634
23460
|
};
|
|
22635
23461
|
}
|
|
22636
23462
|
};
|
|
22637
|
-
function expectDefined22(value) {
|
|
22638
|
-
if (value === null || value === void 0) {
|
|
22639
|
-
throw new Error("Expected value to be defined");
|
|
22640
|
-
}
|
|
22641
|
-
return value;
|
|
22642
|
-
}
|
|
22643
23463
|
var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
|
|
22644
23464
|
var CloudSync = class {
|
|
22645
23465
|
constructor(paths, getConfig, setConfig) {
|
|
@@ -22682,8 +23502,8 @@ var CloudSync = class {
|
|
|
22682
23502
|
const cfg = this.getConfig();
|
|
22683
23503
|
if (!cfg?.enabled) return { ok: false, action: "push", categories: [], message: "Not enabled." };
|
|
22684
23504
|
const parts = cfg.repo.split("/");
|
|
22685
|
-
const owner =
|
|
22686
|
-
const repoName =
|
|
23505
|
+
const owner = expectDefined(parts[0]);
|
|
23506
|
+
const repoName = expectDefined(parts[1]);
|
|
22687
23507
|
const branch = "main";
|
|
22688
23508
|
const baseTreeSha = this.state?.sha;
|
|
22689
23509
|
const { treeEntries, rev } = await this.buildLocalTree(cfg.categories);
|
|
@@ -22735,8 +23555,8 @@ var CloudSync = class {
|
|
|
22735
23555
|
const cfg = this.getConfig();
|
|
22736
23556
|
if (!cfg?.enabled) return { ok: false, action: "pull", categories: [], message: "Not enabled." };
|
|
22737
23557
|
const pullParts = cfg.repo.split("/");
|
|
22738
|
-
const owner =
|
|
22739
|
-
const repoName =
|
|
23558
|
+
const owner = expectDefined(pullParts[0]);
|
|
23559
|
+
const repoName = expectDefined(pullParts[1]);
|
|
22740
23560
|
const branchData = await this.getRef(token, owner, repoName, "main");
|
|
22741
23561
|
const currentSha = branchData.object.sha;
|
|
22742
23562
|
const commitData = await this.getCommit(token, owner, repoName, currentSha);
|
|
@@ -22855,8 +23675,8 @@ var CloudSync = class {
|
|
|
22855
23675
|
const localPath = this.categoryToPath(cat);
|
|
22856
23676
|
if (!localPath) continue;
|
|
22857
23677
|
try {
|
|
22858
|
-
const
|
|
22859
|
-
if (
|
|
23678
|
+
const stat11 = await fsp3.stat(localPath);
|
|
23679
|
+
if (stat11.isDirectory()) {
|
|
22860
23680
|
const files = await this.walkDir(localPath, localPath);
|
|
22861
23681
|
for (const file of files) {
|
|
22862
23682
|
const content = await fsp3.readFile(file, "utf8");
|
|
@@ -22881,8 +23701,8 @@ var CloudSync = class {
|
|
|
22881
23701
|
const localPath = this.categoryToPath(cat);
|
|
22882
23702
|
if (!localPath) continue;
|
|
22883
23703
|
try {
|
|
22884
|
-
const
|
|
22885
|
-
if (
|
|
23704
|
+
const stat11 = await fsp3.stat(localPath);
|
|
23705
|
+
if (stat11.isDirectory()) {
|
|
22886
23706
|
const files = await this.walkDir(localPath, localPath);
|
|
22887
23707
|
for (const file of files) {
|
|
22888
23708
|
const content = await fsp3.readFile(file);
|
|
@@ -23834,8 +24654,8 @@ var ReportGenerator = class {
|
|
|
23834
24654
|
try {
|
|
23835
24655
|
await stat(this.options.outputDir);
|
|
23836
24656
|
} catch {
|
|
23837
|
-
const { mkdir:
|
|
23838
|
-
await
|
|
24657
|
+
const { mkdir: mkdir17 } = await import('fs/promises');
|
|
24658
|
+
await mkdir17(this.options.outputDir, { recursive: true });
|
|
23839
24659
|
}
|
|
23840
24660
|
}
|
|
23841
24661
|
generateMarkdown(result) {
|
|
@@ -24082,12 +24902,6 @@ var defaultGitignoreUpdater = new GitignoreUpdater();
|
|
|
24082
24902
|
|
|
24083
24903
|
// src/security-scanner/orchestrator.ts
|
|
24084
24904
|
init_atomic_write();
|
|
24085
|
-
function expectDefined23(value) {
|
|
24086
|
-
if (value === null || value === void 0) {
|
|
24087
|
-
throw new Error("Expected value to be defined");
|
|
24088
|
-
}
|
|
24089
|
-
return value;
|
|
24090
|
-
}
|
|
24091
24905
|
var SecurityScannerOrchestrator = class {
|
|
24092
24906
|
constructor(retryPolicy, errorHandler) {
|
|
24093
24907
|
this.retryPolicy = retryPolicy;
|
|
@@ -24137,7 +24951,7 @@ var SecurityScannerOrchestrator = class {
|
|
|
24137
24951
|
if (detectionResult.detectedStacks.length === 0) {
|
|
24138
24952
|
throw new Error(`No supported tech stack detected in ${projectRoot}`);
|
|
24139
24953
|
}
|
|
24140
|
-
const techStack =
|
|
24954
|
+
const techStack = expectDefined(detectionResult.detectedStacks[0]);
|
|
24141
24955
|
const generatedSkill = await this.generateSkillLLM(provider, model, projectRoot, techStack);
|
|
24142
24956
|
const scanResult = await this.scanWithLLM(provider, model, projectRoot, generatedSkill, techStack, options);
|
|
24143
24957
|
const synthesizedReport = await this.synthesizeReportLLM(provider, model, projectRoot, techStack, scanResult);
|
|
@@ -24218,7 +25032,7 @@ Return ONLY the JSON object, no markdown, no explanation.`;
|
|
|
24218
25032
|
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
24219
25033
|
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
24220
25034
|
if (jsonMatch) {
|
|
24221
|
-
const sanitized = sanitizeJsonString(
|
|
25035
|
+
const sanitized = sanitizeJsonString(expectDefined(jsonMatch[0])) || expectDefined(jsonMatch[0]);
|
|
24222
25036
|
const skillData = JSON.parse(sanitized);
|
|
24223
25037
|
return {
|
|
24224
25038
|
name: skillData.name || `security-scanner-${techStack.stack}`,
|
|
@@ -24338,7 +25152,7 @@ Return ONLY the JSON array. If no issues found, return [].`;
|
|
|
24338
25152
|
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
24339
25153
|
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
24340
25154
|
if (jsonMatch) {
|
|
24341
|
-
const sanitized = sanitizeJsonString(
|
|
25155
|
+
const sanitized = sanitizeJsonString(expectDefined(jsonMatch[0])) || expectDefined(jsonMatch[0]);
|
|
24342
25156
|
const parsed = JSON.parse(sanitized);
|
|
24343
25157
|
return parsed.map((item, idx) => ({
|
|
24344
25158
|
id: `llm-finding-${idx}-${Date.now()}`,
|
|
@@ -24578,7 +25392,7 @@ ${dirs.join(", ")}`);
|
|
|
24578
25392
|
if (detectionResult.detectedStacks.length === 0) {
|
|
24579
25393
|
throw new Error(`No supported tech stack detected in ${projectRoot}`);
|
|
24580
25394
|
}
|
|
24581
|
-
const techStack =
|
|
25395
|
+
const techStack = expectDefined(detectionResult.detectedStacks[0]);
|
|
24582
25396
|
return {
|
|
24583
25397
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24584
25398
|
projectRoot,
|
|
@@ -24767,16 +25581,16 @@ Use \`/security report <number>\` to view a specific report.` };
|
|
|
24767
25581
|
}
|
|
24768
25582
|
const index = Number.parseInt(reportId, 10) - 1;
|
|
24769
25583
|
if (!Number.isNaN(index) && reports[index]) {
|
|
24770
|
-
const { readFile:
|
|
24771
|
-
const content = await
|
|
25584
|
+
const { readFile: readFile39 } = await import('fs/promises');
|
|
25585
|
+
const content = await readFile39(join(reportsDir, reports[index]), "utf-8");
|
|
24772
25586
|
return { message: `# Security Report
|
|
24773
25587
|
|
|
24774
25588
|
${content}` };
|
|
24775
25589
|
}
|
|
24776
25590
|
const match = reports.find((r) => r.includes(reportId));
|
|
24777
25591
|
if (match) {
|
|
24778
|
-
const { readFile:
|
|
24779
|
-
const content = await
|
|
25592
|
+
const { readFile: readFile39 } = await import('fs/promises');
|
|
25593
|
+
const content = await readFile39(join(reportsDir, match), "utf-8");
|
|
24780
25594
|
return { message: `# Security Report
|
|
24781
25595
|
|
|
24782
25596
|
${content}` };
|
|
@@ -25186,12 +26000,6 @@ var FleetManager = class {
|
|
|
25186
26000
|
this.usage.dispose();
|
|
25187
26001
|
}
|
|
25188
26002
|
};
|
|
25189
|
-
function expectDefined24(value) {
|
|
25190
|
-
if (value === null || value === void 0) {
|
|
25191
|
-
throw new Error("Expected value to be defined");
|
|
25192
|
-
}
|
|
25193
|
-
return value;
|
|
25194
|
-
}
|
|
25195
26003
|
function createMcpControlTool(opts) {
|
|
25196
26004
|
const { getConfig, configPath, registry } = opts;
|
|
25197
26005
|
const inputSchema = {
|
|
@@ -25343,7 +26151,7 @@ async function runDisable(name, deps) {
|
|
|
25343
26151
|
const mcpServers = {
|
|
25344
26152
|
...full.mcpServers ?? {}
|
|
25345
26153
|
};
|
|
25346
|
-
const existing =
|
|
26154
|
+
const existing = expectDefined(mcpServers[name]);
|
|
25347
26155
|
mcpServers[name] = { ...existing, enabled: false };
|
|
25348
26156
|
full.mcpServers = mcpServers;
|
|
25349
26157
|
await writeConfig(deps.configPath, full);
|
|
@@ -25414,12 +26222,6 @@ function badge(state) {
|
|
|
25414
26222
|
}
|
|
25415
26223
|
|
|
25416
26224
|
// src/extension/registry.ts
|
|
25417
|
-
function expectDefined25(value) {
|
|
25418
|
-
if (value === null || value === void 0) {
|
|
25419
|
-
throw new Error("Expected value to be defined");
|
|
25420
|
-
}
|
|
25421
|
-
return value;
|
|
25422
|
-
}
|
|
25423
26225
|
var ExtensionRegistry = class {
|
|
25424
26226
|
extensions = [];
|
|
25425
26227
|
promptContributors = [];
|
|
@@ -25585,7 +26387,7 @@ var ExtensionRegistry = class {
|
|
|
25585
26387
|
* default runner, each subsequent wrapper wraps the previous.
|
|
25586
26388
|
*/
|
|
25587
26389
|
wrapProviderRunner(inner) {
|
|
25588
|
-
const wrappers = this.extensions.filter((e) => e.wrapProviderRunner).map((e) => ({ name: e.name, wrap:
|
|
26390
|
+
const wrappers = this.extensions.filter((e) => e.wrapProviderRunner).map((e) => ({ name: e.name, wrap: expectDefined(e.wrapProviderRunner) }));
|
|
25589
26391
|
if (wrappers.length === 0) return inner;
|
|
25590
26392
|
let composed = inner;
|
|
25591
26393
|
for (let i = wrappers.length - 1; i >= 0; i--) {
|
|
@@ -26602,6 +27404,7 @@ async function bootConfig(options = {}) {
|
|
|
26602
27404
|
await fsp3.mkdir(wpaths.projectDir, { recursive: true });
|
|
26603
27405
|
await fsp3.mkdir(wpaths.projectSessions, { recursive: true });
|
|
26604
27406
|
await writeProjectMeta(wpaths, projectRoot);
|
|
27407
|
+
await ensureGitignore(projectRoot);
|
|
26605
27408
|
cleanupStaleProjects(wpaths).catch(() => {
|
|
26606
27409
|
});
|
|
26607
27410
|
const vault = new DefaultSecretVault({ keyFile: wpaths.secretsKey });
|
|
@@ -26677,6 +27480,22 @@ async function writeProjectMeta(paths, projectRoot) {
|
|
|
26677
27480
|
} catch {
|
|
26678
27481
|
}
|
|
26679
27482
|
}
|
|
27483
|
+
var GITIGNORE_ENTRY = ".wrongstack/\n";
|
|
27484
|
+
async function ensureGitignore(projectRoot) {
|
|
27485
|
+
const gitignorePath = path6.join(projectRoot, ".gitignore");
|
|
27486
|
+
try {
|
|
27487
|
+
let content = "";
|
|
27488
|
+
try {
|
|
27489
|
+
content = await fsp3.readFile(gitignorePath, "utf8");
|
|
27490
|
+
} catch {
|
|
27491
|
+
}
|
|
27492
|
+
if (!content.includes(".wrongstack")) {
|
|
27493
|
+
const updated = content ? content.endsWith("\n") ? content + GITIGNORE_ENTRY : content + "\n" + GITIGNORE_ENTRY : GITIGNORE_ENTRY;
|
|
27494
|
+
await fsp3.writeFile(gitignorePath, updated, "utf8");
|
|
27495
|
+
}
|
|
27496
|
+
} catch {
|
|
27497
|
+
}
|
|
27498
|
+
}
|
|
26680
27499
|
async function cleanupStaleProjects(wpaths) {
|
|
26681
27500
|
const projectsRoot = path6.dirname(wpaths.projectDir);
|
|
26682
27501
|
let entries;
|
|
@@ -27114,7 +27933,10 @@ var DefaultSystemPromptBuilder = class {
|
|
|
27114
27933
|
skillCache;
|
|
27115
27934
|
/** Cached full skill bodies (after frontmatter), built once per session. */
|
|
27116
27935
|
skillBodyCache;
|
|
27936
|
+
/** Tools from last build — used for memory relevance scoring. */
|
|
27937
|
+
_lastBuildTools;
|
|
27117
27938
|
async build(ctx) {
|
|
27939
|
+
this._lastBuildTools = ctx.tools;
|
|
27118
27940
|
if (this.opts.skillLoader && !this.skillCache) {
|
|
27119
27941
|
try {
|
|
27120
27942
|
const entries = await this.opts.skillLoader.listEntries();
|
|
@@ -27418,10 +28240,31 @@ summarize it, and let the tool result hold only the summary.`);
|
|
|
27418
28240
|
const parts = [];
|
|
27419
28241
|
if (this.opts.memoryStore) {
|
|
27420
28242
|
try {
|
|
27421
|
-
|
|
27422
|
-
|
|
28243
|
+
if (this.opts.memoryStore.scoreRelevant) {
|
|
28244
|
+
const toolNames = this._lastBuildTools?.map((t2) => t2.name) ?? [];
|
|
28245
|
+
const scored = await this.opts.memoryStore.scoreRelevant(
|
|
28246
|
+
{
|
|
28247
|
+
currentTask: "",
|
|
28248
|
+
toolNames
|
|
28249
|
+
},
|
|
28250
|
+
"project-memory",
|
|
28251
|
+
8
|
|
28252
|
+
);
|
|
28253
|
+
if (scored.length > 0) {
|
|
28254
|
+
const lines = ["# Relevant Memory"];
|
|
28255
|
+
for (const e of scored) {
|
|
28256
|
+
const badge2 = e.type ? `[\`${e.type.replace("_", "-")}\`] ` : "";
|
|
28257
|
+
const priorityMark = e.priority === "critical" ? "\u26A1" : e.priority === "high" ? "\u25B2" : "";
|
|
28258
|
+
lines.push(`- ${priorityMark}${badge2}${e.text}${e.tags ? ` \`#${e.tags.join(" #")}\`` : ""}`);
|
|
28259
|
+
}
|
|
28260
|
+
parts.push(lines.join("\n"));
|
|
28261
|
+
}
|
|
28262
|
+
} else {
|
|
28263
|
+
const mem = await this.opts.memoryStore.readAll();
|
|
28264
|
+
if (mem.trim()) parts.push(`# Project Memory
|
|
27423
28265
|
|
|
27424
28266
|
${mem}`);
|
|
28267
|
+
}
|
|
27425
28268
|
} catch {
|
|
27426
28269
|
}
|
|
27427
28270
|
}
|
|
@@ -27470,8 +28313,8 @@ ${this.skillBodyCache}`);
|
|
|
27470
28313
|
}
|
|
27471
28314
|
async dirExists(p) {
|
|
27472
28315
|
try {
|
|
27473
|
-
const
|
|
27474
|
-
return
|
|
28316
|
+
const stat11 = await fsp3.stat(p);
|
|
28317
|
+
return stat11.isDirectory();
|
|
27475
28318
|
} catch {
|
|
27476
28319
|
return false;
|
|
27477
28320
|
}
|
|
@@ -27967,7 +28810,7 @@ var DefaultPluginAPI = class {
|
|
|
27967
28810
|
return off;
|
|
27968
28811
|
}
|
|
27969
28812
|
emitCustom(event, payload) {
|
|
27970
|
-
this.events.
|
|
28813
|
+
this.events.emitCustom(event, payload);
|
|
27971
28814
|
}
|
|
27972
28815
|
onConfigChange(handler) {
|
|
27973
28816
|
if (!this.configStore) return () => {
|
|
@@ -28308,20 +29151,28 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
|
|
|
28308
29151
|
}
|
|
28309
29152
|
|
|
28310
29153
|
// src/execution/prompt-enhancer.ts
|
|
28311
|
-
var ENHANCER_SYSTEM_PROMPT = `You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into
|
|
29154
|
+
var ENHANCER_SYSTEM_PROMPT = `You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into clearer, unambiguous instructions that the coding agent can act on confidently.
|
|
28312
29155
|
|
|
28313
29156
|
Rules:
|
|
28314
29157
|
- Preserve the user's intent and scope EXACTLY. Do not add new requirements, features, constraints, or steps the user did not ask for. Do not remove anything they did ask for.
|
|
28315
29158
|
- Do NOT answer, solve, or perform the request. Only restate it more clearly.
|
|
28316
29159
|
- Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.
|
|
28317
29160
|
- Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.
|
|
28318
|
-
- Be concise: one tight instruction (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
|
|
29161
|
+
- Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
|
|
28319
29162
|
- If the message is already clear and complete, return it essentially unchanged.
|
|
28320
|
-
|
|
29163
|
+
|
|
29164
|
+
You MUST output TWO versions of the refined request, separated by a line containing only "---".
|
|
29165
|
+
- First version: refined in the SAME LANGUAGE the user wrote in (if Turkish \u2192 Turkish, if Spanish \u2192 Spanish, etc.).
|
|
29166
|
+
- Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).
|
|
29167
|
+
|
|
29168
|
+
Output format:
|
|
29169
|
+
<refined in user's language>
|
|
29170
|
+
---
|
|
29171
|
+
<refined in English>
|
|
28321
29172
|
|
|
28322
29173
|
When earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 "it", "that", "the same", "the other one", "this file", "again" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation.
|
|
28323
29174
|
|
|
28324
|
-
Output ONLY the
|
|
29175
|
+
Output ONLY the two versions separated by "---" \u2014 nothing else.`;
|
|
28325
29176
|
var AFFIRMATION_RE = /^(y|n|yes|no|yep|nope|ok|okay|sure|go|go ahead|continue|proceed|stop|cancel|done|next|skip|retry|again|please do|do it)\b[.! ]*$/i;
|
|
28326
29177
|
function shouldEnhance(text) {
|
|
28327
29178
|
const t2 = text.trim();
|
|
@@ -28368,12 +29219,23 @@ async function enhanceUserPrompt(opts) {
|
|
|
28368
29219
|
const signal = opts.signal ? AbortSignal.any([opts.signal, timer.signal]) : timer.signal;
|
|
28369
29220
|
try {
|
|
28370
29221
|
const res = await provider.complete(req, { signal });
|
|
28371
|
-
const
|
|
28372
|
-
if (!
|
|
29222
|
+
const raw = res.content.filter(isTextBlock).map((b) => b.text).join("\n").trim();
|
|
29223
|
+
if (!raw) {
|
|
28373
29224
|
opts.onError?.("model returned no text");
|
|
28374
29225
|
return null;
|
|
28375
29226
|
}
|
|
28376
|
-
|
|
29227
|
+
const sepIdx = raw.indexOf("\n---\n");
|
|
29228
|
+
if (sepIdx === -1) {
|
|
29229
|
+
opts.onError?.("model did not produce two versions");
|
|
29230
|
+
return { refined: raw, english: raw };
|
|
29231
|
+
}
|
|
29232
|
+
const refined = raw.slice(0, sepIdx).trim();
|
|
29233
|
+
const english = raw.slice(sepIdx + 5).trim();
|
|
29234
|
+
if (!refined || !english) {
|
|
29235
|
+
opts.onError?.("one or both versions empty");
|
|
29236
|
+
return null;
|
|
29237
|
+
}
|
|
29238
|
+
return { refined, english };
|
|
28377
29239
|
} catch (err) {
|
|
28378
29240
|
if (opts.signal?.aborted) return null;
|
|
28379
29241
|
if (timer.signal.aborted) {
|
|
@@ -28476,8 +29338,8 @@ var PhaseGraphBuilder = class _PhaseGraphBuilder {
|
|
|
28476
29338
|
return graph;
|
|
28477
29339
|
}
|
|
28478
29340
|
/**
|
|
28479
|
-
*
|
|
28480
|
-
*
|
|
29341
|
+
* Create phases from an existing TaskGraph.
|
|
29342
|
+
* Groups tasks into phases by priority and type.
|
|
28481
29343
|
*/
|
|
28482
29344
|
static fromTaskGraph(taskGraph, options) {
|
|
28483
29345
|
const tasksPerPhase = options.tasksPerPhase ?? 5;
|
|
@@ -28555,8 +29417,8 @@ var PhaseOrchestrator = class {
|
|
|
28555
29417
|
}
|
|
28556
29418
|
// ─── Lifecycle ────────────────────────────────────────────────────────────
|
|
28557
29419
|
/**
|
|
28558
|
-
*
|
|
28559
|
-
*
|
|
29420
|
+
* Start the full phase flow.
|
|
29421
|
+
* In autonomous mode, starts root phases and automatically starts the next phase when they finish.
|
|
28560
29422
|
*/
|
|
28561
29423
|
async start() {
|
|
28562
29424
|
this.stopped = false;
|
|
@@ -28583,17 +29445,17 @@ var PhaseOrchestrator = class {
|
|
|
28583
29445
|
this.tickInterval = setInterval(() => this.tick(), 1e3);
|
|
28584
29446
|
}
|
|
28585
29447
|
}
|
|
28586
|
-
/**
|
|
29448
|
+
/** Wait for all pending phase merges, dependency-ordered and globally serialized. */
|
|
28587
29449
|
async drainMerges() {
|
|
28588
29450
|
await Promise.allSettled([...this.phaseMergePromise.values()]);
|
|
28589
29451
|
await this.mergeQueue.catch(() => {
|
|
28590
29452
|
});
|
|
28591
29453
|
}
|
|
28592
|
-
/**
|
|
29454
|
+
/** Pause: active phases continue, but no new phase starts. */
|
|
28593
29455
|
pause() {
|
|
28594
29456
|
this.paused = true;
|
|
28595
29457
|
}
|
|
28596
|
-
/**
|
|
29458
|
+
/** Resume: new phases may start again. */
|
|
28597
29459
|
resume() {
|
|
28598
29460
|
this.paused = false;
|
|
28599
29461
|
this.tick().catch((err) => {
|
|
@@ -28603,7 +29465,7 @@ var PhaseOrchestrator = class {
|
|
|
28603
29465
|
);
|
|
28604
29466
|
});
|
|
28605
29467
|
}
|
|
28606
|
-
/**
|
|
29468
|
+
/** Stop completely, including active phases. */
|
|
28607
29469
|
stop() {
|
|
28608
29470
|
this.stopped = true;
|
|
28609
29471
|
if (this.tickInterval) {
|
|
@@ -28801,7 +29663,10 @@ var PhaseOrchestrator = class {
|
|
|
28801
29663
|
const depPromises = phase.dependsOn.map((d) => this.phaseMergePromise.get(d)).filter((p) => Boolean(p));
|
|
28802
29664
|
const merged = (async () => {
|
|
28803
29665
|
await Promise.allSettled(depPromises);
|
|
28804
|
-
this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle))
|
|
29666
|
+
this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle)).catch((err) => {
|
|
29667
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29668
|
+
console.error(`[PhaseOrchestrator] mergeOne failed for phase ${phase.id}: ${msg}`);
|
|
29669
|
+
});
|
|
28805
29670
|
await this.mergeQueue;
|
|
28806
29671
|
})();
|
|
28807
29672
|
this.phaseMergePromise.set(phase.id, merged);
|
|
@@ -29189,6 +30054,7 @@ var AutoPhaseRunner = class {
|
|
|
29189
30054
|
orchestrator = null;
|
|
29190
30055
|
opts;
|
|
29191
30056
|
progressInterval = null;
|
|
30057
|
+
maxRunTimer = null;
|
|
29192
30058
|
graphCompletedHandler = (payload) => {
|
|
29193
30059
|
const p = payload;
|
|
29194
30060
|
if (this.graph && p.graphId === this.graph.id) {
|
|
@@ -29256,6 +30122,11 @@ var AutoPhaseRunner = class {
|
|
|
29256
30122
|
if (progress) this.opts.onProgress?.(progress);
|
|
29257
30123
|
}, 2e3);
|
|
29258
30124
|
}
|
|
30125
|
+
this.maxRunTimer = setTimeout(
|
|
30126
|
+
() => this.stop(),
|
|
30127
|
+
this.opts.maxRunDurationMs ?? 24 * 60 * 6e4
|
|
30128
|
+
);
|
|
30129
|
+
this.maxRunTimer.unref?.();
|
|
29259
30130
|
if (this.opts.events) {
|
|
29260
30131
|
const events = this.opts.events;
|
|
29261
30132
|
const onUntyped = events.on;
|
|
@@ -29298,6 +30169,10 @@ var AutoPhaseRunner = class {
|
|
|
29298
30169
|
clearInterval(this.progressInterval);
|
|
29299
30170
|
this.progressInterval = null;
|
|
29300
30171
|
}
|
|
30172
|
+
if (this.maxRunTimer) {
|
|
30173
|
+
clearTimeout(this.maxRunTimer);
|
|
30174
|
+
this.maxRunTimer = null;
|
|
30175
|
+
}
|
|
29301
30176
|
this.unsubscribeCompleted?.();
|
|
29302
30177
|
this.unsubscribeCompleted = null;
|
|
29303
30178
|
this.unsubscribeFailed?.();
|
|
@@ -29343,14 +30218,14 @@ var AutoPhasePlanner = class {
|
|
|
29343
30218
|
this.opts = opts;
|
|
29344
30219
|
}
|
|
29345
30220
|
opts;
|
|
29346
|
-
/**
|
|
30221
|
+
/** Convert the goal into a phase-and-todo plan. */
|
|
29347
30222
|
async plan() {
|
|
29348
30223
|
const prompt = this.buildPrompt();
|
|
29349
30224
|
const raw = await this.opts.runOnce(prompt);
|
|
29350
30225
|
const phases = this.parse(raw);
|
|
29351
30226
|
return { phases, raw, parseFailed: phases.length === 0 };
|
|
29352
30227
|
}
|
|
29353
|
-
/**
|
|
30228
|
+
/** Instruction prompt for the plan the model should produce. */
|
|
29354
30229
|
buildPrompt() {
|
|
29355
30230
|
const minP = this.opts.minPhases ?? 3;
|
|
29356
30231
|
const maxP = this.opts.maxPhases ?? 8;
|
|
@@ -29398,7 +30273,7 @@ ${ctx}
|
|
|
29398
30273
|
"```"
|
|
29399
30274
|
].filter((l) => l !== "").join("\n");
|
|
29400
30275
|
}
|
|
29401
|
-
/**
|
|
30276
|
+
/** Extract JSON from raw output, validate it, and convert it to PhaseTemplate[]. */
|
|
29402
30277
|
parse(raw) {
|
|
29403
30278
|
const json = extractJSONArray(raw);
|
|
29404
30279
|
if (!json) return [];
|
|
@@ -30138,7 +31013,8 @@ ${check.stderr}`);
|
|
|
30138
31013
|
const child = spawn(this.gitBin, args, {
|
|
30139
31014
|
cwd,
|
|
30140
31015
|
env: buildChildEnv(),
|
|
30141
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
31016
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
31017
|
+
signal: AbortSignal.timeout(3e4)
|
|
30142
31018
|
});
|
|
30143
31019
|
child.stdout?.on("data", (c) => {
|
|
30144
31020
|
stdout += c.toString();
|
|
@@ -30506,12 +31382,6 @@ var ReplayProviderRunner = class {
|
|
|
30506
31382
|
};
|
|
30507
31383
|
|
|
30508
31384
|
// src/plugins/prompts-plugin.ts
|
|
30509
|
-
function expectDefined26(value) {
|
|
30510
|
-
if (value === null || value === void 0) {
|
|
30511
|
-
throw new Error("Expected value to be defined");
|
|
30512
|
-
}
|
|
30513
|
-
return value;
|
|
30514
|
-
}
|
|
30515
31385
|
function createPromptsPlugin(opts) {
|
|
30516
31386
|
let store = null;
|
|
30517
31387
|
return {
|
|
@@ -30565,7 +31435,7 @@ ${lines.join("\n")}
|
|
|
30565
31435
|
if (!restJoined) return { message: "Usage: /prompts view <title>" };
|
|
30566
31436
|
const matches = await store.find(restJoined);
|
|
30567
31437
|
if (matches.length === 0) return { message: `No prompt matching "${restJoined}".` };
|
|
30568
|
-
const entry = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ??
|
|
31438
|
+
const entry = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined(matches[0]);
|
|
30569
31439
|
const tags = entry.tags.length > 0 ? ` [${entry.tags.join(", ")}]` : "";
|
|
30570
31440
|
return {
|
|
30571
31441
|
message: `# ${entry.title}${tags}
|
|
@@ -30588,7 +31458,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
|
|
|
30588
31458
|
if (!restJoined) return { message: "Usage: /prompts delete <title>" };
|
|
30589
31459
|
const matches = await store.find(restJoined);
|
|
30590
31460
|
if (matches.length === 0) return { message: `No prompt matching "${restJoined}".` };
|
|
30591
|
-
const exact = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ??
|
|
31461
|
+
const exact = matches.find((m) => m.title.toLowerCase() === restJoined.toLowerCase()) ?? expectDefined(matches[0]);
|
|
30592
31462
|
const deleted = await store.delete(exact.id);
|
|
30593
31463
|
return { message: deleted ? `Deleted "${exact.title}".` : "Delete failed." };
|
|
30594
31464
|
}
|
|
@@ -30598,7 +31468,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
|
|
|
30598
31468
|
if (!parsed.title) return { message: 'Usage: /prompts edit "title" "new content"' };
|
|
30599
31469
|
const matches = await store.find(parsed.title);
|
|
30600
31470
|
if (matches.length === 0) return { message: `No prompt matching "${parsed.title}".` };
|
|
30601
|
-
const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ??
|
|
31471
|
+
const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined(matches[0]);
|
|
30602
31472
|
exact.content = parsed.content;
|
|
30603
31473
|
exact.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
30604
31474
|
await store.save(exact);
|
|
@@ -30610,7 +31480,7 @@ ${dim2(`id: ${entry.id} | created: ${entry.createdAt}`)}`
|
|
|
30610
31480
|
if (!parsed.title) return { message: 'Usage: /prompts extend "title" <instructions>' };
|
|
30611
31481
|
const matches = await store.find(parsed.title);
|
|
30612
31482
|
if (matches.length === 0) return { message: `No prompt matching "${parsed.title}".` };
|
|
30613
|
-
const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ??
|
|
31483
|
+
const exact = matches.find((m) => m.title.toLowerCase() === parsed.title?.toLowerCase()) ?? expectDefined(matches[0]);
|
|
30614
31484
|
const prov = ctx.provider;
|
|
30615
31485
|
if (!prov?.complete) return { message: "LLM not available. Configure a provider first." };
|
|
30616
31486
|
const enhanced = await prov.complete(ctx.model, [
|
|
@@ -30645,9 +31515,9 @@ function parseTitleContent(args) {
|
|
|
30645
31515
|
const trimmed = args.trim();
|
|
30646
31516
|
if (!trimmed) return { title: "", content: "" };
|
|
30647
31517
|
const doubleMatch = /^"([^"]+)"\s+"([^"]+)"$/.exec(trimmed) || /^'([^']+)'\s+'([^']+)'$/.exec(trimmed);
|
|
30648
|
-
if (doubleMatch) return { title:
|
|
31518
|
+
if (doubleMatch) return { title: expectDefined(doubleMatch[1]), content: expectDefined(doubleMatch[2]) };
|
|
30649
31519
|
const singleMatch = /^'([^']+)'\s+(.+)$/.exec(trimmed);
|
|
30650
|
-
if (singleMatch) return { title:
|
|
31520
|
+
if (singleMatch) return { title: expectDefined(singleMatch[1]), content: expectDefined(singleMatch[2]) };
|
|
30651
31521
|
const firstSpace = trimmed.indexOf(" ");
|
|
30652
31522
|
if (firstSpace === -1) return { title: trimmed, content: "" };
|
|
30653
31523
|
return { title: trimmed.slice(0, firstSpace), content: trimmed.slice(firstSpace + 1) };
|
|
@@ -30658,12 +31528,6 @@ function dim2(s) {
|
|
|
30658
31528
|
|
|
30659
31529
|
// src/plugins/sync-plugin.ts
|
|
30660
31530
|
init_atomic_write();
|
|
30661
|
-
function expectDefined27(value) {
|
|
30662
|
-
if (value === null || value === void 0) {
|
|
30663
|
-
throw new Error("Expected value to be defined");
|
|
30664
|
-
}
|
|
30665
|
-
return value;
|
|
30666
|
-
}
|
|
30667
31531
|
function createSyncPlugin(opts) {
|
|
30668
31532
|
let cloud = null;
|
|
30669
31533
|
let configStore;
|
|
@@ -30737,7 +31601,7 @@ function buildSyncCommand(cloud, configStore, vault, syncConfigPath) {
|
|
|
30737
31601
|
if (!repo || !repo.includes("/")) {
|
|
30738
31602
|
return { message: 'Invalid repo format. Expected "owner/repo".' };
|
|
30739
31603
|
}
|
|
30740
|
-
const storedToken = vault ? vault.encrypt(
|
|
31604
|
+
const storedToken = vault ? vault.encrypt(expectDefined(token)) : expectDefined(token);
|
|
30741
31605
|
const syncConfig = {
|
|
30742
31606
|
enabled: true,
|
|
30743
31607
|
repo,
|
|
@@ -30878,7 +31742,7 @@ function createGitPlugin() {
|
|
|
30878
31742
|
async function runGit(args, cwd) {
|
|
30879
31743
|
try {
|
|
30880
31744
|
return await new Promise((resolve13, reject) => {
|
|
30881
|
-
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
31745
|
+
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], signal: AbortSignal.timeout(3e4) });
|
|
30882
31746
|
let stdout = "";
|
|
30883
31747
|
let stderr = "";
|
|
30884
31748
|
child.stdout?.on("data", (d) => {
|
|
@@ -31003,6 +31867,21 @@ function buildCommitCommand() {
|
|
|
31003
31867
|
}
|
|
31004
31868
|
const dryRun = args.includes("--dry-run") || args.includes("-n");
|
|
31005
31869
|
const noLlm = args.includes("--no-llm");
|
|
31870
|
+
let worktreeWarning = "";
|
|
31871
|
+
try {
|
|
31872
|
+
const wtResult = await runGit(["worktree", "list", "--porcelain"], cwd);
|
|
31873
|
+
const worktrees = wtResult.stdout.split("\n\n").filter((b) => b.trim().startsWith("worktree "));
|
|
31874
|
+
if (worktrees.length > 1) {
|
|
31875
|
+
const branches = worktrees.map((b) => {
|
|
31876
|
+
const branchLine = b.split("\n").find((l) => l.startsWith("branch "));
|
|
31877
|
+
return branchLine ? branchLine.slice(7).replace("refs/heads/", "") : "?";
|
|
31878
|
+
}).filter(Boolean);
|
|
31879
|
+
worktreeWarning = `
|
|
31880
|
+
${color.yellow("\u26A0")} ${color.dim("Simultaneous edits:")} ${worktrees.length} worktrees active (${branches.join(", ")}). ${color.dim("Changes from other agents may be captured. Review the diff below.")}
|
|
31881
|
+
`;
|
|
31882
|
+
}
|
|
31883
|
+
} catch {
|
|
31884
|
+
}
|
|
31006
31885
|
let message = null;
|
|
31007
31886
|
const provider = noLlm ? null : asLLMProvider(ctx?.provider);
|
|
31008
31887
|
if (provider && ctx?.model) {
|
|
@@ -31011,26 +31890,40 @@ function buildCommitCommand() {
|
|
|
31011
31890
|
}
|
|
31012
31891
|
if (!message) message = await generateCommitMessageHeuristics(cwd);
|
|
31013
31892
|
if (dryRun) {
|
|
31893
|
+
const diffStat2 = (await runGit(["diff", "--stat"], cwd)).stdout || "(no changes)";
|
|
31014
31894
|
return {
|
|
31015
|
-
message:
|
|
31016
|
-
|
|
31017
|
-
|
|
31018
|
-
|
|
31019
|
-
|
|
31895
|
+
message: [
|
|
31896
|
+
worktreeWarning,
|
|
31897
|
+
`${color.dim("Would commit:")}`,
|
|
31898
|
+
` ${color.green(message)}`,
|
|
31899
|
+
"",
|
|
31900
|
+
`${color.dim(diffStat2)}`,
|
|
31901
|
+
`${color.dim("(dry-run \u2014 no actual commit)")}`
|
|
31902
|
+
].filter(Boolean).join("\n")
|
|
31020
31903
|
};
|
|
31021
31904
|
}
|
|
31022
31905
|
const stageResult = await runGit(["add", "."], cwd);
|
|
31023
31906
|
if (stageResult.code !== 0) return { message: `Stage failed: ${stageResult.stderr}` };
|
|
31907
|
+
const diffStat = (await runGit(["diff", "--cached", "--stat"], cwd)).stdout || "";
|
|
31908
|
+
const fullDiff = (await runGit(["diff", "--cached"], cwd)).stdout || "";
|
|
31909
|
+
const diffPreview = fullDiff.length > 8e3 ? fullDiff.slice(0, 8e3) + "\n\n... (diff truncated, showing first 8KB)" : fullDiff;
|
|
31024
31910
|
const commitResult = await runGit(["commit", "-m", message], cwd);
|
|
31025
31911
|
if (commitResult.code !== 0) return { message: `Commit failed: ${commitResult.stderr}` };
|
|
31026
31912
|
const hash = (await runGit(["rev-parse", "--short", "HEAD"], cwd)).stdout.trim();
|
|
31027
31913
|
const hasRemote = (await runGit(["remote"], cwd)).stdout.trim().length > 0;
|
|
31028
31914
|
const pushMsg = hasRemote ? `
|
|
31029
|
-
|
|
31030
31915
|
${color.dim("Tip: Run /push to push to remote")}` : "";
|
|
31031
31916
|
return {
|
|
31032
|
-
message:
|
|
31033
|
-
|
|
31917
|
+
message: [
|
|
31918
|
+
worktreeWarning,
|
|
31919
|
+
`${color.green("\u2713")} Committed: ${color.bold(message)}`,
|
|
31920
|
+
` ${color.dim(hash)}`,
|
|
31921
|
+
"",
|
|
31922
|
+
color.dim("\u2500\u2500\u2500 Staged diff \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
31923
|
+
color.dim(diffStat),
|
|
31924
|
+
diffPreview ? color.dim(diffPreview) : "",
|
|
31925
|
+
pushMsg
|
|
31926
|
+
].filter(Boolean).join("\n")
|
|
31034
31927
|
};
|
|
31035
31928
|
}
|
|
31036
31929
|
};
|
|
@@ -31574,6 +32467,6 @@ ${formatPlan(updated)}`
|
|
|
31574
32467
|
};
|
|
31575
32468
|
}
|
|
31576
32469
|
|
|
31577
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob,
|
|
32470
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MEMORY_TYPE_LABELS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, hookMatcherMatches, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, zaiVisionServer };
|
|
31578
32471
|
//# sourceMappingURL=index.js.map
|
|
31579
32472
|
//# sourceMappingURL=index.js.map
|