@wrongstack/core 0.5.6 → 0.6.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-B07AYFBk.d.ts → agent-bridge-BKNiE1VH.d.ts} +1 -1
- package/dist/{compactor-BWhJXxsW.d.ts → compactor-RIPuTtWK.d.ts} +1 -1
- package/dist/{config-BgM0BIpz.d.ts → config-BGGuP_Ar.d.ts} +1 -1
- package/dist/{context-CLZXPPYk.d.ts → context-CDRyrkKQ.d.ts} +7 -0
- package/dist/coordination/index.d.ts +9 -9
- package/dist/coordination/index.js +339 -80
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +20 -19
- package/dist/defaults/index.js +731 -180
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-qnDZbrtb.d.ts → events-DPQKFX7W.d.ts} +1 -1
- package/dist/execution/index.d.ts +152 -12
- package/dist/execution/index.js +474 -5
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js +2 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/goal-store-BQ3YX1h1.d.ts +75 -0
- package/dist/{index-DPLJw_ZI.d.ts → index-B0qTujQW.d.ts} +52 -5
- package/dist/{index-BDnUCRvL.d.ts → index-DkdRz6yK.d.ts} +351 -15
- package/dist/index.d.ts +109 -28
- package/dist/index.js +1143 -234
- 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/{mcp-servers-CSMfaBuL.d.ts → mcp-servers-DBdh3cee.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +2 -3
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-Cv8wk47i.d.ts → multi-agent-Cpp7FXUl.d.ts} +11 -3
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-DiCUvEg6.d.ts → path-resolver--g_hKJ7V.d.ts} +2 -2
- package/dist/{plan-templates-DaxTCPfk.d.ts → plan-templates-CKJs_sYh.d.ts} +9 -8
- package/dist/{provider-runner-3SHqk9zB.d.ts → provider-runner-hl4Il3xS.d.ts} +3 -3
- package/dist/{retry-policy-LLUxJmYY.d.ts → retry-policy-LKS8MHsB.d.ts} +1 -1
- package/dist/sdd/index.d.ts +6 -4
- package/dist/sdd/index.js +102 -68
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-scrubber-Z_VXuXQT.d.ts → secret-scrubber-BzQR5BiL.d.ts} +1 -1
- package/dist/{secret-scrubber-BhJTNr9v.d.ts → secret-scrubber-CfMdAJ_l.d.ts} +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/security/index.js +82 -82
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-DB2-byKH.d.ts → selector-C7HqnZJU.d.ts} +1 -1
- package/dist/{session-reader-4jxsYLZ8.d.ts → session-reader-CzfRA6Vk.d.ts} +1 -1
- package/dist/skills/index.js +102 -38
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +6 -5
- package/dist/storage/index.js +131 -22
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-DI4Dtc1I.d.ts → system-prompt-Dl2QY1_B.d.ts} +1 -1
- package/dist/{tool-executor-DSvmOBe6.d.ts → tool-executor-B03CRwu-.d.ts} +4 -4
- package/dist/types/index.d.ts +15 -15
- package/dist/types/index.js +100 -102
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/execution/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
1
3
|
import * as fs from 'fs/promises';
|
|
2
4
|
import * as path from 'path';
|
|
5
|
+
import { randomBytes } from 'crypto';
|
|
3
6
|
|
|
4
7
|
// src/utils/token-estimate.ts
|
|
5
8
|
var RoughTokenEstimate = (text) => Math.max(1, Math.ceil(text.length / 4));
|
|
@@ -1414,6 +1417,14 @@ var DoneConditionChecker = class {
|
|
|
1414
1417
|
};
|
|
1415
1418
|
}
|
|
1416
1419
|
break;
|
|
1420
|
+
case "directive":
|
|
1421
|
+
if (this.condition.maxIterations && state.iterations >= this.condition.maxIterations) {
|
|
1422
|
+
return { done: true, reason: `max iterations (${this.condition.maxIterations}) reached`, ...state };
|
|
1423
|
+
}
|
|
1424
|
+
if (this.condition.maxToolCalls && state.toolCalls >= this.condition.maxToolCalls) {
|
|
1425
|
+
return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
|
|
1426
|
+
}
|
|
1427
|
+
break;
|
|
1417
1428
|
}
|
|
1418
1429
|
return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
|
|
1419
1430
|
}
|
|
@@ -1433,10 +1444,16 @@ var AutonomousRunner = class {
|
|
|
1433
1444
|
const offToolExecuted = this.opts.agent.events?.on?.("tool.executed", () => {
|
|
1434
1445
|
this.toolCalls++;
|
|
1435
1446
|
});
|
|
1447
|
+
const offIterationCompleted = this.opts.agent.events?.on?.("iteration.completed", () => {
|
|
1448
|
+
if (this.opts.enableAutonomousContinue && this.opts.doneCondition.type === "directive") {
|
|
1449
|
+
this.iterations++;
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1436
1452
|
try {
|
|
1437
1453
|
return await this.runLoop();
|
|
1438
1454
|
} finally {
|
|
1439
1455
|
offToolExecuted?.();
|
|
1456
|
+
offIterationCompleted?.();
|
|
1440
1457
|
}
|
|
1441
1458
|
}
|
|
1442
1459
|
async runLoop() {
|
|
@@ -1460,12 +1477,14 @@ var AutonomousRunner = class {
|
|
|
1460
1477
|
const ctrl = new AbortController();
|
|
1461
1478
|
const timeout = setTimeout(() => ctrl.abort(), this.opts.iterationTimeoutMs ?? 3e4);
|
|
1462
1479
|
try {
|
|
1480
|
+
const isDirectiveMode = this.opts.doneCondition.type === "directive" && this.opts.enableAutonomousContinue;
|
|
1463
1481
|
const result = await this.opts.agent.run("", {
|
|
1464
1482
|
signal: ctrl.signal,
|
|
1465
|
-
maxIterations: 1,
|
|
1466
|
-
executionStrategy: "sequential"
|
|
1483
|
+
maxIterations: isDirectiveMode ? this.opts.doneCondition.maxIterations ?? 100 : 1,
|
|
1484
|
+
executionStrategy: "sequential",
|
|
1485
|
+
autonomousContinue: isDirectiveMode ? true : void 0
|
|
1467
1486
|
});
|
|
1468
|
-
this.iterations++;
|
|
1487
|
+
if (!isDirectiveMode) this.iterations++;
|
|
1469
1488
|
if (result.status === "done") {
|
|
1470
1489
|
this.lastOutput = result.finalText;
|
|
1471
1490
|
}
|
|
@@ -1517,6 +1536,456 @@ var AutonomousRunner = class {
|
|
|
1517
1536
|
this.stopped = true;
|
|
1518
1537
|
}
|
|
1519
1538
|
};
|
|
1539
|
+
async function atomicWrite(targetPath, content, opts = {}) {
|
|
1540
|
+
const dir = path.dirname(targetPath);
|
|
1541
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1542
|
+
const tmp = path.join(dir, `.${path.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
1543
|
+
try {
|
|
1544
|
+
if (typeof content === "string") {
|
|
1545
|
+
await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
1546
|
+
} else {
|
|
1547
|
+
await fs.writeFile(tmp, content, { flag: "wx" });
|
|
1548
|
+
}
|
|
1549
|
+
try {
|
|
1550
|
+
const fh = await fs.open(tmp, "r+");
|
|
1551
|
+
try {
|
|
1552
|
+
await fh.sync();
|
|
1553
|
+
} finally {
|
|
1554
|
+
await fh.close();
|
|
1555
|
+
}
|
|
1556
|
+
} catch {
|
|
1557
|
+
}
|
|
1558
|
+
let mode;
|
|
1559
|
+
try {
|
|
1560
|
+
const stat2 = await fs.stat(targetPath);
|
|
1561
|
+
mode = stat2.mode & 511;
|
|
1562
|
+
} catch {
|
|
1563
|
+
mode = opts.mode;
|
|
1564
|
+
}
|
|
1565
|
+
if (mode !== void 0) {
|
|
1566
|
+
await fs.chmod(tmp, mode);
|
|
1567
|
+
}
|
|
1568
|
+
await renameWithRetry(tmp, targetPath);
|
|
1569
|
+
} catch (err) {
|
|
1570
|
+
try {
|
|
1571
|
+
await fs.unlink(tmp);
|
|
1572
|
+
} catch {
|
|
1573
|
+
}
|
|
1574
|
+
throw err;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
1578
|
+
async function renameWithRetry(from, to) {
|
|
1579
|
+
if (process.platform !== "win32") {
|
|
1580
|
+
await fs.rename(from, to);
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
const delays = [10, 25, 60, 120, 250];
|
|
1584
|
+
let lastErr;
|
|
1585
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
1586
|
+
try {
|
|
1587
|
+
await fs.rename(from, to);
|
|
1588
|
+
return;
|
|
1589
|
+
} catch (err) {
|
|
1590
|
+
lastErr = err;
|
|
1591
|
+
const code = err?.code;
|
|
1592
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
1593
|
+
throw err;
|
|
1594
|
+
}
|
|
1595
|
+
await new Promise((resolve) => setTimeout(resolve, delays[i]));
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
throw lastErr;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// src/storage/goal-store.ts
|
|
1602
|
+
var MAX_JOURNAL_ENTRIES = 500;
|
|
1603
|
+
function goalFilePath(projectRoot) {
|
|
1604
|
+
return path.join(projectRoot, ".wrongstack", "goal.json");
|
|
1605
|
+
}
|
|
1606
|
+
async function loadGoal(filePath) {
|
|
1607
|
+
let raw;
|
|
1608
|
+
try {
|
|
1609
|
+
raw = await fs.readFile(filePath, "utf8");
|
|
1610
|
+
} catch {
|
|
1611
|
+
return null;
|
|
1612
|
+
}
|
|
1613
|
+
try {
|
|
1614
|
+
const parsed = JSON.parse(raw);
|
|
1615
|
+
if (parsed?.version !== 1 || typeof parsed.goal !== "string" || !Array.isArray(parsed.journal)) {
|
|
1616
|
+
return null;
|
|
1617
|
+
}
|
|
1618
|
+
return parsed;
|
|
1619
|
+
} catch {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
async function saveGoal(filePath, goal) {
|
|
1624
|
+
await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 384 });
|
|
1625
|
+
}
|
|
1626
|
+
function appendJournal(goal, entry) {
|
|
1627
|
+
const iteration = goal.iterations + 1;
|
|
1628
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1629
|
+
const full = { ...entry, iteration, at };
|
|
1630
|
+
const journal = [...goal.journal, full];
|
|
1631
|
+
const trimmed = journal.length > MAX_JOURNAL_ENTRIES ? journal.slice(journal.length - MAX_JOURNAL_ENTRIES) : journal;
|
|
1632
|
+
return {
|
|
1633
|
+
...goal,
|
|
1634
|
+
iterations: iteration,
|
|
1635
|
+
lastActivityAt: at,
|
|
1636
|
+
journal: trimmed
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// src/execution/eternal-autonomy.ts
|
|
1641
|
+
var execFileP = promisify(execFile);
|
|
1642
|
+
var EternalAutonomyEngine = class {
|
|
1643
|
+
constructor(opts) {
|
|
1644
|
+
this.opts = opts;
|
|
1645
|
+
this.goalPath = goalFilePath(opts.projectRoot);
|
|
1646
|
+
}
|
|
1647
|
+
opts;
|
|
1648
|
+
state = "idle";
|
|
1649
|
+
stopRequested = false;
|
|
1650
|
+
consecutiveFailures = 0;
|
|
1651
|
+
currentCtrl = null;
|
|
1652
|
+
iterationsSinceCompact = 0;
|
|
1653
|
+
goalPath;
|
|
1654
|
+
/** Current engine state — readable for UIs. */
|
|
1655
|
+
get currentState() {
|
|
1656
|
+
return this.state;
|
|
1657
|
+
}
|
|
1658
|
+
/** Synchronously request stop. Resolves once the running iteration aborts. */
|
|
1659
|
+
stop() {
|
|
1660
|
+
this.stopRequested = true;
|
|
1661
|
+
this.currentCtrl?.abort();
|
|
1662
|
+
void this.persistEngineState("stopped").catch(() => {
|
|
1663
|
+
});
|
|
1664
|
+
this.state = "stopped";
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Mark the engine as 'running' on disk + reset stop state so a new
|
|
1668
|
+
* batch of `runOneIteration()` calls can proceed. Called by the REPL
|
|
1669
|
+
* when the user invokes `/autonomy eternal`. Idempotent.
|
|
1670
|
+
*/
|
|
1671
|
+
async prime() {
|
|
1672
|
+
this.stopRequested = false;
|
|
1673
|
+
this.state = "running";
|
|
1674
|
+
await this.persistEngineState("running").catch(() => {
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Main loop. Returns when stop() is called or the goal file is removed.
|
|
1679
|
+
* Does NOT throw — every iteration is wrapped to keep the loop alive.
|
|
1680
|
+
*/
|
|
1681
|
+
async run() {
|
|
1682
|
+
this.state = "running";
|
|
1683
|
+
await this.persistEngineState("running");
|
|
1684
|
+
try {
|
|
1685
|
+
while (!this.stopRequested) {
|
|
1686
|
+
let iterationOk = false;
|
|
1687
|
+
try {
|
|
1688
|
+
iterationOk = await this.runOneIteration();
|
|
1689
|
+
} catch (err) {
|
|
1690
|
+
this.consecutiveFailures++;
|
|
1691
|
+
this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
|
|
1692
|
+
await this.appendFailure("engine error", err instanceof Error ? err.message : String(err));
|
|
1693
|
+
}
|
|
1694
|
+
if (iterationOk) {
|
|
1695
|
+
this.consecutiveFailures = 0;
|
|
1696
|
+
}
|
|
1697
|
+
if (this.stopRequested) break;
|
|
1698
|
+
await sleep(this.opts.cycleGapMs ?? 1e3);
|
|
1699
|
+
}
|
|
1700
|
+
} finally {
|
|
1701
|
+
this.state = "stopped";
|
|
1702
|
+
await this.persistEngineState("stopped").catch(() => {
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Execute a single sense-decide-execute-reflect cycle.
|
|
1708
|
+
* Returns true on success, false on handled failure / no-op.
|
|
1709
|
+
*
|
|
1710
|
+
* Exposed publicly so the REPL can pace iterations from its main loop
|
|
1711
|
+
* — running the engine and the REPL as a single sequential consumer of
|
|
1712
|
+
* `agent.run()` avoids race conditions on the shared Context.
|
|
1713
|
+
*/
|
|
1714
|
+
async runOneIteration() {
|
|
1715
|
+
const goal = await loadGoal(this.goalPath);
|
|
1716
|
+
if (!goal) {
|
|
1717
|
+
this.stopRequested = true;
|
|
1718
|
+
return false;
|
|
1719
|
+
}
|
|
1720
|
+
const action = await this.decide(goal);
|
|
1721
|
+
if (!action) {
|
|
1722
|
+
await sleep(5e3);
|
|
1723
|
+
return false;
|
|
1724
|
+
}
|
|
1725
|
+
const ctrl = new AbortController();
|
|
1726
|
+
this.currentCtrl = ctrl;
|
|
1727
|
+
const timer = setTimeout(
|
|
1728
|
+
() => ctrl.abort(),
|
|
1729
|
+
this.opts.iterationTimeoutMs ?? 5 * 6e4
|
|
1730
|
+
);
|
|
1731
|
+
let status = "success";
|
|
1732
|
+
let note;
|
|
1733
|
+
const tc = this.opts.agent.ctx?.tokenCounter;
|
|
1734
|
+
const beforeUsage = tc?.total?.();
|
|
1735
|
+
const beforeCost = tc?.estimateCost?.().total;
|
|
1736
|
+
try {
|
|
1737
|
+
const result = await this.opts.agent.run(
|
|
1738
|
+
[{ type: "text", text: action.directive }],
|
|
1739
|
+
{ signal: ctrl.signal }
|
|
1740
|
+
);
|
|
1741
|
+
if (result.status === "aborted") {
|
|
1742
|
+
status = "aborted";
|
|
1743
|
+
note = "stopped by user";
|
|
1744
|
+
} else if (result.status === "failed") {
|
|
1745
|
+
status = "failure";
|
|
1746
|
+
note = result.error?.describe?.() ?? "agent run failed";
|
|
1747
|
+
} else if (result.status === "max_iterations") {
|
|
1748
|
+
status = "failure";
|
|
1749
|
+
note = `max iterations (${result.iterations})`;
|
|
1750
|
+
} else {
|
|
1751
|
+
status = "success";
|
|
1752
|
+
const tail = (result.finalText ?? "").slice(0, 240).replace(/\s+/g, " ").trim();
|
|
1753
|
+
if (tail) note = tail;
|
|
1754
|
+
}
|
|
1755
|
+
} catch (err) {
|
|
1756
|
+
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
1757
|
+
status = isAbort ? "aborted" : "failure";
|
|
1758
|
+
note = err instanceof Error ? err.message : String(err);
|
|
1759
|
+
} finally {
|
|
1760
|
+
clearTimeout(timer);
|
|
1761
|
+
this.currentCtrl = null;
|
|
1762
|
+
}
|
|
1763
|
+
const afterUsage = tc?.total?.();
|
|
1764
|
+
const afterCost = tc?.estimateCost?.().total;
|
|
1765
|
+
const tokens = beforeUsage && afterUsage ? {
|
|
1766
|
+
input: Math.max(0, afterUsage.input - beforeUsage.input),
|
|
1767
|
+
output: Math.max(0, afterUsage.output - beforeUsage.output)
|
|
1768
|
+
} : void 0;
|
|
1769
|
+
const costUsd = typeof beforeCost === "number" && typeof afterCost === "number" ? Math.max(0, afterCost - beforeCost) : void 0;
|
|
1770
|
+
await this.appendIterationEntry({
|
|
1771
|
+
source: action.source,
|
|
1772
|
+
task: action.task,
|
|
1773
|
+
status,
|
|
1774
|
+
note,
|
|
1775
|
+
tokens,
|
|
1776
|
+
costUsd
|
|
1777
|
+
});
|
|
1778
|
+
let iterationIndex = 0;
|
|
1779
|
+
try {
|
|
1780
|
+
const reloaded = await loadGoal(this.goalPath);
|
|
1781
|
+
iterationIndex = reloaded?.iterations ?? 0;
|
|
1782
|
+
} catch {
|
|
1783
|
+
}
|
|
1784
|
+
this.opts.onIteration?.({
|
|
1785
|
+
at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
1786
|
+
iteration: iterationIndex,
|
|
1787
|
+
source: action.source,
|
|
1788
|
+
task: action.task,
|
|
1789
|
+
status,
|
|
1790
|
+
note,
|
|
1791
|
+
tokens,
|
|
1792
|
+
costUsd
|
|
1793
|
+
});
|
|
1794
|
+
if (status === "failure") {
|
|
1795
|
+
this.consecutiveFailures++;
|
|
1796
|
+
return false;
|
|
1797
|
+
}
|
|
1798
|
+
if (status === "aborted") {
|
|
1799
|
+
if (this.stopRequested) return false;
|
|
1800
|
+
this.consecutiveFailures++;
|
|
1801
|
+
return false;
|
|
1802
|
+
}
|
|
1803
|
+
this.iterationsSinceCompact++;
|
|
1804
|
+
await this.maybeCompact().catch((err) => {
|
|
1805
|
+
this.opts.onError?.(
|
|
1806
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
1807
|
+
this.consecutiveFailures
|
|
1808
|
+
);
|
|
1809
|
+
});
|
|
1810
|
+
return true;
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Run compaction when either trigger fires:
|
|
1814
|
+
* - We've done >= compactEveryNIterations since the last compact.
|
|
1815
|
+
* - Current request tokens exceed aggressiveCompactRatio * maxContext.
|
|
1816
|
+
*
|
|
1817
|
+
* The second check uses *aggressive* mode to free more headroom; the
|
|
1818
|
+
* cadence check uses non-aggressive (cheaper).
|
|
1819
|
+
*/
|
|
1820
|
+
async maybeCompact() {
|
|
1821
|
+
const compactor = this.opts.compactor;
|
|
1822
|
+
if (!compactor) return;
|
|
1823
|
+
const ctx = this.opts.agent.ctx;
|
|
1824
|
+
if (!ctx) return;
|
|
1825
|
+
const cadence = this.opts.compactEveryNIterations ?? 25;
|
|
1826
|
+
const threshold = this.opts.aggressiveCompactRatio ?? 0.85;
|
|
1827
|
+
const maxCtx = this.opts.maxContextTokens;
|
|
1828
|
+
let aggressive = false;
|
|
1829
|
+
let shouldRun = false;
|
|
1830
|
+
if (this.iterationsSinceCompact >= cadence) {
|
|
1831
|
+
shouldRun = true;
|
|
1832
|
+
}
|
|
1833
|
+
if (maxCtx && maxCtx > 0) {
|
|
1834
|
+
const used = ctx.tokenCounter?.currentRequestTokens?.();
|
|
1835
|
+
if (used) {
|
|
1836
|
+
const total = used.input + used.cacheRead;
|
|
1837
|
+
if (total / maxCtx >= threshold) {
|
|
1838
|
+
shouldRun = true;
|
|
1839
|
+
aggressive = true;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (!shouldRun) return;
|
|
1844
|
+
const report = await compactor.compact(ctx, { aggressive });
|
|
1845
|
+
this.iterationsSinceCompact = 0;
|
|
1846
|
+
const saved = report.before - report.after;
|
|
1847
|
+
await this.appendIterationEntry({
|
|
1848
|
+
source: "manual",
|
|
1849
|
+
task: `compaction (${aggressive ? "aggressive" : "cadence"})`,
|
|
1850
|
+
status: "success",
|
|
1851
|
+
note: `saved ~${saved} tokens (${report.before}\u2192${report.after})`
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* Hybrid idea source.
|
|
1856
|
+
* 1. Pending todos on the agent's context.
|
|
1857
|
+
* 2. Dirty git working tree → propose a "review and finish this" task.
|
|
1858
|
+
* 3. Otherwise: brainstorm via the LLM against the goal.
|
|
1859
|
+
*
|
|
1860
|
+
* After failureBudget consecutive failures, force brainstorm so the
|
|
1861
|
+
* engine doesn't loop on the same broken todo or stuck git state.
|
|
1862
|
+
*/
|
|
1863
|
+
async decide(goal) {
|
|
1864
|
+
const forceBrainstorm = this.consecutiveFailures >= (this.opts.failureBudget ?? 3);
|
|
1865
|
+
if (!forceBrainstorm) {
|
|
1866
|
+
const todo = this.pickPendingTodo();
|
|
1867
|
+
if (todo) {
|
|
1868
|
+
return {
|
|
1869
|
+
source: "todo",
|
|
1870
|
+
task: todo.content,
|
|
1871
|
+
directive: this.buildDirective(goal, "todo", todo.content)
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
const gitTask = await this.pickGitTask();
|
|
1875
|
+
if (gitTask) {
|
|
1876
|
+
return {
|
|
1877
|
+
source: "git",
|
|
1878
|
+
task: gitTask,
|
|
1879
|
+
directive: this.buildDirective(goal, "git", gitTask)
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
const brainstormed = await this.brainstormTask(goal);
|
|
1884
|
+
if (!brainstormed) return null;
|
|
1885
|
+
return {
|
|
1886
|
+
source: "brainstorm",
|
|
1887
|
+
task: brainstormed,
|
|
1888
|
+
directive: this.buildDirective(goal, "brainstorm", brainstormed)
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
pickPendingTodo() {
|
|
1892
|
+
const todos = this.opts.agent.ctx.todos;
|
|
1893
|
+
if (!Array.isArray(todos)) return null;
|
|
1894
|
+
return todos.find((t) => t.status === "pending") ?? null;
|
|
1895
|
+
}
|
|
1896
|
+
async pickGitTask() {
|
|
1897
|
+
let out;
|
|
1898
|
+
try {
|
|
1899
|
+
out = await (this.opts.gitStatusReader?.() ?? this.readGitStatus());
|
|
1900
|
+
} catch {
|
|
1901
|
+
return null;
|
|
1902
|
+
}
|
|
1903
|
+
const dirty = out.trim();
|
|
1904
|
+
if (!dirty) return null;
|
|
1905
|
+
const lines = dirty.split("\n").slice(0, 8);
|
|
1906
|
+
const preview = lines.join(", ");
|
|
1907
|
+
return `Inspect the dirty working tree and either finish the in-progress work or revert it. Files: ${preview}`;
|
|
1908
|
+
}
|
|
1909
|
+
async readGitStatus() {
|
|
1910
|
+
const { stdout } = await execFileP("git", ["status", "--porcelain"], {
|
|
1911
|
+
cwd: this.opts.projectRoot,
|
|
1912
|
+
timeout: 5e3
|
|
1913
|
+
});
|
|
1914
|
+
return stdout;
|
|
1915
|
+
}
|
|
1916
|
+
async brainstormTask(goal) {
|
|
1917
|
+
const lastFew = goal.journal.slice(-5).map((e) => ` - [${e.status}] ${e.task}`).join("\n");
|
|
1918
|
+
const directive = [
|
|
1919
|
+
"You are deciding the next action in an autonomous loop pursuing a long-running goal.",
|
|
1920
|
+
"",
|
|
1921
|
+
`Goal: ${goal.goal}`,
|
|
1922
|
+
"",
|
|
1923
|
+
lastFew ? `Recent iterations:
|
|
1924
|
+
${lastFew}` : "No prior iterations yet.",
|
|
1925
|
+
"",
|
|
1926
|
+
"Output ONE concrete, immediately-actionable task that advances the goal.",
|
|
1927
|
+
"Constraints:",
|
|
1928
|
+
"- One sentence, imperative form, under 200 chars.",
|
|
1929
|
+
"- No preamble, no explanation, no markdown \u2014 just the task line.",
|
|
1930
|
+
"- If recent iterations show repeated failures on the same target, pivot.",
|
|
1931
|
+
"- If the goal appears fully accomplished, output exactly: DONE"
|
|
1932
|
+
].join("\n");
|
|
1933
|
+
try {
|
|
1934
|
+
const ctrl = new AbortController();
|
|
1935
|
+
const timer = setTimeout(() => ctrl.abort(), 6e4);
|
|
1936
|
+
try {
|
|
1937
|
+
const result = await this.opts.agent.run(
|
|
1938
|
+
[{ type: "text", text: directive }],
|
|
1939
|
+
{ signal: ctrl.signal, maxIterations: 1 }
|
|
1940
|
+
);
|
|
1941
|
+
if (result.status !== "done") return null;
|
|
1942
|
+
const text = (result.finalText ?? "").trim();
|
|
1943
|
+
if (!text || text === "DONE") return null;
|
|
1944
|
+
const firstLine = text.split("\n").find((l) => l.trim().length > 0)?.trim();
|
|
1945
|
+
if (!firstLine) return null;
|
|
1946
|
+
return firstLine.slice(0, 240);
|
|
1947
|
+
} finally {
|
|
1948
|
+
clearTimeout(timer);
|
|
1949
|
+
}
|
|
1950
|
+
} catch {
|
|
1951
|
+
return null;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
buildDirective(goal, source, task) {
|
|
1955
|
+
return [
|
|
1956
|
+
"[ETERNAL AUTONOMY \u2014 iteration directive]",
|
|
1957
|
+
"",
|
|
1958
|
+
`Goal: ${goal.goal}`,
|
|
1959
|
+
`Source: ${source}`,
|
|
1960
|
+
`Task: ${task}`,
|
|
1961
|
+
"",
|
|
1962
|
+
"Execute this task end-to-end using the tools available to you. Make the",
|
|
1963
|
+
"changes, run tests if relevant, and commit / push as appropriate. Do not",
|
|
1964
|
+
"ask for confirmation \u2014 YOLO mode is active. When the task is done, stop;",
|
|
1965
|
+
"the loop will pick the next action."
|
|
1966
|
+
].join("\n");
|
|
1967
|
+
}
|
|
1968
|
+
async appendIterationEntry(entry) {
|
|
1969
|
+
const current = await loadGoal(this.goalPath);
|
|
1970
|
+
if (!current) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
const updated = appendJournal(current, entry);
|
|
1974
|
+
await saveGoal(this.goalPath, updated);
|
|
1975
|
+
}
|
|
1976
|
+
async appendFailure(task, note) {
|
|
1977
|
+
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
1978
|
+
}
|
|
1979
|
+
async persistEngineState(state) {
|
|
1980
|
+
const current = await loadGoal(this.goalPath);
|
|
1981
|
+
if (!current) return;
|
|
1982
|
+
if (current.engineState === state) return;
|
|
1983
|
+
await saveGoal(this.goalPath, { ...current, engineState: state });
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
function sleep(ms) {
|
|
1987
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1988
|
+
}
|
|
1520
1989
|
|
|
1521
1990
|
// src/types/provider.ts
|
|
1522
1991
|
var ProviderError = class extends WrongStackError {
|
|
@@ -1648,7 +2117,7 @@ function buildRecoveryStrategies(opts) {
|
|
|
1648
2117
|
async attempt(err) {
|
|
1649
2118
|
if (!(err instanceof ProviderError) || err.status !== 429) return null;
|
|
1650
2119
|
const delayMs = err.body?.retryAfterMs ?? 5e3;
|
|
1651
|
-
const delay = Math.
|
|
2120
|
+
const delay = Math.min(6e4, Math.max(1e3, delayMs));
|
|
1652
2121
|
await new Promise((r) => setTimeout(r, delay));
|
|
1653
2122
|
return { action: "retry", reason: "rate_limit_backoff" };
|
|
1654
2123
|
}
|
|
@@ -1830,6 +2299,6 @@ function parseDescription(raw) {
|
|
|
1830
2299
|
return { trigger, scope };
|
|
1831
2300
|
}
|
|
1832
2301
|
|
|
1833
|
-
export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, HybridCompactor, IntelligentCompactor, SelectiveCompactor, ToolExecutor };
|
|
2302
|
+
export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, SelectiveCompactor, ToolExecutor };
|
|
1834
2303
|
//# sourceMappingURL=index.js.map
|
|
1835
2304
|
//# sourceMappingURL=index.js.map
|