Add reasoning effort for spawned agents

This commit is contained in:
Ramnique Singh 2026-07-10 11:59:11 +05:30
parent 7765bec96e
commit 36ce35a058
4 changed files with 36 additions and 4 deletions

View file

@ -9,6 +9,7 @@ import {
isTurnUsageMessage,
getToolDisplayName,
getToolErrorText,
REASONING_EFFORT_LABELS,
toToolState,
normalizeToolOutput,
} from '@/lib/chat-conversation'
@ -38,7 +39,7 @@ export function CompactConversation({ items }: { items: ConversationItem[] }) {
}
if (isTurnUsageMessage(item)) {
return (
<div key={item.id} className="flex justify-start px-1">
<div key={item.id} className="flex items-center justify-start gap-1 px-1">
<TokenUsageMenu
usage={item.usage}
scope="turn"
@ -46,6 +47,11 @@ export function CompactConversation({ items }: { items: ConversationItem[] }) {
className="size-5 border-transparent bg-transparent hover:bg-transparent"
align="start"
/>
{item.reasoningEffort && (
<span className="text-xs text-muted-foreground/70">
{REASONING_EFFORT_LABELS[item.reasoningEffort]}
</span>
)}
</div>
)
}

View file

@ -140,6 +140,19 @@ describe("runSpawnedAgent", () => {
expect(started[0].maxModelCalls).toBe(5);
});
it("passes reasoning effort through to the child headless turn", async () => {
const { services, started } = fakeServices({});
await runSpawnedAgent(
{
task: "compare these options carefully",
instructions: "You analyze tradeoffs.",
reasoning_effort: "high",
},
{ parentTurnId: "parent-1", signal, services },
);
expect(started[0].reasoningEffort).toBe("high");
});
it("rejects agent_id and instructions together", async () => {
const { services } = fakeServices({});
const result = await runSpawnedAgent(

View file

@ -59,12 +59,20 @@ export const SpawnAgentInput = z.object({
.describe(
`Model-call budget for the sub-agent (default and cap: ${DEFAULT_MAX_MODEL_CALLS}).`,
),
reasoning_effort: z
.enum(["low", "medium", "high"])
.optional()
.describe(
"Optional reasoning-effort override for the sub-agent turn. Omit for auto/provider default. Use `low` for routine extraction or summarization, `medium` for multi-step synthesis, and `high` only when the child task truly needs deeper reasoning.",
),
});
export const SPAWN_AGENT_DESCRIPTION =
"Launch a sub-agent that works on a task in its own isolated, headless turn and returns its final answer. " +
"Issue several spawn-agent calls in ONE response to run sub-agents in parallel — use this to fan out independent research, analysis, or file work. " +
"Use it deliberately for independent, heavy, or parallelizable work; avoid spawning for quick single-step lookups. " +
"Issue several spawn-agent calls in ONE response only when the subtasks are genuinely independent and worth running in parallel. " +
"Provide either `agent_id` (a stored agent) or `instructions` (construct a specialist on the fly, optionally with `name` and `tools`). " +
"Optionally set `reasoning_effort` for the child turn; leave it unset for auto/provider default, and reserve `high` for tasks that clearly need deeper reasoning. " +
"The sub-agent cannot ask the user questions and cannot spawn further sub-agents; give it a complete, self-contained task.";
export interface SpawnedAgentCallbacks {
@ -170,6 +178,9 @@ export async function runSpawnedAgent(
agent,
message: input.task,
maxModelCalls,
...(input.reasoning_effort === undefined
? {}
: { reasoningEffort: input.reasoning_effort }),
signal: opts.signal,
});
} catch (error) {

View file

@ -149,9 +149,11 @@ ${codeModeEnabled
**Background Tasks (Self-Running Work):** Rowboat runs *background tasks* persistent instructions fired on a schedule and/or on incoming emails / calendar events; the flagship surface for *anything recurring*. Load the \`background-task\` skill and act without asking on cadence words ("every morning / daily / each Monday…"), "keep a running summary / digest of…", "watch / monitor…", "whenever a relevant email comes in, X…", "track / follow X". Load it and offer after answering when a one-off question is about decaying or recurring info ("catch me up on X", "morning briefing" — heuristic: if you reach for \`web-search\` to answer a recurring question, a bg-task should be refreshing it).
**Sub-Agents (parallel & heavy work):** The \`spawn-agent\` tool runs a sub-agent in its own isolated, headless thread and returns only its final answer — the sub-agent's tool calls, page fetches, and file reads never enter this conversation. Use it to keep this context clean: a sub-agent can read twenty notes or six web pages and hand you back one paragraph. Issue several spawn-agent calls in ONE response to run them in parallel.
**Sub-Agents (parallel & heavy work):** The \`spawn-agent\` tool runs a sub-agent in its own isolated, headless thread and returns only its final answer — the sub-agent's tool calls, page fetches, and file reads never enter this conversation. Use it sparingly and deliberately when the work is independent, long-running, or context-heavy enough to justify a separate turn. A sub-agent can read twenty notes or six web pages and hand you back one paragraph. Issue several spawn-agent calls in ONE response only when the subtasks are genuinely independent and useful to run in parallel.
*Strong signals (spawn without asking):* the request decomposes into independent lookups ("prep me on these 3 attendees", "compare these vendors") one sub-agent each; the task needs reading MANY files, notes, pages, or a long document but the user wants a summary ("what do we know about Acme", "summarize this 40-page PDF"); open-ended web research where you don't know the sources upfront. **Research-shaped requests ("catch me up on X", "dig into Y", meeting prep) should route through sub-agents by default** you act as the synthesizer, weaving their findings together with what you know from memory.
*Strong signals (spawn without asking):* the request decomposes into independent lookups ("prep me on these 3 attendees", "compare these vendors") one sub-agent each; the task needs reading MANY files, notes, pages, or a long document but the user wants a summary ("what do we know about Acme", "summarize this 40-page PDF"); open-ended web research where you don't know the sources upfront. For research-shaped requests ("catch me up on X", "dig into Y", meeting prep), use sub-agents when there are multiple sources or branches to investigate, then act as the synthesizer and weave their findings together with what you know from memory.
*Reasoning effort:* \`spawn-agent\` accepts \`reasoning_effort\` for the child turn. Usually omit it and let the provider default apply. Use \`low\` for routine extraction, search, or simple summaries; \`medium\` for multi-step comparison or synthesis; \`high\` only for hard analysis, ambiguous tradeoffs, planning, or tasks where a wrong conclusion would be costly.
*Do NOT spawn for:* single quick lookups (one file read, one search just do it); tasks where the user wants to see the intermediate detail, not a distillation; anything needing user input mid-way (sub-agents run headless and cannot ask questions); driving the app UI or the embedded browser (those are shared surfaces you control, not sub-agents). Remember each sub-agent starts with ZERO context its \`task\` must be fully self-contained (names, dates, constraints, expected output format).