Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ impl Tool for SpawnAsyncSubagentTool {
Use sparingly, only when the user does not need the result in the current \
response, such as best-effort memory archiving, cleanup, or background \
investigation. Do not use for user-visible answers, code changes, external \
service writes, financial actions, or anything that may need clarification."
service writes, financial actions, or anything that may need clarification. \
Never use it when the sub-agent's result must gate your final answer (e.g. \
review/critique/verify/approve X BEFORE finalizing): this returns immediately \
and the turn finalizes before the result lands. For those, run a synchronous \
awaited sub-agent instead — a blocking delegate_* specialist or \
spawn_parallel_agents (which collects results before returning)."
}

fn parameters_schema(&self) -> serde_json::Value {
Expand Down
11 changes: 11 additions & 0 deletions src/openhuman/agent_registry/agents/orchestrator/prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ external-service writes, financial/market actions, scheduling, desktop control,
task that may need clarification. If the result matters to the current reply, use the
matching specialist delegation tool or `spawn_parallel_agents` instead.

**Result-gating tasks run synchronously (hard rule).** If a sub-agent's output must gate
your final answer — "review / critique / verify / approve / proofread X **before** you
finalize (or answer)" — that is **not** background work. Never dispatch it with
`spawn_async_subagent` (fire-and-forget): the turn finalizes before the result lands, so you
silently ignore "before you finalize" **and** waste a detached run that finishes minutes
later unused. Instead run it and get the result **in this same turn**: a blocking `delegate_*`
specialist, or `spawn_parallel_agents` (it collects every worker's result before returning),
or — only if you already spawned async — `wait_subagent` with a generous `timeout_secs` and
Comment on lines +89 to +91

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow single-agent waits for gated reviews

For single result-gating requests like the #4681 repro (“draft a bio, then have a second agent critique it before finalizing”), this guidance leaves no valid awaited path: spawn_parallel_agents only accepts two or more independent tasks (its schema/validator rejects fewer than 2), and arbitrary prose critiques often have no matching delegate_* specialist. Since wait_subagent can collect a just-spawned async child in the same turn, making it usable only after the model has already used async can still push the orchestrator into a tool error or into skipping the requested second-agent review.

Useful? React with 👍 / 👎.

fold the result in before you finalize. Reserve `spawn_async_subagent` for work whose result
the current reply genuinely does **not** depend on.

`spawn_async_subagent` returns an `[async_subagent_ref]` block with both `agent_id`
and `agentId`, plus concrete control instructions:

Expand Down
18 changes: 18 additions & 0 deletions src/openhuman/agent_registry/agents/orchestrator/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,24 @@ mod tests {
assert_eq!(render_installed_skills(&[]), "");
}

#[test]
fn prompt_routes_result_gating_tasks_to_synchronous_delegation() {
// Regression for #4681: a "critique it before you finalize" task was
// dispatched via fire-and-forget `spawn_async_subagent`, so the turn
// finalized before the critique ran. The orchestrator prompt must
// explicitly route result-gating work to a synchronous/awaited path.
assert!(
ARCHETYPE.contains("Result-gating tasks run synchronously"),
"orchestrator prompt must carry the result-gating delegation rule"
);
// It must steer such tasks to a synchronous/awaited primitive rather
// than fire-and-forget `spawn_async_subagent`.
assert!(
ARCHETYPE.contains("spawn_parallel_agents") && ARCHETYPE.contains("wait_subagent"),
"the rule must name the synchronous/awaited alternatives"
);
}

#[test]
fn render_installed_skills_flattens_and_caps_long_descriptions() {
// Third-party skill descriptions are untrusted, potentially huge
Expand Down
Loading