From 36ce35a058a4236f45047a0639234d0dd2d72b7c Mon Sep 17 00:00:00 2001
From: Ramnique Singh <30795890+ramnique@users.noreply.github.com>
Date: Fri, 10 Jul 2026 11:59:11 +0530
Subject: [PATCH] Add reasoning effort for spawned agents
---
.../src/components/compact-conversation.tsx | 8 +++++++-
apps/x/packages/core/src/agents/spawn-agent.test.ts | 13 +++++++++++++
apps/x/packages/core/src/agents/spawn-agent.ts | 13 ++++++++++++-
.../core/src/application/assistant/instructions.ts | 6 ++++--
4 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/apps/x/apps/renderer/src/components/compact-conversation.tsx b/apps/x/apps/renderer/src/components/compact-conversation.tsx
index 22e1d8f1..75c7a2b5 100644
--- a/apps/x/apps/renderer/src/components/compact-conversation.tsx
+++ b/apps/x/apps/renderer/src/components/compact-conversation.tsx
@@ -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 (
-
+
+ {item.reasoningEffort && (
+
+ {REASONING_EFFORT_LABELS[item.reasoningEffort]}
+
+ )}
)
}
diff --git a/apps/x/packages/core/src/agents/spawn-agent.test.ts b/apps/x/packages/core/src/agents/spawn-agent.test.ts
index fc5b91a3..7ac176d8 100644
--- a/apps/x/packages/core/src/agents/spawn-agent.test.ts
+++ b/apps/x/packages/core/src/agents/spawn-agent.test.ts
@@ -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(
diff --git a/apps/x/packages/core/src/agents/spawn-agent.ts b/apps/x/packages/core/src/agents/spawn-agent.ts
index 72cbf26c..85510a92 100644
--- a/apps/x/packages/core/src/agents/spawn-agent.ts
+++ b/apps/x/packages/core/src/agents/spawn-agent.ts
@@ -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) {
diff --git a/apps/x/packages/core/src/application/assistant/instructions.ts b/apps/x/packages/core/src/application/assistant/instructions.ts
index a2341ac5..d7476d66 100644
--- a/apps/x/packages/core/src/application/assistant/instructions.ts
+++ b/apps/x/packages/core/src/application/assistant/instructions.ts
@@ -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).