diff --git a/apps/x/packages/core/src/application/assistant/instructions.ts b/apps/x/packages/core/src/application/assistant/instructions.ts index 9518990c..1b709589 100644 --- a/apps/x/packages/core/src/application/assistant/instructions.ts +++ b/apps/x/packages/core/src/application/assistant/instructions.ts @@ -149,6 +149,12 @@ ${codeModeEnabled *Medium signals (load the skill, answer the one-off, then offer):* one-off questions about decaying info ("what's the weather?", "top HN stories?"), "what's the latest on X / catch me up on X / any updates on X" about a person, company, project, or topic, recurring artifacts ("morning briefing", "weekly review", "Acme deal dashboard"). **Heuristic:** if you reach for \`web-search\` or a news tool to answer a recurring question, the answer is the kind of thing a bg-task would refresh on a schedule. +**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. + +*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. + +*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). + **Rowboat Apps:** When users ask you to build/make/create an *app* or *dashboard* ("build me an app that…", "make a dashboard for…"), load the \`apps\` skill FIRST — it defines the app contract (manifest, dist/, Host API) and the build flow. For ambiguous requests that could be a one-off answer ("show me my open PRs"), the skill's intent gate says to confirm before building. Do not hand-roll app folders without the skill. **Live Notes:** If the user explicitly says "live note" or "live-note", load the \`live-note\` skill. Otherwise, do not propose live notes — prefer the \`background-task\` skill for anything recurring. diff --git a/apps/x/packages/core/src/background-tasks/agent.ts b/apps/x/packages/core/src/background-tasks/agent.ts index d49f2c68..7016516a 100644 --- a/apps/x/packages/core/src/background-tasks/agent.ts +++ b/apps/x/packages/core/src/background-tasks/agent.ts @@ -53,6 +53,10 @@ Only available when the run message contains a **"# Coding task"** block (the ta - Group related items, then call \`launch-code-task\` once per group (\`taskSlug\` is your own slug). It runs full-auto in an isolated worktree and **owns the \`## Code Sessions\` section of \`index.md\`** — never edit those rows yourself. Write a complete, self-contained \`prompt\`: the coding agent has no other context and no human to ask. - If nothing is actionable, launch nothing and say so in your summary. +# Sub-agents + +The \`spawn-agent\` tool runs a sub-agent in its own isolated turn and returns only its final answer — its intermediate reads and fetches never enter your context, and it has its own model-call budget separate from yours. Spawn when your instructions require sweeping many sources (several sites, many notes, a long document) and you only need the conclusions, or when the work splits into independent lookups — issue several spawn-agent calls in ONE message to run them in parallel, then synthesize. Do not spawn for single quick lookups. Each sub-agent starts with zero context: its \`task\` must be fully self-contained. + # Triggers The run message tells you which trigger fired and how to interpret it: diff --git a/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.test.ts b/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.test.ts index ff69be97..0ec7b586 100644 --- a/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.test.ts +++ b/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.test.ts @@ -30,6 +30,16 @@ const fakeBuiltins = { inputSchema: z.object({ command: z.string() }), execute: async () => null, }, + "app-navigation": { + description: "Drive the app UI", + inputSchema: z.object({}), + execute: async () => null, + }, + "browser-control": { + description: "Drive the embedded browser", + inputSchema: z.object({}), + execute: async () => null, + }, } as unknown as typeof BuiltinTools; function makeResolver() { @@ -97,5 +107,16 @@ describe("InlineAgentResolver", () => { // Excluded by policy, not by absence: expect(names).not.toContain("executeCommand"); expect(names).not.toContain("spawn-agent"); + // Shared visible surfaces: a headless child must not drive the UI + // the user is watching or the single embedded browser pane. + expect(names).not.toContain("app-navigation"); + expect(names).not.toContain("browser-control"); + }); + + it("shared-surface tools remain available via explicit selection", async () => { + const resolved = await makeResolver().resolve({ + inline: { name: "a", instructions: "x", tools: ["browser-control"] }, + }); + expect(resolved.tools.map((t) => t.name)).toEqual(["browser-control"]); }); }); diff --git a/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.ts b/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.ts index fc2ae4e9..22ba14f0 100644 --- a/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.ts +++ b/apps/x/packages/core/src/turns/bridges/inline-agent-resolver.ts @@ -15,7 +15,10 @@ import { builtinToolDescriptor } from "./builtin-descriptors.js"; // except the ones that make no sense headlessly or in a child. Mirrors the // background-task agent's exclusions (no interactive approval surface) plus // the task/session launchers — an ephemeral child should do its own work, -// not schedule more. +// not schedule more — and the shared visible surfaces: a headless child +// navigating the UI the user is looking at, or parallel children fighting +// over the one embedded browser pane, is broken behavior, not just noise. +// All remain available via an explicit `tools` selection. const DEFAULT_PROFILE_EXCLUDED = new Set([ "executeCommand", // headless: no interactive approval "code_agent_run", // headless: needs interactive permission UI @@ -24,6 +27,8 @@ const DEFAULT_PROFILE_EXCLUDED = new Set([ "create-background-task", "patch-background-task", "run-live-note-agent", + "app-navigation", // shared surface: drives the UI the user is watching + "browser-control", // shared surface: the single embedded browser pane SPAWN_AGENT_TOOL_NAME, ]);