@wrongstack/core 0.63.4 → 0.66.13
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-B5rxWrg3.d.ts → agent-bridge-D-j6OOBT.d.ts} +1 -1
- package/dist/agent-subagent-runner-DRZ9-NnR.d.ts +1042 -0
- package/dist/{compactor-0vjZ8KTk.d.ts → compactor-D_ExJajC.d.ts} +1 -1
- package/dist/{config-BdDuaZmB.d.ts → config--86aHSln.d.ts} +1 -1
- package/dist/{context-iFMEO2rN.d.ts → context-y87Jc5ei.d.ts} +3 -3
- package/dist/coordination/index.d.ts +12 -12
- package/dist/coordination/index.js +87 -69
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +22 -22
- package/dist/defaults/index.js +113 -84
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-k8CHjcrN.d.ts → events-CIplI98R.d.ts} +1 -1
- package/dist/execution/index.d.ts +16 -385
- package/dist/execution/index.js +59 -51
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/goal-store-C7jcumEh.d.ts +96 -0
- package/dist/{index-Bc6BiP5q.d.ts → index-DKUvyTvV.d.ts} +28 -442
- package/dist/{index-CWdW_CJt.d.ts → index-b5uhfTSl.d.ts} +8 -8
- package/dist/index.d.ts +34 -32
- package/dist/index.js +647 -677
- 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-CwqQDMYy.d.ts → mcp-servers-DwoNBf6r.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/{multi-agent-coordinator-CNUJYq7U.d.ts → multi-agent-coordinator-CWnH-CiX.d.ts} +10 -2
- package/dist/{null-fleet-bus-DRoJ0uOY.d.ts → null-fleet-bus-VApKRxcp.d.ts} +6 -7
- package/dist/observability/index.d.ts +2 -2
- package/dist/parallel-eternal-engine-0UwotoSx.d.ts +483 -0
- package/dist/{path-resolver-C5sPVne8.d.ts → path-resolver-DVkEcIw8.d.ts} +2 -2
- package/dist/{permission-Ld-i5ugf.d.ts → permission-C1A5whY5.d.ts} +5 -1
- package/dist/{permission-policy-CL-mPufp.d.ts → permission-policy-B2dK-T5N.d.ts} +19 -5
- package/dist/{plan-templates-ThBHOjaM.d.ts → plan-templates-Bprrzhbu.d.ts} +4 -4
- package/dist/{provider-runner-DJQa211J.d.ts → provider-runner-mXvXGSIw.d.ts} +3 -3
- package/dist/{retry-policy-BfBScewS.d.ts → retry-policy-CG3qvH_e.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +58 -51
- package/dist/sdd/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/security/index.js +31 -22
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-DxhW7ML3.d.ts → selector-RvBR_YRW.d.ts} +1 -1
- package/dist/session-event-bridge-CDHxcmQU.d.ts +93 -0
- package/dist/{session-reader-q2ThszgG.d.ts → session-reader-BIpwM60D.d.ts} +1 -1
- package/dist/storage/index.d.ts +7 -6
- package/dist/{system-prompt-7LHyBbIf.d.ts → system-prompt-b61lOd49.d.ts} +2 -2
- package/dist/types/index.d.ts +23 -14
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/multi-agent/SKILL.md +0 -2
- package/dist/agent-subagent-runner-Zc3f37Sg.d.ts +0 -182
- package/dist/goal-store-iHltMi5n.d.ts +0 -188
- package/dist/multi-agent-SASYOrWA.d.ts +0 -554
- package/dist/tool-executor-CIjpGaRA.d.ts +0 -111
package/dist/index.js
CHANGED
|
@@ -3395,6 +3395,29 @@ function createToolOutputSerializer(opts = {}) {
|
|
|
3395
3395
|
}
|
|
3396
3396
|
return { serialize, enforceCap, capBytes };
|
|
3397
3397
|
}
|
|
3398
|
+
function truncateForEvent(content, max = 400) {
|
|
3399
|
+
if (!content) return "";
|
|
3400
|
+
return content.length <= max ? content : `${content.slice(0, max - 1)}\u2026`;
|
|
3401
|
+
}
|
|
3402
|
+
function sizeSignals(toolName, content) {
|
|
3403
|
+
if (!content || content.length === 0) {
|
|
3404
|
+
return { outputBytes: 0, outputTokens: 0, outputLines: void 0 };
|
|
3405
|
+
}
|
|
3406
|
+
const outputBytes = Buffer.byteLength(content, "utf8");
|
|
3407
|
+
const outputTokens = Math.max(1, Math.round(outputBytes / 3.5));
|
|
3408
|
+
let outputLines;
|
|
3409
|
+
if (toolName === "read") {
|
|
3410
|
+
const lineRe = /^\s*\d+→/gm;
|
|
3411
|
+
let count = 0;
|
|
3412
|
+
while (lineRe.exec(content) !== null) count++;
|
|
3413
|
+
if (count > 0) outputLines = count;
|
|
3414
|
+
} else if (toolName === "bash" || toolName === "shell" || toolName === "grep" || toolName === "logs") {
|
|
3415
|
+
let nl = 0;
|
|
3416
|
+
for (let i = 0; i < content.length; i++) if (content.charCodeAt(i) === 10) nl++;
|
|
3417
|
+
outputLines = nl + (content.endsWith("\n") ? 0 : 1);
|
|
3418
|
+
}
|
|
3419
|
+
return { outputBytes, outputTokens, outputLines };
|
|
3420
|
+
}
|
|
3398
3421
|
|
|
3399
3422
|
// src/execution/tool-executor.ts
|
|
3400
3423
|
var ToolExecutor = class {
|
|
@@ -7080,6 +7103,8 @@ var DefaultPermissionPolicy = class {
|
|
|
7080
7103
|
trustFile;
|
|
7081
7104
|
yolo;
|
|
7082
7105
|
yoloDestructive;
|
|
7106
|
+
/** When true, destructive ops still require confirmation even in YOLO mode. */
|
|
7107
|
+
confirmDestructive;
|
|
7083
7108
|
/**
|
|
7084
7109
|
* Session-scoped "soft deny" map. When the user presses 'n' (block once),
|
|
7085
7110
|
* the tool+pattern is added here. If the LLM retries in the same session,
|
|
@@ -7113,6 +7138,7 @@ var DefaultPermissionPolicy = class {
|
|
|
7113
7138
|
this.trustFile = opts.trustFile;
|
|
7114
7139
|
this.yolo = opts.yolo ?? false;
|
|
7115
7140
|
this.yoloDestructive = opts.yoloDestructive ?? opts.forceAllYolo ?? false;
|
|
7141
|
+
this.confirmDestructive = opts.confirmDestructive ?? false;
|
|
7116
7142
|
this.promptDelegate = opts.promptDelegate;
|
|
7117
7143
|
}
|
|
7118
7144
|
/**
|
|
@@ -7140,6 +7166,14 @@ var DefaultPermissionPolicy = class {
|
|
|
7140
7166
|
getYoloDestructive() {
|
|
7141
7167
|
return this.yoloDestructive;
|
|
7142
7168
|
}
|
|
7169
|
+
/** Toggle destructive confirmation gate (only meaningful when yolo is active). */
|
|
7170
|
+
setConfirmDestructive(enabled) {
|
|
7171
|
+
this.confirmDestructive = enabled;
|
|
7172
|
+
}
|
|
7173
|
+
/** Check whether destructive confirmation gate is active. */
|
|
7174
|
+
getConfirmDestructive() {
|
|
7175
|
+
return this.confirmDestructive;
|
|
7176
|
+
}
|
|
7143
7177
|
/** @deprecated Use `setYoloDestructive`. */
|
|
7144
7178
|
setForceAllYolo(enabled) {
|
|
7145
7179
|
this.setYoloDestructive(enabled);
|
|
@@ -7193,30 +7227,28 @@ var DefaultPermissionPolicy = class {
|
|
|
7193
7227
|
return { permission: "auto", source: "trust" };
|
|
7194
7228
|
}
|
|
7195
7229
|
if (this.yolo) {
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
if (
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
permission: "auto",
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
|
|
7230
|
+
if (this.confirmDestructive) {
|
|
7231
|
+
const destructive = this.isDestructiveYoloCall(tool, input, ctx);
|
|
7232
|
+
if (destructive) {
|
|
7233
|
+
if (this.promptDelegate) {
|
|
7234
|
+
const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
7235
|
+
if (decision === "always") {
|
|
7236
|
+
await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
|
|
7237
|
+
return { permission: "auto", source: "user", reason: "destructive yolo always-allowed" };
|
|
7238
|
+
}
|
|
7239
|
+
if (decision === "deny") {
|
|
7240
|
+
await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
|
|
7241
|
+
return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
|
|
7242
|
+
}
|
|
7243
|
+
return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
|
|
7211
7244
|
}
|
|
7212
|
-
return {
|
|
7245
|
+
return {
|
|
7246
|
+
permission: "confirm",
|
|
7247
|
+
source: "yolo_destructive",
|
|
7248
|
+
riskTier: "destructive",
|
|
7249
|
+
reason: "destructive tool needs explicit approval (confirmDestructive is on)"
|
|
7250
|
+
};
|
|
7213
7251
|
}
|
|
7214
|
-
return {
|
|
7215
|
-
permission: "confirm",
|
|
7216
|
-
source: "yolo_destructive",
|
|
7217
|
-
riskTier: "destructive",
|
|
7218
|
-
reason: "destructive tool needs explicit approval even in yolo mode"
|
|
7219
|
-
};
|
|
7220
7252
|
}
|
|
7221
7253
|
return { permission: "auto", source: "yolo" };
|
|
7222
7254
|
}
|
|
@@ -9775,15 +9807,22 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
9775
9807
|
void this.checkLimits();
|
|
9776
9808
|
}
|
|
9777
9809
|
/**
|
|
9778
|
-
* Wall-clock budget check.
|
|
9779
|
-
*
|
|
9780
|
-
*
|
|
9810
|
+
* Wall-clock / idle budget check. Delegates to `checkLimits(elapsed)`, so
|
|
9811
|
+
* `timeout` and `idle_timeout` follow the SAME negotiation path as the other
|
|
9812
|
+
* kinds — they are NOT a special-cased hard stop. This is deliberate: a
|
|
9813
|
+
* heartbeat-aware policy (see `attachAutoExtend` and `CollabSession`) grants
|
|
9814
|
+
* a timeout extension only while the agent is making progress and denies it
|
|
9815
|
+
* once the agent is genuinely stuck, which is safer than an unconditional
|
|
9816
|
+
* hard kill of a long-but-working agent. The runner translates the resulting
|
|
9817
|
+
* `BudgetThresholdSignal` decision (`extend` → patch limits in place,
|
|
9818
|
+
* `stop` → abort) just like every other kind.
|
|
9781
9819
|
*
|
|
9782
|
-
* Decision table:
|
|
9783
|
-
* - no `onThreshold` handler
|
|
9784
|
-
* - `mode === 'sync'` → throw `BudgetExceededError`
|
|
9785
|
-
* - `mode === 'auto'` + no listener
|
|
9786
|
-
* - `mode === 'auto'` + listener
|
|
9820
|
+
* Decision table (same as `checkLimits`):
|
|
9821
|
+
* - no `onThreshold` handler → throw `BudgetExceededError` (hard stop)
|
|
9822
|
+
* - `mode === 'sync'` → throw `BudgetExceededError` (hard stop)
|
|
9823
|
+
* - `mode === 'auto'` + no listener → throw `BudgetExceededError` (no one to ask)
|
|
9824
|
+
* - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` (negotiated;
|
|
9825
|
+
* a heartbeat-aware policy may extend the timeout)
|
|
9787
9826
|
*/
|
|
9788
9827
|
checkTimeout() {
|
|
9789
9828
|
if (this.startTime === null) return;
|
|
@@ -12649,7 +12688,10 @@ var NICKNAME_POOL = {
|
|
|
12649
12688
|
"lavoisier": { name: "Lavoisier", domain: "chemistry" },
|
|
12650
12689
|
"mendeleev": { name: "Mendeleev", domain: "chemistry" }
|
|
12651
12690
|
};
|
|
12652
|
-
var ALL_NICKNAMES = Object.
|
|
12691
|
+
var ALL_NICKNAMES = Object.entries(NICKNAME_POOL);
|
|
12692
|
+
var NAME_TO_KEY = Object.fromEntries(
|
|
12693
|
+
ALL_NICKNAMES.map(([key, entry]) => [entry.name, key])
|
|
12694
|
+
);
|
|
12653
12695
|
var DOMAIN_PREFERENCES = {
|
|
12654
12696
|
"security": ["shannon", "turing", "lamarr", "stallman"],
|
|
12655
12697
|
"bug-hunter": ["darwin", "curie", "feynman", "fermi"],
|
|
@@ -12682,17 +12724,23 @@ function assignNickname(role, used) {
|
|
|
12682
12724
|
for (const key of preferences) {
|
|
12683
12725
|
const entry = NICKNAME_POOL[key];
|
|
12684
12726
|
if (entry && !used.has(key)) {
|
|
12685
|
-
return `${entry.name} (${formatRole(role)})
|
|
12727
|
+
return { key, display: `${entry.name} (${formatRole(role)})` };
|
|
12686
12728
|
}
|
|
12687
12729
|
}
|
|
12688
|
-
for (const entry of ALL_NICKNAMES) {
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
return `${entry.name} (${formatRole(role)})`;
|
|
12730
|
+
for (const [key, entry] of ALL_NICKNAMES) {
|
|
12731
|
+
if (!used.has(key)) {
|
|
12732
|
+
return { key, display: `${entry.name} (${formatRole(role)})` };
|
|
12692
12733
|
}
|
|
12693
12734
|
}
|
|
12694
12735
|
const counter = used.size + 1;
|
|
12695
|
-
return `Scientist #${counter} (${formatRole(role)})
|
|
12736
|
+
return { key: `scientist-${counter}`, display: `Scientist #${counter} (${formatRole(role)})` };
|
|
12737
|
+
}
|
|
12738
|
+
function nicknameKeyFromDisplay(display) {
|
|
12739
|
+
const base = display.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
12740
|
+
const key = NAME_TO_KEY[base];
|
|
12741
|
+
if (key) return key;
|
|
12742
|
+
const synthesized = base.match(/^Scientist #(\d+)$/);
|
|
12743
|
+
return synthesized ? `scientist-${synthesized[1]}` : void 0;
|
|
12696
12744
|
}
|
|
12697
12745
|
function formatRole(role) {
|
|
12698
12746
|
return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
@@ -12779,11 +12827,10 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
12779
12827
|
const name = subagent.name?.trim() ?? "";
|
|
12780
12828
|
const isPlaceholder = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
12781
12829
|
if (!isPlaceholder) return subagent;
|
|
12782
|
-
const
|
|
12783
|
-
|
|
12784
|
-
this.
|
|
12785
|
-
|
|
12786
|
-
return { ...subagent, name: nickname };
|
|
12830
|
+
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
12831
|
+
this.usedNicknames.add(key);
|
|
12832
|
+
this.subagentNicknames.set(subagentId, key);
|
|
12833
|
+
return { ...subagent, name: display };
|
|
12787
12834
|
}
|
|
12788
12835
|
async spawn(subagent) {
|
|
12789
12836
|
const id = subagent.id || randomUUID();
|
|
@@ -13035,23 +13082,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13035
13082
|
*/
|
|
13036
13083
|
drainPendingAsAborted(message) {
|
|
13037
13084
|
const dropped = this.pendingTasks.splice(0, this.pendingTasks.length);
|
|
13038
|
-
for (const t2 of dropped)
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13085
|
+
for (const t2 of dropped) this.emitPendingAborted(t2, message);
|
|
13086
|
+
}
|
|
13087
|
+
/**
|
|
13088
|
+
* Emit a synthetic `stopped`/`aborted_by_parent` completion for a single
|
|
13089
|
+
* PENDING task — one that was never counted in `inFlight`. This MUST bypass
|
|
13090
|
+
* `recordCompletion`: that path does `inFlight--`, which for a pending task
|
|
13091
|
+
* steals a decrement from a genuinely in-flight task and trips the underflow
|
|
13092
|
+
* guard — suppressing that real task's `task.completed` and hanging its
|
|
13093
|
+
* `awaitTasks()` caller. Pushes the result and fires the event directly.
|
|
13094
|
+
*/
|
|
13095
|
+
emitPendingAborted(task, message) {
|
|
13096
|
+
const synthetic = {
|
|
13097
|
+
subagentId: task.subagentId ?? "unassigned",
|
|
13098
|
+
taskId: task.id,
|
|
13099
|
+
status: "stopped",
|
|
13100
|
+
error: {
|
|
13101
|
+
kind: "aborted_by_parent",
|
|
13102
|
+
message,
|
|
13103
|
+
retryable: false
|
|
13104
|
+
},
|
|
13105
|
+
iterations: 0,
|
|
13106
|
+
toolCalls: 0,
|
|
13107
|
+
durationMs: 0
|
|
13108
|
+
};
|
|
13109
|
+
this.completedResults.push(synthetic);
|
|
13110
|
+
this.emit("task.completed", { task, result: synthetic });
|
|
13055
13111
|
}
|
|
13056
13112
|
async runDispatched(subagentId, task) {
|
|
13057
13113
|
const subagent = this.subagents.get(subagentId);
|
|
@@ -13312,20 +13368,10 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13312
13368
|
const orphaned = this.pendingTasks.filter((t2) => t2.subagentId === subagentId);
|
|
13313
13369
|
this.pendingTasks = this.pendingTasks.filter((t2) => t2.subagentId !== subagentId);
|
|
13314
13370
|
for (const t2 of orphaned) {
|
|
13315
|
-
|
|
13316
|
-
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
error: {
|
|
13320
|
-
kind: "aborted_by_parent",
|
|
13321
|
-
message: `Subagent "${subagentId}" was removed while task "${t2.id}" was pending`,
|
|
13322
|
-
retryable: false
|
|
13323
|
-
},
|
|
13324
|
-
iterations: 0,
|
|
13325
|
-
toolCalls: 0,
|
|
13326
|
-
durationMs: 0
|
|
13327
|
-
};
|
|
13328
|
-
this.recordCompletion(synthetic);
|
|
13371
|
+
this.emitPendingAborted(
|
|
13372
|
+
t2,
|
|
13373
|
+
`Subagent "${subagentId}" was removed while task "${t2.id}" was pending`
|
|
13374
|
+
);
|
|
13329
13375
|
}
|
|
13330
13376
|
this.fleetBus?.emit({
|
|
13331
13377
|
subagentId,
|
|
@@ -13703,6 +13749,7 @@ ${personaLine}Task: ${task}
|
|
|
13703
13749
|
} catch {
|
|
13704
13750
|
results = coordinator.results().slice(-taskIds.length);
|
|
13705
13751
|
}
|
|
13752
|
+
await Promise.allSettled(subagentIds.map((id) => coordinator.remove(id)));
|
|
13706
13753
|
const allSuccessful = results.length > 0 && results.every((r) => r.status === "success");
|
|
13707
13754
|
const goalComplete = results.some(
|
|
13708
13755
|
(r) => r.status === "success" && typeof r.result === "string" && GOAL_COMPLETE_MARKER2.test(r.result)
|
|
@@ -14498,6 +14545,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14498
14545
|
return lines.join("\n");
|
|
14499
14546
|
}
|
|
14500
14547
|
cleanup() {
|
|
14548
|
+
if (this._timeoutTimer) {
|
|
14549
|
+
clearTimeout(this._timeoutTimer);
|
|
14550
|
+
this._timeoutTimer = void 0;
|
|
14551
|
+
}
|
|
14501
14552
|
for (const dispose of this.disposers) dispose();
|
|
14502
14553
|
this.disposers.length = 0;
|
|
14503
14554
|
}
|
|
@@ -15627,18 +15678,20 @@ var Director = class _Director {
|
|
|
15627
15678
|
if (e.subagentId.startsWith("bug-hunter-") || e.subagentId.startsWith("refactor-planner-") || e.subagentId.startsWith("critic-")) {
|
|
15628
15679
|
return;
|
|
15629
15680
|
}
|
|
15630
|
-
if (payload.kind === "timeout") {
|
|
15681
|
+
if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
|
|
15682
|
+
const heartbeatKey = `${e.subagentId}:${payload.kind}`;
|
|
15631
15683
|
const progress = progressBySubagent.get(e.subagentId) ?? 0;
|
|
15632
|
-
const lastProgress = lastTimeoutProgress.get(
|
|
15684
|
+
const lastProgress = lastTimeoutProgress.get(heartbeatKey) ?? -1;
|
|
15633
15685
|
if (progress <= lastProgress) {
|
|
15634
15686
|
payload.deny();
|
|
15635
15687
|
return;
|
|
15636
15688
|
}
|
|
15637
|
-
lastTimeoutProgress.set(
|
|
15689
|
+
lastTimeoutProgress.set(heartbeatKey, progress);
|
|
15690
|
+
const field = payload.kind === "timeout" ? "timeoutMs" : "idleTimeoutMs";
|
|
15638
15691
|
setImmediate(() => {
|
|
15639
15692
|
const newLimit = Math.min(Math.ceil(payload.limit * 2), 24 * 60 * 6e4);
|
|
15640
|
-
this.recordExtension(e.subagentId, e.taskId,
|
|
15641
|
-
payload.extend({
|
|
15693
|
+
this.recordExtension(e.subagentId, e.taskId, payload.kind, newLimit);
|
|
15694
|
+
payload.extend({ [field]: newLimit });
|
|
15642
15695
|
});
|
|
15643
15696
|
return;
|
|
15644
15697
|
}
|
|
@@ -15954,10 +16007,9 @@ var Director = class _Director {
|
|
|
15954
16007
|
if (this.fleetManager) {
|
|
15955
16008
|
this.fleetManager.assignNicknameAndRecord(config);
|
|
15956
16009
|
} else {
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
);
|
|
16010
|
+
const { key, display } = assignNickname(role, this._usedNicknames);
|
|
16011
|
+
config.name = display;
|
|
16012
|
+
this._usedNicknames.add(key);
|
|
15961
16013
|
}
|
|
15962
16014
|
}
|
|
15963
16015
|
result = await this.coordinator.spawn(config);
|
|
@@ -16279,8 +16331,8 @@ var Director = class _Director {
|
|
|
16279
16331
|
} else {
|
|
16280
16332
|
const entry = this.manifestEntries.get(subagentId);
|
|
16281
16333
|
if (entry?.name) {
|
|
16282
|
-
const nicknameKey = entry.name
|
|
16283
|
-
this._usedNicknames.delete(nicknameKey);
|
|
16334
|
+
const nicknameKey = nicknameKeyFromDisplay(entry.name);
|
|
16335
|
+
if (nicknameKey) this._usedNicknames.delete(nicknameKey);
|
|
16284
16336
|
}
|
|
16285
16337
|
}
|
|
16286
16338
|
this.manifestEntries.delete(subagentId);
|
|
@@ -24414,11 +24466,10 @@ var FleetManager = class {
|
|
|
24414
24466
|
*/
|
|
24415
24467
|
assignNicknameAndRecord(config) {
|
|
24416
24468
|
const role = config.role ?? "subagent";
|
|
24417
|
-
const
|
|
24418
|
-
|
|
24419
|
-
|
|
24420
|
-
|
|
24421
|
-
return nickname;
|
|
24469
|
+
const { key, display } = assignNickname(role, this._usedNicknames);
|
|
24470
|
+
this._usedNicknames.add(key);
|
|
24471
|
+
config.name = display;
|
|
24472
|
+
return display;
|
|
24422
24473
|
}
|
|
24423
24474
|
/**
|
|
24424
24475
|
* Returns the set of already-assigned nickname keys — useful for debugging
|
|
@@ -24603,8 +24654,8 @@ var FleetManager = class {
|
|
|
24603
24654
|
removeSubagent(subagentId) {
|
|
24604
24655
|
const entry = this.manifestEntries.get(subagentId);
|
|
24605
24656
|
if (entry?.name) {
|
|
24606
|
-
const nicknameKey = entry.name
|
|
24607
|
-
this._usedNicknames.delete(nicknameKey);
|
|
24657
|
+
const nicknameKey = nicknameKeyFromDisplay(entry.name);
|
|
24658
|
+
if (nicknameKey) this._usedNicknames.delete(nicknameKey);
|
|
24608
24659
|
}
|
|
24609
24660
|
for (const [taskId, task] of this.pendingTasks) {
|
|
24610
24661
|
if (task.subagentId === subagentId) {
|
|
@@ -25051,37 +25102,146 @@ var ExtensionRegistry = class {
|
|
|
25051
25102
|
}
|
|
25052
25103
|
};
|
|
25053
25104
|
|
|
25054
|
-
// src/core/
|
|
25055
|
-
|
|
25056
|
-
function
|
|
25057
|
-
|
|
25058
|
-
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
}
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25071
|
-
|
|
25072
|
-
|
|
25073
|
-
|
|
25074
|
-
}
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
|
|
25081
|
-
|
|
25082
|
-
|
|
25083
|
-
|
|
25084
|
-
|
|
25105
|
+
// src/core/agent-tools.ts
|
|
25106
|
+
function createAgentToolHandler(a) {
|
|
25107
|
+
async function executeSingleWithDecision(tool, use) {
|
|
25108
|
+
const start = Date.now();
|
|
25109
|
+
try {
|
|
25110
|
+
const result = await a.toolExecutor.executeTool(
|
|
25111
|
+
tool,
|
|
25112
|
+
use,
|
|
25113
|
+
a.ctx,
|
|
25114
|
+
a.perIterationOutputCapBytes
|
|
25115
|
+
);
|
|
25116
|
+
return { result, durationMs: Date.now() - start };
|
|
25117
|
+
} catch (err) {
|
|
25118
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25119
|
+
return {
|
|
25120
|
+
result: {
|
|
25121
|
+
type: "tool_result",
|
|
25122
|
+
tool_use_id: use.id,
|
|
25123
|
+
content: `Tool "${tool.name}" threw: ${msg}`,
|
|
25124
|
+
is_error: true
|
|
25125
|
+
},
|
|
25126
|
+
durationMs: Date.now() - start
|
|
25127
|
+
};
|
|
25128
|
+
}
|
|
25129
|
+
}
|
|
25130
|
+
function waitForConfirm(info) {
|
|
25131
|
+
return new Promise((resolve13) => {
|
|
25132
|
+
a.events.emit("tool.confirm_needed", {
|
|
25133
|
+
tool: info.tool,
|
|
25134
|
+
input: info.input,
|
|
25135
|
+
toolUseId: info.toolUseId,
|
|
25136
|
+
suggestedPattern: info.suggestedPattern,
|
|
25137
|
+
resolve: resolve13
|
|
25138
|
+
});
|
|
25139
|
+
});
|
|
25140
|
+
}
|
|
25141
|
+
function emitToolExecuted(toolUseId, toolName, durationMs, ok, input, content) {
|
|
25142
|
+
const sig = sizeSignals(toolName, content);
|
|
25143
|
+
a.events.emit("tool.executed", {
|
|
25144
|
+
id: toolUseId,
|
|
25145
|
+
name: toolName,
|
|
25146
|
+
durationMs,
|
|
25147
|
+
ok,
|
|
25148
|
+
input,
|
|
25149
|
+
output: truncateForEvent(content),
|
|
25150
|
+
outputBytes: sig.outputBytes,
|
|
25151
|
+
outputTokens: sig.outputTokens,
|
|
25152
|
+
outputLines: sig.outputLines
|
|
25153
|
+
});
|
|
25154
|
+
}
|
|
25155
|
+
async function executeTools(toolUses) {
|
|
25156
|
+
const selectedToolUses = await a.extensions.runBeforeToolExecution(a.ctx, toolUses);
|
|
25157
|
+
const { outputs } = await a.toolExecutor.executeBatch(
|
|
25158
|
+
selectedToolUses,
|
|
25159
|
+
a.ctx,
|
|
25160
|
+
a.executionStrategy
|
|
25161
|
+
);
|
|
25162
|
+
const useById = new Map(selectedToolUses.map((u) => [u.id, u]));
|
|
25163
|
+
const resultsForMessage = [];
|
|
25164
|
+
for (const { result, tool, durationMs } of outputs) {
|
|
25165
|
+
if (result.type === "tool_confirm_pending") {
|
|
25166
|
+
const decision = await waitForConfirm({
|
|
25167
|
+
tool,
|
|
25168
|
+
input: result.input,
|
|
25169
|
+
toolUseId: result.toolUseId,
|
|
25170
|
+
suggestedPattern: result.suggestedPattern
|
|
25171
|
+
});
|
|
25172
|
+
if (decision === "always") {
|
|
25173
|
+
try {
|
|
25174
|
+
await a.permission.trust({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25175
|
+
a.events.emit("trust.persisted", { tool: tool.name, pattern: result.suggestedPattern, decision });
|
|
25176
|
+
} catch {
|
|
25177
|
+
}
|
|
25178
|
+
} else if (decision === "deny") {
|
|
25179
|
+
try {
|
|
25180
|
+
await a.permission.deny({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25181
|
+
a.events.emit("trust.persisted", { tool: tool.name, pattern: result.suggestedPattern, decision });
|
|
25182
|
+
} catch {
|
|
25183
|
+
}
|
|
25184
|
+
}
|
|
25185
|
+
if (decision === "yes") {
|
|
25186
|
+
const p = a.permission;
|
|
25187
|
+
p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25188
|
+
} else if (decision === "no") {
|
|
25189
|
+
const p = a.permission;
|
|
25190
|
+
p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25191
|
+
}
|
|
25192
|
+
const reRunResult = decision === "yes" || decision === "always" ? await executeSingleWithDecision(tool, {
|
|
25193
|
+
id: result.toolUseId,
|
|
25194
|
+
name: tool.name,
|
|
25195
|
+
input: result.input
|
|
25196
|
+
}) : {
|
|
25197
|
+
result: {
|
|
25198
|
+
type: "tool_result",
|
|
25199
|
+
tool_use_id: result.toolUseId,
|
|
25200
|
+
content: decision === "deny" ? `Tool "${tool.name}" denied and blocked for this pattern.` : `Tool "${tool.name}" denied by user.`,
|
|
25201
|
+
is_error: true
|
|
25202
|
+
},
|
|
25203
|
+
durationMs: 0
|
|
25204
|
+
};
|
|
25205
|
+
const use2 = useById.get(reRunResult.result.tool_use_id);
|
|
25206
|
+
if (use2) {
|
|
25207
|
+
await a.pipelines.toolCall.run({ toolUse: use2, result: reRunResult.result, ctx: a.ctx, tool });
|
|
25208
|
+
await a.ctx.session.append({
|
|
25209
|
+
type: "tool_result",
|
|
25210
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25211
|
+
id: reRunResult.result.tool_use_id,
|
|
25212
|
+
content: reRunResult.result.content,
|
|
25213
|
+
isError: !!reRunResult.result.is_error
|
|
25214
|
+
});
|
|
25215
|
+
emitToolExecuted(
|
|
25216
|
+
reRunResult.result.tool_use_id,
|
|
25217
|
+
tool.name,
|
|
25218
|
+
reRunResult.durationMs,
|
|
25219
|
+
!reRunResult.result.is_error,
|
|
25220
|
+
result.input,
|
|
25221
|
+
reRunResult.result.content
|
|
25222
|
+
);
|
|
25223
|
+
}
|
|
25224
|
+
resultsForMessage.push(reRunResult.result);
|
|
25225
|
+
continue;
|
|
25226
|
+
}
|
|
25227
|
+
resultsForMessage.push(result);
|
|
25228
|
+
const use = useById.get(result.tool_use_id);
|
|
25229
|
+
if (!use) continue;
|
|
25230
|
+
await a.pipelines.toolCall.run({ toolUse: use, result, ctx: a.ctx, tool: tool ?? void 0 });
|
|
25231
|
+
await a.ctx.session.append({
|
|
25232
|
+
type: "tool_result",
|
|
25233
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25234
|
+
id: result.tool_use_id,
|
|
25235
|
+
content: result.content,
|
|
25236
|
+
isError: !!result.is_error
|
|
25237
|
+
});
|
|
25238
|
+
emitToolExecuted(result.tool_use_id, use.name, durationMs, !result.is_error, use.input, result.content);
|
|
25239
|
+
}
|
|
25240
|
+
a.ctx.state.appendMessage({ role: "user", content: resultsForMessage });
|
|
25241
|
+
await a.extensions.runAfterToolExecution(a.ctx, outputs);
|
|
25242
|
+
return resultsForMessage;
|
|
25243
|
+
}
|
|
25244
|
+
return { executeTools, executeSingleWithDecision };
|
|
25085
25245
|
}
|
|
25086
25246
|
|
|
25087
25247
|
// src/core/continue-to-next-iteration.ts
|
|
@@ -25099,13 +25259,13 @@ function parseContinueDirective(text) {
|
|
|
25099
25259
|
}
|
|
25100
25260
|
return lastDirective;
|
|
25101
25261
|
}
|
|
25102
|
-
var
|
|
25262
|
+
var META_KEY = "_autonomousContinue";
|
|
25103
25263
|
function setAutonomousContinue(ctx) {
|
|
25104
|
-
ctx.meta[
|
|
25264
|
+
ctx.meta[META_KEY] = true;
|
|
25105
25265
|
}
|
|
25106
25266
|
function consumeAutonomousContinue(ctx) {
|
|
25107
|
-
const val = ctx.meta[
|
|
25108
|
-
delete ctx.meta[
|
|
25267
|
+
const val = ctx.meta[META_KEY] === true;
|
|
25268
|
+
delete ctx.meta[META_KEY];
|
|
25109
25269
|
return val;
|
|
25110
25270
|
}
|
|
25111
25271
|
function makeContinueToNextIterationTool() {
|
|
@@ -25128,254 +25288,249 @@ function makeContinueToNextIterationTool() {
|
|
|
25128
25288
|
};
|
|
25129
25289
|
}
|
|
25130
25290
|
|
|
25131
|
-
// src/core/
|
|
25132
|
-
function
|
|
25133
|
-
|
|
25134
|
-
|
|
25135
|
-
|
|
25136
|
-
|
|
25137
|
-
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
}
|
|
25291
|
+
// src/core/agent-response.ts
|
|
25292
|
+
function createAgentResponseHandler(a) {
|
|
25293
|
+
async function buildAndRunRequestPipeline(opts) {
|
|
25294
|
+
const repaired = repairToolUseAdjacency(a.ctx.messages);
|
|
25295
|
+
if (repaired.report.changed) {
|
|
25296
|
+
a.ctx.state.replaceMessages(repaired.messages);
|
|
25297
|
+
a.events.emit("context.repaired", {
|
|
25298
|
+
ctx: a.ctx,
|
|
25299
|
+
...repaired.report
|
|
25300
|
+
});
|
|
25301
|
+
a.logger.warn(
|
|
25302
|
+
`Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ${repaired.report.removedToolResults.length} tool_result block(s), ${repaired.report.removedMessages} empty message(s)`
|
|
25303
|
+
);
|
|
25304
|
+
}
|
|
25305
|
+
const baseReq = {
|
|
25306
|
+
model: opts.model ?? a.ctx.model,
|
|
25307
|
+
system: a.ctx.systemPrompt,
|
|
25308
|
+
messages: a.ctx.messages,
|
|
25309
|
+
tools: a.tools.list(),
|
|
25310
|
+
maxTokens: 8192
|
|
25141
25311
|
};
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25312
|
+
return a.pipelines.request.run(baseReq);
|
|
25313
|
+
}
|
|
25314
|
+
async function processResponse(raw, req) {
|
|
25315
|
+
let res = raw;
|
|
25316
|
+
res = await a.pipelines.response.run(res);
|
|
25317
|
+
a.events.emit("provider.response", {
|
|
25318
|
+
ctx: a.ctx,
|
|
25319
|
+
usage: res.usage,
|
|
25320
|
+
stopReason: res.stopReason
|
|
25321
|
+
});
|
|
25322
|
+
a.ctx.tokenCounter.account(res.usage, req.model);
|
|
25323
|
+
a.ctx.state.appendMessage({ role: "assistant", content: res.content });
|
|
25324
|
+
await a.ctx.session.append({
|
|
25325
|
+
type: "llm_response",
|
|
25326
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25327
|
+
content: res.content,
|
|
25328
|
+
stopReason: res.stopReason,
|
|
25329
|
+
usage: res.usage
|
|
25330
|
+
});
|
|
25331
|
+
if (a.ctx.signal.aborted) {
|
|
25332
|
+
let finalText2 = "";
|
|
25333
|
+
for (const block of res.content) {
|
|
25334
|
+
if (isTextBlock(block)) finalText2 += block.text;
|
|
25149
25335
|
}
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25336
|
+
return { finalText: finalText2, aborted: true, done: false };
|
|
25337
|
+
}
|
|
25338
|
+
let finalText = "";
|
|
25339
|
+
const streamed = a.ctx.provider.capabilities.streaming;
|
|
25340
|
+
for (const block of res.content) {
|
|
25341
|
+
if (isTextBlock(block)) {
|
|
25342
|
+
const rendered = await a.pipelines.assistantOutput.run(block);
|
|
25343
|
+
finalText += rendered.text;
|
|
25344
|
+
if (!streamed) a.renderer?.write(rendered);
|
|
25156
25345
|
}
|
|
25157
|
-
};
|
|
25158
|
-
events.emit("iteration.limit_reached", {
|
|
25159
|
-
currentIterations,
|
|
25160
|
-
currentLimit,
|
|
25161
|
-
grant,
|
|
25162
|
-
deny
|
|
25163
|
-
});
|
|
25164
|
-
if (autoExtend) {
|
|
25165
|
-
setImmediate(() => {
|
|
25166
|
-
if (!resolved) {
|
|
25167
|
-
resolved = true;
|
|
25168
|
-
clearTimeout(timer);
|
|
25169
|
-
resolve13(100);
|
|
25170
|
-
}
|
|
25171
|
-
});
|
|
25172
25346
|
}
|
|
25173
|
-
|
|
25347
|
+
let directive = "none";
|
|
25348
|
+
if (finalText) {
|
|
25349
|
+
directive = parseContinueDirective(finalText);
|
|
25350
|
+
}
|
|
25351
|
+
return { finalText, aborted: false, done: false, directive };
|
|
25352
|
+
}
|
|
25353
|
+
return { buildAndRunRequestPipeline, processResponse };
|
|
25174
25354
|
}
|
|
25175
25355
|
|
|
25176
|
-
// src/core/
|
|
25177
|
-
var
|
|
25178
|
-
function
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
}
|
|
25182
|
-
const text = input.filter(isTextBlock).map((b) => b.text).join("");
|
|
25183
|
-
return { blocks: input, text };
|
|
25356
|
+
// src/core/btw.ts
|
|
25357
|
+
var META_KEY2 = "_btwNotes";
|
|
25358
|
+
function readQueue(ctx) {
|
|
25359
|
+
const raw = ctx.meta[META_KEY2];
|
|
25360
|
+
return Array.isArray(raw) ? raw : [];
|
|
25184
25361
|
}
|
|
25185
|
-
function
|
|
25186
|
-
|
|
25187
|
-
|
|
25188
|
-
|
|
25189
|
-
|
|
25190
|
-
|
|
25191
|
-
assistantOutput: new Pipeline(),
|
|
25192
|
-
contextWindow: new Pipeline()
|
|
25193
|
-
};
|
|
25362
|
+
function setBtwNote(ctx, text) {
|
|
25363
|
+
const trimmed = text.trim();
|
|
25364
|
+
if (!trimmed) return readQueue(ctx).length;
|
|
25365
|
+
const next = [...readQueue(ctx), trimmed].slice(-20);
|
|
25366
|
+
ctx.meta[META_KEY2] = next;
|
|
25367
|
+
return next.length;
|
|
25194
25368
|
}
|
|
25195
|
-
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25205
|
-
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
25218
|
-
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
|
|
25226
|
-
|
|
25227
|
-
|
|
25228
|
-
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
disableInteractiveConfirmation() {
|
|
25257
|
-
this.toolExecutor.clearConfirmAwaiter();
|
|
25258
|
-
const policy = this.permission;
|
|
25259
|
-
if (typeof policy.setPromptDelegate === "function") {
|
|
25260
|
-
policy.setPromptDelegate(void 0);
|
|
25369
|
+
function pendingBtwCount(ctx) {
|
|
25370
|
+
return readQueue(ctx).length;
|
|
25371
|
+
}
|
|
25372
|
+
function consumeBtwNotes(ctx) {
|
|
25373
|
+
const notes = readQueue(ctx);
|
|
25374
|
+
if (notes.length > 0) delete ctx.meta[META_KEY2];
|
|
25375
|
+
return notes;
|
|
25376
|
+
}
|
|
25377
|
+
function buildBtwBlock(notes) {
|
|
25378
|
+
const body = notes.map((n) => `- ${n}`).join("\n");
|
|
25379
|
+
return [
|
|
25380
|
+
"[BY THE WAY \u2014 the user added this while you were working. Fold it into",
|
|
25381
|
+
"your current task; do not restart from scratch unless it contradicts the",
|
|
25382
|
+
"goal:",
|
|
25383
|
+
"",
|
|
25384
|
+
body,
|
|
25385
|
+
"]"
|
|
25386
|
+
].join("\n");
|
|
25387
|
+
}
|
|
25388
|
+
|
|
25389
|
+
// src/core/iteration-limit.ts
|
|
25390
|
+
function requestLimitExtension(opts) {
|
|
25391
|
+
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
25392
|
+
return new Promise((resolve13) => {
|
|
25393
|
+
let resolved = false;
|
|
25394
|
+
const timerFired = () => {
|
|
25395
|
+
if (!resolved) {
|
|
25396
|
+
resolved = true;
|
|
25397
|
+
resolve13(0);
|
|
25398
|
+
}
|
|
25399
|
+
};
|
|
25400
|
+
const timer = setTimeout(timerFired, timeoutMs);
|
|
25401
|
+
timer.unref();
|
|
25402
|
+
const deny = () => {
|
|
25403
|
+
if (!resolved) {
|
|
25404
|
+
resolved = true;
|
|
25405
|
+
clearTimeout(timer);
|
|
25406
|
+
resolve13(0);
|
|
25407
|
+
}
|
|
25408
|
+
};
|
|
25409
|
+
const grant = (extra) => {
|
|
25410
|
+
if (!resolved) {
|
|
25411
|
+
resolved = true;
|
|
25412
|
+
clearTimeout(timer);
|
|
25413
|
+
resolve13(Math.max(0, extra));
|
|
25414
|
+
}
|
|
25415
|
+
};
|
|
25416
|
+
events.emit("iteration.limit_reached", {
|
|
25417
|
+
currentIterations,
|
|
25418
|
+
currentLimit,
|
|
25419
|
+
grant,
|
|
25420
|
+
deny
|
|
25421
|
+
});
|
|
25422
|
+
if (autoExtend) {
|
|
25423
|
+
setImmediate(() => {
|
|
25424
|
+
if (!resolved) {
|
|
25425
|
+
resolved = true;
|
|
25426
|
+
clearTimeout(timer);
|
|
25427
|
+
resolve13(100);
|
|
25428
|
+
}
|
|
25429
|
+
});
|
|
25261
25430
|
}
|
|
25431
|
+
});
|
|
25432
|
+
}
|
|
25433
|
+
|
|
25434
|
+
// src/core/agent-loop.ts
|
|
25435
|
+
function toError(err) {
|
|
25436
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
25437
|
+
}
|
|
25438
|
+
function createAgentLoopHandler(a, handlers) {
|
|
25439
|
+
async function compactContextIfNeeded() {
|
|
25440
|
+
await a.pipelines.contextWindow.run(a.ctx);
|
|
25262
25441
|
}
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
await plugin.setup(api);
|
|
25268
|
-
this.plugins.push({ plugin, api });
|
|
25442
|
+
function emitContextPct() {
|
|
25443
|
+
const maxContext = a.ctx.provider.capabilities.maxContext ?? 2e5;
|
|
25444
|
+
const { total } = estimateRequestTokens(a.ctx.messages, a.ctx.systemPrompt, a.ctx.tools ?? []);
|
|
25445
|
+
a.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
|
|
25269
25446
|
}
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
}
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
this.plugins.length = 0;
|
|
25282
|
-
if (errors.length > 0) {
|
|
25283
|
-
throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
|
|
25447
|
+
function injectPendingBtwNotes() {
|
|
25448
|
+
const notes = consumeBtwNotes(a.ctx);
|
|
25449
|
+
if (notes.length === 0) return;
|
|
25450
|
+
const block = { type: "text", text: buildBtwBlock(notes) };
|
|
25451
|
+
const messages = a.ctx.messages;
|
|
25452
|
+
const last = messages[messages.length - 1];
|
|
25453
|
+
if (last && last.role === "user") {
|
|
25454
|
+
const content = typeof last.content === "string" ? [{ type: "text", text: last.content }, block] : [...last.content, block];
|
|
25455
|
+
a.ctx.state.replaceMessages([...messages.slice(0, -1), { ...last, content }]);
|
|
25456
|
+
} else {
|
|
25457
|
+
a.ctx.state.appendMessage({ role: "user", content: [block] });
|
|
25284
25458
|
}
|
|
25285
25459
|
}
|
|
25286
|
-
async
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
span?.setAttribute("agent.status", result.status);
|
|
25301
|
-
span?.setAttribute("agent.iterations", result.iterations);
|
|
25302
|
-
await this.extensions.runAfterRun(this.ctx, result);
|
|
25303
|
-
return result;
|
|
25304
|
-
} catch (err) {
|
|
25305
|
-
const wse = err instanceof AgentError ? err : toWrongStackError(err);
|
|
25306
|
-
this.events.emit("error", { err: toError(err), phase: "agent" });
|
|
25307
|
-
if (err instanceof Error) span?.recordError(err);
|
|
25308
|
-
span?.setAttribute("agent.status", "failed");
|
|
25309
|
-
const result = {
|
|
25310
|
-
status: signal.aborted ? "aborted" : "failed",
|
|
25311
|
-
iterations: 0,
|
|
25312
|
-
error: wse
|
|
25313
|
-
};
|
|
25314
|
-
await this.extensions.runAfterRun(this.ctx, result);
|
|
25315
|
-
return result;
|
|
25316
|
-
} finally {
|
|
25317
|
-
span?.end();
|
|
25318
|
-
await controller.dispose();
|
|
25460
|
+
async function checkIterationLimit(iterationIndex, limit, hasHardLimit, currentIterations, delegateSummaries) {
|
|
25461
|
+
if (hasHardLimit && iterationIndex >= limit) {
|
|
25462
|
+
const extendBy = await requestLimitExtension({
|
|
25463
|
+
events: a.events,
|
|
25464
|
+
currentIterations,
|
|
25465
|
+
currentLimit: limit,
|
|
25466
|
+
autoExtend: a.autoExtendLimit
|
|
25467
|
+
});
|
|
25468
|
+
if (extendBy > 0) {
|
|
25469
|
+
const newLimit = limit + extendBy;
|
|
25470
|
+
a.logger.info(`Iteration limit extended by ${extendBy} (new limit: ${newLimit})`);
|
|
25471
|
+
return { limit: newLimit };
|
|
25472
|
+
}
|
|
25473
|
+
return { limit, exit: { status: "max_iterations", iterations: currentIterations, delegateSummaries } };
|
|
25319
25474
|
}
|
|
25475
|
+
return { limit };
|
|
25320
25476
|
}
|
|
25321
|
-
async runInner(inputPayload, opts, controller) {
|
|
25322
|
-
await
|
|
25323
|
-
|
|
25324
|
-
await
|
|
25477
|
+
async function runInner(inputPayload, opts, controller, autonomousContinue) {
|
|
25478
|
+
await a.pipelines.userInput.run(inputPayload);
|
|
25479
|
+
a.ctx.state.appendMessage({ role: "user", content: inputPayload.content });
|
|
25480
|
+
await a.ctx.session.append({
|
|
25325
25481
|
type: "user_input",
|
|
25326
25482
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25327
25483
|
content: inputPayload.content
|
|
25328
25484
|
});
|
|
25329
|
-
const promptIndex =
|
|
25485
|
+
const promptIndex = a.ctx.messages.filter((m) => m.role === "user").length - 1;
|
|
25330
25486
|
const preview = inputPayload.text.slice(0, 80) + (inputPayload.text.length > 80 ? "\u2026" : "");
|
|
25331
|
-
await
|
|
25487
|
+
await a.ctx.session.writeCheckpoint(promptIndex, preview);
|
|
25332
25488
|
let finalText = "";
|
|
25333
25489
|
let iterations = 0;
|
|
25334
25490
|
const delegateSummaries = [];
|
|
25335
|
-
let effectiveLimit = opts.maxIterations ??
|
|
25491
|
+
let effectiveLimit = opts.maxIterations ?? a.maxIterations;
|
|
25336
25492
|
const hasHardLimit = effectiveLimit > 0 && Number.isFinite(effectiveLimit);
|
|
25337
25493
|
let recoveryRetries = 0;
|
|
25338
|
-
const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
|
|
25339
25494
|
const onSubagentDone = ({ summary, ok }) => {
|
|
25340
25495
|
delegateSummaries.push({ summary, ok });
|
|
25341
25496
|
};
|
|
25342
|
-
const offSubagentDone =
|
|
25343
|
-
const diRunner =
|
|
25497
|
+
const offSubagentDone = a.events.on("subagent.done", onSubagentDone);
|
|
25498
|
+
const diRunner = a.container.has(TOKENS.ProviderRunner) ? a.container.resolve(TOKENS.ProviderRunner) : null;
|
|
25344
25499
|
const baseRunner = diRunner ? (ctx, req) => diRunner.run({
|
|
25345
25500
|
provider: ctx.provider,
|
|
25346
25501
|
request: req,
|
|
25347
25502
|
signal: controller.signal,
|
|
25348
25503
|
ctx,
|
|
25349
|
-
events:
|
|
25350
|
-
retry:
|
|
25351
|
-
logger:
|
|
25352
|
-
tracer:
|
|
25504
|
+
events: a.events,
|
|
25505
|
+
retry: a.retry,
|
|
25506
|
+
logger: a.logger,
|
|
25507
|
+
tracer: a.tracer
|
|
25353
25508
|
}) : async (ctx, req) => runProviderWithRetry({
|
|
25354
25509
|
provider: ctx.provider,
|
|
25355
25510
|
request: req,
|
|
25356
25511
|
signal: controller.signal,
|
|
25357
25512
|
ctx,
|
|
25358
|
-
events:
|
|
25359
|
-
retry:
|
|
25360
|
-
logger:
|
|
25361
|
-
tracer:
|
|
25513
|
+
events: a.events,
|
|
25514
|
+
retry: a.retry,
|
|
25515
|
+
logger: a.logger,
|
|
25516
|
+
tracer: a.tracer
|
|
25362
25517
|
});
|
|
25363
|
-
const customRunner =
|
|
25518
|
+
const customRunner = a.extensions.wrapProviderRunner(baseRunner);
|
|
25364
25519
|
try {
|
|
25365
25520
|
for (let i = 0; ; i++) {
|
|
25366
25521
|
iterations = i + 1;
|
|
25367
25522
|
if (controller.signal.aborted) {
|
|
25368
25523
|
return { status: "aborted", iterations };
|
|
25369
25524
|
}
|
|
25370
|
-
await
|
|
25371
|
-
|
|
25525
|
+
await a.ctx.session.writeInFlightMarker(`iteration ${i} / max ${a.maxIterations}`).catch((err) => {
|
|
25526
|
+
a.logger.debug?.(
|
|
25372
25527
|
`in-flight marker write failed: ${err instanceof Error ? err.message : String(err)}`
|
|
25373
25528
|
);
|
|
25374
25529
|
});
|
|
25375
25530
|
if (autonomousContinue) {
|
|
25376
|
-
consumeAutonomousContinue(
|
|
25531
|
+
consumeAutonomousContinue(a.ctx);
|
|
25377
25532
|
}
|
|
25378
|
-
const limitCheck = await
|
|
25533
|
+
const limitCheck = await checkIterationLimit(
|
|
25379
25534
|
i,
|
|
25380
25535
|
effectiveLimit,
|
|
25381
25536
|
hasHardLimit,
|
|
@@ -25386,11 +25541,11 @@ var Agent = class {
|
|
|
25386
25541
|
if (limitCheck.exit) {
|
|
25387
25542
|
return { ...limitCheck.exit, finalText };
|
|
25388
25543
|
}
|
|
25389
|
-
await
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
const req = await
|
|
25393
|
-
await
|
|
25544
|
+
await a.extensions.runBeforeIteration(a.ctx, i);
|
|
25545
|
+
a.events.emit("iteration.started", { ctx: a.ctx, index: i });
|
|
25546
|
+
injectPendingBtwNotes();
|
|
25547
|
+
const req = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
25548
|
+
await a.ctx.session.append({
|
|
25394
25549
|
type: "llm_request",
|
|
25395
25550
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25396
25551
|
model: req.model,
|
|
@@ -25401,39 +25556,39 @@ var Agent = class {
|
|
|
25401
25556
|
});
|
|
25402
25557
|
let res;
|
|
25403
25558
|
try {
|
|
25404
|
-
res = await customRunner(
|
|
25559
|
+
res = await customRunner(a.ctx, req);
|
|
25405
25560
|
const calibratedEstimate = estimateRequestTokensCalibrated(req.messages, req.system, req.tools ?? []).total;
|
|
25406
25561
|
recordActualUsage(res.usage.input, calibratedEstimate);
|
|
25407
25562
|
recoveryRetries = 0;
|
|
25408
25563
|
} catch (err) {
|
|
25409
25564
|
if (controller.signal.aborted) {
|
|
25410
|
-
|
|
25565
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25411
25566
|
return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED") };
|
|
25412
25567
|
}
|
|
25413
|
-
const extDecision = await
|
|
25568
|
+
const extDecision = await a.extensions.runOnError(a.ctx, err, "provider", i);
|
|
25414
25569
|
if (extDecision) {
|
|
25415
25570
|
if (extDecision.action === "fail") {
|
|
25416
|
-
|
|
25571
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25417
25572
|
return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
|
|
25418
25573
|
}
|
|
25419
25574
|
if (extDecision.action === "continue") {
|
|
25420
|
-
await
|
|
25575
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25421
25576
|
continue;
|
|
25422
25577
|
}
|
|
25423
25578
|
if (extDecision.action === "retry") {
|
|
25424
25579
|
recoveryRetries++;
|
|
25425
25580
|
if (recoveryRetries > 2) {
|
|
25426
|
-
|
|
25581
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25427
25582
|
return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
|
|
25428
25583
|
}
|
|
25429
|
-
if (extDecision.model)
|
|
25430
|
-
|
|
25584
|
+
if (extDecision.model) a.ctx.model = extDecision.model;
|
|
25585
|
+
a.logger.info("Extension requested retry; retrying turn");
|
|
25431
25586
|
continue;
|
|
25432
25587
|
}
|
|
25433
25588
|
}
|
|
25434
|
-
const recovered = await
|
|
25589
|
+
const recovered = await a.errorHandler.recover(err, a.ctx);
|
|
25435
25590
|
if (!recovered || recovered.action === "fail") {
|
|
25436
|
-
|
|
25591
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25437
25592
|
return {
|
|
25438
25593
|
status: "failed",
|
|
25439
25594
|
iterations,
|
|
@@ -25444,17 +25599,17 @@ var Agent = class {
|
|
|
25444
25599
|
if (recovered.action === "retry") {
|
|
25445
25600
|
recoveryRetries++;
|
|
25446
25601
|
if (recoveryRetries > 2) {
|
|
25447
|
-
|
|
25602
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25448
25603
|
return { status: "failed", iterations, error: toWrongStackError(err) };
|
|
25449
25604
|
}
|
|
25450
|
-
if (recovered.model)
|
|
25451
|
-
|
|
25605
|
+
if (recovered.model) a.ctx.model = recovered.model;
|
|
25606
|
+
a.logger.info(`Recovered provider error via ${recovered.reason}; retrying turn`);
|
|
25452
25607
|
continue;
|
|
25453
25608
|
}
|
|
25454
25609
|
recoveryRetries = 0;
|
|
25455
25610
|
res = recovered.response;
|
|
25456
25611
|
}
|
|
25457
|
-
const responseResult = await
|
|
25612
|
+
const responseResult = await handlers.response.processResponse(res, req);
|
|
25458
25613
|
if (responseResult.aborted) {
|
|
25459
25614
|
return { status: "aborted", iterations, finalText: responseResult.finalText, delegateSummaries };
|
|
25460
25615
|
}
|
|
@@ -25464,11 +25619,11 @@ var Agent = class {
|
|
|
25464
25619
|
finalText = responseResult.finalText;
|
|
25465
25620
|
const toolUses = res.content.filter(isToolUseBlock);
|
|
25466
25621
|
if (toolUses.length === 0) {
|
|
25467
|
-
|
|
25468
|
-
|
|
25622
|
+
emitContextPct();
|
|
25623
|
+
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
25469
25624
|
if (autonomousContinue && responseResult.directive === "continue") {
|
|
25470
|
-
await
|
|
25471
|
-
await
|
|
25625
|
+
await compactContextIfNeeded();
|
|
25626
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25472
25627
|
continue;
|
|
25473
25628
|
}
|
|
25474
25629
|
if (autonomousContinue && responseResult.directive === "stop") {
|
|
@@ -25476,18 +25631,18 @@ var Agent = class {
|
|
|
25476
25631
|
}
|
|
25477
25632
|
return { status: "done", iterations, finalText, delegateSummaries };
|
|
25478
25633
|
}
|
|
25479
|
-
await
|
|
25480
|
-
if (autonomousContinue && consumeAutonomousContinue(
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
await
|
|
25484
|
-
await
|
|
25634
|
+
await handlers.tools.executeTools(toolUses);
|
|
25635
|
+
if (autonomousContinue && consumeAutonomousContinue(a.ctx)) {
|
|
25636
|
+
emitContextPct();
|
|
25637
|
+
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
25638
|
+
await compactContextIfNeeded();
|
|
25639
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25485
25640
|
continue;
|
|
25486
25641
|
}
|
|
25487
|
-
|
|
25488
|
-
|
|
25489
|
-
await
|
|
25490
|
-
await
|
|
25642
|
+
emitContextPct();
|
|
25643
|
+
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
25644
|
+
await compactContextIfNeeded();
|
|
25645
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25491
25646
|
if (autonomousContinue && responseResult.directive === "continue") {
|
|
25492
25647
|
continue;
|
|
25493
25648
|
}
|
|
@@ -25498,346 +25653,161 @@ var Agent = class {
|
|
|
25498
25653
|
} finally {
|
|
25499
25654
|
offSubagentDone();
|
|
25500
25655
|
const reason = controller.signal.aborted ? "aborted" : "clean";
|
|
25501
|
-
await
|
|
25502
|
-
|
|
25656
|
+
await a.ctx.session.clearInFlightMarker(reason).catch((err) => {
|
|
25657
|
+
a.logger.debug?.(
|
|
25503
25658
|
`in-flight marker clear failed: ${err instanceof Error ? err.message : String(err)}`
|
|
25504
25659
|
);
|
|
25505
25660
|
});
|
|
25506
25661
|
}
|
|
25507
25662
|
}
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
const extendBy = await requestLimitExtension({
|
|
25517
|
-
events: this.events,
|
|
25518
|
-
currentIterations,
|
|
25519
|
-
currentLimit: limit,
|
|
25520
|
-
autoExtend: this.autoExtendLimit
|
|
25521
|
-
});
|
|
25522
|
-
if (extendBy > 0) {
|
|
25523
|
-
const newLimit = limit + extendBy;
|
|
25524
|
-
this.logger.info(`Iteration limit extended by ${extendBy} (new limit: ${newLimit})`);
|
|
25525
|
-
return { limit: newLimit };
|
|
25526
|
-
}
|
|
25527
|
-
return { limit, exit: { status: "max_iterations", iterations: currentIterations, delegateSummaries } };
|
|
25528
|
-
}
|
|
25529
|
-
return { limit };
|
|
25530
|
-
}
|
|
25531
|
-
/**
|
|
25532
|
-
* Build request and run through request pipeline.
|
|
25533
|
-
*/
|
|
25534
|
-
async buildAndRunRequestPipeline(opts) {
|
|
25535
|
-
const repaired = repairToolUseAdjacency(this.ctx.messages);
|
|
25536
|
-
if (repaired.report.changed) {
|
|
25537
|
-
this.ctx.state.replaceMessages(repaired.messages);
|
|
25538
|
-
this.events.emit("context.repaired", {
|
|
25539
|
-
ctx: this.ctx,
|
|
25540
|
-
...repaired.report
|
|
25541
|
-
});
|
|
25542
|
-
this.logger.warn(
|
|
25543
|
-
`Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ${repaired.report.removedToolResults.length} tool_result block(s), ${repaired.report.removedMessages} empty message(s)`
|
|
25544
|
-
);
|
|
25545
|
-
}
|
|
25546
|
-
const baseReq = {
|
|
25547
|
-
model: opts.model ?? this.ctx.model,
|
|
25548
|
-
system: this.ctx.systemPrompt,
|
|
25549
|
-
messages: this.ctx.messages,
|
|
25550
|
-
tools: this.tools.list(),
|
|
25551
|
-
maxTokens: 8192
|
|
25552
|
-
};
|
|
25553
|
-
return this.pipelines.request.run(baseReq);
|
|
25663
|
+
return { runInner };
|
|
25664
|
+
}
|
|
25665
|
+
|
|
25666
|
+
// src/core/agent-types.ts
|
|
25667
|
+
var DEFAULT_MAX_ITERATIONS = 100;
|
|
25668
|
+
function normalizeInput(input) {
|
|
25669
|
+
if (typeof input === "string") {
|
|
25670
|
+
return { blocks: [{ type: "text", text: input }], text: input };
|
|
25554
25671
|
}
|
|
25555
|
-
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
|
|
25564
|
-
|
|
25565
|
-
|
|
25566
|
-
|
|
25567
|
-
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25572
|
-
|
|
25573
|
-
|
|
25574
|
-
|
|
25672
|
+
const text = input.filter(isTextBlock).map((b) => b.text).join("");
|
|
25673
|
+
return { blocks: input, text };
|
|
25674
|
+
}
|
|
25675
|
+
function createDefaultPipelines() {
|
|
25676
|
+
return {
|
|
25677
|
+
request: new Pipeline(),
|
|
25678
|
+
response: new Pipeline(),
|
|
25679
|
+
toolCall: new Pipeline(),
|
|
25680
|
+
userInput: new Pipeline(),
|
|
25681
|
+
assistantOutput: new Pipeline(),
|
|
25682
|
+
contextWindow: new Pipeline()
|
|
25683
|
+
};
|
|
25684
|
+
}
|
|
25685
|
+
|
|
25686
|
+
// src/core/agent.ts
|
|
25687
|
+
var Agent = class {
|
|
25688
|
+
container;
|
|
25689
|
+
tools;
|
|
25690
|
+
providers;
|
|
25691
|
+
events;
|
|
25692
|
+
pipelines;
|
|
25693
|
+
ctx;
|
|
25694
|
+
maxIterations;
|
|
25695
|
+
executionStrategy;
|
|
25696
|
+
perIterationOutputCapBytes;
|
|
25697
|
+
plugins = [];
|
|
25698
|
+
toolExecutor;
|
|
25699
|
+
autoExtendLimit;
|
|
25700
|
+
autonomousContinue;
|
|
25701
|
+
tracer;
|
|
25702
|
+
extensions;
|
|
25703
|
+
_toolHandler;
|
|
25704
|
+
_responseHandler;
|
|
25705
|
+
_loopHandler;
|
|
25706
|
+
constructor(init) {
|
|
25707
|
+
this.container = init.container;
|
|
25708
|
+
this.tools = init.tools;
|
|
25709
|
+
this.providers = init.providers;
|
|
25710
|
+
this.events = init.events;
|
|
25711
|
+
this.pipelines = init.pipelines;
|
|
25712
|
+
this.ctx = init.context;
|
|
25713
|
+
this.maxIterations = init.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
25714
|
+
this.executionStrategy = init.executionStrategy ?? "smart";
|
|
25715
|
+
this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
|
|
25716
|
+
this.autoExtendLimit = init.autoExtendLimit ?? true;
|
|
25717
|
+
this.autonomousContinue = init.autonomousContinue ?? false;
|
|
25718
|
+
this.tracer = init.tracer;
|
|
25719
|
+
this.extensions = init.extensions ?? new ExtensionRegistry();
|
|
25720
|
+
this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
|
|
25721
|
+
this.toolExecutor = init.toolExecutor;
|
|
25722
|
+
this._toolHandler = createAgentToolHandler(this);
|
|
25723
|
+
this._responseHandler = createAgentResponseHandler(this);
|
|
25724
|
+
this._loopHandler = createAgentLoopHandler(this, {
|
|
25725
|
+
tools: this._toolHandler,
|
|
25726
|
+
response: this._responseHandler
|
|
25575
25727
|
});
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
25584
|
-
|
|
25585
|
-
|
|
25586
|
-
|
|
25587
|
-
|
|
25588
|
-
|
|
25589
|
-
|
|
25590
|
-
|
|
25591
|
-
|
|
25592
|
-
|
|
25593
|
-
|
|
25594
|
-
|
|
25728
|
+
}
|
|
25729
|
+
get logger() {
|
|
25730
|
+
return this.container.resolve(TOKENS.Logger);
|
|
25731
|
+
}
|
|
25732
|
+
get retry() {
|
|
25733
|
+
return this.container.resolve(TOKENS.RetryPolicy);
|
|
25734
|
+
}
|
|
25735
|
+
get errorHandler() {
|
|
25736
|
+
return this.container.resolve(TOKENS.ErrorHandler);
|
|
25737
|
+
}
|
|
25738
|
+
get permission() {
|
|
25739
|
+
return this.container.resolve(TOKENS.PermissionPolicy);
|
|
25740
|
+
}
|
|
25741
|
+
get renderer() {
|
|
25742
|
+
return this.container.has(TOKENS.Renderer) ? this.container.resolve(TOKENS.Renderer) : void 0;
|
|
25743
|
+
}
|
|
25744
|
+
disableInteractiveConfirmation() {
|
|
25745
|
+
this.toolExecutor.clearConfirmAwaiter();
|
|
25746
|
+
const policy = this.permission;
|
|
25747
|
+
if (typeof policy.setPromptDelegate === "function") {
|
|
25748
|
+
policy.setPromptDelegate(void 0);
|
|
25595
25749
|
}
|
|
25596
|
-
return { finalText, aborted: false, done: false, directive };
|
|
25597
25750
|
}
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
|
|
25601
|
-
|
|
25602
|
-
|
|
25603
|
-
|
|
25604
|
-
|
|
25605
|
-
async
|
|
25606
|
-
const
|
|
25607
|
-
const {
|
|
25608
|
-
|
|
25609
|
-
|
|
25610
|
-
|
|
25611
|
-
|
|
25612
|
-
|
|
25613
|
-
const resultsForMessage = [];
|
|
25614
|
-
for (const { result, tool, durationMs } of outputs) {
|
|
25615
|
-
if (result.type === "tool_confirm_pending") {
|
|
25616
|
-
const decision = await this.waitForConfirm({
|
|
25617
|
-
tool,
|
|
25618
|
-
input: result.input,
|
|
25619
|
-
toolUseId: result.toolUseId,
|
|
25620
|
-
suggestedPattern: result.suggestedPattern
|
|
25621
|
-
});
|
|
25622
|
-
if (decision === "always") {
|
|
25623
|
-
try {
|
|
25624
|
-
await this.permission.trust({
|
|
25625
|
-
tool: tool.name,
|
|
25626
|
-
pattern: result.suggestedPattern
|
|
25627
|
-
});
|
|
25628
|
-
this.events.emit("trust.persisted", {
|
|
25629
|
-
tool: tool.name,
|
|
25630
|
-
pattern: result.suggestedPattern,
|
|
25631
|
-
decision
|
|
25632
|
-
});
|
|
25633
|
-
} catch {
|
|
25634
|
-
}
|
|
25635
|
-
} else if (decision === "deny") {
|
|
25636
|
-
try {
|
|
25637
|
-
await this.permission.deny({
|
|
25638
|
-
tool: tool.name,
|
|
25639
|
-
pattern: result.suggestedPattern
|
|
25640
|
-
});
|
|
25641
|
-
this.events.emit("trust.persisted", {
|
|
25642
|
-
tool: tool.name,
|
|
25643
|
-
pattern: result.suggestedPattern,
|
|
25644
|
-
decision
|
|
25645
|
-
});
|
|
25646
|
-
} catch {
|
|
25647
|
-
}
|
|
25648
|
-
}
|
|
25649
|
-
if (decision === "yes") {
|
|
25650
|
-
const p = this.permission;
|
|
25651
|
-
p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25652
|
-
} else if (decision === "no") {
|
|
25653
|
-
const p = this.permission;
|
|
25654
|
-
p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25655
|
-
}
|
|
25656
|
-
const reRunResult = decision === "yes" || decision === "always" ? await this.executeSingleWithDecision(
|
|
25657
|
-
tool,
|
|
25658
|
-
{ id: result.toolUseId, name: tool.name, input: result.input }
|
|
25659
|
-
) : {
|
|
25660
|
-
result: {
|
|
25661
|
-
type: "tool_result",
|
|
25662
|
-
tool_use_id: result.toolUseId,
|
|
25663
|
-
content: decision === "deny" ? `Tool "${tool.name}" denied and blocked for this pattern.` : `Tool "${tool.name}" denied by user.`,
|
|
25664
|
-
is_error: true
|
|
25665
|
-
},
|
|
25666
|
-
durationMs: 0
|
|
25667
|
-
};
|
|
25668
|
-
const use2 = useById.get(reRunResult.result.tool_use_id);
|
|
25669
|
-
if (use2) {
|
|
25670
|
-
await this.pipelines.toolCall.run({
|
|
25671
|
-
toolUse: use2,
|
|
25672
|
-
result: reRunResult.result,
|
|
25673
|
-
ctx: this.ctx,
|
|
25674
|
-
tool
|
|
25675
|
-
});
|
|
25676
|
-
await this.ctx.session.append({
|
|
25677
|
-
type: "tool_result",
|
|
25678
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25679
|
-
id: reRunResult.result.tool_use_id,
|
|
25680
|
-
content: reRunResult.result.content,
|
|
25681
|
-
isError: !!reRunResult.result.is_error
|
|
25682
|
-
});
|
|
25683
|
-
{
|
|
25684
|
-
const sig = sizeSignals(tool?.name, reRunResult.result.content);
|
|
25685
|
-
this.events.emit("tool.executed", {
|
|
25686
|
-
id: reRunResult.result.tool_use_id,
|
|
25687
|
-
name: tool.name,
|
|
25688
|
-
durationMs: reRunResult.durationMs,
|
|
25689
|
-
ok: !reRunResult.result.is_error,
|
|
25690
|
-
input: result.input,
|
|
25691
|
-
output: truncateForEvent(reRunResult.result.content),
|
|
25692
|
-
outputBytes: sig.outputBytes,
|
|
25693
|
-
outputTokens: sig.outputTokens,
|
|
25694
|
-
outputLines: sig.outputLines
|
|
25695
|
-
});
|
|
25696
|
-
}
|
|
25697
|
-
}
|
|
25698
|
-
resultsForMessage.push(reRunResult.result);
|
|
25699
|
-
continue;
|
|
25700
|
-
}
|
|
25701
|
-
resultsForMessage.push(result);
|
|
25702
|
-
const use = useById.get(result.tool_use_id);
|
|
25703
|
-
if (!use) continue;
|
|
25704
|
-
await this.pipelines.toolCall.run({
|
|
25705
|
-
toolUse: use,
|
|
25706
|
-
result,
|
|
25707
|
-
ctx: this.ctx,
|
|
25708
|
-
tool: tool ?? void 0
|
|
25709
|
-
});
|
|
25710
|
-
await this.ctx.session.append({
|
|
25711
|
-
type: "tool_result",
|
|
25712
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25713
|
-
id: result.tool_use_id,
|
|
25714
|
-
content: result.content,
|
|
25715
|
-
isError: !!result.is_error
|
|
25716
|
-
});
|
|
25717
|
-
{
|
|
25718
|
-
const sig = sizeSignals(use.name, result.content);
|
|
25719
|
-
this.events.emit("tool.executed", {
|
|
25720
|
-
id: result.tool_use_id,
|
|
25721
|
-
name: use.name,
|
|
25722
|
-
durationMs,
|
|
25723
|
-
ok: !result.is_error,
|
|
25724
|
-
input: use.input,
|
|
25725
|
-
output: truncateForEvent(result.content),
|
|
25726
|
-
outputBytes: sig.outputBytes,
|
|
25727
|
-
outputTokens: sig.outputTokens,
|
|
25728
|
-
outputLines: sig.outputLines
|
|
25729
|
-
});
|
|
25751
|
+
register(tool) {
|
|
25752
|
+
this.tools.register(tool);
|
|
25753
|
+
}
|
|
25754
|
+
async use(plugin, api) {
|
|
25755
|
+
await plugin.setup(api);
|
|
25756
|
+
this.plugins.push({ plugin, api });
|
|
25757
|
+
}
|
|
25758
|
+
async teardown() {
|
|
25759
|
+
const errors = [];
|
|
25760
|
+
for (const { plugin, api } of this.plugins.toReversed()) {
|
|
25761
|
+
if (typeof plugin.teardown !== "function") continue;
|
|
25762
|
+
try {
|
|
25763
|
+
await plugin.teardown(api);
|
|
25764
|
+
} catch (err) {
|
|
25765
|
+
errors.push(err);
|
|
25730
25766
|
}
|
|
25731
25767
|
}
|
|
25732
|
-
this.
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
});
|
|
25736
|
-
await this.extensions.runAfterToolExecution(this.ctx, outputs);
|
|
25737
|
-
}
|
|
25738
|
-
/**
|
|
25739
|
-
* Fold any pending `/btw` notes into the conversation. Called at the top of
|
|
25740
|
-
* each iteration so a note set mid-run reaches the model on its next turn.
|
|
25741
|
-
*
|
|
25742
|
-
* To stay valid across all provider wire families we never create two
|
|
25743
|
-
* consecutive user messages: if the last message is a user turn (e.g. the
|
|
25744
|
-
* tool_result block from the previous iteration) we append the note as an
|
|
25745
|
-
* extra text block on that turn; otherwise we add a fresh user message.
|
|
25746
|
-
*/
|
|
25747
|
-
injectPendingBtwNotes() {
|
|
25748
|
-
const notes = consumeBtwNotes(this.ctx);
|
|
25749
|
-
if (notes.length === 0) return;
|
|
25750
|
-
const block = { type: "text", text: buildBtwBlock(notes) };
|
|
25751
|
-
const messages = this.ctx.messages;
|
|
25752
|
-
const last = messages[messages.length - 1];
|
|
25753
|
-
if (last && last.role === "user") {
|
|
25754
|
-
const content = typeof last.content === "string" ? [{ type: "text", text: last.content }, block] : [...last.content, block];
|
|
25755
|
-
this.ctx.state.replaceMessages([...messages.slice(0, -1), { ...last, content }]);
|
|
25756
|
-
} else {
|
|
25757
|
-
this.ctx.state.appendMessage({ role: "user", content: [block] });
|
|
25768
|
+
this.plugins.length = 0;
|
|
25769
|
+
if (errors.length > 0) {
|
|
25770
|
+
throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
|
|
25758
25771
|
}
|
|
25759
25772
|
}
|
|
25760
|
-
|
|
25761
|
-
|
|
25762
|
-
|
|
25763
|
-
|
|
25764
|
-
|
|
25765
|
-
|
|
25766
|
-
|
|
25767
|
-
|
|
25768
|
-
});
|
|
25773
|
+
async run(userInput, opts = {}) {
|
|
25774
|
+
const controller = new RunController({ parentSignal: opts.signal });
|
|
25775
|
+
const signal = controller.signal;
|
|
25776
|
+
this.ctx.signal = signal;
|
|
25777
|
+
controller.onAbort(() => this.ctx.drainAbortHooks());
|
|
25778
|
+
const span = this.tracer?.startSpan("agent.run", {
|
|
25779
|
+
"agent.model": opts.model ?? this.ctx.model,
|
|
25780
|
+
"agent.executionStrategy": opts.executionStrategy ?? this.executionStrategy
|
|
25769
25781
|
});
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25782
|
+
const { blocks, text } = normalizeInput(userInput);
|
|
25783
|
+
const inputPayload = { content: blocks, text, ctx: this.ctx };
|
|
25784
|
+
await this.extensions.runBeforeRun(this.ctx, inputPayload);
|
|
25773
25785
|
try {
|
|
25774
|
-
const
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25779
|
-
|
|
25780
|
-
return { result, durationMs: Date.now() - start };
|
|
25786
|
+
const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
|
|
25787
|
+
const result = await this._loopHandler.runInner(inputPayload, opts, controller, autonomousContinue);
|
|
25788
|
+
span?.setAttribute("agent.status", result.status);
|
|
25789
|
+
span?.setAttribute("agent.iterations", result.iterations);
|
|
25790
|
+
await this.extensions.runAfterRun(this.ctx, result);
|
|
25791
|
+
return result;
|
|
25781
25792
|
} catch (err) {
|
|
25782
|
-
const
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
durationMs: Date.now() - start
|
|
25793
|
+
const wse = err instanceof AgentError ? err : toWrongStackError(err);
|
|
25794
|
+
this.events.emit("error", { err: err instanceof Error ? err : new Error(String(err)), phase: "agent" });
|
|
25795
|
+
if (err instanceof Error) span?.recordError(err);
|
|
25796
|
+
span?.setAttribute("agent.status", "failed");
|
|
25797
|
+
const result = {
|
|
25798
|
+
status: signal.aborted ? "aborted" : "failed",
|
|
25799
|
+
iterations: 0,
|
|
25800
|
+
error: wse
|
|
25791
25801
|
};
|
|
25802
|
+
await this.extensions.runAfterRun(this.ctx, result);
|
|
25803
|
+
return result;
|
|
25804
|
+
} finally {
|
|
25805
|
+
span?.end();
|
|
25806
|
+
await controller.dispose();
|
|
25792
25807
|
}
|
|
25793
25808
|
}
|
|
25794
|
-
|
|
25795
|
-
* Run context window pipeline. The pipeline may be empty, or it may contain
|
|
25796
|
-
* middleware with its own injected dependencies.
|
|
25797
|
-
*/
|
|
25798
|
-
async compactContextIfNeeded() {
|
|
25799
|
-
await this.pipelines.contextWindow.run(this.ctx);
|
|
25800
|
-
}
|
|
25801
|
-
/**
|
|
25802
|
-
* Emit the current context window load as a `ctx.pct` event so subscribers
|
|
25803
|
-
* (FleetBus → TUI) can render a live fill bar per agent.
|
|
25804
|
-
*/
|
|
25805
|
-
emitContextPct() {
|
|
25806
|
-
const maxContext = this.ctx.provider.capabilities.maxContext ?? 2e5;
|
|
25807
|
-
const { total } = estimateRequestTokens(
|
|
25808
|
-
this.ctx.messages,
|
|
25809
|
-
this.ctx.systemPrompt,
|
|
25810
|
-
this.ctx.tools ?? []
|
|
25811
|
-
);
|
|
25812
|
-
this.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
|
|
25813
|
-
}
|
|
25809
|
+
// ── Tool + response execution handled by AgentToolHandler / AgentResponseHandler ──
|
|
25814
25810
|
};
|
|
25815
|
-
function toError(err) {
|
|
25816
|
-
return err instanceof Error ? err : new Error(String(err));
|
|
25817
|
-
}
|
|
25818
|
-
function truncateForEvent(content, max = 400) {
|
|
25819
|
-
if (!content) return "";
|
|
25820
|
-
return content.length <= max ? content : `${content.slice(0, max - 1)}\u2026`;
|
|
25821
|
-
}
|
|
25822
|
-
function sizeSignals(toolName, content) {
|
|
25823
|
-
if (typeof content !== "string" || content.length === 0) {
|
|
25824
|
-
return { outputBytes: 0, outputTokens: 0, outputLines: void 0 };
|
|
25825
|
-
}
|
|
25826
|
-
const outputBytes = Buffer.byteLength(content, "utf8");
|
|
25827
|
-
const outputTokens = Math.max(1, Math.round(outputBytes / 3.5));
|
|
25828
|
-
let outputLines;
|
|
25829
|
-
if (toolName === "read") {
|
|
25830
|
-
const lineRe = /^\s*\d+→/gm;
|
|
25831
|
-
let count = 0;
|
|
25832
|
-
while (lineRe.exec(content) !== null) count++;
|
|
25833
|
-
if (count > 0) outputLines = count;
|
|
25834
|
-
} else if (toolName === "bash" || toolName === "shell" || toolName === "grep" || toolName === "logs") {
|
|
25835
|
-
let nl = 0;
|
|
25836
|
-
for (let i = 0; i < content.length; i++) if (content.charCodeAt(i) === 10) nl++;
|
|
25837
|
-
outputLines = nl + (content.endsWith("\n") ? 0 : 1);
|
|
25838
|
-
}
|
|
25839
|
-
return { outputBytes, outputTokens, outputLines };
|
|
25840
|
-
}
|
|
25841
25811
|
|
|
25842
25812
|
// src/hooks/registry.ts
|
|
25843
25813
|
var HookRegistry = class {
|