@wrongstack/core 0.6.0 → 0.6.1
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-DaCvA_uK.d.ts} +1 -1
- package/dist/coordination/index.d.ts +5 -5
- package/dist/coordination/index.js +178 -54
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +10 -10
- package/dist/defaults/index.js +534 -75
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-DPQKFX7W.d.ts → events-CzkeaVVl.d.ts} +8 -2
- package/dist/execution/index.d.ts +149 -6
- package/dist/execution/index.js +345 -16
- 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-BkKLQjea.d.ts} +1 -1
- package/dist/{index-DkdRz6yK.d.ts → index-i9rPR53g.d.ts} +11 -4
- package/dist/index.d.ts +50 -16
- package/dist/index.js +769 -86
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- 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-MfI6dmv-.d.ts} +30 -5
- package/dist/observability/index.d.ts +1 -1
- package/dist/{path-resolver--g_hKJ7V.d.ts → path-resolver-CWINz5XR.d.ts} +1 -1
- package/dist/{plan-templates-CKJs_sYh.d.ts → plan-templates-Bne1pB4d.d.ts} +1 -1
- package/dist/{provider-runner-hl4Il3xS.d.ts → provider-runner-CZYIzeBp.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-40Q6shR-.d.ts} +1 -1
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.js +16 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +5 -1
package/dist/defaults/index.js
CHANGED
|
@@ -3691,17 +3691,23 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
3691
3691
|
handleTextDelta(state, ev.text);
|
|
3692
3692
|
events.emit("provider.text_delta", { ctx, text: ev.text });
|
|
3693
3693
|
break;
|
|
3694
|
-
case "tool_use_start":
|
|
3695
|
-
|
|
3696
|
-
|
|
3694
|
+
case "tool_use_start": {
|
|
3695
|
+
const idVal = ev.id;
|
|
3696
|
+
const nameVal = ev.name;
|
|
3697
|
+
handleToolUseStart(state, { id: idVal, name: nameVal });
|
|
3698
|
+
const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
|
|
3699
|
+
events.emit("provider.tool_use_start", emittedPayload);
|
|
3697
3700
|
break;
|
|
3701
|
+
}
|
|
3698
3702
|
case "tool_use_input_delta":
|
|
3699
3703
|
handleToolUseInputDelta(state, ev);
|
|
3700
3704
|
break;
|
|
3701
|
-
case "tool_use_stop":
|
|
3705
|
+
case "tool_use_stop": {
|
|
3706
|
+
const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
|
|
3702
3707
|
handleToolUseStop(state, ev);
|
|
3703
|
-
events.emit("provider.tool_use_stop", { ctx, id: ev.id });
|
|
3708
|
+
events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
|
|
3704
3709
|
break;
|
|
3710
|
+
}
|
|
3705
3711
|
case "thinking_start":
|
|
3706
3712
|
handleThinkingStart(state, ev);
|
|
3707
3713
|
break;
|
|
@@ -4971,13 +4977,17 @@ var ToolExecutor = class {
|
|
|
4971
4977
|
const ctrl = new AbortController();
|
|
4972
4978
|
const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
|
|
4973
4979
|
const combined = AbortSignal.any([parentSignal, ctrl.signal]);
|
|
4980
|
+
let cleanupCalled = false;
|
|
4981
|
+
let caught = false;
|
|
4974
4982
|
try {
|
|
4975
4983
|
if (typeof tool.executeStream === "function") {
|
|
4976
4984
|
return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
|
|
4977
4985
|
}
|
|
4978
4986
|
return await tool.execute(input, ctx, { signal: combined });
|
|
4979
4987
|
} catch (err) {
|
|
4988
|
+
caught = true;
|
|
4980
4989
|
if (combined.aborted && typeof tool.cleanup === "function") {
|
|
4990
|
+
cleanupCalled = true;
|
|
4981
4991
|
try {
|
|
4982
4992
|
await tool.cleanup(input, ctx);
|
|
4983
4993
|
} catch {
|
|
@@ -4986,6 +4996,16 @@ var ToolExecutor = class {
|
|
|
4986
4996
|
throw err;
|
|
4987
4997
|
} finally {
|
|
4988
4998
|
clearTimeout(timer);
|
|
4999
|
+
if (combined.aborted && !caught) {
|
|
5000
|
+
if (!cleanupCalled && typeof tool.cleanup === "function") {
|
|
5001
|
+
try {
|
|
5002
|
+
await tool.cleanup(input, ctx);
|
|
5003
|
+
} catch {
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
const reason = combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "aborted");
|
|
5007
|
+
throw reason;
|
|
5008
|
+
}
|
|
4989
5009
|
}
|
|
4990
5010
|
}
|
|
4991
5011
|
async runStreamedTool(tool, input, ctx, signal, toolUseId) {
|
|
@@ -5043,7 +5063,8 @@ var ToolExecutor = class {
|
|
|
5043
5063
|
if (subjectKey) {
|
|
5044
5064
|
const v = obj[subjectKey];
|
|
5045
5065
|
if (typeof v === "string") {
|
|
5046
|
-
|
|
5066
|
+
const isPathKey = subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
|
|
5067
|
+
return isPathKey ? normalizePath(v) : escapeGlob(v);
|
|
5047
5068
|
}
|
|
5048
5069
|
}
|
|
5049
5070
|
if (toolName === "bash" && typeof obj.command === "string") {
|
|
@@ -5269,6 +5290,8 @@ function appendJournal(goal, entry) {
|
|
|
5269
5290
|
|
|
5270
5291
|
// src/execution/eternal-autonomy.ts
|
|
5271
5292
|
var execFileP = promisify(execFile);
|
|
5293
|
+
var BRAINSTORM_DONE = /* @__PURE__ */ Symbol("brainstorm-done");
|
|
5294
|
+
var GOAL_COMPLETE_MARKER = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
5272
5295
|
var EternalAutonomyEngine = class {
|
|
5273
5296
|
constructor(opts) {
|
|
5274
5297
|
this.opts = opts;
|
|
@@ -5278,6 +5301,14 @@ var EternalAutonomyEngine = class {
|
|
|
5278
5301
|
state = "idle";
|
|
5279
5302
|
stopRequested = false;
|
|
5280
5303
|
consecutiveFailures = 0;
|
|
5304
|
+
consecutiveBrainstormDone = 0;
|
|
5305
|
+
/**
|
|
5306
|
+
* Count of consecutive transient (recoverable) provider failures. Drives
|
|
5307
|
+
* the exponential backoff between iterations. Reset on the first
|
|
5308
|
+
* successful iteration so a single bad afternoon doesn't permanently
|
|
5309
|
+
* slow the loop down.
|
|
5310
|
+
*/
|
|
5311
|
+
consecutiveTransientRetries = 0;
|
|
5281
5312
|
currentCtrl = null;
|
|
5282
5313
|
iterationsSinceCompact = 0;
|
|
5283
5314
|
goalPath;
|
|
@@ -5347,9 +5378,16 @@ var EternalAutonomyEngine = class {
|
|
|
5347
5378
|
this.stopRequested = true;
|
|
5348
5379
|
return false;
|
|
5349
5380
|
}
|
|
5381
|
+
const missionState = goal.goalState ?? "active";
|
|
5382
|
+
if (missionState !== "active") {
|
|
5383
|
+
this.stopRequested = true;
|
|
5384
|
+
return false;
|
|
5385
|
+
}
|
|
5350
5386
|
const action = await this.decide(goal);
|
|
5351
5387
|
if (!action) {
|
|
5352
|
-
|
|
5388
|
+
if (!this.stopRequested) {
|
|
5389
|
+
await sleep(5e3);
|
|
5390
|
+
}
|
|
5353
5391
|
return false;
|
|
5354
5392
|
}
|
|
5355
5393
|
const ctrl = new AbortController();
|
|
@@ -5360,13 +5398,29 @@ var EternalAutonomyEngine = class {
|
|
|
5360
5398
|
);
|
|
5361
5399
|
let status = "success";
|
|
5362
5400
|
let note;
|
|
5401
|
+
let finalText = "";
|
|
5402
|
+
let isTransientFailure = false;
|
|
5363
5403
|
const tc = this.opts.agent.ctx?.tokenCounter;
|
|
5364
5404
|
const beforeUsage = tc?.total?.();
|
|
5365
5405
|
const beforeCost = tc?.estimateCost?.().total;
|
|
5366
5406
|
try {
|
|
5367
5407
|
const result = await this.opts.agent.run(
|
|
5368
5408
|
[{ type: "text", text: action.directive }],
|
|
5369
|
-
{
|
|
5409
|
+
{
|
|
5410
|
+
signal: ctrl.signal,
|
|
5411
|
+
// Enable per-call autonomous continuation so the agent can chain
|
|
5412
|
+
// multiple internal tool/response cycles end-to-end on one
|
|
5413
|
+
// directive instead of returning to the engine after a single
|
|
5414
|
+
// round-trip. The model uses `[continue]` / `[done]` markers
|
|
5415
|
+
// (or the `continue_to_next_iteration` tool) to control the
|
|
5416
|
+
// inner loop. Without this flag the engine produced shallow
|
|
5417
|
+
// iterations and almost never let a real task finish.
|
|
5418
|
+
autonomousContinue: true,
|
|
5419
|
+
// Cap the inner loop so a runaway agent.run can't burn through
|
|
5420
|
+
// the iteration timeout — the engine's own outer loop is the
|
|
5421
|
+
// long-running thing, each tick should be bounded.
|
|
5422
|
+
maxIterations: this.opts.iterationMaxAgentSteps ?? 50
|
|
5423
|
+
}
|
|
5370
5424
|
);
|
|
5371
5425
|
if (result.status === "aborted") {
|
|
5372
5426
|
status = "aborted";
|
|
@@ -5374,22 +5428,30 @@ var EternalAutonomyEngine = class {
|
|
|
5374
5428
|
} else if (result.status === "failed") {
|
|
5375
5429
|
status = "failure";
|
|
5376
5430
|
note = result.error?.describe?.() ?? "agent run failed";
|
|
5431
|
+
isTransientFailure = result.error?.recoverable === true;
|
|
5377
5432
|
} else if (result.status === "max_iterations") {
|
|
5378
5433
|
status = "failure";
|
|
5379
5434
|
note = `max iterations (${result.iterations})`;
|
|
5380
5435
|
} else {
|
|
5381
5436
|
status = "success";
|
|
5382
|
-
|
|
5437
|
+
finalText = result.finalText ?? "";
|
|
5438
|
+
const tail = finalText.slice(0, 240).replace(/\s+/g, " ").trim();
|
|
5383
5439
|
if (tail) note = tail;
|
|
5384
5440
|
}
|
|
5385
5441
|
} catch (err) {
|
|
5386
5442
|
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
5387
5443
|
status = isAbort ? "aborted" : "failure";
|
|
5388
5444
|
note = err instanceof Error ? err.message : String(err);
|
|
5445
|
+
if (!isAbort && typeof err?.recoverable === "boolean") {
|
|
5446
|
+
isTransientFailure = err.recoverable;
|
|
5447
|
+
}
|
|
5389
5448
|
} finally {
|
|
5390
5449
|
clearTimeout(timer);
|
|
5391
5450
|
this.currentCtrl = null;
|
|
5392
5451
|
}
|
|
5452
|
+
if (action.source === "todo" && action.todoId && status !== "success") {
|
|
5453
|
+
await this.bumpTodoAttempt(action.todoId);
|
|
5454
|
+
}
|
|
5393
5455
|
const afterUsage = tc?.total?.();
|
|
5394
5456
|
const afterCost = tc?.estimateCost?.().total;
|
|
5395
5457
|
const tokens = beforeUsage && afterUsage ? {
|
|
@@ -5422,6 +5484,14 @@ var EternalAutonomyEngine = class {
|
|
|
5422
5484
|
costUsd
|
|
5423
5485
|
});
|
|
5424
5486
|
if (status === "failure") {
|
|
5487
|
+
if (isTransientFailure) {
|
|
5488
|
+
this.consecutiveTransientRetries++;
|
|
5489
|
+
const delay = this.computeTransientBackoffMs();
|
|
5490
|
+
if (delay > 0) {
|
|
5491
|
+
await this.sleepInterruptible(delay);
|
|
5492
|
+
}
|
|
5493
|
+
return false;
|
|
5494
|
+
}
|
|
5425
5495
|
this.consecutiveFailures++;
|
|
5426
5496
|
return false;
|
|
5427
5497
|
}
|
|
@@ -5430,6 +5500,12 @@ var EternalAutonomyEngine = class {
|
|
|
5430
5500
|
this.consecutiveFailures++;
|
|
5431
5501
|
return false;
|
|
5432
5502
|
}
|
|
5503
|
+
this.consecutiveTransientRetries = 0;
|
|
5504
|
+
if (GOAL_COMPLETE_MARKER.test(finalText)) {
|
|
5505
|
+
await this.markGoalCompleted(action, finalText);
|
|
5506
|
+
this.stopRequested = true;
|
|
5507
|
+
return true;
|
|
5508
|
+
}
|
|
5433
5509
|
this.iterationsSinceCompact++;
|
|
5434
5510
|
await this.maybeCompact().catch((err) => {
|
|
5435
5511
|
this.opts.onError?.(
|
|
@@ -5493,11 +5569,12 @@ var EternalAutonomyEngine = class {
|
|
|
5493
5569
|
async decide(goal) {
|
|
5494
5570
|
const forceBrainstorm = this.consecutiveFailures >= (this.opts.failureBudget ?? 3);
|
|
5495
5571
|
if (!forceBrainstorm) {
|
|
5496
|
-
const todo = this.pickPendingTodo();
|
|
5572
|
+
const todo = this.pickPendingTodo(goal);
|
|
5497
5573
|
if (todo) {
|
|
5498
5574
|
return {
|
|
5499
5575
|
source: "todo",
|
|
5500
5576
|
task: todo.content,
|
|
5577
|
+
todoId: todo.id,
|
|
5501
5578
|
directive: this.buildDirective(goal, "todo", todo.content)
|
|
5502
5579
|
};
|
|
5503
5580
|
}
|
|
@@ -5511,17 +5588,38 @@ var EternalAutonomyEngine = class {
|
|
|
5511
5588
|
}
|
|
5512
5589
|
}
|
|
5513
5590
|
const brainstormed = await this.brainstormTask(goal);
|
|
5591
|
+
if (brainstormed === BRAINSTORM_DONE) {
|
|
5592
|
+
this.consecutiveBrainstormDone++;
|
|
5593
|
+
const threshold = this.opts.brainstormDoneStopThreshold ?? 3;
|
|
5594
|
+
if (this.consecutiveBrainstormDone >= threshold) {
|
|
5595
|
+
await this.markGoalCompleted(
|
|
5596
|
+
{ source: "brainstorm", task: "no further work", directive: "" },
|
|
5597
|
+
`brainstorm returned DONE ${this.consecutiveBrainstormDone}x in a row`
|
|
5598
|
+
);
|
|
5599
|
+
this.stopRequested = true;
|
|
5600
|
+
}
|
|
5601
|
+
return null;
|
|
5602
|
+
}
|
|
5514
5603
|
if (!brainstormed) return null;
|
|
5604
|
+
this.consecutiveBrainstormDone = 0;
|
|
5515
5605
|
return {
|
|
5516
5606
|
source: "brainstorm",
|
|
5517
5607
|
task: brainstormed,
|
|
5518
5608
|
directive: this.buildDirective(goal, "brainstorm", brainstormed)
|
|
5519
5609
|
};
|
|
5520
5610
|
}
|
|
5521
|
-
pickPendingTodo() {
|
|
5611
|
+
pickPendingTodo(goal) {
|
|
5522
5612
|
const todos = this.opts.agent.ctx.todos;
|
|
5523
5613
|
if (!Array.isArray(todos)) return null;
|
|
5524
|
-
|
|
5614
|
+
const attempts = goal.todoAttempts ?? {};
|
|
5615
|
+
const ceiling = this.opts.todoMaxAttempts ?? 3;
|
|
5616
|
+
for (const t of todos) {
|
|
5617
|
+
if (t.status !== "pending") continue;
|
|
5618
|
+
const used = attempts[t.id] ?? 0;
|
|
5619
|
+
if (used >= ceiling) continue;
|
|
5620
|
+
return t;
|
|
5621
|
+
}
|
|
5622
|
+
return null;
|
|
5525
5623
|
}
|
|
5526
5624
|
async pickGitTask() {
|
|
5527
5625
|
let out;
|
|
@@ -5558,7 +5656,10 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
5558
5656
|
"- One sentence, imperative form, under 200 chars.",
|
|
5559
5657
|
"- No preamble, no explanation, no markdown \u2014 just the task line.",
|
|
5560
5658
|
"- If recent iterations show repeated failures on the same target, pivot.",
|
|
5561
|
-
"- If the goal appears fully accomplished
|
|
5659
|
+
"- If the goal appears fully accomplished AND you can name a concrete",
|
|
5660
|
+
" artifact / test / output that proves it, output exactly: DONE",
|
|
5661
|
+
"- Be conservative with DONE: if the recent journal contains failures",
|
|
5662
|
+
" or aborted entries, the goal is almost certainly NOT done."
|
|
5562
5663
|
].join("\n");
|
|
5563
5664
|
try {
|
|
5564
5665
|
const ctrl = new AbortController();
|
|
@@ -5570,9 +5671,11 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
5570
5671
|
);
|
|
5571
5672
|
if (result.status !== "done") return null;
|
|
5572
5673
|
const text = (result.finalText ?? "").trim();
|
|
5573
|
-
if (!text
|
|
5674
|
+
if (!text) return null;
|
|
5675
|
+
if (/^DONE\.?$/i.test(text)) return BRAINSTORM_DONE;
|
|
5574
5676
|
const firstLine = text.split("\n").find((l) => l.trim().length > 0)?.trim();
|
|
5575
5677
|
if (!firstLine) return null;
|
|
5678
|
+
if (/^DONE\.?$/i.test(firstLine)) return BRAINSTORM_DONE;
|
|
5576
5679
|
return firstLine.slice(0, 240);
|
|
5577
5680
|
} finally {
|
|
5578
5681
|
clearTimeout(timer);
|
|
@@ -5582,19 +5685,82 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
5582
5685
|
}
|
|
5583
5686
|
}
|
|
5584
5687
|
buildDirective(goal, source, task) {
|
|
5688
|
+
const recentJournal = goal.journal.slice(-5).map((e) => ` #${e.iteration} [${e.status}] ${e.task}${e.note ? ` \u2014 ${e.note.slice(0, 80)}` : ""}`).join("\n");
|
|
5585
5689
|
return [
|
|
5586
|
-
"
|
|
5690
|
+
"\u2550\u2550\u2550 ETERNAL AUTONOMY \u2014 iteration directive \u2550\u2550\u2550",
|
|
5587
5691
|
"",
|
|
5588
|
-
`
|
|
5692
|
+
`Mission: ${goal.goal}`,
|
|
5693
|
+
`Iteration: #${goal.iterations + 1}`,
|
|
5589
5694
|
`Source: ${source}`,
|
|
5590
5695
|
`Task: ${task}`,
|
|
5591
5696
|
"",
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
"
|
|
5595
|
-
"
|
|
5697
|
+
recentJournal ? `Recent journal (last 5):
|
|
5698
|
+
${recentJournal}` : "No prior iterations.",
|
|
5699
|
+
"",
|
|
5700
|
+
"\u2500\u2500 EXECUTION PROTOCOL \u2500\u2500",
|
|
5701
|
+
"You are inside a long-running autonomous loop. Each iteration you",
|
|
5702
|
+
"execute ONE concrete task that advances the Mission. No user is",
|
|
5703
|
+
"available to clarify \u2014 make defensible decisions and move forward.",
|
|
5704
|
+
"",
|
|
5705
|
+
"1. EXECUTE END-TO-END",
|
|
5706
|
+
" \u2022 Use multiple tool calls freely. Emit `[continue]` on its own line",
|
|
5707
|
+
" to chain to the next internal step without returning.",
|
|
5708
|
+
" \u2022 When this iteration's Task is finished (real artifact / passing",
|
|
5709
|
+
" test / applied diff / clean output), emit `[done]` on its own line.",
|
|
5710
|
+
" \u2022 Do not stop on the first obstacle \u2014 try at least 3 distinct",
|
|
5711
|
+
" approaches before giving up. YOLO is active; no confirmations.",
|
|
5712
|
+
"",
|
|
5713
|
+
"2. UPDATE TODO STATE (when Source is `todo`)",
|
|
5714
|
+
" \u2022 Mark this todo `in_progress` via the todos tool before tool work.",
|
|
5715
|
+
" \u2022 Mark it `completed` on success, with a one-line outcome note.",
|
|
5716
|
+
" \u2022 If you cannot make progress after 2 distinct attempts, mark it",
|
|
5717
|
+
" `cancelled` with the obstacle. The loop will skip it next time.",
|
|
5718
|
+
"",
|
|
5719
|
+
"3. MISSION-COMPLETE PROTOCOL",
|
|
5720
|
+
" \u2022 If \u2014 and ONLY if \u2014 the OVERALL Mission (not just this Task) is",
|
|
5721
|
+
" verifiably accomplished, emit on its own line:",
|
|
5722
|
+
" [GOAL_COMPLETE]",
|
|
5723
|
+
" followed by a one-paragraph verification recipe (artifact path,",
|
|
5724
|
+
" test command, or 10-second reproduction). This halts the loop.",
|
|
5725
|
+
" \u2022 NEVER emit [GOAL_COMPLETE] on optimism, partial progress, or",
|
|
5726
|
+
' "looks fine". Required: a concrete artifact that proves it AND',
|
|
5727
|
+
" no recent journal failures contradicting completion.",
|
|
5728
|
+
" \u2022 If unsure, emit `[done]` instead and let the next iteration",
|
|
5729
|
+
" decide. The loop is patient; false completion is not.",
|
|
5730
|
+
"",
|
|
5731
|
+
"4. NO INTERACTIVITY",
|
|
5732
|
+
" \u2022 Do not ask questions, do not request confirmation, do not propose",
|
|
5733
|
+
" options. Pick the best path and execute. The user is asleep."
|
|
5596
5734
|
].join("\n");
|
|
5597
5735
|
}
|
|
5736
|
+
/**
|
|
5737
|
+
* Exponential backoff for transient provider errors. `2^N * base`
|
|
5738
|
+
* capped at `transientBackoffMaxMs`. Zero base disables backoff.
|
|
5739
|
+
* Public-private to keep `runOneIteration` readable; the value is
|
|
5740
|
+
* recomputed each call from the current retry count, so callers
|
|
5741
|
+
* don't have to track state.
|
|
5742
|
+
*/
|
|
5743
|
+
computeTransientBackoffMs() {
|
|
5744
|
+
const base = this.opts.transientBackoffBaseMs ?? 2e3;
|
|
5745
|
+
const cap = this.opts.transientBackoffMaxMs ?? 6e4;
|
|
5746
|
+
if (base <= 0) return 0;
|
|
5747
|
+
const exponent = Math.max(0, this.consecutiveTransientRetries - 1);
|
|
5748
|
+
return Math.min(cap, base * Math.pow(2, exponent));
|
|
5749
|
+
}
|
|
5750
|
+
/**
|
|
5751
|
+
* Sleep that wakes early if `stopRequested` flips. Polls every 250 ms
|
|
5752
|
+
* so SIGINT / `/autonomy stop` can land in the middle of a long
|
|
5753
|
+
* backoff instead of waiting up to a minute for the timer.
|
|
5754
|
+
*/
|
|
5755
|
+
async sleepInterruptible(totalMs) {
|
|
5756
|
+
const step = 250;
|
|
5757
|
+
let remaining = totalMs;
|
|
5758
|
+
while (remaining > 0 && !this.stopRequested) {
|
|
5759
|
+
const chunk = Math.min(step, remaining);
|
|
5760
|
+
await sleep(chunk);
|
|
5761
|
+
remaining -= chunk;
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5598
5764
|
async appendIterationEntry(entry) {
|
|
5599
5765
|
const current = await loadGoal(this.goalPath);
|
|
5600
5766
|
if (!current) {
|
|
@@ -5603,6 +5769,39 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
5603
5769
|
const updated = appendJournal(current, entry);
|
|
5604
5770
|
await saveGoal(this.goalPath, updated);
|
|
5605
5771
|
}
|
|
5772
|
+
/**
|
|
5773
|
+
* Persistent per-todo failure counter. Skipped silently when the goal
|
|
5774
|
+
* file has been removed (graceful clear). Each non-success iteration
|
|
5775
|
+
* against a todo source bumps the counter by 1; `pickPendingTodo` reads
|
|
5776
|
+
* the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
|
|
5777
|
+
*/
|
|
5778
|
+
async bumpTodoAttempt(todoId) {
|
|
5779
|
+
const current = await loadGoal(this.goalPath);
|
|
5780
|
+
if (!current) return;
|
|
5781
|
+
const attempts = { ...current.todoAttempts ?? {} };
|
|
5782
|
+
attempts[todoId] = (attempts[todoId] ?? 0) + 1;
|
|
5783
|
+
await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
|
|
5784
|
+
}
|
|
5785
|
+
/**
|
|
5786
|
+
* Flip the mission to `completed` and journal it. Called from two
|
|
5787
|
+
* paths: (a) `[GOAL_COMPLETE]` marker in a successful iteration's
|
|
5788
|
+
* finalText, (b) `brainstorm` returning DONE consecutively past the
|
|
5789
|
+
* configured threshold. Idempotent — re-entry is a no-op once the
|
|
5790
|
+
* goal is already `completed`.
|
|
5791
|
+
*/
|
|
5792
|
+
async markGoalCompleted(action, note) {
|
|
5793
|
+
const current = await loadGoal(this.goalPath);
|
|
5794
|
+
if (!current) return;
|
|
5795
|
+
if (current.goalState === "completed") return;
|
|
5796
|
+
const withFlag = { ...current, goalState: "completed" };
|
|
5797
|
+
const withEntry = appendJournal(withFlag, {
|
|
5798
|
+
source: action.source,
|
|
5799
|
+
task: `MISSION COMPLETE \u2014 ${action.task}`.slice(0, 240),
|
|
5800
|
+
status: "success",
|
|
5801
|
+
note: note.slice(0, 240)
|
|
5802
|
+
});
|
|
5803
|
+
await saveGoal(this.goalPath, withEntry);
|
|
5804
|
+
}
|
|
5606
5805
|
async appendFailure(task, note) {
|
|
5607
5806
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
5608
5807
|
}
|
|
@@ -5617,6 +5816,142 @@ function sleep(ms) {
|
|
|
5617
5816
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
5618
5817
|
}
|
|
5619
5818
|
|
|
5819
|
+
// src/execution/autonomy-prompt-contributor.ts
|
|
5820
|
+
function makeAutonomyPromptContributor(opts) {
|
|
5821
|
+
return async (ctx) => {
|
|
5822
|
+
if (ctx.subagent) return [];
|
|
5823
|
+
if (!opts.enabled()) return [];
|
|
5824
|
+
let goal;
|
|
5825
|
+
try {
|
|
5826
|
+
goal = await loadGoal(opts.goalPath);
|
|
5827
|
+
} catch {
|
|
5828
|
+
return [];
|
|
5829
|
+
}
|
|
5830
|
+
if (!goal) return [];
|
|
5831
|
+
const missionState = goal.goalState ?? "active";
|
|
5832
|
+
if (missionState !== "active") return [];
|
|
5833
|
+
const tailSize = opts.journalTailSize ?? 5;
|
|
5834
|
+
const journalTail = goal.journal.slice(-tailSize).map((e) => {
|
|
5835
|
+
const note = e.note ? ` \u2014 ${e.note.slice(0, 80)}` : "";
|
|
5836
|
+
return ` #${e.iteration} [${e.status}] ${e.task}${note}`;
|
|
5837
|
+
});
|
|
5838
|
+
const text = [
|
|
5839
|
+
"## ETERNAL AUTONOMY \u2014 active mission",
|
|
5840
|
+
"",
|
|
5841
|
+
"You are inside a long-running autonomous loop. The user is asleep",
|
|
5842
|
+
"and is not available to confirm decisions. Each turn you receive a",
|
|
5843
|
+
"directive describing one concrete sub-task that advances the mission.",
|
|
5844
|
+
"",
|
|
5845
|
+
`Mission: ${goal.goal}`,
|
|
5846
|
+
`Iteration: #${goal.iterations}`,
|
|
5847
|
+
journalTail.length > 0 ? `Recent journal (last ${journalTail.length}):
|
|
5848
|
+
${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first iteration)",
|
|
5849
|
+
"",
|
|
5850
|
+
"### Loop control markers",
|
|
5851
|
+
"Emit these on their own line in your final text \u2014 case-insensitive,",
|
|
5852
|
+
"whitespace-tolerant, but they must occupy the entire line:",
|
|
5853
|
+
"- `[continue]` \u2014 chain to the next internal step without returning.",
|
|
5854
|
+
"- `[done]` \u2014 the current sub-task is finished; return to the engine.",
|
|
5855
|
+
"- `[GOAL_COMPLETE]` \u2014 emit ONLY when the OVERALL mission is",
|
|
5856
|
+
" verifiably done. Must be followed by a one-paragraph verification",
|
|
5857
|
+
" recipe (artifact path, test command, or 10-second reproduction).",
|
|
5858
|
+
" The engine halts on this marker \u2014 false positives waste real",
|
|
5859
|
+
" human time. If unsure, emit `[done]` and let the next iteration",
|
|
5860
|
+
" decide.",
|
|
5861
|
+
"",
|
|
5862
|
+
"### Operating principles",
|
|
5863
|
+
"- YOLO is active. Do NOT ask for confirmation, do NOT propose",
|
|
5864
|
+
" options. Pick the best path and execute it.",
|
|
5865
|
+
"- Use tools freely; multiple calls per turn are normal and expected.",
|
|
5866
|
+
"- When working on a todo, mark it `in_progress` via the todos tool",
|
|
5867
|
+
" before tool work and `completed` (or `cancelled` with a reason)",
|
|
5868
|
+
" when done. The loop reads todo state between iterations.",
|
|
5869
|
+
"- If an approach fails twice in a row, pivot. Don't grind on the",
|
|
5870
|
+
" same wall \u2014 try a different angle, file a cancel on the todo, or",
|
|
5871
|
+
" surface the obstacle via `[done]` and let the next iteration",
|
|
5872
|
+
" re-plan."
|
|
5873
|
+
].join("\n");
|
|
5874
|
+
return [
|
|
5875
|
+
{
|
|
5876
|
+
type: "text",
|
|
5877
|
+
text,
|
|
5878
|
+
cache_control: { type: "ephemeral" }
|
|
5879
|
+
}
|
|
5880
|
+
];
|
|
5881
|
+
};
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
// src/execution/goal-preamble.ts
|
|
5885
|
+
function buildGoalPreamble(goal) {
|
|
5886
|
+
return [
|
|
5887
|
+
"[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
|
|
5888
|
+
"The user granted you full autonomy. Read these constraints once, then act.",
|
|
5889
|
+
"",
|
|
5890
|
+
"YOUR GOAL:",
|
|
5891
|
+
"---",
|
|
5892
|
+
goal,
|
|
5893
|
+
"---",
|
|
5894
|
+
"",
|
|
5895
|
+
"AUTHORITY YOU HAVE:",
|
|
5896
|
+
"- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
|
|
5897
|
+
" Parallel + recursive fan-out are both fine. There is no spawn budget.",
|
|
5898
|
+
"- Use any provider/model per subagent \u2014 pick the right tool for each",
|
|
5899
|
+
" piece of work. Heavy reasoning model for planning, fast model for",
|
|
5900
|
+
" batch work, specialist model for domain code.",
|
|
5901
|
+
"- Run unlimited tool calls and iterations. There is NO hidden budget.",
|
|
5902
|
+
" The Agent loop auto-extends every 100 iterations forever.",
|
|
5903
|
+
"- Retry failed tools with different inputs, alternative paths, fresh",
|
|
5904
|
+
" subagents. Switch providers mid-run if one is rate-limited.",
|
|
5905
|
+
"- Re-plan freely when an approach hits a dead end. You are not obliged",
|
|
5906
|
+
" to stick with the first plan you proposed.",
|
|
5907
|
+
"",
|
|
5908
|
+
'WHAT "DONE" MEANS \u2014 non-negotiable:',
|
|
5909
|
+
"- You can name a concrete artifact (a passing test, a written file at",
|
|
5910
|
+
" a specific path, a fixed bug verified by re-running the failing case,",
|
|
5911
|
+
" a clean grep that previously had matches).",
|
|
5912
|
+
"- You can tell the user HOW to verify it themselves in 10 seconds.",
|
|
5913
|
+
'- You have NOT hedged. None of: "looks like it should work", "I',
|
|
5914
|
+
' believe this fixes it", "the changes appear correct".',
|
|
5915
|
+
"",
|
|
5916
|
+
"WHAT IS NOT DONE \u2014 never report any of these as completion:",
|
|
5917
|
+
"- An error message you didn't recover from.",
|
|
5918
|
+
'- An empty result, a 0-line file, a "no matches found" you accepted',
|
|
5919
|
+
" without questioning the search.",
|
|
5920
|
+
'- "Should I continue?" / "Want me to also...?" / "Let me know if you',
|
|
5921
|
+
' want X." Those are hedges. The user already told you to finish the',
|
|
5922
|
+
" goal \u2014 just do it.",
|
|
5923
|
+
"- Partial progress dressed up as success. Fixed 3 of 5 bugs = 60%",
|
|
5924
|
+
" done, not done.",
|
|
5925
|
+
"- A subagent's failed/timeout/stopped TaskResult that you didn't",
|
|
5926
|
+
" respond to with a fresh attempt (different role, different model,",
|
|
5927
|
+
" tighter prompt).",
|
|
5928
|
+
"",
|
|
5929
|
+
"PERSISTENCE PROTOCOL:",
|
|
5930
|
+
"- If blocked, try at least 3 different angles before reporting the",
|
|
5931
|
+
" problem to the user. Different tool inputs, different subagent",
|
|
5932
|
+
" roles, different providers, different decomposition of the task.",
|
|
5933
|
+
"- If a tool fails, read its error, alter the input, try again. Do",
|
|
5934
|
+
" not just report the failure back.",
|
|
5935
|
+
"- If a subagent returns useless output, respawn with a tighter prompt",
|
|
5936
|
+
' or a different role. Do not accept "I could not determine\u2026" as the',
|
|
5937
|
+
" final answer.",
|
|
5938
|
+
"- Use `ask_subagent` for one-shot questions when you don't need a",
|
|
5939
|
+
" full delegated task.",
|
|
5940
|
+
"",
|
|
5941
|
+
"REPORTING:",
|
|
5942
|
+
"- Stream short progress notes between major actions so the user can",
|
|
5943
|
+
" monitor. Do not go silent for 50 tool calls then dump a wall of",
|
|
5944
|
+
" text \u2014 but also do not narrate every tool call.",
|
|
5945
|
+
"- Use the shared scratchpad (if available) to leave breadcrumbs",
|
|
5946
|
+
" subagents can read.",
|
|
5947
|
+
"- Final response must include: (a) what was accomplished, (b) how",
|
|
5948
|
+
" to verify, (c) any caveats (residual TODOs, things the user",
|
|
5949
|
+
" should know about).",
|
|
5950
|
+
"",
|
|
5951
|
+
"BEGIN.]"
|
|
5952
|
+
].join("\n");
|
|
5953
|
+
}
|
|
5954
|
+
|
|
5620
5955
|
// src/coordination/director.ts
|
|
5621
5956
|
init_atomic_write();
|
|
5622
5957
|
|
|
@@ -6073,7 +6408,7 @@ var BudgetThresholdSignal = class extends Error {
|
|
|
6073
6408
|
this.decision = decision;
|
|
6074
6409
|
}
|
|
6075
6410
|
};
|
|
6076
|
-
var SubagentBudget = class {
|
|
6411
|
+
var SubagentBudget = class _SubagentBudget {
|
|
6077
6412
|
limits;
|
|
6078
6413
|
iterations = 0;
|
|
6079
6414
|
toolCalls = 0;
|
|
@@ -6082,6 +6417,26 @@ var SubagentBudget = class {
|
|
|
6082
6417
|
costUsd = 0;
|
|
6083
6418
|
startTime = null;
|
|
6084
6419
|
_onThreshold;
|
|
6420
|
+
/**
|
|
6421
|
+
* Tracks which budget kinds currently have an extension request
|
|
6422
|
+
* in flight. While a kind is here, further `checkLimit` calls for the
|
|
6423
|
+
* same kind are no-ops — without this dedup, every `recordIteration`
|
|
6424
|
+
* after the limit is reached spawns a fresh decision Promise (until
|
|
6425
|
+
* the first one lands and patches limits), flooding the FleetBus
|
|
6426
|
+
* with redundant threshold events. Cleared in `checkLimitAsync`'s
|
|
6427
|
+
* `finally`.
|
|
6428
|
+
*/
|
|
6429
|
+
pendingExtensions = /* @__PURE__ */ new Set();
|
|
6430
|
+
/**
|
|
6431
|
+
* Hard cap on how long `checkLimitAsync` waits for the coordinator to
|
|
6432
|
+
* respond before defaulting to 'stop'. Without this fallback an absent
|
|
6433
|
+
* or hung listener (Director not built / event filter detached mid-run)
|
|
6434
|
+
* leaves the budget over-limit and never enforces anything, since
|
|
6435
|
+
* `checkLimit` returns synchronously via `void this.checkLimitAsync`.
|
|
6436
|
+
* Hardcoded for now — most fleets set their own per-task timeout that
|
|
6437
|
+
* dwarfs this anyway. 30 s matches the existing event-emit timeoutMs.
|
|
6438
|
+
*/
|
|
6439
|
+
static DECISION_TIMEOUT_MS = 3e4;
|
|
6085
6440
|
/**
|
|
6086
6441
|
* Injected by the runner when wiring the budget to its EventBus.
|
|
6087
6442
|
* Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
|
|
@@ -6124,56 +6479,92 @@ var SubagentBudget = class {
|
|
|
6124
6479
|
if (kind === "timeout" || !this._onThreshold) {
|
|
6125
6480
|
throw new BudgetExceededError(kind, limit, used);
|
|
6126
6481
|
}
|
|
6127
|
-
|
|
6482
|
+
const bus = this._events;
|
|
6483
|
+
if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
|
|
6484
|
+
throw new BudgetExceededError(kind, limit, used);
|
|
6485
|
+
}
|
|
6486
|
+
if (this.pendingExtensions.has(kind)) return;
|
|
6487
|
+
this.pendingExtensions.add(kind);
|
|
6488
|
+
const decision = this.negotiateExtension(kind, used, limit);
|
|
6489
|
+
throw new BudgetThresholdSignal(kind, limit, used, decision);
|
|
6128
6490
|
}
|
|
6129
6491
|
/**
|
|
6130
|
-
*
|
|
6131
|
-
*
|
|
6132
|
-
*
|
|
6492
|
+
* Drive the threshold handler to a decision. Resolves with `'stop'`
|
|
6493
|
+
* (signal the runner to abort) or `{ extend: ... }` (limits already
|
|
6494
|
+
* patched in-place; the runner should not abort). Always releases the
|
|
6495
|
+
* `pendingExtensions` slot in `finally`.
|
|
6496
|
+
*
|
|
6497
|
+
* The 'continue' return from a sync handler is treated as
|
|
6498
|
+
* `{ extend: {} }` — keep going without patching, next overrun will
|
|
6499
|
+
* fire a fresh signal.
|
|
6133
6500
|
*/
|
|
6134
|
-
async
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6501
|
+
async negotiateExtension(kind, used, limit) {
|
|
6502
|
+
try {
|
|
6503
|
+
const result = this._onThreshold({
|
|
6504
|
+
kind,
|
|
6505
|
+
used,
|
|
6506
|
+
limit,
|
|
6507
|
+
// Inject a requestDecision helper the handler can call to emit the
|
|
6508
|
+
// budget.threshold_reached event and wait for the coordinator's verdict.
|
|
6509
|
+
// A hard fallback timer guarantees the promise eventually resolves
|
|
6510
|
+
// even if no listener responds — without it, an absent/detached
|
|
6511
|
+
// Director would leave the budget permanently in "asking" state.
|
|
6512
|
+
requestDecision: () => {
|
|
6513
|
+
const bus = this._events;
|
|
6514
|
+
if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
|
|
6515
|
+
return Promise.resolve("stop");
|
|
6516
|
+
}
|
|
6517
|
+
return new Promise((resolve2) => {
|
|
6518
|
+
let resolved = false;
|
|
6519
|
+
const respond = (d) => {
|
|
6520
|
+
if (resolved) return;
|
|
6521
|
+
resolved = true;
|
|
6522
|
+
resolve2(d);
|
|
6523
|
+
};
|
|
6524
|
+
const fallback = setTimeout(
|
|
6525
|
+
() => respond("stop"),
|
|
6526
|
+
_SubagentBudget.DECISION_TIMEOUT_MS
|
|
6527
|
+
);
|
|
6528
|
+
bus.emit("budget.threshold_reached", {
|
|
6529
|
+
kind,
|
|
6530
|
+
used,
|
|
6531
|
+
limit,
|
|
6532
|
+
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
6533
|
+
extend: (extra) => {
|
|
6534
|
+
clearTimeout(fallback);
|
|
6535
|
+
respond({ extend: extra });
|
|
6536
|
+
},
|
|
6537
|
+
deny: () => {
|
|
6538
|
+
clearTimeout(fallback);
|
|
6539
|
+
respond("stop");
|
|
6540
|
+
}
|
|
6541
|
+
});
|
|
6151
6542
|
});
|
|
6152
|
-
}
|
|
6543
|
+
}
|
|
6544
|
+
});
|
|
6545
|
+
if (result === "throw") return "stop";
|
|
6546
|
+
if (result === "continue") return { extend: {} };
|
|
6547
|
+
const decision = await result;
|
|
6548
|
+
if (decision === "stop") return "stop";
|
|
6549
|
+
const ext = decision.extend;
|
|
6550
|
+
if (ext.maxIterations !== void 0) {
|
|
6551
|
+
this.limits.maxIterations = ext.maxIterations;
|
|
6153
6552
|
}
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
if (ext.maxToolCalls !== void 0) {
|
|
6170
|
-
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
6171
|
-
}
|
|
6172
|
-
if (ext.maxTokens !== void 0) {
|
|
6173
|
-
this.limits.maxTokens = ext.maxTokens;
|
|
6174
|
-
}
|
|
6175
|
-
if (ext.maxCostUsd !== void 0) {
|
|
6176
|
-
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
6553
|
+
if (ext.maxToolCalls !== void 0) {
|
|
6554
|
+
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
6555
|
+
}
|
|
6556
|
+
if (ext.maxTokens !== void 0) {
|
|
6557
|
+
this.limits.maxTokens = ext.maxTokens;
|
|
6558
|
+
}
|
|
6559
|
+
if (ext.maxCostUsd !== void 0) {
|
|
6560
|
+
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
6561
|
+
}
|
|
6562
|
+
if (ext.timeoutMs !== void 0) {
|
|
6563
|
+
this.limits.timeoutMs = ext.timeoutMs;
|
|
6564
|
+
}
|
|
6565
|
+
return decision;
|
|
6566
|
+
} finally {
|
|
6567
|
+
this.pendingExtensions.delete(kind);
|
|
6177
6568
|
}
|
|
6178
6569
|
}
|
|
6179
6570
|
recordIteration() {
|
|
@@ -6456,6 +6847,19 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
6456
6847
|
setRunner(runner) {
|
|
6457
6848
|
this.runner = runner;
|
|
6458
6849
|
}
|
|
6850
|
+
/**
|
|
6851
|
+
* Change the in-flight dispatch ceiling at runtime. Lowering does NOT
|
|
6852
|
+
* preempt running tasks — already-dispatched subagents finish their
|
|
6853
|
+
* current task; only future dispatches respect the new cap. Raising
|
|
6854
|
+
* immediately tries to fill the freed slots from the pending queue.
|
|
6855
|
+
*/
|
|
6856
|
+
setMaxConcurrent(n) {
|
|
6857
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
6858
|
+
throw new Error(`maxConcurrent must be a finite integer >= 1, got ${n}`);
|
|
6859
|
+
}
|
|
6860
|
+
this.config.maxConcurrent = Math.floor(n);
|
|
6861
|
+
this.tryDispatchNext();
|
|
6862
|
+
}
|
|
6459
6863
|
async spawn(subagent) {
|
|
6460
6864
|
const id = subagent.id || randomUUID();
|
|
6461
6865
|
if (this.subagents.has(id)) {
|
|
@@ -6715,14 +7119,65 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
6715
7119
|
this.recordCompletion(result);
|
|
6716
7120
|
}
|
|
6717
7121
|
async executeWithTimeout(runner, task, ctx, budget) {
|
|
6718
|
-
const
|
|
6719
|
-
if (
|
|
7122
|
+
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
7123
|
+
if (initialTimeoutMs === void 0) return runner(task, ctx);
|
|
7124
|
+
const start = Date.now();
|
|
6720
7125
|
let timer = null;
|
|
6721
7126
|
const timeoutPromise = new Promise((_, reject) => {
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
7127
|
+
const armFor = (ms) => {
|
|
7128
|
+
if (timer) clearTimeout(timer);
|
|
7129
|
+
timer = setTimeout(async () => {
|
|
7130
|
+
const elapsed = Date.now() - start;
|
|
7131
|
+
const limit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
7132
|
+
if (!budget.onThreshold) {
|
|
7133
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
7134
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
7135
|
+
return;
|
|
7136
|
+
}
|
|
7137
|
+
try {
|
|
7138
|
+
const result = budget.onThreshold({
|
|
7139
|
+
kind: "timeout",
|
|
7140
|
+
used: elapsed,
|
|
7141
|
+
limit,
|
|
7142
|
+
requestDecision: () => new Promise((resolveDecision) => {
|
|
7143
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
7144
|
+
kind: "timeout",
|
|
7145
|
+
used: elapsed,
|
|
7146
|
+
limit,
|
|
7147
|
+
timeoutMs: 3e4,
|
|
7148
|
+
extend: (extra) => resolveDecision({ extend: extra }),
|
|
7149
|
+
deny: () => resolveDecision("stop")
|
|
7150
|
+
});
|
|
7151
|
+
})
|
|
7152
|
+
});
|
|
7153
|
+
const decision = typeof result === "string" ? result : await result;
|
|
7154
|
+
if (decision === "continue") {
|
|
7155
|
+
armFor(Math.max(1e3, limit));
|
|
7156
|
+
return;
|
|
7157
|
+
}
|
|
7158
|
+
if (decision === "throw" || decision === "stop") {
|
|
7159
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
7160
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
7161
|
+
return;
|
|
7162
|
+
}
|
|
7163
|
+
if (decision.extend.timeoutMs !== void 0) {
|
|
7164
|
+
budget.limits.timeoutMs = decision.extend.timeoutMs;
|
|
7165
|
+
const newLimit = decision.extend.timeoutMs;
|
|
7166
|
+
const remaining = Math.max(1e3, newLimit - elapsed);
|
|
7167
|
+
armFor(remaining);
|
|
7168
|
+
return;
|
|
7169
|
+
}
|
|
7170
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
7171
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
7172
|
+
} catch (err) {
|
|
7173
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
7174
|
+
reject(
|
|
7175
|
+
err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
|
|
7176
|
+
);
|
|
7177
|
+
}
|
|
7178
|
+
}, ms);
|
|
7179
|
+
};
|
|
7180
|
+
armFor(initialTimeoutMs);
|
|
6726
7181
|
});
|
|
6727
7182
|
try {
|
|
6728
7183
|
return await Promise.race([runner(task, ctx), timeoutPromise]);
|
|
@@ -7333,7 +7788,7 @@ var Director = class {
|
|
|
7333
7788
|
return;
|
|
7334
7789
|
}
|
|
7335
7790
|
extendCounts.set(guardKey, prior + 1);
|
|
7336
|
-
|
|
7791
|
+
setImmediate(() => {
|
|
7337
7792
|
const extra = {};
|
|
7338
7793
|
switch (payload.kind) {
|
|
7339
7794
|
case "iterations":
|
|
@@ -7348,9 +7803,12 @@ var Director = class {
|
|
|
7348
7803
|
case "cost":
|
|
7349
7804
|
extra.maxCostUsd = Math.min(payload.limit * 1.5, 10);
|
|
7350
7805
|
break;
|
|
7806
|
+
case "timeout":
|
|
7807
|
+
extra.timeoutMs = Math.min(Math.ceil(payload.limit * 1.5), 60 * 6e4);
|
|
7808
|
+
break;
|
|
7351
7809
|
}
|
|
7352
7810
|
payload.extend(extra);
|
|
7353
|
-
}
|
|
7811
|
+
});
|
|
7354
7812
|
});
|
|
7355
7813
|
}
|
|
7356
7814
|
/** Best-effort session-writer append. Swallows failures — the director
|
|
@@ -8145,6 +8603,7 @@ function makeAgentSubagentRunner(opts) {
|
|
|
8145
8603
|
const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
|
|
8146
8604
|
const aborter = new AbortController();
|
|
8147
8605
|
ctx.budget._events = events;
|
|
8606
|
+
ctx.budget.onThreshold = ({ requestDecision }) => requestDecision();
|
|
8148
8607
|
let budgetError = null;
|
|
8149
8608
|
const onBudgetError = (err) => {
|
|
8150
8609
|
if (err instanceof BudgetThresholdSignal) {
|
|
@@ -12231,6 +12690,6 @@ var allServers = () => ({
|
|
|
12231
12690
|
"minimax-vision": { ...miniMaxVisionServer(), enabled: false }
|
|
12232
12691
|
});
|
|
12233
12692
|
|
|
12234
|
-
export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets2 as decryptConfigSecrets, deriveTodosFromPlanItem, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
12693
|
+
export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets2 as decryptConfigSecrets, deriveTodosFromPlanItem, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeDirectorSessionFactory, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
12235
12694
|
//# sourceMappingURL=index.js.map
|
|
12236
12695
|
//# sourceMappingURL=index.js.map
|