@wrongstack/core 0.257.2 → 0.264.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-BrxWHEOm.d.ts → agent-bridge-D8sa1vtv.d.ts} +1 -1
- package/dist/{agent-subagent-runner-US741uBH.d.ts → agent-subagent-runner-c9DLkaas.d.ts} +31 -9
- package/dist/{brain-TjEEwSpw.d.ts → brain-O1IdKPaK.d.ts} +59 -2
- package/dist/{compactor-C5sT4U7I.d.ts → compactor-BBy0rCtB.d.ts} +1 -1
- package/dist/{config-DuAu23zm.d.ts → config-Dz2F3H2K.d.ts} +7 -1
- package/dist/{context-CGdgA0q6.d.ts → context-BGSpZNSE.d.ts} +33 -0
- package/dist/coordination/index.d.ts +1681 -15
- package/dist/coordination/index.js +2826 -405
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +2258 -1433
- package/dist/defaults/index.js.map +1 -1
- package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +502 -398
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +2 -2
- package/dist/execution/prompt-enhancer.js +7 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{goal-preamble-CznHTZqP.d.ts → goal-preamble-DzjFuN3p.d.ts} +21 -9
- package/dist/{goal-store-CV9Yz2X_.d.ts → goal-store-CxWmCGbH.d.ts} +4 -2
- package/dist/{index-CC0Mcm05.d.ts → index-CYIQrXVF.d.ts} +8 -8
- package/dist/{index-CitPrI3a.d.ts → index-CbLSI66_.d.ts} +5 -5
- package/dist/index.d.ts +50 -94
- package/dist/index.js +16009 -12406
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +6 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-CJ4SyAFE.d.ts → llm-selector-DzxuZnNz.d.ts} +2 -2
- package/dist/{mcp-servers-D8YnLaEp.d.ts → mcp-servers-DC4QRPUI.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +6 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-ByZCdFuQ.d.ts → models-registry-B_siPxqN.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DqTUEAeC.d.ts → multi-agent-coordinator-CK5Jdj9K.d.ts} +2 -2
- package/dist/{null-fleet-bus-B5mfTJXT.d.ts → null-fleet-bus-DgvD4SCO.d.ts} +13 -8
- package/dist/observability/index.d.ts +2 -2
- package/dist/observability/index.js +8 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/{parallel-eternal-engine-C0juOszP.d.ts → parallel-eternal-engine-bK0JQBR_.d.ts} +13 -9
- package/dist/{path-resolver-CbkT-RMU.d.ts → path-resolver-BPEDlN38.d.ts} +3 -3
- package/dist/{permission-CwBBpCoF.d.ts → permission-4yvGmMRB.d.ts} +1 -1
- package/dist/{permission-policy-B8rSu908.d.ts → permission-policy-C6XpsBOy.d.ts} +3 -2
- package/dist/{pipeline-JG8XoudC.d.ts → pipeline-CXCeMz8J.d.ts} +58 -3
- package/dist/{plan-templates-DPiQMkBz.d.ts → plan-templates-BvzRBkJc.d.ts} +32 -11
- package/dist/{provider-runner-hM7EXlLI.d.ts → provider-runner-C5aQpDWE.d.ts} +3 -3
- package/dist/{retry-policy-Tg7LXkoK.d.ts → retry-policy-CFhdtRzz.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +59 -31
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-gxtFZYBt.d.ts → secret-vault-CxiVLbt1.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -4
- package/dist/security/index.js +238 -204
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-DWsqVjGf.d.ts → selector-gIuhRTkN.d.ts} +1 -1
- package/dist/{session-event-bridge-BAFWdgQ3.d.ts → session-event-bridge-DkvvrpDt.d.ts} +8 -2
- package/dist/{session-reader-CqRvaL5v.d.ts → session-reader-KdfVwkKP.d.ts} +1 -1
- package/dist/skills/index.js +67 -64
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +50 -22
- package/dist/storage/index.js +1654 -525
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +57 -0
- package/dist/tools/index.js +411 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +711 -694
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error.d.ts +7 -0
- package/dist/utils/error.js +8 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +7 -67
- package/dist/utils/index.js +17 -5
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/output-standards/SKILL.md +14 -9
- package/skills/output-standards/SKILL.save.md +3 -2
- package/dist/package-outdated-watcher-BSgR_kK-.d.ts +0 -581
package/dist/execution/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
|
-
import { promisify } from 'util';
|
|
3
|
-
import * as fs from 'fs/promises';
|
|
4
1
|
import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
5
3
|
import * as path2 from 'path';
|
|
6
4
|
import * as os from 'os';
|
|
5
|
+
import { execFile } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
8
|
|
|
9
9
|
// src/utils/token-estimate.ts
|
|
@@ -1250,6 +1250,323 @@ function readPolicy(ctx) {
|
|
|
1250
1250
|
if (typeof candidate.preserveK !== "number" || !candidate.thresholds) return null;
|
|
1251
1251
|
return candidate;
|
|
1252
1252
|
}
|
|
1253
|
+
async function atomicWrite(targetPath, content, opts = {}) {
|
|
1254
|
+
const dir = path2.dirname(targetPath);
|
|
1255
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1256
|
+
const tmp = path2.join(dir, `.${path2.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
1257
|
+
try {
|
|
1258
|
+
if (typeof content === "string") {
|
|
1259
|
+
await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
1260
|
+
} else {
|
|
1261
|
+
await fs.writeFile(tmp, content, { flag: "wx" });
|
|
1262
|
+
}
|
|
1263
|
+
try {
|
|
1264
|
+
const fh = await fs.open(tmp, "r+");
|
|
1265
|
+
try {
|
|
1266
|
+
await fh.sync();
|
|
1267
|
+
} finally {
|
|
1268
|
+
await fh.close();
|
|
1269
|
+
}
|
|
1270
|
+
} catch {
|
|
1271
|
+
}
|
|
1272
|
+
let mode;
|
|
1273
|
+
try {
|
|
1274
|
+
const stat2 = await fs.stat(targetPath);
|
|
1275
|
+
mode = stat2.mode & 511;
|
|
1276
|
+
} catch {
|
|
1277
|
+
mode = opts.mode;
|
|
1278
|
+
}
|
|
1279
|
+
if (mode !== void 0) {
|
|
1280
|
+
await fs.chmod(tmp, mode);
|
|
1281
|
+
}
|
|
1282
|
+
await renameWithRetry(tmp, targetPath);
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
try {
|
|
1285
|
+
await fs.unlink(tmp);
|
|
1286
|
+
} catch {
|
|
1287
|
+
}
|
|
1288
|
+
throw err;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
1292
|
+
async function renameWithRetry(from, to) {
|
|
1293
|
+
if (process.platform !== "win32") {
|
|
1294
|
+
await fs.rename(from, to);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
const delays = [10, 25, 60, 120, 250];
|
|
1298
|
+
let lastErr;
|
|
1299
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
1300
|
+
try {
|
|
1301
|
+
await fs.rename(from, to);
|
|
1302
|
+
return;
|
|
1303
|
+
} catch (err) {
|
|
1304
|
+
lastErr = err;
|
|
1305
|
+
const code = err?.code;
|
|
1306
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
1307
|
+
throw err;
|
|
1308
|
+
}
|
|
1309
|
+
await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
throw lastErr;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/utils/error.ts
|
|
1316
|
+
function toErrorMessage(err) {
|
|
1317
|
+
return err instanceof Error ? err.message : String(err);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// src/utils/string.ts
|
|
1321
|
+
function truncate(s, max) {
|
|
1322
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
1323
|
+
}
|
|
1324
|
+
function projectHash(absRoot) {
|
|
1325
|
+
return createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
1326
|
+
}
|
|
1327
|
+
function projectSlug(absRoot) {
|
|
1328
|
+
const base = slugify(path2.basename(absRoot));
|
|
1329
|
+
const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
1330
|
+
return `${base}-${hash}`;
|
|
1331
|
+
}
|
|
1332
|
+
function slugify(name) {
|
|
1333
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
1334
|
+
}
|
|
1335
|
+
function wstackGlobalRoot() {
|
|
1336
|
+
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
1337
|
+
if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
|
|
1338
|
+
return path2.join(os.homedir(), ".wrongstack");
|
|
1339
|
+
}
|
|
1340
|
+
function resolveWstackPaths(opts) {
|
|
1341
|
+
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
1342
|
+
const hash = projectHash(opts.projectRoot);
|
|
1343
|
+
const slug = projectSlug(opts.projectRoot);
|
|
1344
|
+
const projectDir = path2.join(globalRoot, "projects", slug);
|
|
1345
|
+
return {
|
|
1346
|
+
globalRoot,
|
|
1347
|
+
configDir: globalRoot,
|
|
1348
|
+
globalConfig: path2.join(globalRoot, "config.json"),
|
|
1349
|
+
secretsKey: path2.join(globalRoot, ".key"),
|
|
1350
|
+
globalMemory: path2.join(globalRoot, "memory.md"),
|
|
1351
|
+
globalSkills: path2.join(globalRoot, "skills"),
|
|
1352
|
+
globalPrompts: path2.join(globalRoot, "prompts"),
|
|
1353
|
+
cacheDir: path2.join(globalRoot, "cache"),
|
|
1354
|
+
modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
|
|
1355
|
+
modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
|
|
1356
|
+
historyFile: path2.join(globalRoot, "history"),
|
|
1357
|
+
logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
|
|
1358
|
+
projectDir,
|
|
1359
|
+
projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
|
|
1360
|
+
projectMemory: path2.join(projectDir, "memory.md"),
|
|
1361
|
+
projectSessions: path2.join(projectDir, "sessions"),
|
|
1362
|
+
projectTrust: path2.join(projectDir, "trust.json"),
|
|
1363
|
+
projectMeta: path2.join(projectDir, "meta.json"),
|
|
1364
|
+
projectLocalConfig: path2.join(projectDir, "config.local.json"),
|
|
1365
|
+
inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
1366
|
+
inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
1367
|
+
inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
1368
|
+
inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
1369
|
+
projectHash: hash,
|
|
1370
|
+
projectSlug: slug,
|
|
1371
|
+
projectGoal: path2.join(projectDir, "goal.json"),
|
|
1372
|
+
projectSpecs: path2.join(projectDir, "specs"),
|
|
1373
|
+
projectTaskGraphs: path2.join(projectDir, "task-graphs"),
|
|
1374
|
+
projectSddSession: path2.join(projectDir, "sdd-session.json"),
|
|
1375
|
+
projectPlan: path2.join(projectDir, "plan.json"),
|
|
1376
|
+
projectAutophase: path2.join(projectDir, "autophase"),
|
|
1377
|
+
syncConfig: path2.join(globalRoot, "sync.json")
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// src/utils/sleep.ts
|
|
1382
|
+
function sleep(ms) {
|
|
1383
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// src/utils/assert-never.ts
|
|
1387
|
+
function assertNever(x, message) {
|
|
1388
|
+
const err = new Error(
|
|
1389
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
1390
|
+
);
|
|
1391
|
+
err.name = "AssertNeverError";
|
|
1392
|
+
throw err;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// src/utils/tool-output-serializer.ts
|
|
1396
|
+
function createToolOutputSerializer(opts = {}) {
|
|
1397
|
+
const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
|
|
1398
|
+
function serialize(value) {
|
|
1399
|
+
if (typeof value === "string") return value;
|
|
1400
|
+
if (value === null || value === void 0) return "";
|
|
1401
|
+
if (typeof value === "object") {
|
|
1402
|
+
if (Array.isArray(value)) return value.map(serialize).join("\n");
|
|
1403
|
+
if ("text" in value) {
|
|
1404
|
+
const t = value.text;
|
|
1405
|
+
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
1406
|
+
}
|
|
1407
|
+
try {
|
|
1408
|
+
return JSON.stringify(value, null, 2);
|
|
1409
|
+
} catch {
|
|
1410
|
+
return String(value);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return String(value);
|
|
1414
|
+
}
|
|
1415
|
+
function enforceCap(text, remainingBudget) {
|
|
1416
|
+
if (remainingBudget <= 0) {
|
|
1417
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
1418
|
+
}
|
|
1419
|
+
const textBytes = Buffer.byteLength(text, "utf8");
|
|
1420
|
+
if (textBytes <= remainingBudget) {
|
|
1421
|
+
return { text, newBudget: remainingBudget - textBytes };
|
|
1422
|
+
}
|
|
1423
|
+
const marker = `
|
|
1424
|
+
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
1425
|
+
`;
|
|
1426
|
+
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
1427
|
+
const available = remainingBudget - markerBytes;
|
|
1428
|
+
if (available <= 0) {
|
|
1429
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
1430
|
+
}
|
|
1431
|
+
const half = Math.floor(available / 2);
|
|
1432
|
+
const first = text.slice(0, half);
|
|
1433
|
+
const second = text.slice(text.length - half);
|
|
1434
|
+
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
1435
|
+
}
|
|
1436
|
+
return { serialize, enforceCap, capBytes };
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/utils/json-schema-validate.ts
|
|
1440
|
+
function validateAgainstSchema(value, schema) {
|
|
1441
|
+
const errors = [];
|
|
1442
|
+
walk(value, schema, "", errors);
|
|
1443
|
+
return { ok: errors.length === 0, errors };
|
|
1444
|
+
}
|
|
1445
|
+
function walk(value, schema, path4, errors) {
|
|
1446
|
+
if (schema.enum !== void 0) {
|
|
1447
|
+
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
1448
|
+
errors.push({
|
|
1449
|
+
path: path4 || "<root>",
|
|
1450
|
+
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
1451
|
+
});
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
if (typeof schema.type === "string") {
|
|
1456
|
+
if (!checkType(value, schema.type)) {
|
|
1457
|
+
errors.push({
|
|
1458
|
+
path: path4 || "<root>",
|
|
1459
|
+
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
1460
|
+
});
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
if (schema.type === "object" && isPlainObject(value)) {
|
|
1465
|
+
const obj = value;
|
|
1466
|
+
for (const req of schema.required ?? []) {
|
|
1467
|
+
if (!(req in obj)) {
|
|
1468
|
+
errors.push({ path: joinPath(path4, req), message: "required property missing" });
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
if (schema.properties) {
|
|
1472
|
+
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
1473
|
+
if (key in obj) {
|
|
1474
|
+
walk(obj[key], subSchema, joinPath(path4, key), errors);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
1480
|
+
for (let i = 0; i < value.length; i++) {
|
|
1481
|
+
walk(value[i], schema.items, `${path4}[${i}]`, errors);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
function checkType(value, type) {
|
|
1486
|
+
switch (type) {
|
|
1487
|
+
case "string":
|
|
1488
|
+
return typeof value === "string";
|
|
1489
|
+
case "number":
|
|
1490
|
+
return typeof value === "number" && !Number.isNaN(value);
|
|
1491
|
+
case "integer":
|
|
1492
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
1493
|
+
case "boolean":
|
|
1494
|
+
return typeof value === "boolean";
|
|
1495
|
+
case "null":
|
|
1496
|
+
return value === null;
|
|
1497
|
+
case "array":
|
|
1498
|
+
return Array.isArray(value);
|
|
1499
|
+
case "object":
|
|
1500
|
+
return isPlainObject(value);
|
|
1501
|
+
default:
|
|
1502
|
+
return true;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
function isPlainObject(v) {
|
|
1506
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1507
|
+
}
|
|
1508
|
+
function describeType(v) {
|
|
1509
|
+
if (v === null) return "null";
|
|
1510
|
+
if (Array.isArray(v)) return "array";
|
|
1511
|
+
return typeof v;
|
|
1512
|
+
}
|
|
1513
|
+
function joinPath(parent, key) {
|
|
1514
|
+
if (!parent) return key;
|
|
1515
|
+
return `${parent}.${key}`;
|
|
1516
|
+
}
|
|
1517
|
+
function deepEqual(a, b) {
|
|
1518
|
+
if (a === b) return true;
|
|
1519
|
+
if (typeof a !== typeof b) return false;
|
|
1520
|
+
if (a === null || b === null) return a === b;
|
|
1521
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1522
|
+
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
1523
|
+
}
|
|
1524
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
1525
|
+
const ak = Object.keys(a);
|
|
1526
|
+
const bk = Object.keys(b);
|
|
1527
|
+
if (ak.length !== bk.length) return false;
|
|
1528
|
+
return ak.every(
|
|
1529
|
+
(k) => deepEqual(a[k], b[k])
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// src/utils/regex-guard.ts
|
|
1536
|
+
var MAX_PATTERN_LEN = 512;
|
|
1537
|
+
var DANGEROUS_PATTERNS = [
|
|
1538
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
1539
|
+
// (a+)+, (.*)+, etc
|
|
1540
|
+
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
1541
|
+
// same, with non-capturing group
|
|
1542
|
+
];
|
|
1543
|
+
function compileUserRegex(pattern, flags) {
|
|
1544
|
+
if (typeof pattern !== "string") {
|
|
1545
|
+
return { ok: false, reason: "pattern must be a string" };
|
|
1546
|
+
}
|
|
1547
|
+
if (pattern.length === 0) {
|
|
1548
|
+
return { ok: false, reason: "pattern is empty" };
|
|
1549
|
+
}
|
|
1550
|
+
if (pattern.length > MAX_PATTERN_LEN) {
|
|
1551
|
+
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
1552
|
+
}
|
|
1553
|
+
for (const rx of DANGEROUS_PATTERNS) {
|
|
1554
|
+
if (rx.test(pattern)) {
|
|
1555
|
+
return {
|
|
1556
|
+
ok: false,
|
|
1557
|
+
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
try {
|
|
1562
|
+
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
1563
|
+
} catch (err) {
|
|
1564
|
+
return {
|
|
1565
|
+
ok: false,
|
|
1566
|
+
reason: err instanceof Error ? err.message : "invalid regex"
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1253
1570
|
|
|
1254
1571
|
// src/types/errors.ts
|
|
1255
1572
|
var ERROR_CODES = {
|
|
@@ -1312,7 +1629,7 @@ var AgentError = class extends WrongStackError {
|
|
|
1312
1629
|
};
|
|
1313
1630
|
function toWrongStackError(err, code = ERROR_CODES.AGENT_RUN_FAILED) {
|
|
1314
1631
|
if (err instanceof WrongStackError) return err;
|
|
1315
|
-
const message =
|
|
1632
|
+
const message = toErrorMessage(err);
|
|
1316
1633
|
return new AgentError({
|
|
1317
1634
|
message,
|
|
1318
1635
|
code: code === "UNKNOWN" ? ERROR_CODES.AGENT_RUN_FAILED : code,
|
|
@@ -1353,6 +1670,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1353
1670
|
hardThreshold;
|
|
1354
1671
|
/** Writable so model-switch can update the denominator without re-registering the middleware. */
|
|
1355
1672
|
_maxContext;
|
|
1673
|
+
/**
|
|
1674
|
+
* Runtime on/off gate. The middleware is always installed in the pipeline so
|
|
1675
|
+
* auto-compaction can be toggled live from the TUI `/settings` picker; when
|
|
1676
|
+
* disabled the handler is a pass-through. Defaults to enabled.
|
|
1677
|
+
*/
|
|
1678
|
+
_enabled = true;
|
|
1356
1679
|
aggressiveOn;
|
|
1357
1680
|
events;
|
|
1358
1681
|
failureMode;
|
|
@@ -1410,8 +1733,19 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1410
1733
|
setMaxContext(maxContext) {
|
|
1411
1734
|
this._maxContext = maxContext;
|
|
1412
1735
|
}
|
|
1736
|
+
/** Whether auto-compaction is currently active. */
|
|
1737
|
+
get enabled() {
|
|
1738
|
+
return this._enabled;
|
|
1739
|
+
}
|
|
1740
|
+
/** Toggle auto-compaction on a live session (TUI `/settings`). When disabled
|
|
1741
|
+
* the middleware passes every iteration straight through without estimating
|
|
1742
|
+
* tokens or compacting. */
|
|
1743
|
+
setEnabled(enabled) {
|
|
1744
|
+
this._enabled = enabled;
|
|
1745
|
+
}
|
|
1413
1746
|
handler() {
|
|
1414
1747
|
return async (ctx, next) => {
|
|
1748
|
+
if (!this._enabled) return next(ctx);
|
|
1415
1749
|
const msgCount = ctx.messages.length;
|
|
1416
1750
|
const toolCount = (ctx.tools ?? []).length;
|
|
1417
1751
|
let tokens;
|
|
@@ -1546,193 +1880,55 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1546
1880
|
tokens: pressure.tokens,
|
|
1547
1881
|
maxContext: this._maxContext
|
|
1548
1882
|
},
|
|
1549
|
-
cause: err
|
|
1550
|
-
});
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
};
|
|
1555
|
-
|
|
1556
|
-
// src/security/capabilities.ts
|
|
1557
|
-
var ToolCapabilities = {
|
|
1558
|
-
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
1559
|
-
SHELL_ARBITRARY: "shell.arbitrary",
|
|
1560
|
-
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
1561
|
-
SHELL_RESTRICTED: "shell.restricted",
|
|
1562
|
-
/** Can write / modify / delete files inside the project. */
|
|
1563
|
-
FS_WRITE: "fs.write",
|
|
1564
|
-
/** Can write files outside the current project root (very high risk). */
|
|
1565
|
-
FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
|
|
1566
|
-
/** Proxies tools from external MCP servers (unknown capability). */
|
|
1567
|
-
MCP_PROXY: "mcp.proxy",
|
|
1568
|
-
/** Can spawn or manage subagents / multi-agent tasks. */
|
|
1569
|
-
SUBAGENT_SPAWN: "subagent.spawn",
|
|
1570
|
-
/** Can mutate global or session configuration / trust state. */
|
|
1571
|
-
CONFIG_MUTATE: "config.mutate",
|
|
1572
|
-
/** Can install packages or run package managers with side effects. */
|
|
1573
|
-
PACKAGE_INSTALL: "package.install"
|
|
1574
|
-
};
|
|
1575
|
-
var DANGEROUS_FOR_SUBAGENTS = [
|
|
1576
|
-
ToolCapabilities.SHELL_ARBITRARY,
|
|
1577
|
-
ToolCapabilities.SHELL_RESTRICTED,
|
|
1578
|
-
ToolCapabilities.FS_WRITE,
|
|
1579
|
-
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
1580
|
-
ToolCapabilities.MCP_PROXY,
|
|
1581
|
-
ToolCapabilities.SUBAGENT_SPAWN,
|
|
1582
|
-
ToolCapabilities.CONFIG_MUTATE,
|
|
1583
|
-
ToolCapabilities.PACKAGE_INSTALL
|
|
1584
|
-
];
|
|
1585
|
-
function hasDangerousCapabilityForSubagents(toolOrCaps) {
|
|
1586
|
-
if (!toolOrCaps) return false;
|
|
1587
|
-
const input = toolOrCaps;
|
|
1588
|
-
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
1589
|
-
return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
|
|
1590
|
-
}
|
|
1591
|
-
function getDangerousCapabilities(toolOrCaps) {
|
|
1592
|
-
if (!toolOrCaps) return [];
|
|
1593
|
-
const input = toolOrCaps;
|
|
1594
|
-
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
1595
|
-
return caps.filter(
|
|
1596
|
-
(c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
|
|
1597
|
-
);
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
// src/utils/json-schema-validate.ts
|
|
1601
|
-
function validateAgainstSchema(value, schema) {
|
|
1602
|
-
const errors = [];
|
|
1603
|
-
walk(value, schema, "", errors);
|
|
1604
|
-
return { ok: errors.length === 0, errors };
|
|
1605
|
-
}
|
|
1606
|
-
function walk(value, schema, path4, errors) {
|
|
1607
|
-
if (schema.enum !== void 0) {
|
|
1608
|
-
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
1609
|
-
errors.push({
|
|
1610
|
-
path: path4 || "<root>",
|
|
1611
|
-
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
1612
|
-
});
|
|
1613
|
-
return;
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
if (typeof schema.type === "string") {
|
|
1617
|
-
if (!checkType(value, schema.type)) {
|
|
1618
|
-
errors.push({
|
|
1619
|
-
path: path4 || "<root>",
|
|
1620
|
-
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
1621
|
-
});
|
|
1622
|
-
return;
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
if (schema.type === "object" && isPlainObject(value)) {
|
|
1626
|
-
const obj = value;
|
|
1627
|
-
for (const req of schema.required ?? []) {
|
|
1628
|
-
if (!(req in obj)) {
|
|
1629
|
-
errors.push({ path: joinPath(path4, req), message: "required property missing" });
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
if (schema.properties) {
|
|
1633
|
-
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
1634
|
-
if (key in obj) {
|
|
1635
|
-
walk(obj[key], subSchema, joinPath(path4, key), errors);
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
1641
|
-
value.forEach((item, i) => walk(item, schema.items, `${path4}[${i}]`, errors));
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
function checkType(value, type) {
|
|
1645
|
-
switch (type) {
|
|
1646
|
-
case "string":
|
|
1647
|
-
return typeof value === "string";
|
|
1648
|
-
case "number":
|
|
1649
|
-
return typeof value === "number" && !Number.isNaN(value);
|
|
1650
|
-
case "integer":
|
|
1651
|
-
return typeof value === "number" && Number.isInteger(value);
|
|
1652
|
-
case "boolean":
|
|
1653
|
-
return typeof value === "boolean";
|
|
1654
|
-
case "null":
|
|
1655
|
-
return value === null;
|
|
1656
|
-
case "array":
|
|
1657
|
-
return Array.isArray(value);
|
|
1658
|
-
case "object":
|
|
1659
|
-
return isPlainObject(value);
|
|
1660
|
-
default:
|
|
1661
|
-
return true;
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
function isPlainObject(v) {
|
|
1665
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1666
|
-
}
|
|
1667
|
-
function describeType(v) {
|
|
1668
|
-
if (v === null) return "null";
|
|
1669
|
-
if (Array.isArray(v)) return "array";
|
|
1670
|
-
return typeof v;
|
|
1671
|
-
}
|
|
1672
|
-
function joinPath(parent, key) {
|
|
1673
|
-
if (!parent) return key;
|
|
1674
|
-
return `${parent}.${key}`;
|
|
1675
|
-
}
|
|
1676
|
-
function deepEqual(a, b) {
|
|
1677
|
-
if (a === b) return true;
|
|
1678
|
-
if (typeof a !== typeof b) return false;
|
|
1679
|
-
if (a === null || b === null) return a === b;
|
|
1680
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1681
|
-
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
1682
|
-
}
|
|
1683
|
-
if (typeof a === "object" && typeof b === "object") {
|
|
1684
|
-
const ak = Object.keys(a);
|
|
1685
|
-
const bk = Object.keys(b);
|
|
1686
|
-
if (ak.length !== bk.length) return false;
|
|
1687
|
-
return ak.every(
|
|
1688
|
-
(k) => deepEqual(a[k], b[k])
|
|
1689
|
-
);
|
|
1690
|
-
}
|
|
1691
|
-
return false;
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
// src/utils/tool-output-serializer.ts
|
|
1695
|
-
function createToolOutputSerializer(opts = {}) {
|
|
1696
|
-
const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
|
|
1697
|
-
function serialize(value) {
|
|
1698
|
-
if (typeof value === "string") return value;
|
|
1699
|
-
if (value === null || value === void 0) return "";
|
|
1700
|
-
if (typeof value === "object") {
|
|
1701
|
-
if (Array.isArray(value)) return value.map(serialize).join("\n");
|
|
1702
|
-
if ("text" in value) {
|
|
1703
|
-
const t = value.text;
|
|
1704
|
-
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
1705
|
-
}
|
|
1706
|
-
try {
|
|
1707
|
-
return JSON.stringify(value, null, 2);
|
|
1708
|
-
} catch {
|
|
1709
|
-
return String(value);
|
|
1883
|
+
cause: err
|
|
1884
|
+
});
|
|
1710
1885
|
}
|
|
1711
1886
|
}
|
|
1712
|
-
return String(value);
|
|
1713
|
-
}
|
|
1714
|
-
function enforceCap(text, remainingBudget) {
|
|
1715
|
-
if (remainingBudget <= 0) {
|
|
1716
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
1717
|
-
}
|
|
1718
|
-
const textBytes = Buffer.byteLength(text, "utf8");
|
|
1719
|
-
if (textBytes <= remainingBudget) {
|
|
1720
|
-
return { text, newBudget: remainingBudget - textBytes };
|
|
1721
|
-
}
|
|
1722
|
-
const marker = `
|
|
1723
|
-
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
1724
|
-
`;
|
|
1725
|
-
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
1726
|
-
const available = remainingBudget - markerBytes;
|
|
1727
|
-
if (available <= 0) {
|
|
1728
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
1729
|
-
}
|
|
1730
|
-
const half = Math.floor(available / 2);
|
|
1731
|
-
const first = text.slice(0, half);
|
|
1732
|
-
const second = text.slice(text.length - half);
|
|
1733
|
-
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
1734
1887
|
}
|
|
1735
|
-
|
|
1888
|
+
};
|
|
1889
|
+
|
|
1890
|
+
// src/security/capabilities.ts
|
|
1891
|
+
var ToolCapabilities = {
|
|
1892
|
+
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
1893
|
+
SHELL_ARBITRARY: "shell.arbitrary",
|
|
1894
|
+
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
1895
|
+
SHELL_RESTRICTED: "shell.restricted",
|
|
1896
|
+
/** Can write / modify / delete files inside the project. */
|
|
1897
|
+
FS_WRITE: "fs.write",
|
|
1898
|
+
/** Can write files outside the current project root (very high risk). */
|
|
1899
|
+
FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
|
|
1900
|
+
/** Proxies tools from external MCP servers (unknown capability). */
|
|
1901
|
+
MCP_PROXY: "mcp.proxy",
|
|
1902
|
+
/** Can spawn or manage subagents / multi-agent tasks. */
|
|
1903
|
+
SUBAGENT_SPAWN: "subagent.spawn",
|
|
1904
|
+
/** Can mutate global or session configuration / trust state. */
|
|
1905
|
+
CONFIG_MUTATE: "config.mutate",
|
|
1906
|
+
/** Can install packages or run package managers with side effects. */
|
|
1907
|
+
PACKAGE_INSTALL: "package.install"
|
|
1908
|
+
};
|
|
1909
|
+
var DANGEROUS_FOR_SUBAGENTS = [
|
|
1910
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
1911
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
1912
|
+
ToolCapabilities.FS_WRITE,
|
|
1913
|
+
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
1914
|
+
ToolCapabilities.MCP_PROXY,
|
|
1915
|
+
ToolCapabilities.SUBAGENT_SPAWN,
|
|
1916
|
+
ToolCapabilities.CONFIG_MUTATE,
|
|
1917
|
+
ToolCapabilities.PACKAGE_INSTALL
|
|
1918
|
+
];
|
|
1919
|
+
function hasDangerousCapabilityForSubagents(toolOrCaps) {
|
|
1920
|
+
if (!toolOrCaps) return false;
|
|
1921
|
+
const input = toolOrCaps;
|
|
1922
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
1923
|
+
return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
|
|
1924
|
+
}
|
|
1925
|
+
function getDangerousCapabilities(toolOrCaps) {
|
|
1926
|
+
if (!toolOrCaps) return [];
|
|
1927
|
+
const input = toolOrCaps;
|
|
1928
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
1929
|
+
return caps.filter(
|
|
1930
|
+
(c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
|
|
1931
|
+
);
|
|
1736
1932
|
}
|
|
1737
1933
|
|
|
1738
1934
|
// src/execution/tool-executor.ts
|
|
@@ -1898,7 +2094,7 @@ ${post.additionalContext}`;
|
|
|
1898
2094
|
);
|
|
1899
2095
|
return { result, tool, durationMs: Date.now() - start };
|
|
1900
2096
|
} catch (err) {
|
|
1901
|
-
const msg =
|
|
2097
|
+
const msg = toErrorMessage(err);
|
|
1902
2098
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
1903
2099
|
this.opts.renderer?.writeToolResult(tool.name, scrubbed, true);
|
|
1904
2100
|
const result = {
|
|
@@ -1919,7 +2115,7 @@ ${post.additionalContext}`;
|
|
|
1919
2115
|
try {
|
|
1920
2116
|
return await runOne(use);
|
|
1921
2117
|
} catch (err) {
|
|
1922
|
-
const msg =
|
|
2118
|
+
const msg = toErrorMessage(err);
|
|
1923
2119
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
1924
2120
|
const result = {
|
|
1925
2121
|
type: "tool_result",
|
|
@@ -2202,51 +2398,6 @@ function extractMalformedRaw(input) {
|
|
|
2202
2398
|
}
|
|
2203
2399
|
}
|
|
2204
2400
|
|
|
2205
|
-
// src/utils/assert-never.ts
|
|
2206
|
-
function assertNever(x, message) {
|
|
2207
|
-
const err = new Error(
|
|
2208
|
-
`Unhandled case: ${JSON.stringify(x)}`
|
|
2209
|
-
);
|
|
2210
|
-
err.name = "AssertNeverError";
|
|
2211
|
-
throw err;
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
// src/utils/regex-guard.ts
|
|
2215
|
-
var MAX_PATTERN_LEN = 512;
|
|
2216
|
-
var DANGEROUS_PATTERNS = [
|
|
2217
|
-
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
2218
|
-
// (a+)+, (.*)+, etc
|
|
2219
|
-
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
2220
|
-
// same, with non-capturing group
|
|
2221
|
-
];
|
|
2222
|
-
function compileUserRegex(pattern, flags) {
|
|
2223
|
-
if (typeof pattern !== "string") {
|
|
2224
|
-
return { ok: false, reason: "pattern must be a string" };
|
|
2225
|
-
}
|
|
2226
|
-
if (pattern.length === 0) {
|
|
2227
|
-
return { ok: false, reason: "pattern is empty" };
|
|
2228
|
-
}
|
|
2229
|
-
if (pattern.length > MAX_PATTERN_LEN) {
|
|
2230
|
-
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
2231
|
-
}
|
|
2232
|
-
for (const rx of DANGEROUS_PATTERNS) {
|
|
2233
|
-
if (rx.test(pattern)) {
|
|
2234
|
-
return {
|
|
2235
|
-
ok: false,
|
|
2236
|
-
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
2237
|
-
};
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
try {
|
|
2241
|
-
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
2242
|
-
} catch (err) {
|
|
2243
|
-
return {
|
|
2244
|
-
ok: false,
|
|
2245
|
-
reason: err instanceof Error ? err.message : "invalid regex"
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
2401
|
// src/execution/autonomous-runner.ts
|
|
2251
2402
|
var DoneConditionChecker = class {
|
|
2252
2403
|
constructor(condition) {
|
|
@@ -2423,136 +2574,36 @@ var AutonomousRunner = class {
|
|
|
2423
2574
|
this.stopped = true;
|
|
2424
2575
|
}
|
|
2425
2576
|
};
|
|
2426
|
-
async function atomicWrite(targetPath, content, opts = {}) {
|
|
2427
|
-
const dir = path2.dirname(targetPath);
|
|
2428
|
-
await fs.mkdir(dir, { recursive: true });
|
|
2429
|
-
const tmp = path2.join(dir, `.${path2.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
2430
|
-
try {
|
|
2431
|
-
if (typeof content === "string") {
|
|
2432
|
-
await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
2433
|
-
} else {
|
|
2434
|
-
await fs.writeFile(tmp, content, { flag: "wx" });
|
|
2435
|
-
}
|
|
2436
|
-
try {
|
|
2437
|
-
const fh = await fs.open(tmp, "r+");
|
|
2438
|
-
try {
|
|
2439
|
-
await fh.sync();
|
|
2440
|
-
} finally {
|
|
2441
|
-
await fh.close();
|
|
2442
|
-
}
|
|
2443
|
-
} catch {
|
|
2444
|
-
}
|
|
2445
|
-
let mode;
|
|
2446
|
-
try {
|
|
2447
|
-
const stat2 = await fs.stat(targetPath);
|
|
2448
|
-
mode = stat2.mode & 511;
|
|
2449
|
-
} catch {
|
|
2450
|
-
mode = opts.mode;
|
|
2451
|
-
}
|
|
2452
|
-
if (mode !== void 0) {
|
|
2453
|
-
await fs.chmod(tmp, mode);
|
|
2454
|
-
}
|
|
2455
|
-
await renameWithRetry(tmp, targetPath);
|
|
2456
|
-
} catch (err) {
|
|
2457
|
-
try {
|
|
2458
|
-
await fs.unlink(tmp);
|
|
2459
|
-
} catch {
|
|
2460
|
-
}
|
|
2461
|
-
throw err;
|
|
2462
|
-
}
|
|
2463
|
-
}
|
|
2464
|
-
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
2465
|
-
async function renameWithRetry(from, to) {
|
|
2466
|
-
if (process.platform !== "win32") {
|
|
2467
|
-
await fs.rename(from, to);
|
|
2468
|
-
return;
|
|
2469
|
-
}
|
|
2470
|
-
const delays = [10, 25, 60, 120, 250];
|
|
2471
|
-
let lastErr;
|
|
2472
|
-
for (let i = 0; i <= delays.length; i++) {
|
|
2473
|
-
try {
|
|
2474
|
-
await fs.rename(from, to);
|
|
2475
|
-
return;
|
|
2476
|
-
} catch (err) {
|
|
2477
|
-
lastErr = err;
|
|
2478
|
-
const code = err?.code;
|
|
2479
|
-
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
2480
|
-
throw err;
|
|
2481
|
-
}
|
|
2482
|
-
await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
throw lastErr;
|
|
2486
|
-
}
|
|
2487
|
-
function projectHash(absRoot) {
|
|
2488
|
-
return createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
2489
|
-
}
|
|
2490
|
-
function projectSlug(absRoot) {
|
|
2491
|
-
const base = slugify(path2.basename(absRoot));
|
|
2492
|
-
const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
2493
|
-
return `${base}-${hash}`;
|
|
2494
|
-
}
|
|
2495
|
-
function slugify(name) {
|
|
2496
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
2497
|
-
}
|
|
2498
|
-
function wstackGlobalRoot() {
|
|
2499
|
-
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
2500
|
-
if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
|
|
2501
|
-
return path2.join(os.homedir(), ".wrongstack");
|
|
2502
|
-
}
|
|
2503
|
-
function resolveWstackPaths(opts) {
|
|
2504
|
-
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
2505
|
-
const hash = projectHash(opts.projectRoot);
|
|
2506
|
-
const slug = projectSlug(opts.projectRoot);
|
|
2507
|
-
const projectDir = path2.join(globalRoot, "projects", slug);
|
|
2508
|
-
return {
|
|
2509
|
-
globalRoot,
|
|
2510
|
-
configDir: globalRoot,
|
|
2511
|
-
globalConfig: path2.join(globalRoot, "config.json"),
|
|
2512
|
-
secretsKey: path2.join(globalRoot, ".key"),
|
|
2513
|
-
globalMemory: path2.join(globalRoot, "memory.md"),
|
|
2514
|
-
globalSkills: path2.join(globalRoot, "skills"),
|
|
2515
|
-
globalPrompts: path2.join(globalRoot, "prompts"),
|
|
2516
|
-
cacheDir: path2.join(globalRoot, "cache"),
|
|
2517
|
-
modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
|
|
2518
|
-
modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
|
|
2519
|
-
historyFile: path2.join(globalRoot, "history"),
|
|
2520
|
-
logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
|
|
2521
|
-
projectDir,
|
|
2522
|
-
projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
|
|
2523
|
-
projectMemory: path2.join(projectDir, "memory.md"),
|
|
2524
|
-
projectSessions: path2.join(projectDir, "sessions"),
|
|
2525
|
-
projectTrust: path2.join(projectDir, "trust.json"),
|
|
2526
|
-
projectMeta: path2.join(projectDir, "meta.json"),
|
|
2527
|
-
projectLocalConfig: path2.join(projectDir, "config.local.json"),
|
|
2528
|
-
inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
2529
|
-
inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
2530
|
-
inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
2531
|
-
inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
2532
|
-
projectHash: hash,
|
|
2533
|
-
projectSlug: slug,
|
|
2534
|
-
projectGoal: path2.join(projectDir, "goal.json"),
|
|
2535
|
-
projectSpecs: path2.join(projectDir, "specs"),
|
|
2536
|
-
projectTaskGraphs: path2.join(projectDir, "task-graphs"),
|
|
2537
|
-
projectSddSession: path2.join(projectDir, "sdd-session.json"),
|
|
2538
|
-
projectPlan: path2.join(projectDir, "plan.json"),
|
|
2539
|
-
projectAutophase: path2.join(projectDir, "autophase"),
|
|
2540
|
-
syncConfig: path2.join(globalRoot, "sync.json")
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
|
|
2544
|
-
// src/storage/goal-store.ts
|
|
2545
2577
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
2546
2578
|
function goalFilePath(projectRoot) {
|
|
2547
2579
|
return resolveWstackPaths({ projectRoot }).projectGoal;
|
|
2548
2580
|
}
|
|
2549
|
-
async function loadGoal(filePath) {
|
|
2581
|
+
async function loadGoal(filePath, events) {
|
|
2582
|
+
const t0 = Date.now();
|
|
2550
2583
|
let raw;
|
|
2551
2584
|
try {
|
|
2552
2585
|
raw = await fs.readFile(filePath, "utf8");
|
|
2553
2586
|
} catch (err) {
|
|
2554
2587
|
const code = err.code;
|
|
2555
|
-
if (code === "ENOENT")
|
|
2588
|
+
if (code === "ENOENT") {
|
|
2589
|
+
events?.emit("storage.read", {
|
|
2590
|
+
sessionId: "~boot~",
|
|
2591
|
+
store: "goal",
|
|
2592
|
+
filePath,
|
|
2593
|
+
operation: "load",
|
|
2594
|
+
outcome: "success",
|
|
2595
|
+
durationMs: Date.now() - t0
|
|
2596
|
+
});
|
|
2597
|
+
return null;
|
|
2598
|
+
}
|
|
2599
|
+
events?.emit("storage.error", {
|
|
2600
|
+
sessionId: "~boot~",
|
|
2601
|
+
store: "goal",
|
|
2602
|
+
filePath,
|
|
2603
|
+
operation: "load",
|
|
2604
|
+
error: toErrorMessage(err),
|
|
2605
|
+
recoverable: false
|
|
2606
|
+
});
|
|
2556
2607
|
throw err;
|
|
2557
2608
|
}
|
|
2558
2609
|
try {
|
|
@@ -2565,8 +2616,25 @@ async function loadGoal(filePath) {
|
|
|
2565
2616
|
message: "invalid schema \u2014 consider deleting and re-creating",
|
|
2566
2617
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2567
2618
|
}));
|
|
2619
|
+
events?.emit("storage.read", {
|
|
2620
|
+
sessionId: "~boot~",
|
|
2621
|
+
store: "goal",
|
|
2622
|
+
filePath,
|
|
2623
|
+
operation: "load",
|
|
2624
|
+
outcome: "failure",
|
|
2625
|
+
durationMs: Date.now() - t0,
|
|
2626
|
+
error: "invalid_schema"
|
|
2627
|
+
});
|
|
2568
2628
|
return null;
|
|
2569
2629
|
}
|
|
2630
|
+
events?.emit("storage.read", {
|
|
2631
|
+
sessionId: "~boot~",
|
|
2632
|
+
store: "goal",
|
|
2633
|
+
filePath,
|
|
2634
|
+
operation: "load",
|
|
2635
|
+
outcome: "success",
|
|
2636
|
+
durationMs: Date.now() - t0
|
|
2637
|
+
});
|
|
2570
2638
|
return parsed;
|
|
2571
2639
|
} catch {
|
|
2572
2640
|
console.warn(JSON.stringify({
|
|
@@ -2576,15 +2644,41 @@ async function loadGoal(filePath) {
|
|
|
2576
2644
|
message: "JSON parse failed \u2014 consider deleting and re-creating",
|
|
2577
2645
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2578
2646
|
}));
|
|
2647
|
+
events?.emit("storage.read", {
|
|
2648
|
+
sessionId: "~boot~",
|
|
2649
|
+
store: "goal",
|
|
2650
|
+
filePath,
|
|
2651
|
+
operation: "load",
|
|
2652
|
+
outcome: "failure",
|
|
2653
|
+
durationMs: Date.now() - t0,
|
|
2654
|
+
error: "parse_failed"
|
|
2655
|
+
});
|
|
2579
2656
|
return null;
|
|
2580
2657
|
}
|
|
2581
2658
|
}
|
|
2582
|
-
async function saveGoal(filePath, goal) {
|
|
2659
|
+
async function saveGoal(filePath, goal, events) {
|
|
2660
|
+
const t0 = Date.now();
|
|
2583
2661
|
try {
|
|
2584
2662
|
await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 384 });
|
|
2663
|
+
events?.emit("storage.write", {
|
|
2664
|
+
sessionId: "~boot~",
|
|
2665
|
+
store: "goal",
|
|
2666
|
+
filePath,
|
|
2667
|
+
operation: "save",
|
|
2668
|
+
outcome: "success",
|
|
2669
|
+
durationMs: Date.now() - t0
|
|
2670
|
+
});
|
|
2585
2671
|
} catch (err) {
|
|
2672
|
+
events?.emit("storage.error", {
|
|
2673
|
+
sessionId: "~boot~",
|
|
2674
|
+
store: "goal",
|
|
2675
|
+
filePath,
|
|
2676
|
+
operation: "save",
|
|
2677
|
+
error: toErrorMessage(err),
|
|
2678
|
+
recoverable: false
|
|
2679
|
+
});
|
|
2586
2680
|
throw new FsError({
|
|
2587
|
-
message:
|
|
2681
|
+
message: toErrorMessage(err),
|
|
2588
2682
|
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
2589
2683
|
path: filePath,
|
|
2590
2684
|
cause: err
|
|
@@ -2638,11 +2732,6 @@ function computeTrend(history) {
|
|
|
2638
2732
|
return "steady";
|
|
2639
2733
|
}
|
|
2640
2734
|
|
|
2641
|
-
// src/utils/sleep.ts
|
|
2642
|
-
function sleep(ms) {
|
|
2643
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2644
|
-
}
|
|
2645
|
-
|
|
2646
2735
|
// src/execution/autonomy-brain.ts
|
|
2647
2736
|
var RISK_LEVELS = {
|
|
2648
2737
|
low: 0,
|
|
@@ -2879,7 +2968,7 @@ var EternalAutonomyEngine = class {
|
|
|
2879
2968
|
console.error(JSON.stringify({
|
|
2880
2969
|
level: "error",
|
|
2881
2970
|
event: "engine.persist_state_failed",
|
|
2882
|
-
message:
|
|
2971
|
+
message: toErrorMessage(err),
|
|
2883
2972
|
context: { expectedState: "stopped" },
|
|
2884
2973
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2885
2974
|
}));
|
|
@@ -2912,7 +3001,7 @@ var EternalAutonomyEngine = class {
|
|
|
2912
3001
|
} catch (err) {
|
|
2913
3002
|
this.consecutiveFailures++;
|
|
2914
3003
|
this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
|
|
2915
|
-
await this.appendFailure("engine error",
|
|
3004
|
+
await this.appendFailure("engine error", toErrorMessage(err));
|
|
2916
3005
|
}
|
|
2917
3006
|
if (iterationOk) {
|
|
2918
3007
|
this.consecutiveFailures = 0;
|
|
@@ -2938,7 +3027,7 @@ var EternalAutonomyEngine = class {
|
|
|
2938
3027
|
const emit = (stage) => {
|
|
2939
3028
|
this.opts.onStage?.(stage);
|
|
2940
3029
|
};
|
|
2941
|
-
const goal = await loadGoal(this.goalPath);
|
|
3030
|
+
const goal = await loadGoal(this.goalPath, this.opts.events);
|
|
2942
3031
|
if (!goal) {
|
|
2943
3032
|
emit({ phase: "stopped" });
|
|
2944
3033
|
this.stopRequested = true;
|
|
@@ -3013,7 +3102,7 @@ var EternalAutonomyEngine = class {
|
|
|
3013
3102
|
} catch (err) {
|
|
3014
3103
|
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
3015
3104
|
status = isAbort ? "aborted" : "failure";
|
|
3016
|
-
note =
|
|
3105
|
+
note = toErrorMessage(err);
|
|
3017
3106
|
if (!isAbort && typeof err?.recoverable === "boolean") {
|
|
3018
3107
|
isTransientFailure = err.recoverable;
|
|
3019
3108
|
}
|
|
@@ -3042,7 +3131,7 @@ var EternalAutonomyEngine = class {
|
|
|
3042
3131
|
emit({ phase: "reflect", status, note });
|
|
3043
3132
|
let iterationIndex = 0;
|
|
3044
3133
|
try {
|
|
3045
|
-
const reloaded = await loadGoal(this.goalPath);
|
|
3134
|
+
const reloaded = await loadGoal(this.goalPath, this.opts.events);
|
|
3046
3135
|
iterationIndex = reloaded?.iterations ?? 0;
|
|
3047
3136
|
} catch {
|
|
3048
3137
|
}
|
|
@@ -3362,12 +3451,12 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
3362
3451
|
}
|
|
3363
3452
|
}
|
|
3364
3453
|
async appendIterationEntry(entry) {
|
|
3365
|
-
const current = await loadGoal(this.goalPath);
|
|
3454
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
3366
3455
|
if (!current) {
|
|
3367
3456
|
return;
|
|
3368
3457
|
}
|
|
3369
3458
|
const updated = appendJournal(current, entry);
|
|
3370
|
-
await saveGoal(this.goalPath, updated);
|
|
3459
|
+
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
3371
3460
|
}
|
|
3372
3461
|
/**
|
|
3373
3462
|
* Persistent per-todo failure counter. Skipped silently when the goal
|
|
@@ -3376,11 +3465,11 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
3376
3465
|
* the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
|
|
3377
3466
|
*/
|
|
3378
3467
|
async bumpTodoAttempt(todoId) {
|
|
3379
|
-
const current = await loadGoal(this.goalPath);
|
|
3468
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
3380
3469
|
if (!current) return;
|
|
3381
3470
|
const attempts = { ...current.todoAttempts ?? {} };
|
|
3382
3471
|
attempts[todoId] = (attempts[todoId] ?? 0) + 1;
|
|
3383
|
-
await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
|
|
3472
|
+
await saveGoal(this.goalPath, { ...current, todoAttempts: attempts }, this.opts.events);
|
|
3384
3473
|
}
|
|
3385
3474
|
/**
|
|
3386
3475
|
* Flip the mission to `completed` and journal it. Called from two
|
|
@@ -3390,7 +3479,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
3390
3479
|
* goal is already `completed`.
|
|
3391
3480
|
*/
|
|
3392
3481
|
async markGoalCompleted(action, note) {
|
|
3393
|
-
const current = await loadGoal(this.goalPath);
|
|
3482
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
3394
3483
|
if (!current) return;
|
|
3395
3484
|
if (current.goalState === "completed") return;
|
|
3396
3485
|
const withFlag = { ...current, goalState: "completed" };
|
|
@@ -3400,7 +3489,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
3400
3489
|
status: "success",
|
|
3401
3490
|
note: note.slice(0, 240)
|
|
3402
3491
|
});
|
|
3403
|
-
await saveGoal(this.goalPath, withEntry);
|
|
3492
|
+
await saveGoal(this.goalPath, withEntry, this.opts.events);
|
|
3404
3493
|
this.opts.onEternalStop?.();
|
|
3405
3494
|
}
|
|
3406
3495
|
/**
|
|
@@ -3409,10 +3498,10 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
3409
3498
|
* `onEternalStop` so the REPL returns to normal mode.
|
|
3410
3499
|
*/
|
|
3411
3500
|
async clearGoalManually(note) {
|
|
3412
|
-
const current = await loadGoal(this.goalPath);
|
|
3501
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
3413
3502
|
if (current) {
|
|
3414
3503
|
const abandoned = { ...current, goalState: "abandoned" };
|
|
3415
|
-
await saveGoal(this.goalPath, abandoned);
|
|
3504
|
+
await saveGoal(this.goalPath, abandoned, this.opts.events);
|
|
3416
3505
|
}
|
|
3417
3506
|
try {
|
|
3418
3507
|
const { unlink: unlink3 } = await import('fs/promises');
|
|
@@ -3488,16 +3577,16 @@ ${recentJournal}` : ""
|
|
|
3488
3577
|
* Persist a progress update from the agent's [PROGRESS: N%] output.
|
|
3489
3578
|
*/
|
|
3490
3579
|
async updateProgress(progress, note) {
|
|
3491
|
-
const current = await loadGoal(this.goalPath);
|
|
3580
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
3492
3581
|
if (!current) return;
|
|
3493
3582
|
const updated = recordProgress(current, progress, note);
|
|
3494
|
-
await saveGoal(this.goalPath, updated);
|
|
3583
|
+
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
3495
3584
|
}
|
|
3496
3585
|
async persistEngineState(state) {
|
|
3497
|
-
const current = await loadGoal(this.goalPath);
|
|
3586
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
3498
3587
|
if (!current) return;
|
|
3499
3588
|
if (current.engineState === state) return;
|
|
3500
|
-
await saveGoal(this.goalPath, { ...current, engineState: state });
|
|
3589
|
+
await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
|
|
3501
3590
|
}
|
|
3502
3591
|
};
|
|
3503
3592
|
|
|
@@ -3846,6 +3935,20 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
3846
3935
|
};
|
|
3847
3936
|
|
|
3848
3937
|
// src/coordination/agent-subagent-runner.ts
|
|
3938
|
+
function withDisabledToolFiltering(factory) {
|
|
3939
|
+
return async (config) => {
|
|
3940
|
+
const result = await factory(config);
|
|
3941
|
+
const disabled = config.disabledTools ?? [];
|
|
3942
|
+
if (disabled.length === 0) return result;
|
|
3943
|
+
const registry = result.agent.tools;
|
|
3944
|
+
if (registry && typeof registry.unregister === "function") {
|
|
3945
|
+
for (const toolName of disabled) {
|
|
3946
|
+
registry.unregister(toolName);
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
return result;
|
|
3950
|
+
};
|
|
3951
|
+
}
|
|
3849
3952
|
function makeAgentSubagentRunner(opts) {
|
|
3850
3953
|
const format = opts.formatTaskInput ?? defaultFormatTaskInput;
|
|
3851
3954
|
return async (task, ctx) => {
|
|
@@ -6743,11 +6846,6 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
6743
6846
|
};
|
|
6744
6847
|
}
|
|
6745
6848
|
|
|
6746
|
-
// src/utils/string.ts
|
|
6747
|
-
function truncate(s, max) {
|
|
6748
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
6749
|
-
}
|
|
6750
|
-
|
|
6751
6849
|
// src/types/provider.ts
|
|
6752
6850
|
var ProviderError = class extends WrongStackError {
|
|
6753
6851
|
status;
|
|
@@ -6827,7 +6925,7 @@ function classifySubagentError(err, hints = {}) {
|
|
|
6827
6925
|
const baseMessage2 = err.describe();
|
|
6828
6926
|
return providerErrorToSubagentError(err, baseMessage2, cause);
|
|
6829
6927
|
}
|
|
6830
|
-
const baseMessage =
|
|
6928
|
+
const baseMessage = toErrorMessage(err);
|
|
6831
6929
|
if (err instanceof BudgetExceededError) {
|
|
6832
6930
|
const map = {
|
|
6833
6931
|
iterations: "budget_iterations",
|
|
@@ -7807,7 +7905,7 @@ var ParallelEternalEngine = class {
|
|
|
7807
7905
|
console.error(JSON.stringify({
|
|
7808
7906
|
level: "error",
|
|
7809
7907
|
event: "engine.persist_state_failed",
|
|
7810
|
-
message:
|
|
7908
|
+
message: toErrorMessage(err),
|
|
7811
7909
|
context: { expectedState: "stopped" },
|
|
7812
7910
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7813
7911
|
}));
|
|
@@ -7828,7 +7926,8 @@ var ParallelEternalEngine = class {
|
|
|
7828
7926
|
doneCondition: { type: "all_tasks_done" }
|
|
7829
7927
|
};
|
|
7830
7928
|
this.coordinator = new DefaultMultiAgentCoordinator(config);
|
|
7831
|
-
const
|
|
7929
|
+
const filteredFactory = withDisabledToolFiltering(this.agentFactory);
|
|
7930
|
+
const runner = makeAgentSubagentRunner({ factory: filteredFactory });
|
|
7832
7931
|
this.coordinator.setRunner?.(runner);
|
|
7833
7932
|
try {
|
|
7834
7933
|
while (!this.stopRequested) {
|
|
@@ -7842,7 +7941,7 @@ var ParallelEternalEngine = class {
|
|
|
7842
7941
|
);
|
|
7843
7942
|
await this.appendFailure(
|
|
7844
7943
|
"engine error",
|
|
7845
|
-
|
|
7944
|
+
toErrorMessage(err)
|
|
7846
7945
|
);
|
|
7847
7946
|
}
|
|
7848
7947
|
if (this.stopRequested) break;
|
|
@@ -7863,7 +7962,7 @@ var ParallelEternalEngine = class {
|
|
|
7863
7962
|
this.opts.onStage?.(stage);
|
|
7864
7963
|
};
|
|
7865
7964
|
this.iterations++;
|
|
7866
|
-
const goal = await loadGoal(this.goalPath);
|
|
7965
|
+
const goal = await loadGoal(this.goalPath, this.opts.events);
|
|
7867
7966
|
if (!goal) {
|
|
7868
7967
|
this.stopRequested = true;
|
|
7869
7968
|
emit({ phase: "stopped" });
|
|
@@ -7881,7 +7980,8 @@ var ParallelEternalEngine = class {
|
|
|
7881
7980
|
doneCondition: { type: "all_tasks_done" }
|
|
7882
7981
|
};
|
|
7883
7982
|
this.coordinator = new DefaultMultiAgentCoordinator(config);
|
|
7884
|
-
const
|
|
7983
|
+
const filteredFactory = withDisabledToolFiltering(this.agentFactory);
|
|
7984
|
+
const runner = makeAgentSubagentRunner({ factory: filteredFactory });
|
|
7885
7985
|
this.coordinator.setRunner?.(runner);
|
|
7886
7986
|
}
|
|
7887
7987
|
emit({ phase: "decompose" });
|
|
@@ -7990,13 +8090,17 @@ ${personaLine}Task: ${task}
|
|
|
7990
8090
|
role: route.role,
|
|
7991
8091
|
tools: route.definition.config.tools,
|
|
7992
8092
|
systemPromptOverride: route.definition.config.prompt,
|
|
7993
|
-
timeoutMs: this.timeoutMs
|
|
8093
|
+
timeoutMs: this.timeoutMs,
|
|
8094
|
+
// Disable delegation — subagents are leaf workers, not orchestrators
|
|
8095
|
+
disabledTools: ["delegate"]
|
|
7994
8096
|
} : {
|
|
7995
8097
|
id: subagentId,
|
|
7996
8098
|
name: `slot-${subagentId.slice(-6)}`,
|
|
7997
8099
|
// Let the coordinator apply its default budget (roster or generic).
|
|
7998
8100
|
// Hardcoding low limits here defeats the x10 budget improvement.
|
|
7999
|
-
timeoutMs: this.timeoutMs
|
|
8101
|
+
timeoutMs: this.timeoutMs,
|
|
8102
|
+
// Disable delegation — subagents are leaf workers, not orchestrators
|
|
8103
|
+
disabledTools: ["delegate"]
|
|
8000
8104
|
}
|
|
8001
8105
|
);
|
|
8002
8106
|
subagentIds.push(subagentId);
|
|
@@ -8142,10 +8246,10 @@ ${lastFew}` : "No prior iterations.",
|
|
|
8142
8246
|
// Helpers
|
|
8143
8247
|
// -------------------------------------------------------------------------
|
|
8144
8248
|
async appendIterationEntry(entry) {
|
|
8145
|
-
const current = await loadGoal(this.goalPath);
|
|
8249
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8146
8250
|
if (!current) return;
|
|
8147
8251
|
const updated = appendJournal(current, entry);
|
|
8148
|
-
await saveGoal(this.goalPath, updated);
|
|
8252
|
+
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
8149
8253
|
const entryWithMeta = {
|
|
8150
8254
|
at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
8151
8255
|
iteration: updated.iterations,
|
|
@@ -8157,10 +8261,10 @@ ${lastFew}` : "No prior iterations.",
|
|
|
8157
8261
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
8158
8262
|
}
|
|
8159
8263
|
async persistState(state) {
|
|
8160
|
-
const current = await loadGoal(this.goalPath);
|
|
8264
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8161
8265
|
if (!current) return;
|
|
8162
8266
|
if (current.engineState === state) return;
|
|
8163
|
-
await saveGoal(this.goalPath, { ...current, engineState: state });
|
|
8267
|
+
await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
|
|
8164
8268
|
}
|
|
8165
8269
|
};
|
|
8166
8270
|
|
|
@@ -8629,7 +8733,7 @@ function parseDescription(raw) {
|
|
|
8629
8733
|
const scope = [];
|
|
8630
8734
|
const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
|
|
8631
8735
|
if (coversMatch) {
|
|
8632
|
-
const items = coversMatch[1] ?? "".replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
|
|
8736
|
+
const items = (coversMatch[1] ?? "").replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
|
|
8633
8737
|
scope.push(...items);
|
|
8634
8738
|
}
|
|
8635
8739
|
return { trigger, scope };
|