@wrongstack/core 0.63.4 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-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 +265 -275
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +22 -22
- package/dist/defaults/index.js +181 -180
- 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 +124 -146
- 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 +692 -750
- 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-CuN0ObJr.d.ts} +24 -31
- 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 +123 -146
- 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;
|
|
@@ -12285,6 +12324,71 @@ Do not add prose, markdown, or code fences.`;
|
|
|
12285
12324
|
};
|
|
12286
12325
|
}
|
|
12287
12326
|
|
|
12327
|
+
// src/coordination/coordinator/error-classifier.ts
|
|
12328
|
+
function classifySubagentError(err, hints = {}) {
|
|
12329
|
+
const cause = err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : void 0;
|
|
12330
|
+
if (err instanceof ProviderError) {
|
|
12331
|
+
const baseMessage2 = err.describe();
|
|
12332
|
+
return providerErrorToSubagentError(err, baseMessage2, cause);
|
|
12333
|
+
}
|
|
12334
|
+
const baseMessage = err instanceof Error ? err.message : String(err);
|
|
12335
|
+
if (err instanceof BudgetExceededError) {
|
|
12336
|
+
const map = {
|
|
12337
|
+
iterations: "budget_iterations",
|
|
12338
|
+
tool_calls: "budget_tool_calls",
|
|
12339
|
+
tokens: "budget_tokens",
|
|
12340
|
+
cost: "budget_cost",
|
|
12341
|
+
timeout: "budget_timeout",
|
|
12342
|
+
idle_timeout: "budget_timeout"
|
|
12343
|
+
};
|
|
12344
|
+
return {
|
|
12345
|
+
kind: map[err.kind],
|
|
12346
|
+
message: baseMessage,
|
|
12347
|
+
retryable: false,
|
|
12348
|
+
cause
|
|
12349
|
+
};
|
|
12350
|
+
}
|
|
12351
|
+
if (hints.parentAborted) {
|
|
12352
|
+
return { kind: "aborted_by_parent", message: baseMessage, retryable: false, cause };
|
|
12353
|
+
}
|
|
12354
|
+
const lower = baseMessage.toLowerCase();
|
|
12355
|
+
if (/agent aborted$/i.test(baseMessage)) {
|
|
12356
|
+
return { kind: "aborted_by_parent", message: baseMessage, retryable: false, cause };
|
|
12357
|
+
}
|
|
12358
|
+
if (/agent exhausted iteration limit$/i.test(baseMessage)) {
|
|
12359
|
+
return { kind: "budget_iterations", message: baseMessage, retryable: false, cause };
|
|
12360
|
+
}
|
|
12361
|
+
if (/empty response$/i.test(baseMessage)) {
|
|
12362
|
+
return { kind: "empty_response", message: baseMessage, retryable: false, cause };
|
|
12363
|
+
}
|
|
12364
|
+
if (/^tool failed: /i.test(baseMessage)) {
|
|
12365
|
+
return { kind: "tool_failed", message: baseMessage, retryable: false, cause };
|
|
12366
|
+
}
|
|
12367
|
+
if (lower.includes("bridge transport") || /bridge.*(closed|disconnect)/i.test(baseMessage)) {
|
|
12368
|
+
return { kind: "bridge_failed", message: baseMessage, retryable: false, cause };
|
|
12369
|
+
}
|
|
12370
|
+
if (/context length|max.*tokens?.*exceeded|prompt is too long/i.test(baseMessage)) {
|
|
12371
|
+
return { kind: "context_overflow", message: baseMessage, retryable: false, cause };
|
|
12372
|
+
}
|
|
12373
|
+
return { kind: "unknown", message: baseMessage, retryable: false, cause };
|
|
12374
|
+
}
|
|
12375
|
+
function providerErrorToSubagentError(err, message, cause) {
|
|
12376
|
+
const status = err.status;
|
|
12377
|
+
if (status === 429 || err.body?.type === "rate_limit_error") {
|
|
12378
|
+
return { kind: "provider_rate_limit", message, retryable: true, backoffMs: 5e3, cause };
|
|
12379
|
+
}
|
|
12380
|
+
if (status === 401 || status === 403 || err.body?.type === "authentication_error") {
|
|
12381
|
+
return { kind: "provider_auth", message, retryable: false, cause };
|
|
12382
|
+
}
|
|
12383
|
+
if (status === 408 || status === 0) {
|
|
12384
|
+
return { kind: "provider_timeout", message, retryable: true, cause };
|
|
12385
|
+
}
|
|
12386
|
+
if (status >= 500 && status < 600) {
|
|
12387
|
+
return { kind: "provider_5xx", message, retryable: true, backoffMs: 3e3, cause };
|
|
12388
|
+
}
|
|
12389
|
+
return { kind: "unknown", message, retryable: err.retryable, cause };
|
|
12390
|
+
}
|
|
12391
|
+
|
|
12288
12392
|
// src/coordination/fleet.ts
|
|
12289
12393
|
var AUDIT_LOG_AGENT = {
|
|
12290
12394
|
id: "audit-log",
|
|
@@ -12649,7 +12753,10 @@ var NICKNAME_POOL = {
|
|
|
12649
12753
|
"lavoisier": { name: "Lavoisier", domain: "chemistry" },
|
|
12650
12754
|
"mendeleev": { name: "Mendeleev", domain: "chemistry" }
|
|
12651
12755
|
};
|
|
12652
|
-
var ALL_NICKNAMES = Object.
|
|
12756
|
+
var ALL_NICKNAMES = Object.entries(NICKNAME_POOL);
|
|
12757
|
+
var NAME_TO_KEY = Object.fromEntries(
|
|
12758
|
+
ALL_NICKNAMES.map(([key, entry]) => [entry.name, key])
|
|
12759
|
+
);
|
|
12653
12760
|
var DOMAIN_PREFERENCES = {
|
|
12654
12761
|
"security": ["shannon", "turing", "lamarr", "stallman"],
|
|
12655
12762
|
"bug-hunter": ["darwin", "curie", "feynman", "fermi"],
|
|
@@ -12682,17 +12789,23 @@ function assignNickname(role, used) {
|
|
|
12682
12789
|
for (const key of preferences) {
|
|
12683
12790
|
const entry = NICKNAME_POOL[key];
|
|
12684
12791
|
if (entry && !used.has(key)) {
|
|
12685
|
-
return `${entry.name} (${formatRole(role)})
|
|
12792
|
+
return { key, display: `${entry.name} (${formatRole(role)})` };
|
|
12686
12793
|
}
|
|
12687
12794
|
}
|
|
12688
|
-
for (const entry of ALL_NICKNAMES) {
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
return `${entry.name} (${formatRole(role)})`;
|
|
12795
|
+
for (const [key, entry] of ALL_NICKNAMES) {
|
|
12796
|
+
if (!used.has(key)) {
|
|
12797
|
+
return { key, display: `${entry.name} (${formatRole(role)})` };
|
|
12692
12798
|
}
|
|
12693
12799
|
}
|
|
12694
12800
|
const counter = used.size + 1;
|
|
12695
|
-
return `Scientist #${counter} (${formatRole(role)})
|
|
12801
|
+
return { key: `scientist-${counter}`, display: `Scientist #${counter} (${formatRole(role)})` };
|
|
12802
|
+
}
|
|
12803
|
+
function nicknameKeyFromDisplay(display) {
|
|
12804
|
+
const base = display.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
12805
|
+
const key = NAME_TO_KEY[base];
|
|
12806
|
+
if (key) return key;
|
|
12807
|
+
const synthesized = base.match(/^Scientist #(\d+)$/);
|
|
12808
|
+
return synthesized ? `scientist-${synthesized[1]}` : void 0;
|
|
12696
12809
|
}
|
|
12697
12810
|
function formatRole(role) {
|
|
12698
12811
|
return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
@@ -12779,11 +12892,10 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
12779
12892
|
const name = subagent.name?.trim() ?? "";
|
|
12780
12893
|
const isPlaceholder = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
12781
12894
|
if (!isPlaceholder) return subagent;
|
|
12782
|
-
const
|
|
12783
|
-
|
|
12784
|
-
this.
|
|
12785
|
-
|
|
12786
|
-
return { ...subagent, name: nickname };
|
|
12895
|
+
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
12896
|
+
this.usedNicknames.add(key);
|
|
12897
|
+
this.subagentNicknames.set(subagentId, key);
|
|
12898
|
+
return { ...subagent, name: display };
|
|
12787
12899
|
}
|
|
12788
12900
|
async spawn(subagent) {
|
|
12789
12901
|
const id = subagent.id || randomUUID();
|
|
@@ -13035,23 +13147,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13035
13147
|
*/
|
|
13036
13148
|
drainPendingAsAborted(message) {
|
|
13037
13149
|
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
|
-
|
|
13150
|
+
for (const t2 of dropped) this.emitPendingAborted(t2, message);
|
|
13151
|
+
}
|
|
13152
|
+
/**
|
|
13153
|
+
* Emit a synthetic `stopped`/`aborted_by_parent` completion for a single
|
|
13154
|
+
* PENDING task — one that was never counted in `inFlight`. This MUST bypass
|
|
13155
|
+
* `recordCompletion`: that path does `inFlight--`, which for a pending task
|
|
13156
|
+
* steals a decrement from a genuinely in-flight task and trips the underflow
|
|
13157
|
+
* guard — suppressing that real task's `task.completed` and hanging its
|
|
13158
|
+
* `awaitTasks()` caller. Pushes the result and fires the event directly.
|
|
13159
|
+
*/
|
|
13160
|
+
emitPendingAborted(task, message) {
|
|
13161
|
+
const synthetic = {
|
|
13162
|
+
subagentId: task.subagentId ?? "unassigned",
|
|
13163
|
+
taskId: task.id,
|
|
13164
|
+
status: "stopped",
|
|
13165
|
+
error: {
|
|
13166
|
+
kind: "aborted_by_parent",
|
|
13167
|
+
message,
|
|
13168
|
+
retryable: false
|
|
13169
|
+
},
|
|
13170
|
+
iterations: 0,
|
|
13171
|
+
toolCalls: 0,
|
|
13172
|
+
durationMs: 0
|
|
13173
|
+
};
|
|
13174
|
+
this.completedResults.push(synthetic);
|
|
13175
|
+
this.emit("task.completed", { task, result: synthetic });
|
|
13055
13176
|
}
|
|
13056
13177
|
async runDispatched(subagentId, task) {
|
|
13057
13178
|
const subagent = this.subagents.get(subagentId);
|
|
@@ -13312,20 +13433,10 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13312
13433
|
const orphaned = this.pendingTasks.filter((t2) => t2.subagentId === subagentId);
|
|
13313
13434
|
this.pendingTasks = this.pendingTasks.filter((t2) => t2.subagentId !== subagentId);
|
|
13314
13435
|
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);
|
|
13436
|
+
this.emitPendingAborted(
|
|
13437
|
+
t2,
|
|
13438
|
+
`Subagent "${subagentId}" was removed while task "${t2.id}" was pending`
|
|
13439
|
+
);
|
|
13329
13440
|
}
|
|
13330
13441
|
this.fleetBus?.emit({
|
|
13331
13442
|
subagentId,
|
|
@@ -13345,101 +13456,6 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
13345
13456
|
return false;
|
|
13346
13457
|
}
|
|
13347
13458
|
};
|
|
13348
|
-
function classifySubagentError(err, hints = {}) {
|
|
13349
|
-
const cause = err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : void 0;
|
|
13350
|
-
if (err instanceof ProviderError) {
|
|
13351
|
-
const baseMessage2 = err.describe();
|
|
13352
|
-
return providerErrorToSubagentError(err, baseMessage2, cause);
|
|
13353
|
-
}
|
|
13354
|
-
const baseMessage = err instanceof Error ? err.message : String(err);
|
|
13355
|
-
if (err instanceof BudgetExceededError) {
|
|
13356
|
-
const map = {
|
|
13357
|
-
iterations: "budget_iterations",
|
|
13358
|
-
tool_calls: "budget_tool_calls",
|
|
13359
|
-
tokens: "budget_tokens",
|
|
13360
|
-
cost: "budget_cost",
|
|
13361
|
-
timeout: "budget_timeout",
|
|
13362
|
-
idle_timeout: "budget_timeout"
|
|
13363
|
-
};
|
|
13364
|
-
return {
|
|
13365
|
-
kind: map[err.kind],
|
|
13366
|
-
message: baseMessage,
|
|
13367
|
-
// Budgets are user-configured ceilings, not transient failures —
|
|
13368
|
-
// retrying with the same budget will hit the same ceiling. The
|
|
13369
|
-
// orchestrator must raise the budget or narrow the task first.
|
|
13370
|
-
retryable: false,
|
|
13371
|
-
cause
|
|
13372
|
-
};
|
|
13373
|
-
}
|
|
13374
|
-
if (hints.parentAborted) {
|
|
13375
|
-
return {
|
|
13376
|
-
kind: "aborted_by_parent",
|
|
13377
|
-
message: baseMessage,
|
|
13378
|
-
retryable: false,
|
|
13379
|
-
cause
|
|
13380
|
-
};
|
|
13381
|
-
}
|
|
13382
|
-
const lower = baseMessage.toLowerCase();
|
|
13383
|
-
if (/agent aborted$/i.test(baseMessage)) {
|
|
13384
|
-
return {
|
|
13385
|
-
kind: "aborted_by_parent",
|
|
13386
|
-
message: baseMessage,
|
|
13387
|
-
retryable: false,
|
|
13388
|
-
cause
|
|
13389
|
-
};
|
|
13390
|
-
}
|
|
13391
|
-
if (/agent exhausted iteration limit$/i.test(baseMessage)) {
|
|
13392
|
-
return { kind: "budget_iterations", message: baseMessage, retryable: false, cause };
|
|
13393
|
-
}
|
|
13394
|
-
if (/empty response$/i.test(baseMessage)) {
|
|
13395
|
-
return { kind: "empty_response", message: baseMessage, retryable: false, cause };
|
|
13396
|
-
}
|
|
13397
|
-
if (/^tool failed: /i.test(baseMessage)) {
|
|
13398
|
-
return { kind: "tool_failed", message: baseMessage, retryable: false, cause };
|
|
13399
|
-
}
|
|
13400
|
-
if (lower.includes("bridge transport") || /bridge.*(closed|disconnect)/i.test(baseMessage)) {
|
|
13401
|
-
return { kind: "bridge_failed", message: baseMessage, retryable: false, cause };
|
|
13402
|
-
}
|
|
13403
|
-
if (/context length|max.*tokens?.*exceeded|prompt is too long/i.test(baseMessage)) {
|
|
13404
|
-
return { kind: "context_overflow", message: baseMessage, retryable: false, cause };
|
|
13405
|
-
}
|
|
13406
|
-
return {
|
|
13407
|
-
kind: "unknown",
|
|
13408
|
-
message: baseMessage,
|
|
13409
|
-
retryable: false,
|
|
13410
|
-
cause
|
|
13411
|
-
};
|
|
13412
|
-
}
|
|
13413
|
-
function providerErrorToSubagentError(err, message, cause) {
|
|
13414
|
-
const status = err.status;
|
|
13415
|
-
if (status === 429 || err.body?.type === "rate_limit_error") {
|
|
13416
|
-
return {
|
|
13417
|
-
kind: "provider_rate_limit",
|
|
13418
|
-
message,
|
|
13419
|
-
retryable: true,
|
|
13420
|
-
// Conservative default: 5s. Provider-specific code can override
|
|
13421
|
-
// by emitting an error whose body carries an explicit hint.
|
|
13422
|
-
backoffMs: 5e3,
|
|
13423
|
-
cause
|
|
13424
|
-
};
|
|
13425
|
-
}
|
|
13426
|
-
if (status === 401 || status === 403 || err.body?.type === "authentication_error") {
|
|
13427
|
-
return { kind: "provider_auth", message, retryable: false, cause };
|
|
13428
|
-
}
|
|
13429
|
-
if (status === 408 || status === 0) {
|
|
13430
|
-
return { kind: "provider_timeout", message, retryable: true, cause };
|
|
13431
|
-
}
|
|
13432
|
-
if (status >= 500 && status < 600) {
|
|
13433
|
-
return {
|
|
13434
|
-
kind: "provider_5xx",
|
|
13435
|
-
message,
|
|
13436
|
-
retryable: true,
|
|
13437
|
-
backoffMs: 3e3,
|
|
13438
|
-
cause
|
|
13439
|
-
};
|
|
13440
|
-
}
|
|
13441
|
-
return { kind: "unknown", message, retryable: err.retryable, cause };
|
|
13442
|
-
}
|
|
13443
13459
|
|
|
13444
13460
|
// src/execution/parallel-eternal-engine.ts
|
|
13445
13461
|
function sleep2(ms) {
|
|
@@ -13703,6 +13719,7 @@ ${personaLine}Task: ${task}
|
|
|
13703
13719
|
} catch {
|
|
13704
13720
|
results = coordinator.results().slice(-taskIds.length);
|
|
13705
13721
|
}
|
|
13722
|
+
await Promise.allSettled(subagentIds.map((id) => coordinator.remove(id)));
|
|
13706
13723
|
const allSuccessful = results.length > 0 && results.every((r) => r.status === "success");
|
|
13707
13724
|
const goalComplete = results.some(
|
|
13708
13725
|
(r) => r.status === "success" && typeof r.result === "string" && GOAL_COMPLETE_MARKER2.test(r.result)
|
|
@@ -14498,6 +14515,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14498
14515
|
return lines.join("\n");
|
|
14499
14516
|
}
|
|
14500
14517
|
cleanup() {
|
|
14518
|
+
if (this._timeoutTimer) {
|
|
14519
|
+
clearTimeout(this._timeoutTimer);
|
|
14520
|
+
this._timeoutTimer = void 0;
|
|
14521
|
+
}
|
|
14501
14522
|
for (const dispose of this.disposers) dispose();
|
|
14502
14523
|
this.disposers.length = 0;
|
|
14503
14524
|
}
|
|
@@ -15317,7 +15338,7 @@ function isValidMatrixKey(key) {
|
|
|
15317
15338
|
return matrixKeyKind(key) !== "unknown";
|
|
15318
15339
|
}
|
|
15319
15340
|
|
|
15320
|
-
// src/coordination/director.ts
|
|
15341
|
+
// src/coordination/director/director-errors.ts
|
|
15321
15342
|
var FleetSpawnBudgetError = class extends Error {
|
|
15322
15343
|
kind;
|
|
15323
15344
|
limit;
|
|
@@ -15359,6 +15380,8 @@ var FleetContextOverflowError = class extends Error {
|
|
|
15359
15380
|
this.observed = observed;
|
|
15360
15381
|
}
|
|
15361
15382
|
};
|
|
15383
|
+
|
|
15384
|
+
// src/coordination/director.ts
|
|
15362
15385
|
var Director = class _Director {
|
|
15363
15386
|
/** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */
|
|
15364
15387
|
get coordinatorId() {
|
|
@@ -15627,18 +15650,20 @@ var Director = class _Director {
|
|
|
15627
15650
|
if (e.subagentId.startsWith("bug-hunter-") || e.subagentId.startsWith("refactor-planner-") || e.subagentId.startsWith("critic-")) {
|
|
15628
15651
|
return;
|
|
15629
15652
|
}
|
|
15630
|
-
if (payload.kind === "timeout") {
|
|
15653
|
+
if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
|
|
15654
|
+
const heartbeatKey = `${e.subagentId}:${payload.kind}`;
|
|
15631
15655
|
const progress = progressBySubagent.get(e.subagentId) ?? 0;
|
|
15632
|
-
const lastProgress = lastTimeoutProgress.get(
|
|
15656
|
+
const lastProgress = lastTimeoutProgress.get(heartbeatKey) ?? -1;
|
|
15633
15657
|
if (progress <= lastProgress) {
|
|
15634
15658
|
payload.deny();
|
|
15635
15659
|
return;
|
|
15636
15660
|
}
|
|
15637
|
-
lastTimeoutProgress.set(
|
|
15661
|
+
lastTimeoutProgress.set(heartbeatKey, progress);
|
|
15662
|
+
const field = payload.kind === "timeout" ? "timeoutMs" : "idleTimeoutMs";
|
|
15638
15663
|
setImmediate(() => {
|
|
15639
15664
|
const newLimit = Math.min(Math.ceil(payload.limit * 2), 24 * 60 * 6e4);
|
|
15640
|
-
this.recordExtension(e.subagentId, e.taskId,
|
|
15641
|
-
payload.extend({
|
|
15665
|
+
this.recordExtension(e.subagentId, e.taskId, payload.kind, newLimit);
|
|
15666
|
+
payload.extend({ [field]: newLimit });
|
|
15642
15667
|
});
|
|
15643
15668
|
return;
|
|
15644
15669
|
}
|
|
@@ -15954,10 +15979,9 @@ var Director = class _Director {
|
|
|
15954
15979
|
if (this.fleetManager) {
|
|
15955
15980
|
this.fleetManager.assignNicknameAndRecord(config);
|
|
15956
15981
|
} else {
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
);
|
|
15982
|
+
const { key, display } = assignNickname(role, this._usedNicknames);
|
|
15983
|
+
config.name = display;
|
|
15984
|
+
this._usedNicknames.add(key);
|
|
15961
15985
|
}
|
|
15962
15986
|
}
|
|
15963
15987
|
result = await this.coordinator.spawn(config);
|
|
@@ -16279,8 +16303,8 @@ var Director = class _Director {
|
|
|
16279
16303
|
} else {
|
|
16280
16304
|
const entry = this.manifestEntries.get(subagentId);
|
|
16281
16305
|
if (entry?.name) {
|
|
16282
|
-
const nicknameKey = entry.name
|
|
16283
|
-
this._usedNicknames.delete(nicknameKey);
|
|
16306
|
+
const nicknameKey = nicknameKeyFromDisplay(entry.name);
|
|
16307
|
+
if (nicknameKey) this._usedNicknames.delete(nicknameKey);
|
|
16284
16308
|
}
|
|
16285
16309
|
}
|
|
16286
16310
|
this.manifestEntries.delete(subagentId);
|
|
@@ -24414,11 +24438,10 @@ var FleetManager = class {
|
|
|
24414
24438
|
*/
|
|
24415
24439
|
assignNicknameAndRecord(config) {
|
|
24416
24440
|
const role = config.role ?? "subagent";
|
|
24417
|
-
const
|
|
24418
|
-
|
|
24419
|
-
|
|
24420
|
-
|
|
24421
|
-
return nickname;
|
|
24441
|
+
const { key, display } = assignNickname(role, this._usedNicknames);
|
|
24442
|
+
this._usedNicknames.add(key);
|
|
24443
|
+
config.name = display;
|
|
24444
|
+
return display;
|
|
24422
24445
|
}
|
|
24423
24446
|
/**
|
|
24424
24447
|
* Returns the set of already-assigned nickname keys — useful for debugging
|
|
@@ -24603,8 +24626,8 @@ var FleetManager = class {
|
|
|
24603
24626
|
removeSubagent(subagentId) {
|
|
24604
24627
|
const entry = this.manifestEntries.get(subagentId);
|
|
24605
24628
|
if (entry?.name) {
|
|
24606
|
-
const nicknameKey = entry.name
|
|
24607
|
-
this._usedNicknames.delete(nicknameKey);
|
|
24629
|
+
const nicknameKey = nicknameKeyFromDisplay(entry.name);
|
|
24630
|
+
if (nicknameKey) this._usedNicknames.delete(nicknameKey);
|
|
24608
24631
|
}
|
|
24609
24632
|
for (const [taskId, task] of this.pendingTasks) {
|
|
24610
24633
|
if (task.subagentId === subagentId) {
|
|
@@ -25051,61 +25074,170 @@ var ExtensionRegistry = class {
|
|
|
25051
25074
|
}
|
|
25052
25075
|
};
|
|
25053
25076
|
|
|
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
|
-
|
|
25085
|
-
|
|
25086
|
-
|
|
25087
|
-
|
|
25088
|
-
|
|
25089
|
-
|
|
25090
|
-
|
|
25091
|
-
|
|
25092
|
-
|
|
25093
|
-
|
|
25094
|
-
|
|
25095
|
-
|
|
25096
|
-
|
|
25097
|
-
|
|
25098
|
-
|
|
25099
|
-
|
|
25077
|
+
// src/core/agent-tools.ts
|
|
25078
|
+
function createAgentToolHandler(a) {
|
|
25079
|
+
async function executeSingleWithDecision(tool, use) {
|
|
25080
|
+
const start = Date.now();
|
|
25081
|
+
try {
|
|
25082
|
+
const result = await a.toolExecutor.executeTool(
|
|
25083
|
+
tool,
|
|
25084
|
+
use,
|
|
25085
|
+
a.ctx,
|
|
25086
|
+
a.perIterationOutputCapBytes
|
|
25087
|
+
);
|
|
25088
|
+
return { result, durationMs: Date.now() - start };
|
|
25089
|
+
} catch (err) {
|
|
25090
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25091
|
+
return {
|
|
25092
|
+
result: {
|
|
25093
|
+
type: "tool_result",
|
|
25094
|
+
tool_use_id: use.id,
|
|
25095
|
+
content: `Tool "${tool.name}" threw: ${msg}`,
|
|
25096
|
+
is_error: true
|
|
25097
|
+
},
|
|
25098
|
+
durationMs: Date.now() - start
|
|
25099
|
+
};
|
|
25100
|
+
}
|
|
25101
|
+
}
|
|
25102
|
+
function waitForConfirm(info) {
|
|
25103
|
+
return new Promise((resolve13) => {
|
|
25104
|
+
a.events.emit("tool.confirm_needed", {
|
|
25105
|
+
tool: info.tool,
|
|
25106
|
+
input: info.input,
|
|
25107
|
+
toolUseId: info.toolUseId,
|
|
25108
|
+
suggestedPattern: info.suggestedPattern,
|
|
25109
|
+
resolve: resolve13
|
|
25110
|
+
});
|
|
25111
|
+
});
|
|
25112
|
+
}
|
|
25113
|
+
function emitToolExecuted(toolUseId, toolName, durationMs, ok, input, content) {
|
|
25114
|
+
const sig = sizeSignals(toolName, content);
|
|
25115
|
+
a.events.emit("tool.executed", {
|
|
25116
|
+
id: toolUseId,
|
|
25117
|
+
name: toolName,
|
|
25118
|
+
durationMs,
|
|
25119
|
+
ok,
|
|
25120
|
+
input,
|
|
25121
|
+
output: truncateForEvent(content),
|
|
25122
|
+
outputBytes: sig.outputBytes,
|
|
25123
|
+
outputTokens: sig.outputTokens,
|
|
25124
|
+
outputLines: sig.outputLines
|
|
25125
|
+
});
|
|
25126
|
+
}
|
|
25127
|
+
async function executeTools(toolUses) {
|
|
25128
|
+
const selectedToolUses = await a.extensions.runBeforeToolExecution(a.ctx, toolUses);
|
|
25129
|
+
const { outputs } = await a.toolExecutor.executeBatch(
|
|
25130
|
+
selectedToolUses,
|
|
25131
|
+
a.ctx,
|
|
25132
|
+
a.executionStrategy
|
|
25133
|
+
);
|
|
25134
|
+
const useById = new Map(selectedToolUses.map((u) => [u.id, u]));
|
|
25135
|
+
const resultsForMessage = [];
|
|
25136
|
+
for (const { result, tool, durationMs } of outputs) {
|
|
25137
|
+
if (result.type === "tool_confirm_pending") {
|
|
25138
|
+
const decision = await waitForConfirm({
|
|
25139
|
+
tool,
|
|
25140
|
+
input: result.input,
|
|
25141
|
+
toolUseId: result.toolUseId,
|
|
25142
|
+
suggestedPattern: result.suggestedPattern
|
|
25143
|
+
});
|
|
25144
|
+
if (decision === "always") {
|
|
25145
|
+
try {
|
|
25146
|
+
await a.permission.trust({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25147
|
+
a.events.emit("trust.persisted", { tool: tool.name, pattern: result.suggestedPattern, decision });
|
|
25148
|
+
} catch {
|
|
25149
|
+
}
|
|
25150
|
+
} else if (decision === "deny") {
|
|
25151
|
+
try {
|
|
25152
|
+
await a.permission.deny({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25153
|
+
a.events.emit("trust.persisted", { tool: tool.name, pattern: result.suggestedPattern, decision });
|
|
25154
|
+
} catch {
|
|
25155
|
+
}
|
|
25156
|
+
}
|
|
25157
|
+
if (decision === "yes") {
|
|
25158
|
+
const p = a.permission;
|
|
25159
|
+
p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25160
|
+
} else if (decision === "no") {
|
|
25161
|
+
const p = a.permission;
|
|
25162
|
+
p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
|
|
25163
|
+
}
|
|
25164
|
+
const reRunResult = decision === "yes" || decision === "always" ? await executeSingleWithDecision(tool, {
|
|
25165
|
+
id: result.toolUseId,
|
|
25166
|
+
name: tool.name,
|
|
25167
|
+
input: result.input
|
|
25168
|
+
}) : {
|
|
25169
|
+
result: {
|
|
25170
|
+
type: "tool_result",
|
|
25171
|
+
tool_use_id: result.toolUseId,
|
|
25172
|
+
content: decision === "deny" ? `Tool "${tool.name}" denied and blocked for this pattern.` : `Tool "${tool.name}" denied by user.`,
|
|
25173
|
+
is_error: true
|
|
25174
|
+
},
|
|
25175
|
+
durationMs: 0
|
|
25176
|
+
};
|
|
25177
|
+
const use2 = useById.get(reRunResult.result.tool_use_id);
|
|
25178
|
+
if (use2) {
|
|
25179
|
+
await a.pipelines.toolCall.run({ toolUse: use2, result: reRunResult.result, ctx: a.ctx, tool });
|
|
25180
|
+
await a.ctx.session.append({
|
|
25181
|
+
type: "tool_result",
|
|
25182
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25183
|
+
id: reRunResult.result.tool_use_id,
|
|
25184
|
+
content: reRunResult.result.content,
|
|
25185
|
+
isError: !!reRunResult.result.is_error
|
|
25186
|
+
});
|
|
25187
|
+
emitToolExecuted(
|
|
25188
|
+
reRunResult.result.tool_use_id,
|
|
25189
|
+
tool.name,
|
|
25190
|
+
reRunResult.durationMs,
|
|
25191
|
+
!reRunResult.result.is_error,
|
|
25192
|
+
result.input,
|
|
25193
|
+
reRunResult.result.content
|
|
25194
|
+
);
|
|
25195
|
+
}
|
|
25196
|
+
resultsForMessage.push(reRunResult.result);
|
|
25197
|
+
continue;
|
|
25198
|
+
}
|
|
25199
|
+
resultsForMessage.push(result);
|
|
25200
|
+
const use = useById.get(result.tool_use_id);
|
|
25201
|
+
if (!use) continue;
|
|
25202
|
+
await a.pipelines.toolCall.run({ toolUse: use, result, ctx: a.ctx, tool: tool ?? void 0 });
|
|
25203
|
+
await a.ctx.session.append({
|
|
25204
|
+
type: "tool_result",
|
|
25205
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25206
|
+
id: result.tool_use_id,
|
|
25207
|
+
content: result.content,
|
|
25208
|
+
isError: !!result.is_error
|
|
25209
|
+
});
|
|
25210
|
+
emitToolExecuted(result.tool_use_id, use.name, durationMs, !result.is_error, use.input, result.content);
|
|
25211
|
+
}
|
|
25212
|
+
a.ctx.state.appendMessage({ role: "user", content: resultsForMessage });
|
|
25213
|
+
await a.extensions.runAfterToolExecution(a.ctx, outputs);
|
|
25214
|
+
return resultsForMessage;
|
|
25215
|
+
}
|
|
25216
|
+
return { executeTools, executeSingleWithDecision };
|
|
25217
|
+
}
|
|
25218
|
+
|
|
25219
|
+
// src/core/continue-to-next-iteration.ts
|
|
25220
|
+
function parseContinueDirective(text) {
|
|
25221
|
+
const LINE_MARKERS = /^\s*\[(continue|next step|proceed|done)\]\s*$/gim;
|
|
25222
|
+
let match;
|
|
25223
|
+
let lastDirective = "none";
|
|
25224
|
+
while ((match = LINE_MARKERS.exec(text)) !== null) {
|
|
25225
|
+
const value = (match[1] ?? "").toLowerCase();
|
|
25226
|
+
if (value === "continue" || value === "next step" || value === "proceed") {
|
|
25227
|
+
lastDirective = "continue";
|
|
25228
|
+
} else if (value === "done") {
|
|
25229
|
+
lastDirective = "stop";
|
|
25230
|
+
}
|
|
25231
|
+
}
|
|
25100
25232
|
return lastDirective;
|
|
25101
25233
|
}
|
|
25102
|
-
var
|
|
25234
|
+
var META_KEY = "_autonomousContinue";
|
|
25103
25235
|
function setAutonomousContinue(ctx) {
|
|
25104
|
-
ctx.meta[
|
|
25236
|
+
ctx.meta[META_KEY] = true;
|
|
25105
25237
|
}
|
|
25106
25238
|
function consumeAutonomousContinue(ctx) {
|
|
25107
|
-
const val = ctx.meta[
|
|
25108
|
-
delete ctx.meta[
|
|
25239
|
+
const val = ctx.meta[META_KEY] === true;
|
|
25240
|
+
delete ctx.meta[META_KEY];
|
|
25109
25241
|
return val;
|
|
25110
25242
|
}
|
|
25111
25243
|
function makeContinueToNextIterationTool() {
|
|
@@ -25128,6 +25260,104 @@ function makeContinueToNextIterationTool() {
|
|
|
25128
25260
|
};
|
|
25129
25261
|
}
|
|
25130
25262
|
|
|
25263
|
+
// src/core/agent-response.ts
|
|
25264
|
+
function createAgentResponseHandler(a) {
|
|
25265
|
+
async function buildAndRunRequestPipeline(opts) {
|
|
25266
|
+
const repaired = repairToolUseAdjacency(a.ctx.messages);
|
|
25267
|
+
if (repaired.report.changed) {
|
|
25268
|
+
a.ctx.state.replaceMessages(repaired.messages);
|
|
25269
|
+
a.events.emit("context.repaired", {
|
|
25270
|
+
ctx: a.ctx,
|
|
25271
|
+
...repaired.report
|
|
25272
|
+
});
|
|
25273
|
+
a.logger.warn(
|
|
25274
|
+
`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)`
|
|
25275
|
+
);
|
|
25276
|
+
}
|
|
25277
|
+
const baseReq = {
|
|
25278
|
+
model: opts.model ?? a.ctx.model,
|
|
25279
|
+
system: a.ctx.systemPrompt,
|
|
25280
|
+
messages: a.ctx.messages,
|
|
25281
|
+
tools: a.tools.list(),
|
|
25282
|
+
maxTokens: 8192
|
|
25283
|
+
};
|
|
25284
|
+
return a.pipelines.request.run(baseReq);
|
|
25285
|
+
}
|
|
25286
|
+
async function processResponse(raw, req) {
|
|
25287
|
+
let res = raw;
|
|
25288
|
+
res = await a.pipelines.response.run(res);
|
|
25289
|
+
a.events.emit("provider.response", {
|
|
25290
|
+
ctx: a.ctx,
|
|
25291
|
+
usage: res.usage,
|
|
25292
|
+
stopReason: res.stopReason
|
|
25293
|
+
});
|
|
25294
|
+
a.ctx.tokenCounter.account(res.usage, req.model);
|
|
25295
|
+
a.ctx.state.appendMessage({ role: "assistant", content: res.content });
|
|
25296
|
+
await a.ctx.session.append({
|
|
25297
|
+
type: "llm_response",
|
|
25298
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25299
|
+
content: res.content,
|
|
25300
|
+
stopReason: res.stopReason,
|
|
25301
|
+
usage: res.usage
|
|
25302
|
+
});
|
|
25303
|
+
if (a.ctx.signal.aborted) {
|
|
25304
|
+
let finalText2 = "";
|
|
25305
|
+
for (const block of res.content) {
|
|
25306
|
+
if (isTextBlock(block)) finalText2 += block.text;
|
|
25307
|
+
}
|
|
25308
|
+
return { finalText: finalText2, aborted: true, done: false };
|
|
25309
|
+
}
|
|
25310
|
+
let finalText = "";
|
|
25311
|
+
const streamed = a.ctx.provider.capabilities.streaming;
|
|
25312
|
+
for (const block of res.content) {
|
|
25313
|
+
if (isTextBlock(block)) {
|
|
25314
|
+
const rendered = await a.pipelines.assistantOutput.run(block);
|
|
25315
|
+
finalText += rendered.text;
|
|
25316
|
+
if (!streamed) a.renderer?.write(rendered);
|
|
25317
|
+
}
|
|
25318
|
+
}
|
|
25319
|
+
let directive = "none";
|
|
25320
|
+
if (finalText) {
|
|
25321
|
+
directive = parseContinueDirective(finalText);
|
|
25322
|
+
}
|
|
25323
|
+
return { finalText, aborted: false, done: false, directive };
|
|
25324
|
+
}
|
|
25325
|
+
return { buildAndRunRequestPipeline, processResponse };
|
|
25326
|
+
}
|
|
25327
|
+
|
|
25328
|
+
// src/core/btw.ts
|
|
25329
|
+
var META_KEY2 = "_btwNotes";
|
|
25330
|
+
function readQueue(ctx) {
|
|
25331
|
+
const raw = ctx.meta[META_KEY2];
|
|
25332
|
+
return Array.isArray(raw) ? raw : [];
|
|
25333
|
+
}
|
|
25334
|
+
function setBtwNote(ctx, text) {
|
|
25335
|
+
const trimmed = text.trim();
|
|
25336
|
+
if (!trimmed) return readQueue(ctx).length;
|
|
25337
|
+
const next = [...readQueue(ctx), trimmed].slice(-20);
|
|
25338
|
+
ctx.meta[META_KEY2] = next;
|
|
25339
|
+
return next.length;
|
|
25340
|
+
}
|
|
25341
|
+
function pendingBtwCount(ctx) {
|
|
25342
|
+
return readQueue(ctx).length;
|
|
25343
|
+
}
|
|
25344
|
+
function consumeBtwNotes(ctx) {
|
|
25345
|
+
const notes = readQueue(ctx);
|
|
25346
|
+
if (notes.length > 0) delete ctx.meta[META_KEY2];
|
|
25347
|
+
return notes;
|
|
25348
|
+
}
|
|
25349
|
+
function buildBtwBlock(notes) {
|
|
25350
|
+
const body = notes.map((n) => `- ${n}`).join("\n");
|
|
25351
|
+
return [
|
|
25352
|
+
"[BY THE WAY \u2014 the user added this while you were working. Fold it into",
|
|
25353
|
+
"your current task; do not restart from scratch unless it contradicts the",
|
|
25354
|
+
"goal:",
|
|
25355
|
+
"",
|
|
25356
|
+
body,
|
|
25357
|
+
"]"
|
|
25358
|
+
].join("\n");
|
|
25359
|
+
}
|
|
25360
|
+
|
|
25131
25361
|
// src/core/iteration-limit.ts
|
|
25132
25362
|
function requestLimitExtension(opts) {
|
|
25133
25363
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
@@ -25173,209 +25403,106 @@ function requestLimitExtension(opts) {
|
|
|
25173
25403
|
});
|
|
25174
25404
|
}
|
|
25175
25405
|
|
|
25176
|
-
// src/core/agent.ts
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
if (typeof input === "string") {
|
|
25180
|
-
return { blocks: [{ type: "text", text: input }], text: input };
|
|
25181
|
-
}
|
|
25182
|
-
const text = input.filter(isTextBlock).map((b) => b.text).join("");
|
|
25183
|
-
return { blocks: input, text };
|
|
25184
|
-
}
|
|
25185
|
-
function createDefaultPipelines() {
|
|
25186
|
-
return {
|
|
25187
|
-
request: new Pipeline(),
|
|
25188
|
-
response: new Pipeline(),
|
|
25189
|
-
toolCall: new Pipeline(),
|
|
25190
|
-
userInput: new Pipeline(),
|
|
25191
|
-
assistantOutput: new Pipeline(),
|
|
25192
|
-
contextWindow: new Pipeline()
|
|
25193
|
-
};
|
|
25406
|
+
// src/core/agent-loop.ts
|
|
25407
|
+
function toError(err) {
|
|
25408
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
25194
25409
|
}
|
|
25195
|
-
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
providers;
|
|
25199
|
-
events;
|
|
25200
|
-
pipelines;
|
|
25201
|
-
ctx;
|
|
25202
|
-
maxIterations;
|
|
25203
|
-
executionStrategy;
|
|
25204
|
-
perIterationOutputCapBytes;
|
|
25205
|
-
plugins = [];
|
|
25206
|
-
toolExecutor;
|
|
25207
|
-
autoExtendLimit;
|
|
25208
|
-
/** Enables autonomous continue: model can signal `[continue]` or call continue_to_next_iteration() to re-run. */
|
|
25209
|
-
autonomousContinue;
|
|
25210
|
-
tracer;
|
|
25211
|
-
extensions;
|
|
25212
|
-
constructor(init) {
|
|
25213
|
-
this.container = init.container;
|
|
25214
|
-
this.tools = init.tools;
|
|
25215
|
-
this.providers = init.providers;
|
|
25216
|
-
this.events = init.events;
|
|
25217
|
-
this.pipelines = init.pipelines;
|
|
25218
|
-
this.ctx = init.context;
|
|
25219
|
-
this.maxIterations = init.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
25220
|
-
this.executionStrategy = init.executionStrategy ?? "smart";
|
|
25221
|
-
this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
|
|
25222
|
-
this.autoExtendLimit = init.autoExtendLimit ?? true;
|
|
25223
|
-
this.autonomousContinue = init.autonomousContinue ?? false;
|
|
25224
|
-
this.tracer = init.tracer;
|
|
25225
|
-
this.extensions = init.extensions ?? new ExtensionRegistry();
|
|
25226
|
-
this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
|
|
25227
|
-
this.toolExecutor = init.toolExecutor;
|
|
25410
|
+
function createAgentLoopHandler(a, handlers) {
|
|
25411
|
+
async function compactContextIfNeeded() {
|
|
25412
|
+
await a.pipelines.contextWindow.run(a.ctx);
|
|
25228
25413
|
}
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
return this.container.resolve(TOKENS.RetryPolicy);
|
|
25234
|
-
}
|
|
25235
|
-
get errorHandler() {
|
|
25236
|
-
return this.container.resolve(TOKENS.ErrorHandler);
|
|
25414
|
+
function emitContextPct() {
|
|
25415
|
+
const maxContext = a.ctx.provider.capabilities.maxContext ?? 2e5;
|
|
25416
|
+
const { total } = estimateRequestTokens(a.ctx.messages, a.ctx.systemPrompt, a.ctx.tools ?? []);
|
|
25417
|
+
a.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
|
|
25237
25418
|
}
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
*
|
|
25250
|
-
* Call this before entering TUI or any mode where stdin is owned
|
|
25251
|
-
* by a UI framework (Ink, WebUI WS bridge). Without this, the
|
|
25252
|
-
* inline prompt writes to stdout and blocks on stdin — both of
|
|
25253
|
-
* which are owned by the framework — making the prompt invisible
|
|
25254
|
-
* and the input deadlocked.
|
|
25255
|
-
*/
|
|
25256
|
-
disableInteractiveConfirmation() {
|
|
25257
|
-
this.toolExecutor.clearConfirmAwaiter();
|
|
25258
|
-
const policy = this.permission;
|
|
25259
|
-
if (typeof policy.setPromptDelegate === "function") {
|
|
25260
|
-
policy.setPromptDelegate(void 0);
|
|
25419
|
+
function injectPendingBtwNotes() {
|
|
25420
|
+
const notes = consumeBtwNotes(a.ctx);
|
|
25421
|
+
if (notes.length === 0) return;
|
|
25422
|
+
const block = { type: "text", text: buildBtwBlock(notes) };
|
|
25423
|
+
const messages = a.ctx.messages;
|
|
25424
|
+
const last = messages[messages.length - 1];
|
|
25425
|
+
if (last && last.role === "user") {
|
|
25426
|
+
const content = typeof last.content === "string" ? [{ type: "text", text: last.content }, block] : [...last.content, block];
|
|
25427
|
+
a.ctx.state.replaceMessages([...messages.slice(0, -1), { ...last, content }]);
|
|
25428
|
+
} else {
|
|
25429
|
+
a.ctx.state.appendMessage({ role: "user", content: [block] });
|
|
25261
25430
|
}
|
|
25262
25431
|
}
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
try {
|
|
25276
|
-
await plugin.teardown(api);
|
|
25277
|
-
} catch (err) {
|
|
25278
|
-
errors.push(err);
|
|
25432
|
+
async function checkIterationLimit(iterationIndex, limit, hasHardLimit, currentIterations, delegateSummaries) {
|
|
25433
|
+
if (hasHardLimit && iterationIndex >= limit) {
|
|
25434
|
+
const extendBy = await requestLimitExtension({
|
|
25435
|
+
events: a.events,
|
|
25436
|
+
currentIterations,
|
|
25437
|
+
currentLimit: limit,
|
|
25438
|
+
autoExtend: a.autoExtendLimit
|
|
25439
|
+
});
|
|
25440
|
+
if (extendBy > 0) {
|
|
25441
|
+
const newLimit = limit + extendBy;
|
|
25442
|
+
a.logger.info(`Iteration limit extended by ${extendBy} (new limit: ${newLimit})`);
|
|
25443
|
+
return { limit: newLimit };
|
|
25279
25444
|
}
|
|
25445
|
+
return { limit, exit: { status: "max_iterations", iterations: currentIterations, delegateSummaries } };
|
|
25280
25446
|
}
|
|
25281
|
-
|
|
25282
|
-
if (errors.length > 0) {
|
|
25283
|
-
throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
|
|
25284
|
-
}
|
|
25285
|
-
}
|
|
25286
|
-
async run(userInput, opts = {}) {
|
|
25287
|
-
const controller = new RunController({ parentSignal: opts.signal });
|
|
25288
|
-
const signal = controller.signal;
|
|
25289
|
-
this.ctx.signal = signal;
|
|
25290
|
-
controller.onAbort(() => this.ctx.drainAbortHooks());
|
|
25291
|
-
const span = this.tracer?.startSpan("agent.run", {
|
|
25292
|
-
"agent.model": opts.model ?? this.ctx.model,
|
|
25293
|
-
"agent.executionStrategy": opts.executionStrategy ?? this.executionStrategy
|
|
25294
|
-
});
|
|
25295
|
-
const { blocks, text } = normalizeInput(userInput);
|
|
25296
|
-
const inputPayload = { content: blocks, text, ctx: this.ctx };
|
|
25297
|
-
await this.extensions.runBeforeRun(this.ctx, inputPayload);
|
|
25298
|
-
try {
|
|
25299
|
-
const result = await this.runInner(inputPayload, opts, controller);
|
|
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();
|
|
25319
|
-
}
|
|
25447
|
+
return { limit };
|
|
25320
25448
|
}
|
|
25321
|
-
async runInner(inputPayload, opts, controller) {
|
|
25322
|
-
await
|
|
25323
|
-
|
|
25324
|
-
await
|
|
25449
|
+
async function runInner(inputPayload, opts, controller, autonomousContinue) {
|
|
25450
|
+
await a.pipelines.userInput.run(inputPayload);
|
|
25451
|
+
a.ctx.state.appendMessage({ role: "user", content: inputPayload.content });
|
|
25452
|
+
await a.ctx.session.append({
|
|
25325
25453
|
type: "user_input",
|
|
25326
25454
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25327
25455
|
content: inputPayload.content
|
|
25328
25456
|
});
|
|
25329
|
-
const promptIndex =
|
|
25457
|
+
const promptIndex = a.ctx.messages.filter((m) => m.role === "user").length - 1;
|
|
25330
25458
|
const preview = inputPayload.text.slice(0, 80) + (inputPayload.text.length > 80 ? "\u2026" : "");
|
|
25331
|
-
await
|
|
25459
|
+
await a.ctx.session.writeCheckpoint(promptIndex, preview);
|
|
25332
25460
|
let finalText = "";
|
|
25333
25461
|
let iterations = 0;
|
|
25334
25462
|
const delegateSummaries = [];
|
|
25335
|
-
let effectiveLimit = opts.maxIterations ??
|
|
25463
|
+
let effectiveLimit = opts.maxIterations ?? a.maxIterations;
|
|
25336
25464
|
const hasHardLimit = effectiveLimit > 0 && Number.isFinite(effectiveLimit);
|
|
25337
25465
|
let recoveryRetries = 0;
|
|
25338
|
-
const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
|
|
25339
25466
|
const onSubagentDone = ({ summary, ok }) => {
|
|
25340
25467
|
delegateSummaries.push({ summary, ok });
|
|
25341
25468
|
};
|
|
25342
|
-
const offSubagentDone =
|
|
25343
|
-
const diRunner =
|
|
25469
|
+
const offSubagentDone = a.events.on("subagent.done", onSubagentDone);
|
|
25470
|
+
const diRunner = a.container.has(TOKENS.ProviderRunner) ? a.container.resolve(TOKENS.ProviderRunner) : null;
|
|
25344
25471
|
const baseRunner = diRunner ? (ctx, req) => diRunner.run({
|
|
25345
25472
|
provider: ctx.provider,
|
|
25346
25473
|
request: req,
|
|
25347
25474
|
signal: controller.signal,
|
|
25348
25475
|
ctx,
|
|
25349
|
-
events:
|
|
25350
|
-
retry:
|
|
25351
|
-
logger:
|
|
25352
|
-
tracer:
|
|
25476
|
+
events: a.events,
|
|
25477
|
+
retry: a.retry,
|
|
25478
|
+
logger: a.logger,
|
|
25479
|
+
tracer: a.tracer
|
|
25353
25480
|
}) : async (ctx, req) => runProviderWithRetry({
|
|
25354
25481
|
provider: ctx.provider,
|
|
25355
25482
|
request: req,
|
|
25356
25483
|
signal: controller.signal,
|
|
25357
25484
|
ctx,
|
|
25358
|
-
events:
|
|
25359
|
-
retry:
|
|
25360
|
-
logger:
|
|
25361
|
-
tracer:
|
|
25485
|
+
events: a.events,
|
|
25486
|
+
retry: a.retry,
|
|
25487
|
+
logger: a.logger,
|
|
25488
|
+
tracer: a.tracer
|
|
25362
25489
|
});
|
|
25363
|
-
const customRunner =
|
|
25490
|
+
const customRunner = a.extensions.wrapProviderRunner(baseRunner);
|
|
25364
25491
|
try {
|
|
25365
25492
|
for (let i = 0; ; i++) {
|
|
25366
25493
|
iterations = i + 1;
|
|
25367
25494
|
if (controller.signal.aborted) {
|
|
25368
25495
|
return { status: "aborted", iterations };
|
|
25369
25496
|
}
|
|
25370
|
-
await
|
|
25371
|
-
|
|
25497
|
+
await a.ctx.session.writeInFlightMarker(`iteration ${i} / max ${a.maxIterations}`).catch((err) => {
|
|
25498
|
+
a.logger.debug?.(
|
|
25372
25499
|
`in-flight marker write failed: ${err instanceof Error ? err.message : String(err)}`
|
|
25373
25500
|
);
|
|
25374
25501
|
});
|
|
25375
25502
|
if (autonomousContinue) {
|
|
25376
|
-
consumeAutonomousContinue(
|
|
25503
|
+
consumeAutonomousContinue(a.ctx);
|
|
25377
25504
|
}
|
|
25378
|
-
const limitCheck = await
|
|
25505
|
+
const limitCheck = await checkIterationLimit(
|
|
25379
25506
|
i,
|
|
25380
25507
|
effectiveLimit,
|
|
25381
25508
|
hasHardLimit,
|
|
@@ -25386,11 +25513,11 @@ var Agent = class {
|
|
|
25386
25513
|
if (limitCheck.exit) {
|
|
25387
25514
|
return { ...limitCheck.exit, finalText };
|
|
25388
25515
|
}
|
|
25389
|
-
await
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
const req = await
|
|
25393
|
-
await
|
|
25516
|
+
await a.extensions.runBeforeIteration(a.ctx, i);
|
|
25517
|
+
a.events.emit("iteration.started", { ctx: a.ctx, index: i });
|
|
25518
|
+
injectPendingBtwNotes();
|
|
25519
|
+
const req = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
25520
|
+
await a.ctx.session.append({
|
|
25394
25521
|
type: "llm_request",
|
|
25395
25522
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25396
25523
|
model: req.model,
|
|
@@ -25401,39 +25528,39 @@ var Agent = class {
|
|
|
25401
25528
|
});
|
|
25402
25529
|
let res;
|
|
25403
25530
|
try {
|
|
25404
|
-
res = await customRunner(
|
|
25531
|
+
res = await customRunner(a.ctx, req);
|
|
25405
25532
|
const calibratedEstimate = estimateRequestTokensCalibrated(req.messages, req.system, req.tools ?? []).total;
|
|
25406
25533
|
recordActualUsage(res.usage.input, calibratedEstimate);
|
|
25407
25534
|
recoveryRetries = 0;
|
|
25408
25535
|
} catch (err) {
|
|
25409
25536
|
if (controller.signal.aborted) {
|
|
25410
|
-
|
|
25537
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25411
25538
|
return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED") };
|
|
25412
25539
|
}
|
|
25413
|
-
const extDecision = await
|
|
25540
|
+
const extDecision = await a.extensions.runOnError(a.ctx, err, "provider", i);
|
|
25414
25541
|
if (extDecision) {
|
|
25415
25542
|
if (extDecision.action === "fail") {
|
|
25416
|
-
|
|
25543
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25417
25544
|
return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
|
|
25418
25545
|
}
|
|
25419
25546
|
if (extDecision.action === "continue") {
|
|
25420
|
-
await
|
|
25547
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25421
25548
|
continue;
|
|
25422
25549
|
}
|
|
25423
25550
|
if (extDecision.action === "retry") {
|
|
25424
25551
|
recoveryRetries++;
|
|
25425
25552
|
if (recoveryRetries > 2) {
|
|
25426
|
-
|
|
25553
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25427
25554
|
return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
|
|
25428
25555
|
}
|
|
25429
|
-
if (extDecision.model)
|
|
25430
|
-
|
|
25556
|
+
if (extDecision.model) a.ctx.model = extDecision.model;
|
|
25557
|
+
a.logger.info("Extension requested retry; retrying turn");
|
|
25431
25558
|
continue;
|
|
25432
25559
|
}
|
|
25433
25560
|
}
|
|
25434
|
-
const recovered = await
|
|
25561
|
+
const recovered = await a.errorHandler.recover(err, a.ctx);
|
|
25435
25562
|
if (!recovered || recovered.action === "fail") {
|
|
25436
|
-
|
|
25563
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25437
25564
|
return {
|
|
25438
25565
|
status: "failed",
|
|
25439
25566
|
iterations,
|
|
@@ -25444,17 +25571,17 @@ var Agent = class {
|
|
|
25444
25571
|
if (recovered.action === "retry") {
|
|
25445
25572
|
recoveryRetries++;
|
|
25446
25573
|
if (recoveryRetries > 2) {
|
|
25447
|
-
|
|
25574
|
+
a.events.emit("error", { err: toError(err), phase: "provider" });
|
|
25448
25575
|
return { status: "failed", iterations, error: toWrongStackError(err) };
|
|
25449
25576
|
}
|
|
25450
|
-
if (recovered.model)
|
|
25451
|
-
|
|
25577
|
+
if (recovered.model) a.ctx.model = recovered.model;
|
|
25578
|
+
a.logger.info(`Recovered provider error via ${recovered.reason}; retrying turn`);
|
|
25452
25579
|
continue;
|
|
25453
25580
|
}
|
|
25454
25581
|
recoveryRetries = 0;
|
|
25455
25582
|
res = recovered.response;
|
|
25456
25583
|
}
|
|
25457
|
-
const responseResult = await
|
|
25584
|
+
const responseResult = await handlers.response.processResponse(res, req);
|
|
25458
25585
|
if (responseResult.aborted) {
|
|
25459
25586
|
return { status: "aborted", iterations, finalText: responseResult.finalText, delegateSummaries };
|
|
25460
25587
|
}
|
|
@@ -25464,11 +25591,11 @@ var Agent = class {
|
|
|
25464
25591
|
finalText = responseResult.finalText;
|
|
25465
25592
|
const toolUses = res.content.filter(isToolUseBlock);
|
|
25466
25593
|
if (toolUses.length === 0) {
|
|
25467
|
-
|
|
25468
|
-
|
|
25594
|
+
emitContextPct();
|
|
25595
|
+
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
25469
25596
|
if (autonomousContinue && responseResult.directive === "continue") {
|
|
25470
|
-
await
|
|
25471
|
-
await
|
|
25597
|
+
await compactContextIfNeeded();
|
|
25598
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25472
25599
|
continue;
|
|
25473
25600
|
}
|
|
25474
25601
|
if (autonomousContinue && responseResult.directive === "stop") {
|
|
@@ -25476,18 +25603,18 @@ var Agent = class {
|
|
|
25476
25603
|
}
|
|
25477
25604
|
return { status: "done", iterations, finalText, delegateSummaries };
|
|
25478
25605
|
}
|
|
25479
|
-
await
|
|
25480
|
-
if (autonomousContinue && consumeAutonomousContinue(
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
await
|
|
25484
|
-
await
|
|
25606
|
+
await handlers.tools.executeTools(toolUses);
|
|
25607
|
+
if (autonomousContinue && consumeAutonomousContinue(a.ctx)) {
|
|
25608
|
+
emitContextPct();
|
|
25609
|
+
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
25610
|
+
await compactContextIfNeeded();
|
|
25611
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25485
25612
|
continue;
|
|
25486
25613
|
}
|
|
25487
|
-
|
|
25488
|
-
|
|
25489
|
-
await
|
|
25490
|
-
await
|
|
25614
|
+
emitContextPct();
|
|
25615
|
+
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
25616
|
+
await compactContextIfNeeded();
|
|
25617
|
+
await a.extensions.runAfterIteration(a.ctx, i);
|
|
25491
25618
|
if (autonomousContinue && responseResult.directive === "continue") {
|
|
25492
25619
|
continue;
|
|
25493
25620
|
}
|
|
@@ -25498,346 +25625,161 @@ var Agent = class {
|
|
|
25498
25625
|
} finally {
|
|
25499
25626
|
offSubagentDone();
|
|
25500
25627
|
const reason = controller.signal.aborted ? "aborted" : "clean";
|
|
25501
|
-
await
|
|
25502
|
-
|
|
25628
|
+
await a.ctx.session.clearInFlightMarker(reason).catch((err) => {
|
|
25629
|
+
a.logger.debug?.(
|
|
25503
25630
|
`in-flight marker clear failed: ${err instanceof Error ? err.message : String(err)}`
|
|
25504
25631
|
);
|
|
25505
25632
|
});
|
|
25506
25633
|
}
|
|
25507
25634
|
}
|
|
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);
|
|
25635
|
+
return { runInner };
|
|
25636
|
+
}
|
|
25637
|
+
|
|
25638
|
+
// src/core/agent-types.ts
|
|
25639
|
+
var DEFAULT_MAX_ITERATIONS = 100;
|
|
25640
|
+
function normalizeInput(input) {
|
|
25641
|
+
if (typeof input === "string") {
|
|
25642
|
+
return { blocks: [{ type: "text", text: input }], text: input };
|
|
25554
25643
|
}
|
|
25555
|
-
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
|
|
25564
|
-
|
|
25565
|
-
|
|
25566
|
-
|
|
25567
|
-
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25572
|
-
|
|
25573
|
-
|
|
25574
|
-
|
|
25644
|
+
const text = input.filter(isTextBlock).map((b) => b.text).join("");
|
|
25645
|
+
return { blocks: input, text };
|
|
25646
|
+
}
|
|
25647
|
+
function createDefaultPipelines() {
|
|
25648
|
+
return {
|
|
25649
|
+
request: new Pipeline(),
|
|
25650
|
+
response: new Pipeline(),
|
|
25651
|
+
toolCall: new Pipeline(),
|
|
25652
|
+
userInput: new Pipeline(),
|
|
25653
|
+
assistantOutput: new Pipeline(),
|
|
25654
|
+
contextWindow: new Pipeline()
|
|
25655
|
+
};
|
|
25656
|
+
}
|
|
25657
|
+
|
|
25658
|
+
// src/core/agent.ts
|
|
25659
|
+
var Agent = class {
|
|
25660
|
+
container;
|
|
25661
|
+
tools;
|
|
25662
|
+
providers;
|
|
25663
|
+
events;
|
|
25664
|
+
pipelines;
|
|
25665
|
+
ctx;
|
|
25666
|
+
maxIterations;
|
|
25667
|
+
executionStrategy;
|
|
25668
|
+
perIterationOutputCapBytes;
|
|
25669
|
+
plugins = [];
|
|
25670
|
+
toolExecutor;
|
|
25671
|
+
autoExtendLimit;
|
|
25672
|
+
autonomousContinue;
|
|
25673
|
+
tracer;
|
|
25674
|
+
extensions;
|
|
25675
|
+
_toolHandler;
|
|
25676
|
+
_responseHandler;
|
|
25677
|
+
_loopHandler;
|
|
25678
|
+
constructor(init) {
|
|
25679
|
+
this.container = init.container;
|
|
25680
|
+
this.tools = init.tools;
|
|
25681
|
+
this.providers = init.providers;
|
|
25682
|
+
this.events = init.events;
|
|
25683
|
+
this.pipelines = init.pipelines;
|
|
25684
|
+
this.ctx = init.context;
|
|
25685
|
+
this.maxIterations = init.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
25686
|
+
this.executionStrategy = init.executionStrategy ?? "smart";
|
|
25687
|
+
this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
|
|
25688
|
+
this.autoExtendLimit = init.autoExtendLimit ?? true;
|
|
25689
|
+
this.autonomousContinue = init.autonomousContinue ?? false;
|
|
25690
|
+
this.tracer = init.tracer;
|
|
25691
|
+
this.extensions = init.extensions ?? new ExtensionRegistry();
|
|
25692
|
+
this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
|
|
25693
|
+
this.toolExecutor = init.toolExecutor;
|
|
25694
|
+
this._toolHandler = createAgentToolHandler(this);
|
|
25695
|
+
this._responseHandler = createAgentResponseHandler(this);
|
|
25696
|
+
this._loopHandler = createAgentLoopHandler(this, {
|
|
25697
|
+
tools: this._toolHandler,
|
|
25698
|
+
response: this._responseHandler
|
|
25575
25699
|
});
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
25584
|
-
|
|
25585
|
-
|
|
25586
|
-
|
|
25587
|
-
|
|
25588
|
-
|
|
25589
|
-
|
|
25590
|
-
|
|
25591
|
-
|
|
25592
|
-
|
|
25593
|
-
|
|
25594
|
-
|
|
25700
|
+
}
|
|
25701
|
+
get logger() {
|
|
25702
|
+
return this.container.resolve(TOKENS.Logger);
|
|
25703
|
+
}
|
|
25704
|
+
get retry() {
|
|
25705
|
+
return this.container.resolve(TOKENS.RetryPolicy);
|
|
25706
|
+
}
|
|
25707
|
+
get errorHandler() {
|
|
25708
|
+
return this.container.resolve(TOKENS.ErrorHandler);
|
|
25709
|
+
}
|
|
25710
|
+
get permission() {
|
|
25711
|
+
return this.container.resolve(TOKENS.PermissionPolicy);
|
|
25712
|
+
}
|
|
25713
|
+
get renderer() {
|
|
25714
|
+
return this.container.has(TOKENS.Renderer) ? this.container.resolve(TOKENS.Renderer) : void 0;
|
|
25715
|
+
}
|
|
25716
|
+
disableInteractiveConfirmation() {
|
|
25717
|
+
this.toolExecutor.clearConfirmAwaiter();
|
|
25718
|
+
const policy = this.permission;
|
|
25719
|
+
if (typeof policy.setPromptDelegate === "function") {
|
|
25720
|
+
policy.setPromptDelegate(void 0);
|
|
25595
25721
|
}
|
|
25596
|
-
return { finalText, aborted: false, done: false, directive };
|
|
25597
25722
|
}
|
|
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
|
-
});
|
|
25723
|
+
register(tool) {
|
|
25724
|
+
this.tools.register(tool);
|
|
25725
|
+
}
|
|
25726
|
+
async use(plugin, api) {
|
|
25727
|
+
await plugin.setup(api);
|
|
25728
|
+
this.plugins.push({ plugin, api });
|
|
25729
|
+
}
|
|
25730
|
+
async teardown() {
|
|
25731
|
+
const errors = [];
|
|
25732
|
+
for (const { plugin, api } of this.plugins.toReversed()) {
|
|
25733
|
+
if (typeof plugin.teardown !== "function") continue;
|
|
25734
|
+
try {
|
|
25735
|
+
await plugin.teardown(api);
|
|
25736
|
+
} catch (err) {
|
|
25737
|
+
errors.push(err);
|
|
25730
25738
|
}
|
|
25731
25739
|
}
|
|
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] });
|
|
25740
|
+
this.plugins.length = 0;
|
|
25741
|
+
if (errors.length > 0) {
|
|
25742
|
+
throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
|
|
25758
25743
|
}
|
|
25759
25744
|
}
|
|
25760
|
-
|
|
25761
|
-
|
|
25762
|
-
|
|
25763
|
-
|
|
25764
|
-
|
|
25765
|
-
|
|
25766
|
-
|
|
25767
|
-
|
|
25768
|
-
});
|
|
25745
|
+
async run(userInput, opts = {}) {
|
|
25746
|
+
const controller = new RunController({ parentSignal: opts.signal });
|
|
25747
|
+
const signal = controller.signal;
|
|
25748
|
+
this.ctx.signal = signal;
|
|
25749
|
+
controller.onAbort(() => this.ctx.drainAbortHooks());
|
|
25750
|
+
const span = this.tracer?.startSpan("agent.run", {
|
|
25751
|
+
"agent.model": opts.model ?? this.ctx.model,
|
|
25752
|
+
"agent.executionStrategy": opts.executionStrategy ?? this.executionStrategy
|
|
25769
25753
|
});
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25754
|
+
const { blocks, text } = normalizeInput(userInput);
|
|
25755
|
+
const inputPayload = { content: blocks, text, ctx: this.ctx };
|
|
25756
|
+
await this.extensions.runBeforeRun(this.ctx, inputPayload);
|
|
25773
25757
|
try {
|
|
25774
|
-
const
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25779
|
-
|
|
25780
|
-
return { result, durationMs: Date.now() - start };
|
|
25758
|
+
const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
|
|
25759
|
+
const result = await this._loopHandler.runInner(inputPayload, opts, controller, autonomousContinue);
|
|
25760
|
+
span?.setAttribute("agent.status", result.status);
|
|
25761
|
+
span?.setAttribute("agent.iterations", result.iterations);
|
|
25762
|
+
await this.extensions.runAfterRun(this.ctx, result);
|
|
25763
|
+
return result;
|
|
25781
25764
|
} catch (err) {
|
|
25782
|
-
const
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
durationMs: Date.now() - start
|
|
25765
|
+
const wse = err instanceof AgentError ? err : toWrongStackError(err);
|
|
25766
|
+
this.events.emit("error", { err: err instanceof Error ? err : new Error(String(err)), phase: "agent" });
|
|
25767
|
+
if (err instanceof Error) span?.recordError(err);
|
|
25768
|
+
span?.setAttribute("agent.status", "failed");
|
|
25769
|
+
const result = {
|
|
25770
|
+
status: signal.aborted ? "aborted" : "failed",
|
|
25771
|
+
iterations: 0,
|
|
25772
|
+
error: wse
|
|
25791
25773
|
};
|
|
25774
|
+
await this.extensions.runAfterRun(this.ctx, result);
|
|
25775
|
+
return result;
|
|
25776
|
+
} finally {
|
|
25777
|
+
span?.end();
|
|
25778
|
+
await controller.dispose();
|
|
25792
25779
|
}
|
|
25793
25780
|
}
|
|
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
|
-
}
|
|
25781
|
+
// ── Tool + response execution handled by AgentToolHandler / AgentResponseHandler ──
|
|
25814
25782
|
};
|
|
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
25783
|
|
|
25842
25784
|
// src/hooks/registry.ts
|
|
25843
25785
|
var HookRegistry = class {
|