@wrongstack/core 0.260.0 → 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-BbskZ7HH.d.ts → agent-bridge-D8sa1vtv.d.ts} +1 -1
- package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-c9DLkaas.d.ts} +11 -9
- package/dist/{brain-C2yDd7Lw.d.ts → brain-O1IdKPaK.d.ts} +2 -2
- package/dist/{compactor-t0R_AIt_.d.ts → compactor-BBy0rCtB.d.ts} +1 -1
- package/dist/{config-FG6As4H5.d.ts → config-Dz2F3H2K.d.ts} +7 -1
- package/dist/{context-JFOVvu6z.d.ts → context-BGSpZNSE.d.ts} +11 -0
- package/dist/coordination/index.d.ts +1681 -15
- package/dist/coordination/index.js +2648 -388
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +1414 -1387
- 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 +410 -388
- 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-B1IXJtLX.d.ts → goal-preamble-DzjFuN3p.d.ts} +21 -9
- package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-CxWmCGbH.d.ts} +1 -1
- package/dist/{index-CebbJB94.d.ts → index-CYIQrXVF.d.ts} +8 -8
- package/dist/{index-BPcg4N3M.d.ts → index-CbLSI66_.d.ts} +5 -5
- package/dist/index.d.ts +45 -91
- package/dist/index.js +14976 -12551
- 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-DXxI2tlu.d.ts → llm-selector-DzxuZnNz.d.ts} +2 -2
- package/dist/{mcp-servers-OwNHo43-.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-Djlmq4uB.d.ts → models-registry-B_siPxqN.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-CK5Jdj9K.d.ts} +2 -2
- package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-DgvD4SCO.d.ts} +6 -6
- 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-0SItuq5r.d.ts → parallel-eternal-engine-bK0JQBR_.d.ts} +9 -9
- package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-BPEDlN38.d.ts} +3 -3
- package/dist/{permission-BJ7eO9Vl.d.ts → permission-4yvGmMRB.d.ts} +1 -1
- package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-C6XpsBOy.d.ts} +2 -2
- package/dist/{pipeline-zflkI2dp.d.ts → pipeline-CXCeMz8J.d.ts} +58 -3
- package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-BvzRBkJc.d.ts} +5 -5
- package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-C5aQpDWE.d.ts} +3 -3
- package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CFhdtRzz.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +39 -29
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-CxiVLbt1.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -4
- package/dist/security/index.js +208 -203
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-B7AivHsu.d.ts → selector-gIuhRTkN.d.ts} +1 -1
- package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-DkvvrpDt.d.ts} +8 -2
- package/dist/{session-reader-DtofsB-2.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 +31 -12
- package/dist/storage/index.js +441 -360
- 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 +703 -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/dist/package-outdated-watcher-C70ag2G9.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;
|
|
@@ -1532,207 +1866,69 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1532
1866
|
aggressive,
|
|
1533
1867
|
level: pressure.level,
|
|
1534
1868
|
tokens: pressure.tokens,
|
|
1535
|
-
maxContext: this._maxContext,
|
|
1536
|
-
load: pressure.load,
|
|
1537
|
-
fatal
|
|
1538
|
-
});
|
|
1539
|
-
if (fatal) {
|
|
1540
|
-
throw new AgentError({
|
|
1541
|
-
message: `Auto-compaction failed at ${pressure.level} threshold`,
|
|
1542
|
-
code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
|
|
1543
|
-
recoverable: true,
|
|
1544
|
-
context: {
|
|
1545
|
-
level: pressure.level,
|
|
1546
|
-
tokens: pressure.tokens,
|
|
1547
|
-
maxContext: this._maxContext
|
|
1548
|
-
},
|
|
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);
|
|
1869
|
+
maxContext: this._maxContext,
|
|
1870
|
+
load: pressure.load,
|
|
1871
|
+
fatal
|
|
1872
|
+
});
|
|
1873
|
+
if (fatal) {
|
|
1874
|
+
throw new AgentError({
|
|
1875
|
+
message: `Auto-compaction failed at ${pressure.level} threshold`,
|
|
1876
|
+
code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
|
|
1877
|
+
recoverable: true,
|
|
1878
|
+
context: {
|
|
1879
|
+
level: pressure.level,
|
|
1880
|
+
tokens: pressure.tokens,
|
|
1881
|
+
maxContext: this._maxContext
|
|
1882
|
+
},
|
|
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,125 +2574,6 @@ 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;
|
|
@@ -2569,7 +2601,7 @@ async function loadGoal(filePath, events) {
|
|
|
2569
2601
|
store: "goal",
|
|
2570
2602
|
filePath,
|
|
2571
2603
|
operation: "load",
|
|
2572
|
-
error:
|
|
2604
|
+
error: toErrorMessage(err),
|
|
2573
2605
|
recoverable: false
|
|
2574
2606
|
});
|
|
2575
2607
|
throw err;
|
|
@@ -2642,11 +2674,11 @@ async function saveGoal(filePath, goal, events) {
|
|
|
2642
2674
|
store: "goal",
|
|
2643
2675
|
filePath,
|
|
2644
2676
|
operation: "save",
|
|
2645
|
-
error:
|
|
2677
|
+
error: toErrorMessage(err),
|
|
2646
2678
|
recoverable: false
|
|
2647
2679
|
});
|
|
2648
2680
|
throw new FsError({
|
|
2649
|
-
message:
|
|
2681
|
+
message: toErrorMessage(err),
|
|
2650
2682
|
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
2651
2683
|
path: filePath,
|
|
2652
2684
|
cause: err
|
|
@@ -2700,11 +2732,6 @@ function computeTrend(history) {
|
|
|
2700
2732
|
return "steady";
|
|
2701
2733
|
}
|
|
2702
2734
|
|
|
2703
|
-
// src/utils/sleep.ts
|
|
2704
|
-
function sleep(ms) {
|
|
2705
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
2735
|
// src/execution/autonomy-brain.ts
|
|
2709
2736
|
var RISK_LEVELS = {
|
|
2710
2737
|
low: 0,
|
|
@@ -2941,7 +2968,7 @@ var EternalAutonomyEngine = class {
|
|
|
2941
2968
|
console.error(JSON.stringify({
|
|
2942
2969
|
level: "error",
|
|
2943
2970
|
event: "engine.persist_state_failed",
|
|
2944
|
-
message:
|
|
2971
|
+
message: toErrorMessage(err),
|
|
2945
2972
|
context: { expectedState: "stopped" },
|
|
2946
2973
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2947
2974
|
}));
|
|
@@ -2974,7 +3001,7 @@ var EternalAutonomyEngine = class {
|
|
|
2974
3001
|
} catch (err) {
|
|
2975
3002
|
this.consecutiveFailures++;
|
|
2976
3003
|
this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
|
|
2977
|
-
await this.appendFailure("engine error",
|
|
3004
|
+
await this.appendFailure("engine error", toErrorMessage(err));
|
|
2978
3005
|
}
|
|
2979
3006
|
if (iterationOk) {
|
|
2980
3007
|
this.consecutiveFailures = 0;
|
|
@@ -3075,7 +3102,7 @@ var EternalAutonomyEngine = class {
|
|
|
3075
3102
|
} catch (err) {
|
|
3076
3103
|
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
3077
3104
|
status = isAbort ? "aborted" : "failure";
|
|
3078
|
-
note =
|
|
3105
|
+
note = toErrorMessage(err);
|
|
3079
3106
|
if (!isAbort && typeof err?.recoverable === "boolean") {
|
|
3080
3107
|
isTransientFailure = err.recoverable;
|
|
3081
3108
|
}
|
|
@@ -6819,11 +6846,6 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
6819
6846
|
};
|
|
6820
6847
|
}
|
|
6821
6848
|
|
|
6822
|
-
// src/utils/string.ts
|
|
6823
|
-
function truncate(s, max) {
|
|
6824
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
6825
|
-
}
|
|
6826
|
-
|
|
6827
6849
|
// src/types/provider.ts
|
|
6828
6850
|
var ProviderError = class extends WrongStackError {
|
|
6829
6851
|
status;
|
|
@@ -6903,7 +6925,7 @@ function classifySubagentError(err, hints = {}) {
|
|
|
6903
6925
|
const baseMessage2 = err.describe();
|
|
6904
6926
|
return providerErrorToSubagentError(err, baseMessage2, cause);
|
|
6905
6927
|
}
|
|
6906
|
-
const baseMessage =
|
|
6928
|
+
const baseMessage = toErrorMessage(err);
|
|
6907
6929
|
if (err instanceof BudgetExceededError) {
|
|
6908
6930
|
const map = {
|
|
6909
6931
|
iterations: "budget_iterations",
|
|
@@ -7883,7 +7905,7 @@ var ParallelEternalEngine = class {
|
|
|
7883
7905
|
console.error(JSON.stringify({
|
|
7884
7906
|
level: "error",
|
|
7885
7907
|
event: "engine.persist_state_failed",
|
|
7886
|
-
message:
|
|
7908
|
+
message: toErrorMessage(err),
|
|
7887
7909
|
context: { expectedState: "stopped" },
|
|
7888
7910
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7889
7911
|
}));
|
|
@@ -7919,7 +7941,7 @@ var ParallelEternalEngine = class {
|
|
|
7919
7941
|
);
|
|
7920
7942
|
await this.appendFailure(
|
|
7921
7943
|
"engine error",
|
|
7922
|
-
|
|
7944
|
+
toErrorMessage(err)
|
|
7923
7945
|
);
|
|
7924
7946
|
}
|
|
7925
7947
|
if (this.stopRequested) break;
|
|
@@ -8711,7 +8733,7 @@ function parseDescription(raw) {
|
|
|
8711
8733
|
const scope = [];
|
|
8712
8734
|
const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
|
|
8713
8735
|
if (coversMatch) {
|
|
8714
|
-
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);
|
|
8715
8737
|
scope.push(...items);
|
|
8716
8738
|
}
|
|
8717
8739
|
return { trigger, scope };
|