@aexol/spectral 0.9.221 → 0.9.223
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/index.d.ts +20 -3
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +286 -80
- package/dist/extensions/aexol-backend-client.d.ts +85 -0
- package/dist/extensions/aexol-backend-client.d.ts.map +1 -0
- package/dist/extensions/aexol-backend-client.js +159 -0
- package/dist/extensions/aexol-mcp.d.ts +10 -6
- package/dist/extensions/aexol-mcp.d.ts.map +1 -1
- package/dist/extensions/aexol-mcp.js +21 -106
- package/dist/extensions/logic-trace-backend-draft.d.ts +68 -0
- package/dist/extensions/logic-trace-backend-draft.d.ts.map +1 -0
- package/dist/extensions/logic-trace-backend-draft.js +171 -0
- package/dist/extensions/logic-trace-validate.d.ts +33 -0
- package/dist/extensions/logic-trace-validate.d.ts.map +1 -0
- package/dist/extensions/logic-trace-validate.js +73 -0
- package/dist/extensions/logic-trace.d.ts +213 -0
- package/dist/extensions/logic-trace.d.ts.map +1 -0
- package/dist/extensions/logic-trace.js +1098 -0
- package/dist/generated/parser/lexer.d.ts +148 -0
- package/dist/generated/parser/lexer.d.ts.map +1 -0
- package/dist/generated/parser/lexer.js +877 -0
- package/dist/generated/parser/mod.d.ts +26 -0
- package/dist/generated/parser/mod.d.ts.map +1 -0
- package/dist/generated/parser/mod.js +30 -0
- package/dist/generated/parser/parser.d.ts +161 -0
- package/dist/generated/parser/parser.d.ts.map +1 -0
- package/dist/generated/parser/parser.js +754 -0
- package/dist/generated/parser/tokenizer.d.ts +107 -0
- package/dist/generated/parser/tokenizer.d.ts.map +1 -0
- package/dist/generated/parser/tokenizer.js +202 -0
- package/dist/generated/parser/validator.d.ts +45 -0
- package/dist/generated/parser/validator.d.ts.map +1 -0
- package/dist/generated/parser/validator.js +202 -0
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +4 -170
- package/dist/generated/zeus/index.d.ts +8 -873
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/generated/zeus/index.js +0 -7
- package/dist/mcp/ui-stream-types.d.ts +2 -2
- package/dist/memory/tools/share-project-observation.js +1 -1
- package/dist/memory/tools/write-project-observation.d.ts.map +1 -1
- package/dist/memory/tools/write-project-observation.js +4 -3
- package/dist/sdk/agent-core/agent-loop.d.ts.map +1 -1
- package/dist/sdk/agent-core/agent-loop.js +2 -4
- package/dist/sdk/agent-core/harness/types.d.ts +0 -1
- package/dist/sdk/agent-core/harness/types.d.ts.map +1 -1
- package/dist/sdk/agent-core/index.d.ts +0 -8
- package/dist/sdk/agent-core/index.d.ts.map +1 -1
- package/dist/sdk/agent-core/index.js +0 -9
- package/dist/sdk/ai/index.d.ts +1 -0
- package/dist/sdk/ai/index.d.ts.map +1 -1
- package/dist/sdk/ai/index.js +1 -0
- package/dist/sdk/ai/utils/transient-error.d.ts +28 -0
- package/dist/sdk/ai/utils/transient-error.d.ts.map +1 -0
- package/dist/sdk/ai/utils/transient-error.js +55 -0
- package/dist/sdk/coding-agent/core/agent-session.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/agent-session.js +7 -3
- package/dist/sdk/coding-agent/core/extensions/index.d.ts +1 -1
- package/dist/sdk/coding-agent/core/extensions/index.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/extensions/native-extensions.js +2 -2
- package/dist/sdk/coding-agent/core/extensions/types.d.ts +20 -1
- package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts +9 -4
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.js +15 -2
- package/dist/sdk/coding-agent/index.d.ts +1 -1
- package/dist/sdk/coding-agent/index.d.ts.map +1 -1
- package/dist/server/agent-bridge/headless-ui.d.ts +20 -0
- package/dist/server/agent-bridge/headless-ui.d.ts.map +1 -0
- package/dist/server/agent-bridge/headless-ui.js +85 -0
- package/dist/server/agent-bridge/pricing.d.ts +54 -0
- package/dist/server/agent-bridge/pricing.d.ts.map +1 -0
- package/dist/server/agent-bridge/pricing.js +226 -0
- package/dist/server/agent-bridge/rehydrate.d.ts +35 -0
- package/dist/server/agent-bridge/rehydrate.d.ts.map +1 -0
- package/dist/server/agent-bridge/rehydrate.js +122 -0
- package/dist/server/agent-bridge/synthetic-model.d.ts +27 -0
- package/dist/server/agent-bridge/synthetic-model.d.ts.map +1 -0
- package/dist/server/agent-bridge/synthetic-model.js +92 -0
- package/dist/server/agent-bridge.d.ts +17 -1
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +81 -496
- package/dist/server/error-humanizer.d.ts +11 -3
- package/dist/server/error-humanizer.d.ts.map +1 -1
- package/dist/server/error-humanizer.js +11 -3
- package/dist/server/session-stream/compaction-events.d.ts +13 -0
- package/dist/server/session-stream/compaction-events.d.ts.map +1 -0
- package/dist/server/session-stream/compaction-events.js +32 -0
- package/dist/server/session-stream/history-prune.d.ts +35 -0
- package/dist/server/session-stream/history-prune.d.ts.map +1 -0
- package/dist/server/session-stream/history-prune.js +182 -0
- package/dist/server/session-stream/memory.d.ts +48 -0
- package/dist/server/session-stream/memory.d.ts.map +1 -0
- package/dist/server/session-stream/memory.js +136 -0
- package/dist/server/session-stream/replay-tail.d.ts +4 -0
- package/dist/server/session-stream/replay-tail.d.ts.map +1 -0
- package/dist/server/session-stream/replay-tail.js +39 -0
- package/dist/server/session-stream/turn-events.d.ts +33 -0
- package/dist/server/session-stream/turn-events.d.ts.map +1 -0
- package/dist/server/session-stream/turn-events.js +65 -0
- package/dist/server/session-stream/turn-silence.d.ts +8 -0
- package/dist/server/session-stream/turn-silence.d.ts.map +1 -0
- package/dist/server/session-stream/turn-silence.js +72 -0
- package/dist/server/session-stream.d.ts +137 -36
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +18 -509
- package/dist/server/storage/observation-ranking.d.ts +14 -0
- package/dist/server/storage/observation-ranking.d.ts.map +1 -0
- package/dist/server/storage/observation-ranking.js +37 -0
- package/dist/server/storage/preflight.d.ts +20 -0
- package/dist/server/storage/preflight.d.ts.map +1 -0
- package/dist/server/storage/preflight.js +22 -0
- package/dist/server/storage/project-binding.d.ts +5 -0
- package/dist/server/storage/project-binding.d.ts.map +1 -0
- package/dist/server/storage/project-binding.js +32 -0
- package/dist/server/storage/rows.d.ts +104 -0
- package/dist/server/storage/rows.d.ts.map +1 -0
- package/dist/server/storage/rows.js +2 -0
- package/dist/server/storage/schema.d.ts +20 -0
- package/dist/server/storage/schema.d.ts.map +1 -0
- package/dist/server/storage/schema.js +134 -0
- package/dist/server/storage/types.d.ts +66 -0
- package/dist/server/storage/types.d.ts.map +1 -0
- package/dist/server/storage/types.js +1 -0
- package/dist/server/storage.d.ts +7 -94
- package/dist/server/storage.d.ts.map +1 -1
- package/dist/server/storage.js +6 -224
- package/dist/server/wire.d.ts +19 -0
- package/dist/server/wire.d.ts.map +1 -1
- package/package.json +2 -1
- package/dist/sdk/agent-core/harness/agent-harness.d.ts +0 -92
- package/dist/sdk/agent-core/harness/agent-harness.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/agent-harness.js +0 -899
- package/dist/sdk/agent-core/harness/env/nodejs.d.ts +0 -51
- package/dist/sdk/agent-core/harness/env/nodejs.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/env/nodejs.js +0 -485
- package/dist/sdk/agent-core/harness/prompt-templates.d.ts +0 -48
- package/dist/sdk/agent-core/harness/prompt-templates.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/prompt-templates.js +0 -229
- package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts +0 -26
- package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/jsonl-repo.js +0 -100
- package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts +0 -33
- package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/jsonl-storage.js +0 -230
- package/dist/sdk/agent-core/harness/session/memory-repo.d.ts +0 -18
- package/dist/sdk/agent-core/harness/session/memory-repo.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/memory-repo.js +0 -41
- package/dist/sdk/agent-core/harness/session/memory-storage.d.ts +0 -25
- package/dist/sdk/agent-core/harness/session/memory-storage.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/memory-storage.js +0 -113
- package/dist/sdk/agent-core/harness/session/repo-utils.d.ts +0 -11
- package/dist/sdk/agent-core/harness/session/repo-utils.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/repo-utils.js +0 -38
- package/dist/sdk/agent-core/harness/skills.d.ts +0 -44
- package/dist/sdk/agent-core/harness/skills.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/skills.js +0 -310
- package/dist/sdk/agent-core/harness/utils/shell-output.d.ts +0 -14
- package/dist/sdk/agent-core/harness/utils/shell-output.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/utils/shell-output.js +0 -125
- package/dist/sdk/agent-core/node.d.ts +0 -3
- package/dist/sdk/agent-core/node.d.ts.map +0 -1
- package/dist/sdk/agent-core/node.js +0 -2
- package/dist/sdk/agent-core/proxy.d.ts +0 -69
- package/dist/sdk/agent-core/proxy.d.ts.map +0 -1
- package/dist/sdk/agent-core/proxy.js +0 -297
package/dist/agent/index.d.ts
CHANGED
|
@@ -22,13 +22,17 @@
|
|
|
22
22
|
* - Tool filtering (only the tools specified in agent config)
|
|
23
23
|
* - Model selection (session model preferred, agent-configured as fallback)
|
|
24
24
|
* - Signal propagation (abort passthrough)
|
|
25
|
-
* - Access to observational memory via the recall
|
|
25
|
+
* - Access to observational memory via the `recall` and
|
|
26
|
+
* `write_project_observation` tools, which run against a parent-session
|
|
27
|
+
* point-in-time snapshot (branch + session id); other observation tools
|
|
28
|
+
* (`share_project_observation`, `receive_agent_observations`,
|
|
29
|
+
* `compact_context`, `subagent`) remain excluded
|
|
26
30
|
*/
|
|
27
31
|
import type { AgentTool } from "../sdk/agent-core/index.js";
|
|
28
32
|
import { type ExtensionAPI, type ExtensionContext } from "../sdk/coding-agent/index.js";
|
|
29
33
|
import { type AgentConfig } from "./agents.js";
|
|
30
34
|
import type { ServerEvent } from "../server/wire.ts";
|
|
31
|
-
import type {
|
|
35
|
+
import type { ReadonlySessionManager } from "../sdk/coding-agent/core/session-manager.js";
|
|
32
36
|
/**
|
|
33
37
|
* Build the system prompt for a subagent session.
|
|
34
38
|
*
|
|
@@ -44,8 +48,21 @@ export declare function buildSubagentSystemPrompt(baseSystemPrompt: string | und
|
|
|
44
48
|
* Give subagents a point-in-time branch view without exposing the live parent
|
|
45
49
|
* SessionManager. Each getBranch call returns fresh clones of the snapshot.
|
|
46
50
|
*/
|
|
47
|
-
export declare function createSubagentSessionManagerSnapshot(sessionManager: ExtensionContext["sessionManager"]): Pick<
|
|
51
|
+
export declare function createSubagentSessionManagerSnapshot(sessionManager: ExtensionContext["sessionManager"]): Pick<ReadonlySessionManager, "getBranch" | "getSessionId">;
|
|
48
52
|
export declare function resolveSubagentTools(cwd: string, ext: ExtensionAPI, ctx: ExtensionContext): AgentTool[];
|
|
53
|
+
/** Test hook: replace the run-level retry sleep (e.g. with an instant/collecting stub). */
|
|
54
|
+
export declare function setSubagentRetrySleepForTesting(fn?: (ms: number, signal?: AbortSignal) => Promise<void>): void;
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the max number of child-run attempts for the run-level retry loop.
|
|
57
|
+
* An explicit tool param (clamped to 0-10) overrides everything — including a
|
|
58
|
+
* globally disabled retry switch. Without the param, the session default
|
|
59
|
+
* (3 retries) applies unless retry is explicitly disabled, in which case a
|
|
60
|
+
* single attempt is made.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveSubagentMaxAttempts(explicit: number | undefined, settings: {
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
maxRetries?: number;
|
|
65
|
+
} | undefined): number;
|
|
49
66
|
export default function subagentExtension(ext: ExtensionAPI, opts?: {
|
|
50
67
|
disabledAgents?: string[];
|
|
51
68
|
emitSubagentEvent?: (event: ServerEvent) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAIX,SAAS,EAET,MAAM,4BAA4B,CAAC;AAqBpC,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAErB,MAAM,8BAA8B,CAAC;AAItC,OAAO,EACN,KAAK,WAAW,EAKhB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQrD,OAAO,KAAK,EAAE,sBAAsB,EAAgB,MAAM,6CAA6C,CAAC;AAuGxG;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACxC,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC,GACpF,MAAM,CAYR;AA4JD;;;GAGG;AACH,wBAAgB,oCAAoC,CACnD,cAAc,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,GAChD,IAAI,CAAC,sBAAsB,EAAE,WAAW,GAAG,cAAc,CAAC,CAe5D;AA6BD,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,gBAAgB,GACnB,SAAS,EAAE,CAwCb;AAqHD,2FAA2F;AAC3F,wBAAgB,+BAA+B,CAC9C,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GACtD,IAAI,CAEN;AAmBD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAC9D,MAAM,CAiBR;AA2eD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACxC,GAAG,EAAE,YAAY,EACjB,IAAI,CAAC,EAAE;IACN,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,GACC,IAAI,CA2aN"}
|
package/dist/agent/index.js
CHANGED
|
@@ -22,12 +22,17 @@
|
|
|
22
22
|
* - Tool filtering (only the tools specified in agent config)
|
|
23
23
|
* - Model selection (session model preferred, agent-configured as fallback)
|
|
24
24
|
* - Signal propagation (abort passthrough)
|
|
25
|
-
* - Access to observational memory via the recall
|
|
25
|
+
* - Access to observational memory via the `recall` and
|
|
26
|
+
* `write_project_observation` tools, which run against a parent-session
|
|
27
|
+
* point-in-time snapshot (branch + session id); other observation tools
|
|
28
|
+
* (`share_project_observation`, `receive_agent_observations`,
|
|
29
|
+
* `compact_context`, `subagent`) remain excluded
|
|
26
30
|
*/
|
|
27
|
-
import { agentLoop } from "../sdk/agent-core/index.js";
|
|
31
|
+
import { agentLoop, agentLoopContinue, } from "../sdk/agent-core/index.js";
|
|
28
32
|
import { buildIsolatedRequestTimePrunedMessagesResult, COMPACT_CONTEXT_TOOL_NAME, } from "../memory/tools/compact-context.js";
|
|
29
|
-
import { StringEnum } from "../sdk/ai/index.js";
|
|
33
|
+
import { isRateLimitError, isTransientApiError, isContextOverflow, isPaymentRequiredError, StringEnum, streamSimple, } from "../sdk/ai/index.js";
|
|
30
34
|
import { mutateSystemPrompt } from "../sdk/coding-agent/core/system-prompt-mutator.js";
|
|
35
|
+
import { sleep } from "../sdk/coding-agent/utils/sleep.js";
|
|
31
36
|
import { Type } from "typebox";
|
|
32
37
|
import { discoverAgents, agentMetadataBlock, } from "./agents.js";
|
|
33
38
|
import { loadAgentSettings } from "../server/handlers/agent-settings.js";
|
|
@@ -40,6 +45,7 @@ import { wrapToolDefinition } from "../sdk/coding-agent/core/tools/tool-definiti
|
|
|
40
45
|
import { RECALL_OBSERVATION_TOOL_NAME } from "../memory/tools/recall-observation.js";
|
|
41
46
|
import { RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME } from "../memory/tools/receive-agent-observations.js";
|
|
42
47
|
import { SHARE_PROJECT_OBSERVATION_TOOL_NAME } from "../memory/tools/share-project-observation.js";
|
|
48
|
+
import { WRITE_PROJECT_OBSERVATION_TOOL_NAME } from "../memory/tools/write-project-observation.js";
|
|
43
49
|
import { compressSubagentToolResult } from "../memory/tool-output-compressor.js";
|
|
44
50
|
// ---------------------------------------------------------------------------
|
|
45
51
|
// Constants
|
|
@@ -230,8 +236,18 @@ function cloneSessionEntry(entry) {
|
|
|
230
236
|
*/
|
|
231
237
|
export function createSubagentSessionManagerSnapshot(sessionManager) {
|
|
232
238
|
const snapshot = sessionManager.getBranch().map(cloneSessionEntry);
|
|
239
|
+
// Optional call tolerates minimal branch-only snapshots in tests; a
|
|
240
|
+
// missing id falls through to the tools' `no_session` handling.
|
|
241
|
+
const sessionId = sessionManager.getSessionId?.();
|
|
233
242
|
return {
|
|
234
243
|
getBranch: () => snapshot.map(cloneSessionEntry),
|
|
244
|
+
// Captured eagerly so the session id and the branch snapshot come
|
|
245
|
+
// from the same point in time. fork/newSession mutate the live
|
|
246
|
+
// SessionManager's sessionId in place, so a lazy read could diverge
|
|
247
|
+
// from the point-in-time branch on a mid-run fork. Subagent
|
|
248
|
+
// observations must be attributed to the parent session id at
|
|
249
|
+
// snapshot creation, same branch semantics as `recall`.
|
|
250
|
+
getSessionId: () => sessionId,
|
|
235
251
|
};
|
|
236
252
|
}
|
|
237
253
|
/**
|
|
@@ -251,14 +267,27 @@ const SUBAGENT_EXCLUDED_TOOL_NAMES = new Set([
|
|
|
251
267
|
RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME,
|
|
252
268
|
SHARE_PROJECT_OBSERVATION_TOOL_NAME,
|
|
253
269
|
]);
|
|
270
|
+
/**
|
|
271
|
+
* Extension tools that run with the parent session's point-in-time branch
|
|
272
|
+
* snapshot instead of the bare `{ cwd, getSystemPrompt }` isolated context.
|
|
273
|
+
*/
|
|
274
|
+
const SUBAGENT_SESSION_SNAPSHOT_TOOL_NAMES = new Set([
|
|
275
|
+
RECALL_OBSERVATION_TOOL_NAME,
|
|
276
|
+
WRITE_PROJECT_OBSERVATION_TOOL_NAME,
|
|
277
|
+
]);
|
|
254
278
|
export function resolveSubagentTools(cwd, ext, ctx) {
|
|
255
279
|
// Subagents receive every tool registered on the primary agent. Built-in
|
|
256
280
|
// tools are created fresh with the subagent's isolated cwd/jobs, and
|
|
257
|
-
// extension-registered tools are wrapped with
|
|
258
|
-
//
|
|
259
|
-
//
|
|
281
|
+
// extension-registered tools are wrapped with a `SubagentToolContext`:
|
|
282
|
+
// always `cwd` + `getSystemPrompt`, plus a point-in-time parent-session
|
|
283
|
+
// branch snapshot for the observation tools that need branch/session
|
|
284
|
+
// semantics. Everything else (ui, modelRegistry, live sessionManager) is
|
|
285
|
+
// absent at runtime, so handlers must guard those members.
|
|
260
286
|
const tools = [];
|
|
261
287
|
const jobs = new BashJobRegistry();
|
|
288
|
+
// One shared snapshot per invocation so all snapshot-routed tools observe
|
|
289
|
+
// the same parent-session epoch (branch + session id).
|
|
290
|
+
const sharedSnapshot = createSubagentSessionManagerSnapshot(ctx.sessionManager);
|
|
262
291
|
for (const toolInfo of ext.getAllTools()) {
|
|
263
292
|
const name = toolInfo.name;
|
|
264
293
|
if (SUBAGENT_EXCLUDED_TOOL_NAMES.has(name))
|
|
@@ -278,11 +307,10 @@ export function resolveSubagentTools(cwd, ext, ctx) {
|
|
|
278
307
|
const toolDef = ext.getToolDefinition(name);
|
|
279
308
|
if (!toolDef)
|
|
280
309
|
continue;
|
|
281
|
-
const isolatedContext = name
|
|
282
|
-
? { cwd, sessionManager:
|
|
310
|
+
const isolatedContext = SUBAGENT_SESSION_SNAPSHOT_TOOL_NAMES.has(name)
|
|
311
|
+
? { cwd, sessionManager: sharedSnapshot, getSystemPrompt: ctx.getSystemPrompt }
|
|
283
312
|
: { cwd, getSystemPrompt: ctx.getSystemPrompt };
|
|
284
|
-
|
|
285
|
-
tools.push(wrapToolDefinition(toolDef, () => isolatedContextWithNoParent));
|
|
313
|
+
tools.push(wrapToolDefinition(toolDef, () => isolatedContext));
|
|
286
314
|
}
|
|
287
315
|
return tools;
|
|
288
316
|
}
|
|
@@ -307,15 +335,17 @@ async function mapWithConcurrencyLimit(items, concurrency, fn) {
|
|
|
307
335
|
return results;
|
|
308
336
|
}
|
|
309
337
|
/**
|
|
310
|
-
*
|
|
338
|
+
* Runs a child agent with an independent per-subagent timeout.
|
|
311
339
|
*
|
|
312
340
|
* Combines the parent's abort signal with a fresh timeout AbortController so
|
|
313
341
|
* that no single subagent can exhaust the parent prompt's full timeout budget.
|
|
342
|
+
* The budget covers the WHOLE child run including any resume retries (see
|
|
343
|
+
* `runSingleAgent`), so wall time stays bounded by SUBAGENT_TIMEOUT_MS.
|
|
314
344
|
* On timeout, returns a clear error result instead of throwing so the parent
|
|
315
345
|
* agent can continue (e.g. stop a chain, or surface the error as the tool
|
|
316
346
|
* result for parallel/single modes).
|
|
317
347
|
*/
|
|
318
|
-
async function runSingleAgentWithTimeout(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, parentToolCallId, onInternalEvent, ext, ctx) {
|
|
348
|
+
async function runSingleAgentWithTimeout(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, parentToolCallId, onInternalEvent, ext, ctx, maxRetries) {
|
|
319
349
|
const timeoutMs = SUBAGENT_TIMEOUT_MS;
|
|
320
350
|
const timeoutSeconds = timeoutMs / 1000;
|
|
321
351
|
const controller = new AbortController();
|
|
@@ -329,7 +359,7 @@ async function runSingleAgentWithTimeout(defaultCwd, agents, agentName, task, cw
|
|
|
329
359
|
signal.addEventListener("abort", onParentAbort, { once: true });
|
|
330
360
|
}
|
|
331
361
|
try {
|
|
332
|
-
const result = await runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, controller.signal, onUpdate, makeDetails, parentToolCallId, onInternalEvent, ext, ctx);
|
|
362
|
+
const result = await runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, controller.signal, onUpdate, makeDetails, parentToolCallId, onInternalEvent, ext, ctx, maxRetries);
|
|
333
363
|
// If the timeout fired (and parent signal did not), surface a clean
|
|
334
364
|
// timeout error result rather than a bare "aborted" result.
|
|
335
365
|
if (controller.signal.aborted &&
|
|
@@ -352,10 +382,61 @@ async function runSingleAgentWithTimeout(defaultCwd, agents, agentName, task, cw
|
|
|
352
382
|
clearTimeout(timeoutId);
|
|
353
383
|
}
|
|
354
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Backoff sleep for the run-level retry loop; overridable in tests. Abort-aware
|
|
387
|
+
* (mirrors the main session's abortable retry sleep): it rejects as soon as the
|
|
388
|
+
* run signal aborts instead of stalling for the full delay.
|
|
389
|
+
*/
|
|
390
|
+
let retrySleep = (ms, signal) => sleep(ms, signal);
|
|
391
|
+
/** Test hook: replace the run-level retry sleep (e.g. with an instant/collecting stub). */
|
|
392
|
+
export function setSubagentRetrySleepForTesting(fn) {
|
|
393
|
+
retrySleep = fn ?? ((ms, signal) => sleep(ms, signal));
|
|
394
|
+
}
|
|
395
|
+
/** Upper bound for the tool-level `maxRetries` param (defense against bad values). */
|
|
396
|
+
const SUBAGENT_MAX_RETRIES_CAP = 10;
|
|
397
|
+
/**
|
|
398
|
+
* Minimal assistant-shaped probe used to ask `isContextOverflow` about a bare
|
|
399
|
+
* error string during retry classification.
|
|
400
|
+
*/
|
|
401
|
+
function buildOverflowProbe(errorMessage) {
|
|
402
|
+
return {
|
|
403
|
+
role: "assistant",
|
|
404
|
+
content: [],
|
|
405
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
406
|
+
stopReason: "error",
|
|
407
|
+
errorMessage,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Resolve the max number of child-run attempts for the run-level retry loop.
|
|
412
|
+
* An explicit tool param (clamped to 0-10) overrides everything — including a
|
|
413
|
+
* globally disabled retry switch. Without the param, the session default
|
|
414
|
+
* (3 retries) applies unless retry is explicitly disabled, in which case a
|
|
415
|
+
* single attempt is made.
|
|
416
|
+
*/
|
|
417
|
+
export function resolveSubagentMaxAttempts(explicit, settings) {
|
|
418
|
+
// Non-finite values (NaN/Infinity) from direct internal callers are treated
|
|
419
|
+
// as absent — otherwise `1 + NaN` would poison the attempts cap and loop.
|
|
420
|
+
const capped = explicit !== undefined && Number.isFinite(explicit)
|
|
421
|
+
? Math.max(0, Math.min(SUBAGENT_MAX_RETRIES_CAP, Math.floor(explicit)))
|
|
422
|
+
: undefined;
|
|
423
|
+
if (capped !== undefined)
|
|
424
|
+
return 1 + capped;
|
|
425
|
+
if (settings && settings.enabled === false)
|
|
426
|
+
return 1;
|
|
427
|
+
// Settings-derived values are hardened the same way as the explicit param:
|
|
428
|
+
// a non-finite maxRetries would otherwise poison the cap (`segments >= NaN`
|
|
429
|
+
// is never true → unbounded retries).
|
|
430
|
+
const settingsRetries = Number.isFinite(settings?.maxRetries)
|
|
431
|
+
? Math.min(SUBAGENT_MAX_RETRIES_CAP, settings.maxRetries)
|
|
432
|
+
: undefined;
|
|
433
|
+
const retries = settingsRetries ?? 3;
|
|
434
|
+
return 1 + retries;
|
|
435
|
+
}
|
|
355
436
|
// ---------------------------------------------------------------------------
|
|
356
437
|
// Core — run a single subagent in-process via agentLoop
|
|
357
438
|
// ---------------------------------------------------------------------------
|
|
358
|
-
async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, parentToolCallId, onInternalEvent, ext, ctx) {
|
|
439
|
+
async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, parentToolCallId, onInternalEvent, ext, ctx, maxRetries) {
|
|
359
440
|
const normalizedAgentName = agentName.replace(/^@/, "");
|
|
360
441
|
const agent = agents.find((a) => a.name === normalizedAgentName);
|
|
361
442
|
if (!agent) {
|
|
@@ -466,7 +547,29 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
466
547
|
});
|
|
467
548
|
}
|
|
468
549
|
};
|
|
550
|
+
// Run-level retry policy for the resume loop below (mirrors
|
|
551
|
+
// AgentSession._prepareRetry + its settings-driven backoff).
|
|
552
|
+
const retrySettings = ctx.settingsManager?.getRetrySettings?.();
|
|
553
|
+
const maxSegments = resolveSubagentMaxAttempts(maxRetries, retrySettings);
|
|
554
|
+
const baseDelayMs = Number.isFinite(retrySettings?.baseDelayMs)
|
|
555
|
+
? retrySettings?.baseDelayMs
|
|
556
|
+
: 2000;
|
|
469
557
|
try {
|
|
558
|
+
// Provider retry settings: subagents must inherit the same configurable
|
|
559
|
+
// retry policy as the main session (see sdk.ts streamFn wrapper) instead
|
|
560
|
+
// of silently falling back to provider SDK defaults (e.g. OpenAI's
|
|
561
|
+
// maxRetries=2 is too few for multi-minute outages). `ctx.settingsManager`
|
|
562
|
+
// is optional on ExtensionContext — when absent the wrapper below simply
|
|
563
|
+
// forwards options untouched, so provider/SDK defaults still apply.
|
|
564
|
+
// Optional call: symmetric with getRetrySettings?.() above — partial
|
|
565
|
+
// settingsManager stubs (extension contexts, tests) must not throw.
|
|
566
|
+
const providerRetrySettings = ctx.settingsManager?.getProviderRetrySettings?.();
|
|
567
|
+
const subagentStreamFn = (streamModel, streamContext, options) => streamSimple(streamModel, streamContext, {
|
|
568
|
+
...options,
|
|
569
|
+
timeoutMs: options?.timeoutMs ?? providerRetrySettings?.timeoutMs,
|
|
570
|
+
maxRetries: options?.maxRetries ?? providerRetrySettings?.maxRetries,
|
|
571
|
+
maxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings?.maxRetryDelayMs,
|
|
572
|
+
});
|
|
470
573
|
// Invocation-local hook state; never reuse the parent extension context/runtime.
|
|
471
574
|
const config = {
|
|
472
575
|
model,
|
|
@@ -496,81 +599,166 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
496
599
|
}
|
|
497
600
|
},
|
|
498
601
|
};
|
|
499
|
-
const
|
|
602
|
+
const initialPrompt = [
|
|
603
|
+
{
|
|
604
|
+
role: "user",
|
|
605
|
+
content: [{ type: "text", text: task }],
|
|
606
|
+
timestamp: Date.now(),
|
|
607
|
+
},
|
|
608
|
+
];
|
|
609
|
+
const baseContext = {
|
|
500
610
|
systemPrompt: `${effectivePrompt}\n\n---\n\n# Task\n\n${task}`,
|
|
501
611
|
messages: [],
|
|
502
612
|
tools,
|
|
503
|
-
}
|
|
613
|
+
};
|
|
504
614
|
// Collect messages from the stream
|
|
505
615
|
const collectedMessages = [];
|
|
506
616
|
let hadError = false;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (inner?.type === "text_delta" && inner.delta) {
|
|
518
|
-
streamingText += inner.delta;
|
|
519
|
-
emitUpdate();
|
|
617
|
+
let segments = 0;
|
|
618
|
+
// One child run may span several segments: the first one starts the
|
|
619
|
+
// loop, any later one resumes it. Tool results already recorded in
|
|
620
|
+
// `collectedMessages` are part of the resumed context, so tools are
|
|
621
|
+
// never re-executed by a retry.
|
|
622
|
+
const consumeStream = async (stream) => {
|
|
623
|
+
for await (const event of stream) {
|
|
624
|
+
if (signal?.aborted) {
|
|
625
|
+
hadError = true;
|
|
626
|
+
break;
|
|
520
627
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
628
|
+
onInternalEvent?.(event, parentToolCallId);
|
|
629
|
+
if (event.type === "message_update" && event.message?.role === "assistant") {
|
|
630
|
+
// Forward streaming text/thinking deltas so the UI can show the
|
|
631
|
+
// subagent process as it's generated, not just the final result.
|
|
632
|
+
const inner = event.assistantMessageEvent;
|
|
633
|
+
if (inner?.type === "text_delta" && inner.delta) {
|
|
634
|
+
streamingText += inner.delta;
|
|
635
|
+
emitUpdate();
|
|
525
636
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
collectedMessages.push(msg);
|
|
533
|
-
if (msg.role === "assistant") {
|
|
534
|
-
// Reset streaming accumulator — final text is now in messages
|
|
535
|
-
streamingText = "";
|
|
536
|
-
currentResult.usage.turns++;
|
|
537
|
-
const usage = msg.usage;
|
|
538
|
-
if (usage) {
|
|
539
|
-
currentResult.usage.input += usage.input || 0;
|
|
540
|
-
currentResult.usage.output += usage.output || 0;
|
|
541
|
-
currentResult.usage.cacheRead += usage.cacheRead || 0;
|
|
542
|
-
currentResult.usage.cacheWrite += usage.cacheWrite || 0;
|
|
543
|
-
currentResult.usage.cost += usage.cost?.total || 0;
|
|
544
|
-
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
637
|
+
else if (inner?.type === "thinking_delta" && inner.delta) {
|
|
638
|
+
thinkingText += inner.delta;
|
|
639
|
+
if (inner.metadata) {
|
|
640
|
+
thinkingMetadata = { ...(thinkingMetadata ?? {}), ...inner.metadata };
|
|
641
|
+
}
|
|
642
|
+
emitUpdate();
|
|
545
643
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (event.type === "message_end" && event.message) {
|
|
647
|
+
const msg = event.message;
|
|
648
|
+
collectedMessages.push(msg);
|
|
649
|
+
if (msg.role === "assistant") {
|
|
650
|
+
// Reset streaming accumulator — final text is now in messages
|
|
651
|
+
streamingText = "";
|
|
652
|
+
currentResult.usage.turns++;
|
|
653
|
+
const usage = msg.usage;
|
|
654
|
+
if (usage) {
|
|
655
|
+
currentResult.usage.input += usage.input || 0;
|
|
656
|
+
currentResult.usage.output += usage.output || 0;
|
|
657
|
+
currentResult.usage.cacheRead += usage.cacheRead || 0;
|
|
658
|
+
currentResult.usage.cacheWrite += usage.cacheWrite || 0;
|
|
659
|
+
currentResult.usage.cost += usage.cost?.total || 0;
|
|
660
|
+
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
661
|
+
}
|
|
662
|
+
if (!currentResult.model && msg.model)
|
|
663
|
+
currentResult.model = msg.model;
|
|
664
|
+
if (msg.stopReason)
|
|
665
|
+
currentResult.stopReason = msg.stopReason;
|
|
666
|
+
if (msg.errorMessage)
|
|
667
|
+
currentResult.errorMessage = msg.errorMessage;
|
|
668
|
+
if (msg.stopReason === "error" || msg.stopReason === "aborted") {
|
|
669
|
+
hadError = true;
|
|
670
|
+
}
|
|
554
671
|
}
|
|
672
|
+
currentResult.messages = [...collectedMessages];
|
|
673
|
+
emitUpdate();
|
|
674
|
+
}
|
|
675
|
+
if (event.type === "tool_execution_end" && event.isError) {
|
|
676
|
+
emitUpdate();
|
|
677
|
+
}
|
|
678
|
+
if (event.type === "agent_end" && event.error) {
|
|
679
|
+
hadError = true;
|
|
680
|
+
currentResult.errorMessage = event.error;
|
|
681
|
+
currentResult.stderr = event.error;
|
|
555
682
|
}
|
|
556
|
-
currentResult.messages = [...collectedMessages];
|
|
557
|
-
emitUpdate();
|
|
558
683
|
}
|
|
559
|
-
|
|
560
|
-
|
|
684
|
+
};
|
|
685
|
+
for (;;) {
|
|
686
|
+
segments++;
|
|
687
|
+
// Per-segment transient state: a resumed segment must be able to
|
|
688
|
+
// succeed without inheriting the previous segment's failure flags.
|
|
689
|
+
hadError = false;
|
|
690
|
+
streamingText = "";
|
|
691
|
+
// Drop thinking accumulated by the failed segment so the live sheet
|
|
692
|
+
// shows the resumed segment's reasoning, not stale text.
|
|
693
|
+
thinkingText = "";
|
|
694
|
+
thinkingMetadata = undefined;
|
|
695
|
+
currentResult.stopReason = undefined;
|
|
696
|
+
currentResult.errorMessage = undefined;
|
|
697
|
+
currentResult.stderr = "";
|
|
698
|
+
// Resume (segments > 1) continues the child with its existing
|
|
699
|
+
// transcript through `agentLoopContinue` — the same mechanism the
|
|
700
|
+
// main session uses (Agent.continue → runAgentLoopContinue) — so
|
|
701
|
+
// already-executed tools are NOT re-run. Only when there is nothing
|
|
702
|
+
// to resume from (empty transcript) do we fall back to a fresh
|
|
703
|
+
// `agentLoop` call, which then re-emits the initial prompt.
|
|
704
|
+
const canResume = collectedMessages.length > 0 &&
|
|
705
|
+
collectedMessages[collectedMessages.length - 1].role !== "assistant";
|
|
706
|
+
const stream = segments === 1 || !canResume
|
|
707
|
+
? agentLoop(initialPrompt, baseContext, config, signal, subagentStreamFn)
|
|
708
|
+
: agentLoopContinue({ ...baseContext, messages: [...collectedMessages] }, config, signal, subagentStreamFn);
|
|
709
|
+
await consumeStream(stream);
|
|
710
|
+
// Drain the stream result (already consumed by iteration, but ensure cleanup)
|
|
711
|
+
try {
|
|
712
|
+
await stream.result();
|
|
561
713
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
currentResult.errorMessage = event.error;
|
|
565
|
-
currentResult.stderr = event.error;
|
|
714
|
+
catch {
|
|
715
|
+
// Stream result error — already handled by message events
|
|
566
716
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
//
|
|
717
|
+
// ── Run-level retry decision (mirrors AgentSession._prepareRetry) ──
|
|
718
|
+
if (segments >= maxSegments)
|
|
719
|
+
break;
|
|
720
|
+
if (currentResult.stopReason !== "error")
|
|
721
|
+
break;
|
|
722
|
+
// Abort after a completed segment (parent stop or budget timeout):
|
|
723
|
+
// never retry, and never sleep into a phantom extra segment.
|
|
724
|
+
if (signal?.aborted)
|
|
725
|
+
break;
|
|
726
|
+
const errorMessage = currentResult.errorMessage || "";
|
|
727
|
+
// Payment required (402) is a billing problem — retries cannot help.
|
|
728
|
+
// Explicit for parity with AgentSession._isRetryableError.
|
|
729
|
+
if (isPaymentRequiredError(errorMessage))
|
|
730
|
+
break;
|
|
731
|
+
// Context overflow is a hard failure, not transient. It must be
|
|
732
|
+
// checked before the classifiers because overflow messages can
|
|
733
|
+
// contain bare status-code-like numerals (e.g. "501234 tokens >
|
|
734
|
+
// 500000 maximum").
|
|
735
|
+
if (isContextOverflow(buildOverflowProbe(errorMessage), model.contextWindow)) {
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
if (!isRateLimitError(errorMessage) && !isTransientApiError(errorMessage)) {
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
// Drop the failed assistant message(s) so the child resumes AT the
|
|
742
|
+
// failed turn instead of re-running it. Several trailing assistant
|
|
743
|
+
// messages are possible: the in-loop retry emits a failed assistant
|
|
744
|
+
// message before popping it from the loop context.
|
|
745
|
+
while (collectedMessages.length > 0 &&
|
|
746
|
+
collectedMessages[collectedMessages.length - 1].role === "assistant") {
|
|
747
|
+
collectedMessages.pop();
|
|
748
|
+
}
|
|
749
|
+
currentResult.messages = [...collectedMessages];
|
|
750
|
+
try {
|
|
751
|
+
await retrySleep(baseDelayMs * 2 ** (segments - 1), signal);
|
|
752
|
+
}
|
|
753
|
+
catch {
|
|
754
|
+
// Aborted during the backoff window (the sleep rejects on abort):
|
|
755
|
+
// fall through to the check below instead of throwing out of the
|
|
756
|
+
// child run.
|
|
757
|
+
}
|
|
758
|
+
// Abort during the backoff window: break so the attempt annotation
|
|
759
|
+
// counts only completed segments.
|
|
760
|
+
if (signal?.aborted)
|
|
761
|
+
break;
|
|
574
762
|
}
|
|
575
763
|
currentResult.exitCode = hadError ? 1 : 0;
|
|
576
764
|
if (signal?.aborted) {
|
|
@@ -578,6 +766,12 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
578
766
|
currentResult.stopReason = "aborted";
|
|
579
767
|
currentResult.errorMessage = "Subagent was aborted";
|
|
580
768
|
}
|
|
769
|
+
if (segments > 1) {
|
|
770
|
+
currentResult.attempts = segments;
|
|
771
|
+
if (currentResult.exitCode !== 0 && currentResult.errorMessage) {
|
|
772
|
+
currentResult.errorMessage = `${currentResult.errorMessage} (failed after ${segments} attempts)`;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
581
775
|
return currentResult;
|
|
582
776
|
}
|
|
583
777
|
catch (err) {
|
|
@@ -625,6 +819,11 @@ const SubagentParams = Type.Object({
|
|
|
625
819
|
cwd: Type.Optional(Type.String({
|
|
626
820
|
description: "Working directory for the agent process (single mode)",
|
|
627
821
|
})),
|
|
822
|
+
maxRetries: Type.Optional(Type.Integer({
|
|
823
|
+
description: "Max automatic resume retries after a transient provider failure: the child resumes at the failed turn instead of restarting, so its tools are not re-executed (overrides the configured retry count and a disabled retry switch; 0-10).",
|
|
824
|
+
minimum: 0,
|
|
825
|
+
maximum: 10,
|
|
826
|
+
})),
|
|
628
827
|
});
|
|
629
828
|
// ---------------------------------------------------------------------------
|
|
630
829
|
// Extension entry point
|
|
@@ -807,7 +1006,7 @@ export default function subagentExtension(ext, opts) {
|
|
|
807
1006
|
}
|
|
808
1007
|
}
|
|
809
1008
|
: undefined;
|
|
810
|
-
const result = await runSingleAgentWithTimeout(ctx.cwd, agents, step.agent, taskWithContext, step.cwd, i + 1, signal, chainUpdate, makeDetails("chain"), _toolCallId, emitSubagentToolProgress, ext, ctx);
|
|
1009
|
+
const result = await runSingleAgentWithTimeout(ctx.cwd, agents, step.agent, taskWithContext, step.cwd, i + 1, signal, chainUpdate, makeDetails("chain"), _toolCallId, emitSubagentToolProgress, ext, ctx, params.maxRetries);
|
|
811
1010
|
results.push(result);
|
|
812
1011
|
const isError = result.exitCode !== 0 ||
|
|
813
1012
|
result.stopReason === "error" ||
|
|
@@ -834,7 +1033,9 @@ export default function subagentExtension(ext, opts) {
|
|
|
834
1033
|
const output = truncateOutput(getFinalOutput(r.messages) || "(no output)", SUBAGENT_OUTPUT_LIMIT);
|
|
835
1034
|
const status = r.exitCode === 0
|
|
836
1035
|
? "completed"
|
|
837
|
-
:
|
|
1036
|
+
: r.attempts && r.attempts > 1
|
|
1037
|
+
? `failed (after ${r.attempts} attempts)`
|
|
1038
|
+
: "failed";
|
|
838
1039
|
return `[step ${idx + 1} - ${r.agent}] ${status}:\n${output}`;
|
|
839
1040
|
});
|
|
840
1041
|
return {
|
|
@@ -892,7 +1093,7 @@ export default function subagentExtension(ext, opts) {
|
|
|
892
1093
|
allResults[index] = partial.details.results[0];
|
|
893
1094
|
emitParallelUpdate();
|
|
894
1095
|
}
|
|
895
|
-
}, makeDetails("parallel"), _toolCallId, emitSubagentToolProgress, ext, ctx);
|
|
1096
|
+
}, makeDetails("parallel"), _toolCallId, emitSubagentToolProgress, ext, ctx, params.maxRetries);
|
|
896
1097
|
allResults[index] = result;
|
|
897
1098
|
emitParallelUpdate();
|
|
898
1099
|
return result;
|
|
@@ -900,7 +1101,12 @@ export default function subagentExtension(ext, opts) {
|
|
|
900
1101
|
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
901
1102
|
const summaries = results.map((r) => {
|
|
902
1103
|
const output = truncateOutput(getFinalOutput(r.messages), SUBAGENT_OUTPUT_LIMIT);
|
|
903
|
-
|
|
1104
|
+
const status = r.exitCode === 0
|
|
1105
|
+
? "completed"
|
|
1106
|
+
: r.attempts && r.attempts > 1
|
|
1107
|
+
? `failed (after ${r.attempts} attempts)`
|
|
1108
|
+
: "failed";
|
|
1109
|
+
return `[${r.agent}] ${status}:\n${output || "(no output)"}`;
|
|
904
1110
|
});
|
|
905
1111
|
return {
|
|
906
1112
|
content: [
|
|
@@ -914,7 +1120,7 @@ export default function subagentExtension(ext, opts) {
|
|
|
914
1120
|
}
|
|
915
1121
|
// ── Single mode ─────────────────────────────────────────────
|
|
916
1122
|
if (params.agent && params.task) {
|
|
917
|
-
const result = await runSingleAgentWithTimeout(ctx.cwd, agents, params.agent, params.task, params.cwd, undefined, signal, onUpdate, makeDetails("single"), _toolCallId, emitSubagentToolProgress, ext, ctx);
|
|
1123
|
+
const result = await runSingleAgentWithTimeout(ctx.cwd, agents, params.agent, params.task, params.cwd, undefined, signal, onUpdate, makeDetails("single"), _toolCallId, emitSubagentToolProgress, ext, ctx, params.maxRetries);
|
|
918
1124
|
const isError = result.exitCode !== 0 ||
|
|
919
1125
|
result.stopReason === "error" ||
|
|
920
1126
|
result.stopReason === "aborted";
|