@dreb/coding-agent 2.45.4 → 2.47.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/README.md +10 -4
- package/dist/core/agent-session.d.ts +10 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +94 -23
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/context-buffer.d.ts +2 -0
- package/dist/core/context-buffer.d.ts.map +1 -1
- package/dist/core/context-buffer.js +8 -0
- package/dist/core/context-buffer.js.map +1 -1
- package/dist/core/dispatch-arbiter.d.ts +94 -0
- package/dist/core/dispatch-arbiter.d.ts.map +1 -0
- package/dist/core/dispatch-arbiter.js +316 -0
- package/dist/core/dispatch-arbiter.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +7 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +10 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +35 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/git-repo-state.d.ts +2 -0
- package/dist/core/git-repo-state.d.ts.map +1 -1
- package/dist/core/git-repo-state.js +19 -0
- package/dist/core/git-repo-state.js.map +1 -1
- package/dist/core/model-routing-guide.d.ts +15 -0
- package/dist/core/model-routing-guide.d.ts.map +1 -0
- package/dist/core/model-routing-guide.js +186 -0
- package/dist/core/model-routing-guide.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +38 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +18 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +33 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/thinking.d.ts +14 -0
- package/dist/core/thinking.d.ts.map +1 -1
- package/dist/core/thinking.js +35 -2
- package/dist/core/thinking.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +45 -0
- package/dist/core/tools/ask-user.d.ts.map +1 -0
- package/dist/core/tools/ask-user.js +210 -0
- package/dist/core/tools/ask-user.js.map +1 -0
- package/dist/core/tools/index.d.ts +18 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +53 -6
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +343 -53
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/modes/interactive/components/ask-user.d.ts +59 -0
- package/dist/modes/interactive/components/ask-user.d.ts.map +1 -0
- package/dist/modes/interactive/components/ask-user.js +240 -0
- package/dist/modes/interactive/components/ask-user.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +99 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +167 -6
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +18 -3
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +177 -59
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +32 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/agent-models.md +33 -1
- package/docs/dashboard.md +9 -3
- package/docs/extensions.md +33 -3
- package/docs/json.md +31 -5
- package/docs/rpc.md +105 -15
- package/docs/session.md +8 -2
- package/docs/settings.md +33 -1
- package/docs/skills.md +38 -0
- package/package.json +1 -1
- package/skills/model-routing-guide/SKILL.md +182 -0
|
@@ -11,11 +11,12 @@ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js"
|
|
|
11
11
|
import { attachJsonlLineReader } from "../../modes/rpc/jsonl.js";
|
|
12
12
|
import { log } from "../logger.js";
|
|
13
13
|
import { resolveCliModel } from "../model-resolver.js";
|
|
14
|
-
import { resolveEffectiveThinkingLevel, thinkingLevelToReasoning } from "../thinking.js";
|
|
14
|
+
import { resolveEffectiveThinkingLevel, thinkingLevelToReasoning, validateThinkingLevelForModel } from "../thinking.js";
|
|
15
15
|
import { getTextOutput, invalidArgText, str } from "./render-utils.js";
|
|
16
16
|
import { wrapToolDefinition } from "./tool-definition-wrapper.js";
|
|
17
17
|
import { DEFAULT_MAX_BYTES, formatSize } from "./truncate.js";
|
|
18
18
|
const DEFAULT_AGENT = "Explore";
|
|
19
|
+
const SUBAGENT_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
19
20
|
export const DEFAULT_MODEL_AVAILABILITY_PROBE_TIMEOUT_MS = 120_000;
|
|
20
21
|
export function parseAgentFrontmatter(content) {
|
|
21
22
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
@@ -123,6 +124,7 @@ const SUBAGENT_EXCLUDED_TOOLS = ["wait", "subagent", "suggest_next"];
|
|
|
123
124
|
// the child process adds them unconditionally regardless of --tools.
|
|
124
125
|
// Internal tools (tmp_read) are also excluded.
|
|
125
126
|
const SUBAGENT_DEFAULT_TOOLS = ["read", "bash", "edit", "write", "grep", "find", "ls", "web_search", "web_fetch"];
|
|
127
|
+
const SUBAGENT_ALWAYS_ACTIVE_TOOLS = ["search", "skill", "tasks_update"];
|
|
126
128
|
/**
|
|
127
129
|
* Filter a comma-separated tools string, removing any tools in SUBAGENT_EXCLUDED_TOOLS.
|
|
128
130
|
* Returns the filtered tools as a comma-separated string (always non-empty — falls
|
|
@@ -138,11 +140,38 @@ export function filterSubagentTools(tools) {
|
|
|
138
140
|
.join(",");
|
|
139
141
|
return filtered || SUBAGENT_DEFAULT_TOOLS.join(",");
|
|
140
142
|
}
|
|
143
|
+
function summarizeAgentsForArbitration(agents, getAgentModelsForAgent) {
|
|
144
|
+
return [...agents.values()].map((agent) => {
|
|
145
|
+
const settingsModels = getAgentModelsForAgent?.(agent.name);
|
|
146
|
+
return {
|
|
147
|
+
name: agent.name,
|
|
148
|
+
description: agent.description,
|
|
149
|
+
tools: [
|
|
150
|
+
...new Set([
|
|
151
|
+
...filterSubagentTools(agent.tools).split(",").filter(Boolean),
|
|
152
|
+
...SUBAGENT_ALWAYS_ACTIVE_TOOLS,
|
|
153
|
+
]),
|
|
154
|
+
],
|
|
155
|
+
modelDefaults: settingsModels && settingsModels.length > 0
|
|
156
|
+
? [...settingsModels]
|
|
157
|
+
: agent.model
|
|
158
|
+
? Array.isArray(agent.model)
|
|
159
|
+
? [...agent.model]
|
|
160
|
+
: [agent.model]
|
|
161
|
+
: [],
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
141
165
|
// TODO: Support PATH-based binary discovery.
|
|
142
166
|
// Currently returns the captured argv[1].
|
|
143
167
|
function findDrebBinary() {
|
|
144
168
|
return DREB_SCRIPT;
|
|
145
169
|
}
|
|
170
|
+
function canonicalModelRef(provider, modelId) {
|
|
171
|
+
if (!provider || modelId.startsWith(`${provider}/`))
|
|
172
|
+
return modelId;
|
|
173
|
+
return `${provider}/${modelId}`;
|
|
174
|
+
}
|
|
146
175
|
/**
|
|
147
176
|
* Handle one line of a subagent child's JSONL stdout. Parses the line and
|
|
148
177
|
* dispatches to the sinks: full-event relay, assistant-message collection,
|
|
@@ -175,8 +204,15 @@ export function handleChildJsonlLine(line, sinks) {
|
|
|
175
204
|
if (typeof event.type === "string") {
|
|
176
205
|
sinks.onEvent?.(event);
|
|
177
206
|
}
|
|
178
|
-
if (event.type === "agent_start"
|
|
179
|
-
|
|
207
|
+
if (event.type === "agent_start") {
|
|
208
|
+
if (typeof event.model?.id === "string") {
|
|
209
|
+
const provider = typeof event.model.provider === "string" ? event.model.provider : undefined;
|
|
210
|
+
sinks.onModel(canonicalModelRef(provider, event.model.id));
|
|
211
|
+
}
|
|
212
|
+
if (typeof event.thinkingLevel === "string" &&
|
|
213
|
+
SUBAGENT_THINKING_LEVELS.includes(event.thinkingLevel)) {
|
|
214
|
+
sinks.onThinking?.(event.thinkingLevel);
|
|
215
|
+
}
|
|
180
216
|
}
|
|
181
217
|
if (event.type === "message_end" && event.message?.role === "assistant") {
|
|
182
218
|
sinks.onAssistantMessage(event.message);
|
|
@@ -189,7 +225,7 @@ export function handleChildJsonlLine(line, sinks) {
|
|
|
189
225
|
sinks.onProgress(`${sinks.toolNameRef.current} done`);
|
|
190
226
|
}
|
|
191
227
|
}
|
|
192
|
-
async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentProvider, sessionDir, parentSessionFile, onChildEvent) {
|
|
228
|
+
async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentProvider, sessionDir, parentSessionFile, onChildEvent, thinkingOverride) {
|
|
193
229
|
const drebBin = findDrebBinary();
|
|
194
230
|
log.debug(`[subagent] spawn: agent=${agentConfig.name} cwd=${cwd}`);
|
|
195
231
|
// Validate cwd exists — spawn() throws a misleading ENOENT blaming the
|
|
@@ -216,13 +252,16 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
216
252
|
const modelStr = Array.isArray(agentConfig.model) ? agentConfig.model[0] : agentConfig.model;
|
|
217
253
|
if (modelStr) {
|
|
218
254
|
args.push("--model", modelStr);
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
if (parentProvider
|
|
255
|
+
// executeSingle resolves the model and provider independently. Always pass
|
|
256
|
+
// that exact provider because raw model IDs may themselves contain slashes
|
|
257
|
+
// (for example, OpenRouter IDs such as "openai/gpt-oss-120b").
|
|
258
|
+
if (parentProvider) {
|
|
223
259
|
args.push("--provider", parentProvider);
|
|
224
260
|
}
|
|
225
261
|
}
|
|
262
|
+
if (thinkingOverride) {
|
|
263
|
+
args.push("--thinking", thinkingOverride);
|
|
264
|
+
}
|
|
226
265
|
// Always pass --tools to ensure wait/subagent/suggest_next are excluded from child processes.
|
|
227
266
|
// filterSubagentTools always returns a non-empty string.
|
|
228
267
|
args.push("--tools", filterSubagentTools(agentConfig.tools));
|
|
@@ -272,6 +311,7 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
272
311
|
const plainStdoutLines = [];
|
|
273
312
|
const toolNameRef = { current: "" };
|
|
274
313
|
let resolvedModel;
|
|
314
|
+
let resolvedThinking;
|
|
275
315
|
// Drain stderr concurrently to avoid pipe deadlock (capped to prevent OOM from verbose subagents)
|
|
276
316
|
proc.stderr?.on("data", (chunk) => {
|
|
277
317
|
if (stderrSize < MAX_STDERR_BYTES) {
|
|
@@ -293,8 +333,11 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
293
333
|
onEvent: onChildEvent,
|
|
294
334
|
onAssistantMessage: (message) => collectedMessages.push(message),
|
|
295
335
|
onProgress,
|
|
296
|
-
onModel: (
|
|
297
|
-
resolvedModel =
|
|
336
|
+
onModel: (modelRef) => {
|
|
337
|
+
resolvedModel = modelRef;
|
|
338
|
+
},
|
|
339
|
+
onThinking: (thinkingLevel) => {
|
|
340
|
+
resolvedThinking = thinkingLevel;
|
|
298
341
|
},
|
|
299
342
|
onPlainLine: (plain) => plainStdoutLines.push(plain),
|
|
300
343
|
toolNameRef,
|
|
@@ -390,15 +433,13 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
390
433
|
}
|
|
391
434
|
// Discover the session file written by the child process
|
|
392
435
|
const sessionFile = sessionDir ? discoverSessionFile(sessionDir, agentConfig.name) : undefined;
|
|
436
|
+
const configuredModel = Array.isArray(agentConfig.model) ? agentConfig.model[0] : agentConfig.model;
|
|
393
437
|
resolvePromise({
|
|
394
438
|
agent: agentConfig.name,
|
|
395
439
|
task,
|
|
396
440
|
model: resolvedModel ??
|
|
397
|
-
(exitCode === 0
|
|
398
|
-
|
|
399
|
-
? agentConfig.model[0]
|
|
400
|
-
: agentConfig.model
|
|
401
|
-
: undefined),
|
|
441
|
+
(exitCode === 0 && configuredModel ? canonicalModelRef(parentProvider, configuredModel) : undefined),
|
|
442
|
+
thinking: resolvedThinking ?? (exitCode === 0 ? thinkingOverride : undefined),
|
|
402
443
|
exitCode,
|
|
403
444
|
output,
|
|
404
445
|
stderr: stderr.slice(0, 2000), // cap stderr
|
|
@@ -747,14 +788,17 @@ logPrefix = "[subagent]") {
|
|
|
747
788
|
].join(", ")}. Last error: ${lastError || "all probes failed"}`,
|
|
748
789
|
};
|
|
749
790
|
}
|
|
750
|
-
export function formatModelFallbackSummary(skippedModels,
|
|
791
|
+
export function formatModelFallbackSummary(skippedModels, proposalModel, finalModel) {
|
|
751
792
|
if (skippedModels.length === 0)
|
|
752
793
|
return undefined;
|
|
753
794
|
const skipped = skippedModels.map((s) => `- ${s.model}: ${s.reason}`).join("\n");
|
|
754
|
-
|
|
795
|
+
const routeSummary = finalModel && finalModel !== proposalModel
|
|
796
|
+
? `proposal resolved to "${proposalModel ?? "unknown"}" before arbitration selected "${finalModel}".`
|
|
797
|
+
: `using "${finalModel ?? proposalModel ?? "unknown"}".`;
|
|
798
|
+
return `[MODEL FALLBACK: skipped ${skippedModels.length} unavailable model(s); ${routeSummary}]\n${skipped}`;
|
|
755
799
|
}
|
|
756
|
-
export function prependModelFallbackSummary(output, skippedModels,
|
|
757
|
-
const fallbackSummary = formatModelFallbackSummary(skippedModels,
|
|
800
|
+
export function prependModelFallbackSummary(output, skippedModels, proposalModel, finalModel) {
|
|
801
|
+
const fallbackSummary = formatModelFallbackSummary(skippedModels, proposalModel, finalModel);
|
|
758
802
|
return fallbackSummary ? `${fallbackSummary}\n\n${output}` : output;
|
|
759
803
|
}
|
|
760
804
|
function formatSkippedModelFailureDetails(skippedModels) {
|
|
@@ -765,6 +809,10 @@ function formatSkippedModelFailureDetails(skippedModels) {
|
|
|
765
809
|
const MAX_PARALLEL_TASKS = 8;
|
|
766
810
|
const MAX_CONCURRENCY = 4;
|
|
767
811
|
const MAX_TASK_LENGTH = 32_768; // 32 KB — prevent E2BIG from oversized argv
|
|
812
|
+
/** Resolve per-task thinking precedence for parallel and chain modes. */
|
|
813
|
+
export function resolveSubagentThinkingOverride(taskThinking, topLevelThinking) {
|
|
814
|
+
return taskThinking ?? topLevelThinking;
|
|
815
|
+
}
|
|
768
816
|
// Semaphore for background task concurrency — shared across all background launches
|
|
769
817
|
let bgRunning = 0;
|
|
770
818
|
const bgWaiters = [];
|
|
@@ -804,9 +852,9 @@ function clampCwd(defaultCwd, itemCwd) {
|
|
|
804
852
|
}
|
|
805
853
|
return { ok: true, cwd: resolved };
|
|
806
854
|
}
|
|
807
|
-
export async function executeSingle(agents, agentName, task, cwd, signal, onProgress, modelOverride, parentProvider, registry, sessionDir, parentModel, agentModels, parentSessionFile, onChildEvent) {
|
|
808
|
-
|
|
809
|
-
|
|
855
|
+
export async function executeSingle(agents, agentName, task, cwd, signal, onProgress, modelOverride, parentProvider, registry, sessionDir, parentModel, agentModels, parentSessionFile, onChildEvent, thinkingOverride, arbitration) {
|
|
856
|
+
let name = agentName || DEFAULT_AGENT;
|
|
857
|
+
let config = agents.get(name);
|
|
810
858
|
if (!config) {
|
|
811
859
|
return {
|
|
812
860
|
agent: name,
|
|
@@ -817,7 +865,6 @@ export async function executeSingle(agents, agentName, task, cwd, signal, onProg
|
|
|
817
865
|
errorMessage: `Unknown agent type "${name}". Available: ${[...agents.keys()].join(", ")}. If you expected "${name}" to exist, check the .md file in ~/.dreb/agents/ or .dreb/agents/ for syntax errors.`,
|
|
818
866
|
};
|
|
819
867
|
}
|
|
820
|
-
// Validate task length for all modes (single, parallel items, chain steps)
|
|
821
868
|
if (task.length > MAX_TASK_LENGTH) {
|
|
822
869
|
return {
|
|
823
870
|
agent: name,
|
|
@@ -828,21 +875,18 @@ export async function executeSingle(agents, agentName, task, cwd, signal, onProg
|
|
|
828
875
|
errorMessage: `Task prompt too long (${task.length} chars, max ${MAX_TASK_LENGTH}). Shorten the prompt.`,
|
|
829
876
|
};
|
|
830
877
|
}
|
|
831
|
-
//
|
|
832
|
-
//
|
|
833
|
-
const
|
|
878
|
+
// Phase 1: resolve the parent's proposal with the existing precedence and
|
|
879
|
+
// fallback behavior so the arbiter receives one concrete canonical route.
|
|
880
|
+
const configuredModelSpec = modelOverride || (agentModels && agentModels.length > 0 ? agentModels : undefined) || config.model;
|
|
881
|
+
const modelSpec = configuredModelSpec || parentModel;
|
|
834
882
|
let effectiveConfig = modelOverride ? { ...config, model: modelOverride } : config;
|
|
835
883
|
let resolvedProvider = parentProvider;
|
|
884
|
+
let resolvedModel;
|
|
836
885
|
let warning;
|
|
837
886
|
let skippedModels = [];
|
|
838
|
-
// Resolve and validate the model against the registry before spawning.
|
|
839
|
-
// This catches typos and invalid model names immediately instead of failing
|
|
840
|
-
// silently in the child process. Also passes the canonical model ID to the
|
|
841
|
-
// child, avoiding fuzzy matching entirely. Agent definition fallback lists get
|
|
842
|
-
// an additional best-effort 1-token probe before spawn so runtime-unavailable
|
|
843
|
-
// models are skipped before committing to a child process.
|
|
844
887
|
if (modelSpec) {
|
|
845
|
-
const
|
|
888
|
+
const parentFallback = configuredModelSpec ? parentModel : undefined;
|
|
889
|
+
const resolved = await resolveModelForSubagentSpawn(modelSpec, parentProvider, registry, parentFallback, signal);
|
|
846
890
|
skippedModels = resolved.skippedModels;
|
|
847
891
|
if (!resolved.ok) {
|
|
848
892
|
const skippedDetails = formatSkippedModelFailureDetails(skippedModels);
|
|
@@ -856,21 +900,201 @@ export async function executeSingle(agents, agentName, task, cwd, signal, onProg
|
|
|
856
900
|
};
|
|
857
901
|
}
|
|
858
902
|
effectiveConfig = { ...effectiveConfig, model: resolved.modelId };
|
|
859
|
-
if (resolved.provider)
|
|
903
|
+
if (resolved.provider)
|
|
860
904
|
resolvedProvider = resolved.provider;
|
|
861
|
-
|
|
905
|
+
if (registry && resolvedProvider)
|
|
906
|
+
resolvedModel = registry.find(resolvedProvider, resolved.modelId);
|
|
862
907
|
warning = resolved.warning;
|
|
863
908
|
}
|
|
909
|
+
if (thinkingOverride && thinkingOverride !== "off" && !modelSpec) {
|
|
910
|
+
return {
|
|
911
|
+
agent: name,
|
|
912
|
+
task,
|
|
913
|
+
exitCode: 1,
|
|
914
|
+
output: "",
|
|
915
|
+
stderr: "",
|
|
916
|
+
errorMessage: `Cannot validate thinking level "${thinkingOverride}" because agent "${name}" has no configured model and no parent model is available. Set a model on the agent or pass a per-call model override.`,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
const proposalModelId = Array.isArray(effectiveConfig.model) ? effectiveConfig.model[0] : effectiveConfig.model;
|
|
920
|
+
const proposalSelectedModel = proposalModelId ? canonicalModelRef(resolvedProvider, proposalModelId) : undefined;
|
|
921
|
+
let finalThinking = thinkingOverride;
|
|
922
|
+
let arbitrationEnabled = false;
|
|
923
|
+
if (arbitration) {
|
|
924
|
+
const proposed = {
|
|
925
|
+
agent: name,
|
|
926
|
+
model: proposalSelectedModel ?? "",
|
|
927
|
+
thinking: resolveEffectiveThinkingLevel(resolvedModel, thinkingOverride, arbitration.defaultThinkingLevel),
|
|
928
|
+
};
|
|
929
|
+
let arbitrationResult;
|
|
930
|
+
try {
|
|
931
|
+
arbitrationResult = await arbitration.arbitrate({
|
|
932
|
+
task,
|
|
933
|
+
cwd,
|
|
934
|
+
proposed,
|
|
935
|
+
agents: summarizeAgentsForArbitration(agents, arbitration.getAgentModelsForAgent),
|
|
936
|
+
parentSessionFile,
|
|
937
|
+
step: arbitration.step,
|
|
938
|
+
}, signal);
|
|
939
|
+
}
|
|
940
|
+
catch {
|
|
941
|
+
const errorMessage = "Dispatch arbiter failed internally before child spawn.";
|
|
942
|
+
try {
|
|
943
|
+
arbitration.onRecord({
|
|
944
|
+
status: "failure",
|
|
945
|
+
proposed,
|
|
946
|
+
final: null,
|
|
947
|
+
changed: [],
|
|
948
|
+
step: arbitration.step,
|
|
949
|
+
errorCode: "internal_error",
|
|
950
|
+
errorMessage,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
catch { }
|
|
954
|
+
return {
|
|
955
|
+
agent: name,
|
|
956
|
+
task,
|
|
957
|
+
exitCode: 1,
|
|
958
|
+
output: "",
|
|
959
|
+
stderr: "",
|
|
960
|
+
errorMessage,
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
if (arbitrationResult.enabled) {
|
|
964
|
+
arbitrationEnabled = true;
|
|
965
|
+
if (!arbitrationResult.ok) {
|
|
966
|
+
const record = {
|
|
967
|
+
status: "failure",
|
|
968
|
+
proposed,
|
|
969
|
+
final: null,
|
|
970
|
+
changed: [],
|
|
971
|
+
step: arbitration.step,
|
|
972
|
+
errorCode: arbitrationResult.code,
|
|
973
|
+
errorMessage: arbitrationResult.error,
|
|
974
|
+
};
|
|
975
|
+
try {
|
|
976
|
+
arbitration.onRecord(record);
|
|
977
|
+
}
|
|
978
|
+
catch {
|
|
979
|
+
// The original failure still prevents spawn; do not replace it with an observer error.
|
|
980
|
+
}
|
|
981
|
+
return {
|
|
982
|
+
agent: name,
|
|
983
|
+
task,
|
|
984
|
+
exitCode: 1,
|
|
985
|
+
output: "",
|
|
986
|
+
stderr: "",
|
|
987
|
+
errorMessage: `Dispatch arbitration failed: ${arbitrationResult.error}`,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
const decision = arbitrationResult.decision;
|
|
991
|
+
const selectedConfig = agents.get(decision.agent);
|
|
992
|
+
const slash = decision.model.indexOf("/");
|
|
993
|
+
const selectedProvider = slash > 0 ? decision.model.slice(0, slash) : "";
|
|
994
|
+
const selectedModelId = slash > 0 ? decision.model.slice(slash + 1) : "";
|
|
995
|
+
const selectedModel = registry?.find(selectedProvider, selectedModelId);
|
|
996
|
+
if (!selectedConfig || !selectedModel) {
|
|
997
|
+
const errorMessage = !selectedConfig
|
|
998
|
+
? "Arbiter selected an unknown agent."
|
|
999
|
+
: "Arbiter selected an unavailable model.";
|
|
1000
|
+
const record = {
|
|
1001
|
+
status: "failure",
|
|
1002
|
+
proposed,
|
|
1003
|
+
final: null,
|
|
1004
|
+
changed: [],
|
|
1005
|
+
step: arbitration.step,
|
|
1006
|
+
errorCode: !selectedConfig ? "unknown_agent" : "out_of_scope_model",
|
|
1007
|
+
errorMessage,
|
|
1008
|
+
};
|
|
1009
|
+
try {
|
|
1010
|
+
arbitration.onRecord(record);
|
|
1011
|
+
}
|
|
1012
|
+
catch { }
|
|
1013
|
+
return {
|
|
1014
|
+
agent: name,
|
|
1015
|
+
task,
|
|
1016
|
+
exitCode: 1,
|
|
1017
|
+
output: "",
|
|
1018
|
+
stderr: "",
|
|
1019
|
+
errorMessage: `Dispatch arbitration failed: ${errorMessage}`,
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
const finalThinkingValidation = validateThinkingLevelForModel(selectedModel, decision.thinking);
|
|
1023
|
+
if (!finalThinkingValidation.ok) {
|
|
1024
|
+
const record = {
|
|
1025
|
+
status: "failure",
|
|
1026
|
+
proposed,
|
|
1027
|
+
final: null,
|
|
1028
|
+
changed: [],
|
|
1029
|
+
step: arbitration.step,
|
|
1030
|
+
errorCode: "unsupported_thinking",
|
|
1031
|
+
errorMessage: finalThinkingValidation.error,
|
|
1032
|
+
};
|
|
1033
|
+
try {
|
|
1034
|
+
arbitration.onRecord(record);
|
|
1035
|
+
}
|
|
1036
|
+
catch { }
|
|
1037
|
+
return {
|
|
1038
|
+
agent: name,
|
|
1039
|
+
task,
|
|
1040
|
+
exitCode: 1,
|
|
1041
|
+
output: "",
|
|
1042
|
+
stderr: "",
|
|
1043
|
+
errorMessage: `Dispatch arbitration failed: ${finalThinkingValidation.error}`,
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
name = decision.agent;
|
|
1047
|
+
config = selectedConfig;
|
|
1048
|
+
effectiveConfig = { ...config, model: selectedModel.id };
|
|
1049
|
+
resolvedProvider = selectedModel.provider;
|
|
1050
|
+
resolvedModel = selectedModel;
|
|
1051
|
+
finalThinking = decision.thinking;
|
|
1052
|
+
try {
|
|
1053
|
+
arbitration.onRecord({
|
|
1054
|
+
status: "success",
|
|
1055
|
+
proposed,
|
|
1056
|
+
final: decision,
|
|
1057
|
+
changed: arbitrationResult.changed,
|
|
1058
|
+
step: arbitration.step,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
catch (error) {
|
|
1062
|
+
return {
|
|
1063
|
+
agent: name,
|
|
1064
|
+
task,
|
|
1065
|
+
exitCode: 1,
|
|
1066
|
+
output: "",
|
|
1067
|
+
stderr: "",
|
|
1068
|
+
errorMessage: `Dispatch arbitration observability failed before spawn: ${error instanceof Error ? error.message : String(error)}`,
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (finalThinking) {
|
|
1074
|
+
const validation = validateThinkingLevelForModel(resolvedModel, finalThinking);
|
|
1075
|
+
if (!validation.ok) {
|
|
1076
|
+
return {
|
|
1077
|
+
agent: name,
|
|
1078
|
+
task,
|
|
1079
|
+
exitCode: 1,
|
|
1080
|
+
output: "",
|
|
1081
|
+
stderr: "",
|
|
1082
|
+
errorMessage: validation.error,
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
864
1086
|
const usedModel = effectiveConfig.model?.toString();
|
|
865
1087
|
onProgress?.(`Running ${name} agent${usedModel ? ` (${usedModel})` : ""}...`);
|
|
866
|
-
const result = await spawnSubagent(effectiveConfig, task, cwd, signal, onProgress, resolvedProvider, sessionDir, parentSessionFile, onChildEvent);
|
|
867
|
-
|
|
1088
|
+
const result = await spawnSubagent(effectiveConfig, task, cwd, signal, onProgress, resolvedProvider, sessionDir, parentSessionFile, onChildEvent, finalThinking);
|
|
1089
|
+
const finalSelectedModel = result.model ?? (usedModel ? canonicalModelRef(resolvedProvider, usedModel) : undefined);
|
|
1090
|
+
result.output = prependModelFallbackSummary(result.output, skippedModels, arbitrationEnabled ? proposalSelectedModel : finalSelectedModel, arbitrationEnabled ? finalSelectedModel : undefined);
|
|
868
1091
|
if (warning) {
|
|
869
|
-
|
|
1092
|
+
const warningContext = arbitrationEnabled ? `Proposal resolution: ${warning}` : warning;
|
|
1093
|
+
result.output = `[WARNING: ${warningContext}]\n\n${result.output}`;
|
|
870
1094
|
}
|
|
871
1095
|
return result;
|
|
872
1096
|
}
|
|
873
|
-
async function executeChain(agents, chain, defaultCwd, signal, onProgress, parentProvider, registry, sessionBaseDir, defaultAgent, defaultModel, parentModel, getAgentModelsForAgentFn, parentSessionFile, onChildEvent) {
|
|
1097
|
+
async function executeChain(agents, chain, defaultCwd, signal, onProgress, parentProvider, registry, sessionBaseDir, defaultAgent, defaultModel, defaultThinking, parentModel, getAgentModelsForAgentFn, parentSessionFile, onChildEvent, arbitration) {
|
|
874
1098
|
const results = [];
|
|
875
1099
|
let previousOutput = "";
|
|
876
1100
|
for (let i = 0; i < chain.length; i++) {
|
|
@@ -907,7 +1131,7 @@ async function executeChain(agents, chain, defaultCwd, signal, onProgress, paren
|
|
|
907
1131
|
const stepSessionDir = sessionBaseDir ? join(sessionBaseDir, `step-${i + 1}`) : undefined;
|
|
908
1132
|
const stepAgentName = step.agent || defaultAgent || DEFAULT_AGENT;
|
|
909
1133
|
const stepMach6Models = getAgentModelsForAgentFn?.(stepAgentName);
|
|
910
|
-
const result = await executeSingle(agents, step.agent || defaultAgent, task, cwdResult.cwd, signal, onProgress, step.model || defaultModel, parentProvider, registry, stepSessionDir, parentModel, stepMach6Models, parentSessionFile, onChildEvent);
|
|
1134
|
+
const result = await executeSingle(agents, step.agent || defaultAgent, task, cwdResult.cwd, signal, onProgress, step.model || defaultModel, parentProvider, registry, stepSessionDir, parentModel, stepMach6Models, parentSessionFile, onChildEvent, resolveSubagentThinkingOverride(step.thinking, defaultThinking), arbitration ? { ...arbitration, step: i + 1 } : undefined);
|
|
911
1135
|
results.push(result);
|
|
912
1136
|
if (result.exitCode !== 0) {
|
|
913
1137
|
break; // stop chain on error
|
|
@@ -1201,13 +1425,19 @@ export function rehydrateBackgroundAgentsFromDisk(parentSessionFile, subagentSes
|
|
|
1201
1425
|
}
|
|
1202
1426
|
return registered;
|
|
1203
1427
|
}
|
|
1428
|
+
function cloneBackgroundAgentInfo(info) {
|
|
1429
|
+
return {
|
|
1430
|
+
...info,
|
|
1431
|
+
arbitrations: info.arbitrations?.map((record) => structuredClone(record)),
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1204
1434
|
/** Get a snapshot of all tracked background agents (running and recently completed). Returns readonly clones. */
|
|
1205
1435
|
export function getBackgroundAgents() {
|
|
1206
|
-
return [...backgroundAgentRegistry.values()].map(
|
|
1436
|
+
return [...backgroundAgentRegistry.values()].map(cloneBackgroundAgentInfo);
|
|
1207
1437
|
}
|
|
1208
1438
|
/** Get only currently running background agents. Returns readonly clones. */
|
|
1209
1439
|
export function getRunningBackgroundAgents() {
|
|
1210
|
-
return [...backgroundAgentRegistry.values()].filter((a) => a.status === "running").map(
|
|
1440
|
+
return [...backgroundAgentRegistry.values()].filter((a) => a.status === "running").map(cloneBackgroundAgentInfo);
|
|
1211
1441
|
}
|
|
1212
1442
|
/** Abort all running background agents. */
|
|
1213
1443
|
export function abortBackgroundAgents() {
|
|
@@ -1233,6 +1463,14 @@ export function pruneBackgroundAgents(maxAgeMs = 5 * 60 * 1000) {
|
|
|
1233
1463
|
// ---------------------------------------------------------------------------
|
|
1234
1464
|
// Tool schema and definition
|
|
1235
1465
|
// ---------------------------------------------------------------------------
|
|
1466
|
+
const thinkingLevelSchema = Type.Union([
|
|
1467
|
+
Type.Literal("off"),
|
|
1468
|
+
Type.Literal("minimal"),
|
|
1469
|
+
Type.Literal("low"),
|
|
1470
|
+
Type.Literal("medium"),
|
|
1471
|
+
Type.Literal("high"),
|
|
1472
|
+
Type.Literal("xhigh"),
|
|
1473
|
+
], { description: "Thinking level override for the child model." });
|
|
1236
1474
|
const taskItemSchema = Type.Object({
|
|
1237
1475
|
agent: Type.Optional(Type.String({ description: "Agent type name (default: 'Explore')" })),
|
|
1238
1476
|
task: Type.String({ description: "The task prompt for this subagent" }),
|
|
@@ -1242,6 +1480,7 @@ const taskItemSchema = Type.Object({
|
|
|
1242
1480
|
model: Type.Optional(Type.String({
|
|
1243
1481
|
description: "Model override for this task. Takes precedence over agent definition model. Note: a single-string override discards the agent's fallback list.",
|
|
1244
1482
|
})),
|
|
1483
|
+
thinking: Type.Optional(thinkingLevelSchema),
|
|
1245
1484
|
});
|
|
1246
1485
|
const subagentSchema = Type.Object({
|
|
1247
1486
|
agent: Type.Optional(Type.String({ description: "Agent type name (default: 'Explore')" })),
|
|
@@ -1249,6 +1488,16 @@ const subagentSchema = Type.Object({
|
|
|
1249
1488
|
model: Type.Optional(Type.String({
|
|
1250
1489
|
description: "Model override. Takes precedence over agent definition model. Note: a single-string override discards the agent's fallback list. For parallel/chain, set per-task instead.",
|
|
1251
1490
|
})),
|
|
1491
|
+
thinking: Type.Optional(Type.Union([
|
|
1492
|
+
Type.Literal("off"),
|
|
1493
|
+
Type.Literal("minimal"),
|
|
1494
|
+
Type.Literal("low"),
|
|
1495
|
+
Type.Literal("medium"),
|
|
1496
|
+
Type.Literal("high"),
|
|
1497
|
+
Type.Literal("xhigh"),
|
|
1498
|
+
], {
|
|
1499
|
+
description: "Thinking level override. Per-task values take precedence in parallel and chain modes. Omit to preserve child defaults.",
|
|
1500
|
+
})),
|
|
1252
1501
|
tasks: Type.Optional(Type.Array(taskItemSchema, {
|
|
1253
1502
|
description: "Array of tasks to run in parallel (max 8)",
|
|
1254
1503
|
minItems: 1,
|
|
@@ -1322,7 +1571,13 @@ function formatSubagentResult(result, options, theme, showImages) {
|
|
|
1322
1571
|
return text;
|
|
1323
1572
|
}
|
|
1324
1573
|
export function formatSingleResult(result) {
|
|
1325
|
-
|
|
1574
|
+
const metadata = [
|
|
1575
|
+
result.model ? `model: ${result.model}` : undefined,
|
|
1576
|
+
result.thinking ? `thinking: ${result.thinking}` : undefined,
|
|
1577
|
+
]
|
|
1578
|
+
.filter((value) => value !== undefined)
|
|
1579
|
+
.join(", ");
|
|
1580
|
+
let text = `## Agent: ${result.agent}${metadata ? ` (${metadata})` : ""}\n`;
|
|
1326
1581
|
if (result.exitCode !== 0) {
|
|
1327
1582
|
text += `**Error** (exit ${result.exitCode}): ${result.errorMessage || "Unknown error"}\n`;
|
|
1328
1583
|
if (result.stderr) {
|
|
@@ -1353,6 +1608,9 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1353
1608
|
const getParentSessionFile = options?.parentSessionFile ?? (() => undefined);
|
|
1354
1609
|
const modelRegistry = options?.modelRegistry;
|
|
1355
1610
|
const getAgentModelsForAgent = options?.getAgentModelsForAgent;
|
|
1611
|
+
const arbitrate = options?.arbitrate;
|
|
1612
|
+
const onArbitration = options?.onArbitration;
|
|
1613
|
+
const getDefaultThinkingLevel = options?.defaultThinkingLevel;
|
|
1356
1614
|
// Discover agents at definition time to build the prompt guidelines.
|
|
1357
1615
|
// This is cheap (reads .md files) and the same call happens on every execute().
|
|
1358
1616
|
const knownAgents = discoverAgentTypes(cwd);
|
|
@@ -1382,6 +1640,7 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1382
1640
|
"Each agent notifies independently when done — completion messages include a list of any still-running agents. If you need their results before proceeding, end your current turn with no tool calls (as if you were asking the user a question and waiting for their reply). This emits `agent_end` and lets the framework deliver the completion as a new message that resumes your turn automatically. Do not call `sleep` or any other waiting action, and do not launch filler work.",
|
|
1383
1641
|
"Agent definitions specify a `model` field with a provider fallback list (comma-separated or YAML list). The spawner tries each in order and uses the first one that resolves for the current provider. This makes agents portable across providers.",
|
|
1384
1642
|
"Per-invocation `model` overrides take precedence but **discard the entire fallback list** — if the single override model isn't available on the current provider, the agent fails. Only override when you have a specific reason (e.g. escalating to a stronger tier for a complex task).",
|
|
1643
|
+
"Optional `thinking` overrides accept off/minimal/low/medium/high/xhigh. Per-task values override a top-level value; unsupported levels fail before spawn. Omit thinking to preserve the child's configured default.",
|
|
1385
1644
|
"**Model routing** — agent definitions already specify the right tier for their role. Most subagent tasks (exploration, file discovery, grep, navigation, summarization) are handled well by the defaults. Do not override the model unless the task genuinely requires a different capability tier than what the agent definition provides.",
|
|
1386
1645
|
"**Model identity** — Your current model is stated in the system prompt as `You are running on: provider/id`. Use this for explicit routing decisions — e.g. delegate vision tasks if you're on a text-only model, or use a differently-architected model as a critic for tasks where diverse model perspectives improve reliability.",
|
|
1387
1646
|
],
|
|
@@ -1426,9 +1685,9 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1426
1685
|
* Shared lifecycle for all background launches: generates agent ID,
|
|
1427
1686
|
* sets up registry/abort/notification, gates on the concurrency
|
|
1428
1687
|
* semaphore, and handles errors. The caller provides the actual
|
|
1429
|
-
* work via `runFn(signal, onChildEvent)` which must
|
|
1430
|
-
* SubagentResult
|
|
1431
|
-
*
|
|
1688
|
+
* work via `runFn(signal, onChildEvent, onArbitrationRecord)` which must
|
|
1689
|
+
* return a SubagentResult. Child events are relayed live; arbitration
|
|
1690
|
+
* records update the registry before being persisted/emitted.
|
|
1432
1691
|
*/
|
|
1433
1692
|
const launchBackgroundLifecycle = (agentName, taskSummary, sessionDir, agentCwd, runFn) => {
|
|
1434
1693
|
const agentId = generateAgentId();
|
|
@@ -1456,6 +1715,16 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1456
1715
|
}
|
|
1457
1716
|
}
|
|
1458
1717
|
: undefined;
|
|
1718
|
+
const onArbitrationRecord = (record) => {
|
|
1719
|
+
const entry = backgroundAgentRegistry.get(agentId);
|
|
1720
|
+
if (entry) {
|
|
1721
|
+
entry.arbitrations ??= [];
|
|
1722
|
+
entry.arbitrations.push(record);
|
|
1723
|
+
if (record.status === "success" && record.final)
|
|
1724
|
+
entry.agentType = record.final.agent;
|
|
1725
|
+
}
|
|
1726
|
+
onArbitration?.({ type: "subagent_arbitration", agentId, ...record });
|
|
1727
|
+
};
|
|
1459
1728
|
const bgSignal = bgAbort.signal;
|
|
1460
1729
|
const safeNotify = (result) => {
|
|
1461
1730
|
try {
|
|
@@ -1468,7 +1737,7 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1468
1737
|
const run = async () => {
|
|
1469
1738
|
await bgAcquire();
|
|
1470
1739
|
try {
|
|
1471
|
-
const result = await runFn(bgSignal, onChildEvent);
|
|
1740
|
+
const result = await runFn(bgSignal, onChildEvent, onArbitrationRecord);
|
|
1472
1741
|
const entry = backgroundAgentRegistry.get(agentId);
|
|
1473
1742
|
if (entry && !bgSignal.aborted)
|
|
1474
1743
|
entry.status = result.exitCode === 0 ? "completed" : "failed";
|
|
@@ -1519,18 +1788,25 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1519
1788
|
};
|
|
1520
1789
|
// Helper to launch a single background task
|
|
1521
1790
|
const subagentSessionsBase = getSubagentSessionsDir();
|
|
1522
|
-
const launchBackgroundTask = (agentName, task, taskLabel, taskCwd, modelOverride) => {
|
|
1791
|
+
const launchBackgroundTask = (agentName, task, taskLabel, taskCwd, modelOverride, thinkingOverride) => {
|
|
1523
1792
|
const resolvedCwd = taskCwd ?? cwd;
|
|
1524
1793
|
// Each background agent gets its own session subdirectory
|
|
1525
1794
|
const sessionId = generateAgentId();
|
|
1526
1795
|
const sessionDir = join(subagentSessionsBase, sessionId);
|
|
1527
1796
|
const agentModels = getAgentModelsForAgent?.(agentName || DEFAULT_AGENT);
|
|
1528
|
-
return launchBackgroundLifecycle(agentName, taskLabel, sessionDir, resolvedCwd, (signal, onChildEvent) => executeSingle(agents, agentName === DEFAULT_AGENT ? undefined : agentName, task, resolvedCwd, signal, undefined, modelOverride, getParentProvider(), modelRegistry, sessionDir, getParentModel(), agentModels, getParentSessionFile(), onChildEvent
|
|
1797
|
+
return launchBackgroundLifecycle(agentName, taskLabel, sessionDir, resolvedCwd, (signal, onChildEvent, onArbitrationRecord) => executeSingle(agents, agentName === DEFAULT_AGENT ? undefined : agentName, task, resolvedCwd, signal, undefined, modelOverride, getParentProvider(), modelRegistry, sessionDir, getParentModel(), agentModels, getParentSessionFile(), onChildEvent, thinkingOverride, arbitrate
|
|
1798
|
+
? {
|
|
1799
|
+
arbitrate,
|
|
1800
|
+
onRecord: onArbitrationRecord,
|
|
1801
|
+
defaultThinkingLevel: getDefaultThinkingLevel?.(),
|
|
1802
|
+
getAgentModelsForAgent,
|
|
1803
|
+
}
|
|
1804
|
+
: undefined));
|
|
1529
1805
|
};
|
|
1530
1806
|
if (params.task) {
|
|
1531
1807
|
// Single background task
|
|
1532
1808
|
const agentName = params.agent || DEFAULT_AGENT;
|
|
1533
|
-
const agentId = launchBackgroundTask(agentName, params.task, `${agentName} task`, undefined, params.model);
|
|
1809
|
+
const agentId = launchBackgroundTask(agentName, params.task, `${agentName} task`, undefined, params.model, params.thinking);
|
|
1534
1810
|
return {
|
|
1535
1811
|
content: [
|
|
1536
1812
|
{
|
|
@@ -1554,7 +1830,7 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1554
1830
|
skipped.push({ taskText: item.task, error: cwdResult.error });
|
|
1555
1831
|
continue;
|
|
1556
1832
|
}
|
|
1557
|
-
const agentId = launchBackgroundTask(agentName, item.task, `${agentName} task ${i + 1}/${params.tasks.length}`, cwdResult.cwd, item.model || params.model);
|
|
1833
|
+
const agentId = launchBackgroundTask(agentName, item.task, `${agentName} task ${i + 1}/${params.tasks.length}`, cwdResult.cwd, item.model || params.model, resolveSubagentThinkingOverride(item.thinking, params.thinking));
|
|
1558
1834
|
launched.push({ id: agentId, agentName, taskText: item.task });
|
|
1559
1835
|
}
|
|
1560
1836
|
const listing = launched
|
|
@@ -1590,8 +1866,15 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1590
1866
|
const taskSummary = `${params.chain.length}-step chain`;
|
|
1591
1867
|
const chainSteps = params.chain;
|
|
1592
1868
|
const chainSessionDir = join(subagentSessionsBase, `chain-${generateAgentId()}`);
|
|
1593
|
-
const agentId = launchBackgroundLifecycle(agentName, taskSummary, chainSessionDir, cwd, async (signal, onChildEvent) => {
|
|
1594
|
-
const results = await executeChain(agents, chainSteps, cwd, signal, undefined, getParentProvider(), modelRegistry, chainSessionDir, params.agent, params.model, getParentModel(), getAgentModelsForAgent, getParentSessionFile(), onChildEvent
|
|
1869
|
+
const agentId = launchBackgroundLifecycle(agentName, taskSummary, chainSessionDir, cwd, async (signal, onChildEvent, onArbitrationRecord) => {
|
|
1870
|
+
const results = await executeChain(agents, chainSteps, cwd, signal, undefined, getParentProvider(), modelRegistry, chainSessionDir, params.agent, params.model, params.thinking, getParentModel(), getAgentModelsForAgent, getParentSessionFile(), onChildEvent, arbitrate
|
|
1871
|
+
? {
|
|
1872
|
+
arbitrate,
|
|
1873
|
+
onRecord: onArbitrationRecord,
|
|
1874
|
+
defaultThinkingLevel: getDefaultThinkingLevel?.(),
|
|
1875
|
+
getAgentModelsForAgent,
|
|
1876
|
+
}
|
|
1877
|
+
: undefined);
|
|
1595
1878
|
const resultText = results
|
|
1596
1879
|
.map((r, i) => `### Step ${i + 1}\n${formatSingleResult(r)}`)
|
|
1597
1880
|
.join("\n\n---\n\n");
|
|
@@ -1600,6 +1883,13 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
1600
1883
|
return {
|
|
1601
1884
|
agent: agentName,
|
|
1602
1885
|
task: taskSummary,
|
|
1886
|
+
steps: results.map((result, index) => ({
|
|
1887
|
+
step: index + 1,
|
|
1888
|
+
agent: result.agent,
|
|
1889
|
+
success: result.exitCode === 0,
|
|
1890
|
+
model: result.model,
|
|
1891
|
+
thinking: result.thinking,
|
|
1892
|
+
})),
|
|
1603
1893
|
exitCode: failed.length > 0 ? 1 : 0,
|
|
1604
1894
|
output: resultText,
|
|
1605
1895
|
stderr: "",
|