@wrongstack/core 0.6.0 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-BKNiE1VH.d.ts → agent-bridge-eb7qnNrd.d.ts} +1 -1
- package/dist/coordination/index.d.ts +5 -5
- package/dist/coordination/index.js +183 -87
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +10 -10
- package/dist/defaults/index.js +596 -114
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-DPQKFX7W.d.ts → events-BHuIHekD.d.ts} +27 -4
- package/dist/execution/index.d.ts +149 -6
- package/dist/execution/index.js +401 -21
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +2 -2
- package/dist/{goal-store-BQ3YX1h1.d.ts → goal-store-DVCfj7Ff.d.ts} +20 -0
- package/dist/{index-B0qTujQW.d.ts → index-BOn9NK7D.d.ts} +1 -1
- package/dist/{index-DkdRz6yK.d.ts → index-CPcDqvZh.d.ts} +11 -4
- package/dist/index.d.ts +50 -16
- package/dist/index.js +831 -125
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- package/dist/infrastructure/index.js +1 -1
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +2 -2
- package/dist/kernel/index.js.map +1 -1
- package/dist/{multi-agent-Cpp7FXUl.d.ts → multi-agent-CxSb-9dQ.d.ts} +30 -5
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.js +1 -1
- package/dist/observability/index.js.map +1 -1
- package/dist/{path-resolver--g_hKJ7V.d.ts → path-resolver-CMGNadvq.d.ts} +1 -1
- package/dist/{plan-templates-CKJs_sYh.d.ts → plan-templates-BJflQY2i.d.ts} +1 -1
- package/dist/{provider-runner-hl4Il3xS.d.ts → provider-runner-BFgNXpaP.d.ts} +1 -1
- package/dist/sdd/index.d.ts +2 -2
- package/dist/storage/index.d.ts +3 -3
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/{tool-executor-B03CRwu-.d.ts → tool-executor-FoxBjULX.d.ts} +1 -1
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.js +30 -4
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
package/dist/execution/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as path from 'path';
|
|
|
5
5
|
import { randomBytes } from 'crypto';
|
|
6
6
|
|
|
7
7
|
// src/utils/token-estimate.ts
|
|
8
|
-
var RoughTokenEstimate = (text) => Math.max(1, Math.ceil(text.length /
|
|
8
|
+
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
9
9
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
10
10
|
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
11
11
|
function getCachedEstimate(key, compute) {
|
|
@@ -136,7 +136,7 @@ var HybridCompactor = class {
|
|
|
136
136
|
eliseThreshold;
|
|
137
137
|
estimator;
|
|
138
138
|
constructor(opts = {}) {
|
|
139
|
-
this.preserveK = opts.preserveK ??
|
|
139
|
+
this.preserveK = opts.preserveK ?? 5;
|
|
140
140
|
this.eliseThreshold = opts.eliseThreshold ?? 2e3;
|
|
141
141
|
this.estimator = opts.estimator ?? estimateTextTokens;
|
|
142
142
|
}
|
|
@@ -180,6 +180,17 @@ var HybridCompactor = class {
|
|
|
180
180
|
preserveStart = i;
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
for (let i = preserveStart; i < messages.length; i++) {
|
|
184
|
+
const m = messages[i];
|
|
185
|
+
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
186
|
+
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
187
|
+
if (hasToolUse2 && i + 1 < messages.length) {
|
|
188
|
+
const next = messages[i + 1];
|
|
189
|
+
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
190
|
+
preserveStart = i + 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
183
194
|
let saved = 0;
|
|
184
195
|
let changed = false;
|
|
185
196
|
const nextMessages = new Array(messages.length);
|
|
@@ -201,7 +212,7 @@ var HybridCompactor = class {
|
|
|
201
212
|
const elided = {
|
|
202
213
|
type: "tool_result",
|
|
203
214
|
tool_use_id: b.tool_use_id,
|
|
204
|
-
content: `[elided: ~${tokens} tokens
|
|
215
|
+
content: `[elided: ~${tokens} tokens]`,
|
|
205
216
|
is_error: b.is_error
|
|
206
217
|
};
|
|
207
218
|
return elided;
|
|
@@ -340,7 +351,28 @@ var IntelligentCompactor = class {
|
|
|
340
351
|
try {
|
|
341
352
|
summaryText = await this.callSummarizer(toSummarize, ctx);
|
|
342
353
|
} catch {
|
|
343
|
-
|
|
354
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
355
|
+
const filePaths = /* @__PURE__ */ new Set();
|
|
356
|
+
let userTurns = 0, assistantTurns = 0;
|
|
357
|
+
for (const m of toSummarize) {
|
|
358
|
+
if (m.role === "user") userTurns++;
|
|
359
|
+
else if (m.role === "assistant") {
|
|
360
|
+
assistantTurns++;
|
|
361
|
+
if (Array.isArray(m.content)) {
|
|
362
|
+
for (const b of m.content) {
|
|
363
|
+
if (b.type === "tool_use") toolNames.add(b.name ?? "unknown");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const text = typeof m.content === "string" ? m.content : "";
|
|
368
|
+
const matches = text.matchAll(/(?:[\w.,\-/@]+\/)*[\w.,\-/@]+\.\w+/g);
|
|
369
|
+
for (const m_ of matches) filePaths.add(m_[0]);
|
|
370
|
+
}
|
|
371
|
+
const parts = [`${toSummarize.length} turns (${userTurns} user, ${assistantTurns} assistant)`];
|
|
372
|
+
if (toolNames.size > 0) parts.push(`tools: ${[...toolNames].join(", ")}`);
|
|
373
|
+
if (filePaths.size > 0) parts.push(`files: ${[...filePaths].slice(0, 10).join(", ")}`);
|
|
374
|
+
summaryText = parts.join(" | ");
|
|
375
|
+
if (!summaryText) summaryText = `${toSummarize.length} earlier turns omitted`;
|
|
344
376
|
}
|
|
345
377
|
const summaryMsg = {
|
|
346
378
|
role: "system",
|
|
@@ -424,6 +456,17 @@ var IntelligentCompactor = class {
|
|
|
424
456
|
preserveStart = i;
|
|
425
457
|
}
|
|
426
458
|
}
|
|
459
|
+
for (let i = preserveStart; i < messages.length; i++) {
|
|
460
|
+
const m = messages[i];
|
|
461
|
+
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
462
|
+
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
463
|
+
if (hasToolUse2 && i + 1 < messages.length) {
|
|
464
|
+
const next = messages[i + 1];
|
|
465
|
+
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
466
|
+
preserveStart = i + 1;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
427
470
|
let saved = 0;
|
|
428
471
|
let changed = false;
|
|
429
472
|
const nextMessages = new Array(messages.length);
|
|
@@ -991,7 +1034,15 @@ var AutoCompactionMiddleware = class {
|
|
|
991
1034
|
}
|
|
992
1035
|
async compact(ctx, aggressive, pressure) {
|
|
993
1036
|
try {
|
|
994
|
-
await this.compactor.compact(ctx, { aggressive });
|
|
1037
|
+
const report = await this.compactor.compact(ctx, { aggressive });
|
|
1038
|
+
this.events?.emit("compaction.fired", {
|
|
1039
|
+
level: pressure.level,
|
|
1040
|
+
tokens: pressure.tokens,
|
|
1041
|
+
load: pressure.load,
|
|
1042
|
+
maxContext: this._maxContext,
|
|
1043
|
+
report,
|
|
1044
|
+
aggressive
|
|
1045
|
+
});
|
|
995
1046
|
} catch (err) {
|
|
996
1047
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
997
1048
|
const fatal = this.failureMode === "throw" || this.failureMode === "throw_on_hard" && pressure.level === "hard";
|
|
@@ -1245,13 +1296,17 @@ var ToolExecutor = class {
|
|
|
1245
1296
|
const ctrl = new AbortController();
|
|
1246
1297
|
const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
|
|
1247
1298
|
const combined = AbortSignal.any([parentSignal, ctrl.signal]);
|
|
1299
|
+
let cleanupCalled = false;
|
|
1300
|
+
let caught = false;
|
|
1248
1301
|
try {
|
|
1249
1302
|
if (typeof tool.executeStream === "function") {
|
|
1250
1303
|
return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
|
|
1251
1304
|
}
|
|
1252
1305
|
return await tool.execute(input, ctx, { signal: combined });
|
|
1253
1306
|
} catch (err) {
|
|
1307
|
+
caught = true;
|
|
1254
1308
|
if (combined.aborted && typeof tool.cleanup === "function") {
|
|
1309
|
+
cleanupCalled = true;
|
|
1255
1310
|
try {
|
|
1256
1311
|
await tool.cleanup(input, ctx);
|
|
1257
1312
|
} catch {
|
|
@@ -1260,6 +1315,16 @@ var ToolExecutor = class {
|
|
|
1260
1315
|
throw err;
|
|
1261
1316
|
} finally {
|
|
1262
1317
|
clearTimeout(timer);
|
|
1318
|
+
if (combined.aborted && !caught) {
|
|
1319
|
+
if (!cleanupCalled && typeof tool.cleanup === "function") {
|
|
1320
|
+
try {
|
|
1321
|
+
await tool.cleanup(input, ctx);
|
|
1322
|
+
} catch {
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
const reason = combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "aborted");
|
|
1326
|
+
throw reason;
|
|
1327
|
+
}
|
|
1263
1328
|
}
|
|
1264
1329
|
}
|
|
1265
1330
|
async runStreamedTool(tool, input, ctx, signal, toolUseId) {
|
|
@@ -1317,7 +1382,8 @@ var ToolExecutor = class {
|
|
|
1317
1382
|
if (subjectKey) {
|
|
1318
1383
|
const v = obj[subjectKey];
|
|
1319
1384
|
if (typeof v === "string") {
|
|
1320
|
-
|
|
1385
|
+
const isPathKey = subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
|
|
1386
|
+
return isPathKey ? normalizePath(v) : escapeGlob(v);
|
|
1321
1387
|
}
|
|
1322
1388
|
}
|
|
1323
1389
|
if (toolName === "bash" && typeof obj.command === "string") {
|
|
@@ -1639,6 +1705,8 @@ function appendJournal(goal, entry) {
|
|
|
1639
1705
|
|
|
1640
1706
|
// src/execution/eternal-autonomy.ts
|
|
1641
1707
|
var execFileP = promisify(execFile);
|
|
1708
|
+
var BRAINSTORM_DONE = /* @__PURE__ */ Symbol("brainstorm-done");
|
|
1709
|
+
var GOAL_COMPLETE_MARKER = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
1642
1710
|
var EternalAutonomyEngine = class {
|
|
1643
1711
|
constructor(opts) {
|
|
1644
1712
|
this.opts = opts;
|
|
@@ -1648,6 +1716,14 @@ var EternalAutonomyEngine = class {
|
|
|
1648
1716
|
state = "idle";
|
|
1649
1717
|
stopRequested = false;
|
|
1650
1718
|
consecutiveFailures = 0;
|
|
1719
|
+
consecutiveBrainstormDone = 0;
|
|
1720
|
+
/**
|
|
1721
|
+
* Count of consecutive transient (recoverable) provider failures. Drives
|
|
1722
|
+
* the exponential backoff between iterations. Reset on the first
|
|
1723
|
+
* successful iteration so a single bad afternoon doesn't permanently
|
|
1724
|
+
* slow the loop down.
|
|
1725
|
+
*/
|
|
1726
|
+
consecutiveTransientRetries = 0;
|
|
1651
1727
|
currentCtrl = null;
|
|
1652
1728
|
iterationsSinceCompact = 0;
|
|
1653
1729
|
goalPath;
|
|
@@ -1717,9 +1793,16 @@ var EternalAutonomyEngine = class {
|
|
|
1717
1793
|
this.stopRequested = true;
|
|
1718
1794
|
return false;
|
|
1719
1795
|
}
|
|
1796
|
+
const missionState = goal.goalState ?? "active";
|
|
1797
|
+
if (missionState !== "active") {
|
|
1798
|
+
this.stopRequested = true;
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1720
1801
|
const action = await this.decide(goal);
|
|
1721
1802
|
if (!action) {
|
|
1722
|
-
|
|
1803
|
+
if (!this.stopRequested) {
|
|
1804
|
+
await sleep(5e3);
|
|
1805
|
+
}
|
|
1723
1806
|
return false;
|
|
1724
1807
|
}
|
|
1725
1808
|
const ctrl = new AbortController();
|
|
@@ -1730,13 +1813,29 @@ var EternalAutonomyEngine = class {
|
|
|
1730
1813
|
);
|
|
1731
1814
|
let status = "success";
|
|
1732
1815
|
let note;
|
|
1816
|
+
let finalText = "";
|
|
1817
|
+
let isTransientFailure = false;
|
|
1733
1818
|
const tc = this.opts.agent.ctx?.tokenCounter;
|
|
1734
1819
|
const beforeUsage = tc?.total?.();
|
|
1735
1820
|
const beforeCost = tc?.estimateCost?.().total;
|
|
1736
1821
|
try {
|
|
1737
1822
|
const result = await this.opts.agent.run(
|
|
1738
1823
|
[{ type: "text", text: action.directive }],
|
|
1739
|
-
{
|
|
1824
|
+
{
|
|
1825
|
+
signal: ctrl.signal,
|
|
1826
|
+
// Enable per-call autonomous continuation so the agent can chain
|
|
1827
|
+
// multiple internal tool/response cycles end-to-end on one
|
|
1828
|
+
// directive instead of returning to the engine after a single
|
|
1829
|
+
// round-trip. The model uses `[continue]` / `[done]` markers
|
|
1830
|
+
// (or the `continue_to_next_iteration` tool) to control the
|
|
1831
|
+
// inner loop. Without this flag the engine produced shallow
|
|
1832
|
+
// iterations and almost never let a real task finish.
|
|
1833
|
+
autonomousContinue: true,
|
|
1834
|
+
// Cap the inner loop so a runaway agent.run can't burn through
|
|
1835
|
+
// the iteration timeout — the engine's own outer loop is the
|
|
1836
|
+
// long-running thing, each tick should be bounded.
|
|
1837
|
+
maxIterations: this.opts.iterationMaxAgentSteps ?? 50
|
|
1838
|
+
}
|
|
1740
1839
|
);
|
|
1741
1840
|
if (result.status === "aborted") {
|
|
1742
1841
|
status = "aborted";
|
|
@@ -1744,22 +1843,30 @@ var EternalAutonomyEngine = class {
|
|
|
1744
1843
|
} else if (result.status === "failed") {
|
|
1745
1844
|
status = "failure";
|
|
1746
1845
|
note = result.error?.describe?.() ?? "agent run failed";
|
|
1846
|
+
isTransientFailure = result.error?.recoverable === true;
|
|
1747
1847
|
} else if (result.status === "max_iterations") {
|
|
1748
1848
|
status = "failure";
|
|
1749
1849
|
note = `max iterations (${result.iterations})`;
|
|
1750
1850
|
} else {
|
|
1751
1851
|
status = "success";
|
|
1752
|
-
|
|
1852
|
+
finalText = result.finalText ?? "";
|
|
1853
|
+
const tail = finalText.slice(0, 240).replace(/\s+/g, " ").trim();
|
|
1753
1854
|
if (tail) note = tail;
|
|
1754
1855
|
}
|
|
1755
1856
|
} catch (err) {
|
|
1756
1857
|
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
1757
1858
|
status = isAbort ? "aborted" : "failure";
|
|
1758
1859
|
note = err instanceof Error ? err.message : String(err);
|
|
1860
|
+
if (!isAbort && typeof err?.recoverable === "boolean") {
|
|
1861
|
+
isTransientFailure = err.recoverable;
|
|
1862
|
+
}
|
|
1759
1863
|
} finally {
|
|
1760
1864
|
clearTimeout(timer);
|
|
1761
1865
|
this.currentCtrl = null;
|
|
1762
1866
|
}
|
|
1867
|
+
if (action.source === "todo" && action.todoId && status !== "success") {
|
|
1868
|
+
await this.bumpTodoAttempt(action.todoId);
|
|
1869
|
+
}
|
|
1763
1870
|
const afterUsage = tc?.total?.();
|
|
1764
1871
|
const afterCost = tc?.estimateCost?.().total;
|
|
1765
1872
|
const tokens = beforeUsage && afterUsage ? {
|
|
@@ -1792,6 +1899,14 @@ var EternalAutonomyEngine = class {
|
|
|
1792
1899
|
costUsd
|
|
1793
1900
|
});
|
|
1794
1901
|
if (status === "failure") {
|
|
1902
|
+
if (isTransientFailure) {
|
|
1903
|
+
this.consecutiveTransientRetries++;
|
|
1904
|
+
const delay = this.computeTransientBackoffMs();
|
|
1905
|
+
if (delay > 0) {
|
|
1906
|
+
await this.sleepInterruptible(delay);
|
|
1907
|
+
}
|
|
1908
|
+
return false;
|
|
1909
|
+
}
|
|
1795
1910
|
this.consecutiveFailures++;
|
|
1796
1911
|
return false;
|
|
1797
1912
|
}
|
|
@@ -1800,6 +1915,12 @@ var EternalAutonomyEngine = class {
|
|
|
1800
1915
|
this.consecutiveFailures++;
|
|
1801
1916
|
return false;
|
|
1802
1917
|
}
|
|
1918
|
+
this.consecutiveTransientRetries = 0;
|
|
1919
|
+
if (GOAL_COMPLETE_MARKER.test(finalText)) {
|
|
1920
|
+
await this.markGoalCompleted(action, finalText);
|
|
1921
|
+
this.stopRequested = true;
|
|
1922
|
+
return true;
|
|
1923
|
+
}
|
|
1803
1924
|
this.iterationsSinceCompact++;
|
|
1804
1925
|
await this.maybeCompact().catch((err) => {
|
|
1805
1926
|
this.opts.onError?.(
|
|
@@ -1863,11 +1984,12 @@ var EternalAutonomyEngine = class {
|
|
|
1863
1984
|
async decide(goal) {
|
|
1864
1985
|
const forceBrainstorm = this.consecutiveFailures >= (this.opts.failureBudget ?? 3);
|
|
1865
1986
|
if (!forceBrainstorm) {
|
|
1866
|
-
const todo = this.pickPendingTodo();
|
|
1987
|
+
const todo = this.pickPendingTodo(goal);
|
|
1867
1988
|
if (todo) {
|
|
1868
1989
|
return {
|
|
1869
1990
|
source: "todo",
|
|
1870
1991
|
task: todo.content,
|
|
1992
|
+
todoId: todo.id,
|
|
1871
1993
|
directive: this.buildDirective(goal, "todo", todo.content)
|
|
1872
1994
|
};
|
|
1873
1995
|
}
|
|
@@ -1881,17 +2003,38 @@ var EternalAutonomyEngine = class {
|
|
|
1881
2003
|
}
|
|
1882
2004
|
}
|
|
1883
2005
|
const brainstormed = await this.brainstormTask(goal);
|
|
2006
|
+
if (brainstormed === BRAINSTORM_DONE) {
|
|
2007
|
+
this.consecutiveBrainstormDone++;
|
|
2008
|
+
const threshold = this.opts.brainstormDoneStopThreshold ?? 3;
|
|
2009
|
+
if (this.consecutiveBrainstormDone >= threshold) {
|
|
2010
|
+
await this.markGoalCompleted(
|
|
2011
|
+
{ source: "brainstorm", task: "no further work", directive: "" },
|
|
2012
|
+
`brainstorm returned DONE ${this.consecutiveBrainstormDone}x in a row`
|
|
2013
|
+
);
|
|
2014
|
+
this.stopRequested = true;
|
|
2015
|
+
}
|
|
2016
|
+
return null;
|
|
2017
|
+
}
|
|
1884
2018
|
if (!brainstormed) return null;
|
|
2019
|
+
this.consecutiveBrainstormDone = 0;
|
|
1885
2020
|
return {
|
|
1886
2021
|
source: "brainstorm",
|
|
1887
2022
|
task: brainstormed,
|
|
1888
2023
|
directive: this.buildDirective(goal, "brainstorm", brainstormed)
|
|
1889
2024
|
};
|
|
1890
2025
|
}
|
|
1891
|
-
pickPendingTodo() {
|
|
2026
|
+
pickPendingTodo(goal) {
|
|
1892
2027
|
const todos = this.opts.agent.ctx.todos;
|
|
1893
2028
|
if (!Array.isArray(todos)) return null;
|
|
1894
|
-
|
|
2029
|
+
const attempts = goal.todoAttempts ?? {};
|
|
2030
|
+
const ceiling = this.opts.todoMaxAttempts ?? 3;
|
|
2031
|
+
for (const t of todos) {
|
|
2032
|
+
if (t.status !== "pending") continue;
|
|
2033
|
+
const used = attempts[t.id] ?? 0;
|
|
2034
|
+
if (used >= ceiling) continue;
|
|
2035
|
+
return t;
|
|
2036
|
+
}
|
|
2037
|
+
return null;
|
|
1895
2038
|
}
|
|
1896
2039
|
async pickGitTask() {
|
|
1897
2040
|
let out;
|
|
@@ -1928,7 +2071,10 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
1928
2071
|
"- One sentence, imperative form, under 200 chars.",
|
|
1929
2072
|
"- No preamble, no explanation, no markdown \u2014 just the task line.",
|
|
1930
2073
|
"- If recent iterations show repeated failures on the same target, pivot.",
|
|
1931
|
-
"- If the goal appears fully accomplished
|
|
2074
|
+
"- If the goal appears fully accomplished AND you can name a concrete",
|
|
2075
|
+
" artifact / test / output that proves it, output exactly: DONE",
|
|
2076
|
+
"- Be conservative with DONE: if the recent journal contains failures",
|
|
2077
|
+
" or aborted entries, the goal is almost certainly NOT done."
|
|
1932
2078
|
].join("\n");
|
|
1933
2079
|
try {
|
|
1934
2080
|
const ctrl = new AbortController();
|
|
@@ -1940,9 +2086,11 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
1940
2086
|
);
|
|
1941
2087
|
if (result.status !== "done") return null;
|
|
1942
2088
|
const text = (result.finalText ?? "").trim();
|
|
1943
|
-
if (!text
|
|
2089
|
+
if (!text) return null;
|
|
2090
|
+
if (/^DONE\.?$/i.test(text)) return BRAINSTORM_DONE;
|
|
1944
2091
|
const firstLine = text.split("\n").find((l) => l.trim().length > 0)?.trim();
|
|
1945
2092
|
if (!firstLine) return null;
|
|
2093
|
+
if (/^DONE\.?$/i.test(firstLine)) return BRAINSTORM_DONE;
|
|
1946
2094
|
return firstLine.slice(0, 240);
|
|
1947
2095
|
} finally {
|
|
1948
2096
|
clearTimeout(timer);
|
|
@@ -1952,19 +2100,82 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
1952
2100
|
}
|
|
1953
2101
|
}
|
|
1954
2102
|
buildDirective(goal, source, task) {
|
|
2103
|
+
const recentJournal = goal.journal.slice(-5).map((e) => ` #${e.iteration} [${e.status}] ${e.task}${e.note ? ` \u2014 ${e.note.slice(0, 80)}` : ""}`).join("\n");
|
|
1955
2104
|
return [
|
|
1956
|
-
"
|
|
2105
|
+
"\u2550\u2550\u2550 ETERNAL AUTONOMY \u2014 iteration directive \u2550\u2550\u2550",
|
|
1957
2106
|
"",
|
|
1958
|
-
`
|
|
2107
|
+
`Mission: ${goal.goal}`,
|
|
2108
|
+
`Iteration: #${goal.iterations + 1}`,
|
|
1959
2109
|
`Source: ${source}`,
|
|
1960
2110
|
`Task: ${task}`,
|
|
1961
2111
|
"",
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
"
|
|
1965
|
-
"
|
|
2112
|
+
recentJournal ? `Recent journal (last 5):
|
|
2113
|
+
${recentJournal}` : "No prior iterations.",
|
|
2114
|
+
"",
|
|
2115
|
+
"\u2500\u2500 EXECUTION PROTOCOL \u2500\u2500",
|
|
2116
|
+
"You are inside a long-running autonomous loop. Each iteration you",
|
|
2117
|
+
"execute ONE concrete task that advances the Mission. No user is",
|
|
2118
|
+
"available to clarify \u2014 make defensible decisions and move forward.",
|
|
2119
|
+
"",
|
|
2120
|
+
"1. EXECUTE END-TO-END",
|
|
2121
|
+
" \u2022 Use multiple tool calls freely. Emit `[continue]` on its own line",
|
|
2122
|
+
" to chain to the next internal step without returning.",
|
|
2123
|
+
" \u2022 When this iteration's Task is finished (real artifact / passing",
|
|
2124
|
+
" test / applied diff / clean output), emit `[done]` on its own line.",
|
|
2125
|
+
" \u2022 Do not stop on the first obstacle \u2014 try at least 3 distinct",
|
|
2126
|
+
" approaches before giving up. YOLO is active; no confirmations.",
|
|
2127
|
+
"",
|
|
2128
|
+
"2. UPDATE TODO STATE (when Source is `todo`)",
|
|
2129
|
+
" \u2022 Mark this todo `in_progress` via the todos tool before tool work.",
|
|
2130
|
+
" \u2022 Mark it `completed` on success, with a one-line outcome note.",
|
|
2131
|
+
" \u2022 If you cannot make progress after 2 distinct attempts, mark it",
|
|
2132
|
+
" `cancelled` with the obstacle. The loop will skip it next time.",
|
|
2133
|
+
"",
|
|
2134
|
+
"3. MISSION-COMPLETE PROTOCOL",
|
|
2135
|
+
" \u2022 If \u2014 and ONLY if \u2014 the OVERALL Mission (not just this Task) is",
|
|
2136
|
+
" verifiably accomplished, emit on its own line:",
|
|
2137
|
+
" [GOAL_COMPLETE]",
|
|
2138
|
+
" followed by a one-paragraph verification recipe (artifact path,",
|
|
2139
|
+
" test command, or 10-second reproduction). This halts the loop.",
|
|
2140
|
+
" \u2022 NEVER emit [GOAL_COMPLETE] on optimism, partial progress, or",
|
|
2141
|
+
' "looks fine". Required: a concrete artifact that proves it AND',
|
|
2142
|
+
" no recent journal failures contradicting completion.",
|
|
2143
|
+
" \u2022 If unsure, emit `[done]` instead and let the next iteration",
|
|
2144
|
+
" decide. The loop is patient; false completion is not.",
|
|
2145
|
+
"",
|
|
2146
|
+
"4. NO INTERACTIVITY",
|
|
2147
|
+
" \u2022 Do not ask questions, do not request confirmation, do not propose",
|
|
2148
|
+
" options. Pick the best path and execute. The user is asleep."
|
|
1966
2149
|
].join("\n");
|
|
1967
2150
|
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Exponential backoff for transient provider errors. `2^N * base`
|
|
2153
|
+
* capped at `transientBackoffMaxMs`. Zero base disables backoff.
|
|
2154
|
+
* Public-private to keep `runOneIteration` readable; the value is
|
|
2155
|
+
* recomputed each call from the current retry count, so callers
|
|
2156
|
+
* don't have to track state.
|
|
2157
|
+
*/
|
|
2158
|
+
computeTransientBackoffMs() {
|
|
2159
|
+
const base = this.opts.transientBackoffBaseMs ?? 2e3;
|
|
2160
|
+
const cap = this.opts.transientBackoffMaxMs ?? 6e4;
|
|
2161
|
+
if (base <= 0) return 0;
|
|
2162
|
+
const exponent = Math.max(0, this.consecutiveTransientRetries - 1);
|
|
2163
|
+
return Math.min(cap, base * Math.pow(2, exponent));
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* Sleep that wakes early if `stopRequested` flips. Polls every 250 ms
|
|
2167
|
+
* so SIGINT / `/autonomy stop` can land in the middle of a long
|
|
2168
|
+
* backoff instead of waiting up to a minute for the timer.
|
|
2169
|
+
*/
|
|
2170
|
+
async sleepInterruptible(totalMs) {
|
|
2171
|
+
const step = 250;
|
|
2172
|
+
let remaining = totalMs;
|
|
2173
|
+
while (remaining > 0 && !this.stopRequested) {
|
|
2174
|
+
const chunk = Math.min(step, remaining);
|
|
2175
|
+
await sleep(chunk);
|
|
2176
|
+
remaining -= chunk;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
1968
2179
|
async appendIterationEntry(entry) {
|
|
1969
2180
|
const current = await loadGoal(this.goalPath);
|
|
1970
2181
|
if (!current) {
|
|
@@ -1973,6 +2184,39 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
1973
2184
|
const updated = appendJournal(current, entry);
|
|
1974
2185
|
await saveGoal(this.goalPath, updated);
|
|
1975
2186
|
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Persistent per-todo failure counter. Skipped silently when the goal
|
|
2189
|
+
* file has been removed (graceful clear). Each non-success iteration
|
|
2190
|
+
* against a todo source bumps the counter by 1; `pickPendingTodo` reads
|
|
2191
|
+
* the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
|
|
2192
|
+
*/
|
|
2193
|
+
async bumpTodoAttempt(todoId) {
|
|
2194
|
+
const current = await loadGoal(this.goalPath);
|
|
2195
|
+
if (!current) return;
|
|
2196
|
+
const attempts = { ...current.todoAttempts ?? {} };
|
|
2197
|
+
attempts[todoId] = (attempts[todoId] ?? 0) + 1;
|
|
2198
|
+
await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Flip the mission to `completed` and journal it. Called from two
|
|
2202
|
+
* paths: (a) `[GOAL_COMPLETE]` marker in a successful iteration's
|
|
2203
|
+
* finalText, (b) `brainstorm` returning DONE consecutively past the
|
|
2204
|
+
* configured threshold. Idempotent — re-entry is a no-op once the
|
|
2205
|
+
* goal is already `completed`.
|
|
2206
|
+
*/
|
|
2207
|
+
async markGoalCompleted(action, note) {
|
|
2208
|
+
const current = await loadGoal(this.goalPath);
|
|
2209
|
+
if (!current) return;
|
|
2210
|
+
if (current.goalState === "completed") return;
|
|
2211
|
+
const withFlag = { ...current, goalState: "completed" };
|
|
2212
|
+
const withEntry = appendJournal(withFlag, {
|
|
2213
|
+
source: action.source,
|
|
2214
|
+
task: `MISSION COMPLETE \u2014 ${action.task}`.slice(0, 240),
|
|
2215
|
+
status: "success",
|
|
2216
|
+
note: note.slice(0, 240)
|
|
2217
|
+
});
|
|
2218
|
+
await saveGoal(this.goalPath, withEntry);
|
|
2219
|
+
}
|
|
1976
2220
|
async appendFailure(task, note) {
|
|
1977
2221
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
1978
2222
|
}
|
|
@@ -1987,6 +2231,142 @@ function sleep(ms) {
|
|
|
1987
2231
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1988
2232
|
}
|
|
1989
2233
|
|
|
2234
|
+
// src/execution/autonomy-prompt-contributor.ts
|
|
2235
|
+
function makeAutonomyPromptContributor(opts) {
|
|
2236
|
+
return async (ctx) => {
|
|
2237
|
+
if (ctx.subagent) return [];
|
|
2238
|
+
if (!opts.enabled()) return [];
|
|
2239
|
+
let goal;
|
|
2240
|
+
try {
|
|
2241
|
+
goal = await loadGoal(opts.goalPath);
|
|
2242
|
+
} catch {
|
|
2243
|
+
return [];
|
|
2244
|
+
}
|
|
2245
|
+
if (!goal) return [];
|
|
2246
|
+
const missionState = goal.goalState ?? "active";
|
|
2247
|
+
if (missionState !== "active") return [];
|
|
2248
|
+
const tailSize = opts.journalTailSize ?? 5;
|
|
2249
|
+
const journalTail = goal.journal.slice(-tailSize).map((e) => {
|
|
2250
|
+
const note = e.note ? ` \u2014 ${e.note.slice(0, 80)}` : "";
|
|
2251
|
+
return ` #${e.iteration} [${e.status}] ${e.task}${note}`;
|
|
2252
|
+
});
|
|
2253
|
+
const text = [
|
|
2254
|
+
"## ETERNAL AUTONOMY \u2014 active mission",
|
|
2255
|
+
"",
|
|
2256
|
+
"You are inside a long-running autonomous loop. The user is asleep",
|
|
2257
|
+
"and is not available to confirm decisions. Each turn you receive a",
|
|
2258
|
+
"directive describing one concrete sub-task that advances the mission.",
|
|
2259
|
+
"",
|
|
2260
|
+
`Mission: ${goal.goal}`,
|
|
2261
|
+
`Iteration: #${goal.iterations}`,
|
|
2262
|
+
journalTail.length > 0 ? `Recent journal (last ${journalTail.length}):
|
|
2263
|
+
${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first iteration)",
|
|
2264
|
+
"",
|
|
2265
|
+
"### Loop control markers",
|
|
2266
|
+
"Emit these on their own line in your final text \u2014 case-insensitive,",
|
|
2267
|
+
"whitespace-tolerant, but they must occupy the entire line:",
|
|
2268
|
+
"- `[continue]` \u2014 chain to the next internal step without returning.",
|
|
2269
|
+
"- `[done]` \u2014 the current sub-task is finished; return to the engine.",
|
|
2270
|
+
"- `[GOAL_COMPLETE]` \u2014 emit ONLY when the OVERALL mission is",
|
|
2271
|
+
" verifiably done. Must be followed by a one-paragraph verification",
|
|
2272
|
+
" recipe (artifact path, test command, or 10-second reproduction).",
|
|
2273
|
+
" The engine halts on this marker \u2014 false positives waste real",
|
|
2274
|
+
" human time. If unsure, emit `[done]` and let the next iteration",
|
|
2275
|
+
" decide.",
|
|
2276
|
+
"",
|
|
2277
|
+
"### Operating principles",
|
|
2278
|
+
"- YOLO is active. Do NOT ask for confirmation, do NOT propose",
|
|
2279
|
+
" options. Pick the best path and execute it.",
|
|
2280
|
+
"- Use tools freely; multiple calls per turn are normal and expected.",
|
|
2281
|
+
"- When working on a todo, mark it `in_progress` via the todos tool",
|
|
2282
|
+
" before tool work and `completed` (or `cancelled` with a reason)",
|
|
2283
|
+
" when done. The loop reads todo state between iterations.",
|
|
2284
|
+
"- If an approach fails twice in a row, pivot. Don't grind on the",
|
|
2285
|
+
" same wall \u2014 try a different angle, file a cancel on the todo, or",
|
|
2286
|
+
" surface the obstacle via `[done]` and let the next iteration",
|
|
2287
|
+
" re-plan."
|
|
2288
|
+
].join("\n");
|
|
2289
|
+
return [
|
|
2290
|
+
{
|
|
2291
|
+
type: "text",
|
|
2292
|
+
text,
|
|
2293
|
+
cache_control: { type: "ephemeral" }
|
|
2294
|
+
}
|
|
2295
|
+
];
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
// src/execution/goal-preamble.ts
|
|
2300
|
+
function buildGoalPreamble(goal) {
|
|
2301
|
+
return [
|
|
2302
|
+
"[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
|
|
2303
|
+
"The user granted you full autonomy. Read these constraints once, then act.",
|
|
2304
|
+
"",
|
|
2305
|
+
"YOUR GOAL:",
|
|
2306
|
+
"---",
|
|
2307
|
+
goal,
|
|
2308
|
+
"---",
|
|
2309
|
+
"",
|
|
2310
|
+
"AUTHORITY YOU HAVE:",
|
|
2311
|
+
"- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
|
|
2312
|
+
" Parallel + recursive fan-out are both fine. There is no spawn budget.",
|
|
2313
|
+
"- Use any provider/model per subagent \u2014 pick the right tool for each",
|
|
2314
|
+
" piece of work. Heavy reasoning model for planning, fast model for",
|
|
2315
|
+
" batch work, specialist model for domain code.",
|
|
2316
|
+
"- Run unlimited tool calls and iterations. There is NO hidden budget.",
|
|
2317
|
+
" The Agent loop auto-extends every 100 iterations forever.",
|
|
2318
|
+
"- Retry failed tools with different inputs, alternative paths, fresh",
|
|
2319
|
+
" subagents. Switch providers mid-run if one is rate-limited.",
|
|
2320
|
+
"- Re-plan freely when an approach hits a dead end. You are not obliged",
|
|
2321
|
+
" to stick with the first plan you proposed.",
|
|
2322
|
+
"",
|
|
2323
|
+
'WHAT "DONE" MEANS \u2014 non-negotiable:',
|
|
2324
|
+
"- You can name a concrete artifact (a passing test, a written file at",
|
|
2325
|
+
" a specific path, a fixed bug verified by re-running the failing case,",
|
|
2326
|
+
" a clean grep that previously had matches).",
|
|
2327
|
+
"- You can tell the user HOW to verify it themselves in 10 seconds.",
|
|
2328
|
+
'- You have NOT hedged. None of: "looks like it should work", "I',
|
|
2329
|
+
' believe this fixes it", "the changes appear correct".',
|
|
2330
|
+
"",
|
|
2331
|
+
"WHAT IS NOT DONE \u2014 never report any of these as completion:",
|
|
2332
|
+
"- An error message you didn't recover from.",
|
|
2333
|
+
'- An empty result, a 0-line file, a "no matches found" you accepted',
|
|
2334
|
+
" without questioning the search.",
|
|
2335
|
+
'- "Should I continue?" / "Want me to also...?" / "Let me know if you',
|
|
2336
|
+
' want X." Those are hedges. The user already told you to finish the',
|
|
2337
|
+
" goal \u2014 just do it.",
|
|
2338
|
+
"- Partial progress dressed up as success. Fixed 3 of 5 bugs = 60%",
|
|
2339
|
+
" done, not done.",
|
|
2340
|
+
"- A subagent's failed/timeout/stopped TaskResult that you didn't",
|
|
2341
|
+
" respond to with a fresh attempt (different role, different model,",
|
|
2342
|
+
" tighter prompt).",
|
|
2343
|
+
"",
|
|
2344
|
+
"PERSISTENCE PROTOCOL:",
|
|
2345
|
+
"- If blocked, try at least 3 different angles before reporting the",
|
|
2346
|
+
" problem to the user. Different tool inputs, different subagent",
|
|
2347
|
+
" roles, different providers, different decomposition of the task.",
|
|
2348
|
+
"- If a tool fails, read its error, alter the input, try again. Do",
|
|
2349
|
+
" not just report the failure back.",
|
|
2350
|
+
"- If a subagent returns useless output, respawn with a tighter prompt",
|
|
2351
|
+
' or a different role. Do not accept "I could not determine\u2026" as the',
|
|
2352
|
+
" final answer.",
|
|
2353
|
+
"- Use `ask_subagent` for one-shot questions when you don't need a",
|
|
2354
|
+
" full delegated task.",
|
|
2355
|
+
"",
|
|
2356
|
+
"REPORTING:",
|
|
2357
|
+
"- Stream short progress notes between major actions so the user can",
|
|
2358
|
+
" monitor. Do not go silent for 50 tool calls then dump a wall of",
|
|
2359
|
+
" text \u2014 but also do not narrate every tool call.",
|
|
2360
|
+
"- Use the shared scratchpad (if available) to leave breadcrumbs",
|
|
2361
|
+
" subagents can read.",
|
|
2362
|
+
"- Final response must include: (a) what was accomplished, (b) how",
|
|
2363
|
+
" to verify, (c) any caveats (residual TODOs, things the user",
|
|
2364
|
+
" should know about).",
|
|
2365
|
+
"",
|
|
2366
|
+
"BEGIN.]"
|
|
2367
|
+
].join("\n");
|
|
2368
|
+
}
|
|
2369
|
+
|
|
1990
2370
|
// src/types/provider.ts
|
|
1991
2371
|
var ProviderError = class extends WrongStackError {
|
|
1992
2372
|
status;
|
|
@@ -2299,6 +2679,6 @@ function parseDescription(raw) {
|
|
|
2299
2679
|
return { trigger, scope };
|
|
2300
2680
|
}
|
|
2301
2681
|
|
|
2302
|
-
export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, SelectiveCompactor, ToolExecutor };
|
|
2682
|
+
export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, SelectiveCompactor, ToolExecutor, buildGoalPreamble, makeAutonomyPromptContributor };
|
|
2303
2683
|
//# sourceMappingURL=index.js.map
|
|
2304
2684
|
//# sourceMappingURL=index.js.map
|