Skip to content

Bound direct subagent waits#311

Open
ritamgh wants to merge 1 commit into
1jehuang:masterfrom
ritamgh:fix/bound-subagent-waits
Open

Bound direct subagent waits#311
ritamgh wants to merge 1 commit into
1jehuang:masterfrom
ritamgh:fix/bound-subagent-waits

Conversation

@ritamgh
Copy link
Copy Markdown

@ritamgh ritamgh commented Jun 2, 2026

Summary

Direct subagent tool calls could block the parent agent indefinitely when the child session never produced a final captured answer. This adds a bounded wait with a recoverable timeout.

Changes

  • Adds optional timeout_secs to the subagent tool schema.
  • Defaults direct subagent waits to 300 seconds.
  • Returns a recoverable timeout message with the child session id.
  • Suggests resuming the child session or using swarm for durable long-running work.
  • Adds unit coverage for the timeout recovery message.

Validation

CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=cc \
CC=gcc \
RUSTFLAGS='-C linker=cc' \
cargo test -p jcode-app-core tool::task::tests

Result: 7 passed.

Copilot AI review requested due to automatic review settings June 2, 2026 13:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR adds a configurable timeout to subagent execution so that long-running calls fail fast with a guidance-rich, recoverable error message.

Changes:

  • Introduced timeout_secs in SubagentInput and exposed it in the tool JSON schema.
  • Wrapped subagent execution in tokio::time::timeout, returning a specific timeout message on expiry.
  • Added a helper to format timeout errors and a unit test validating the message contents.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +232 to +258
let final_text = match tokio::time::timeout(
Duration::from_secs(timeout_secs),
agent.run_once_capture(&params.prompt),
)
.await
{
Ok(result) => result.map_err(|err| {
logging::warn(&format!(
"[tool:subagent] subagent failed description={} type={} session_id={} model={} error={}",
params.description,
params.subagent_type,
sub_session_id,
resolved_model,
err
));
err
})?,
Err(_) => {
listener.abort();
let message = subagent_timeout_error(&params.description, &sub_session_id, timeout_secs);
logging::warn(&format!(
"[tool:subagent] {} type={} model={}",
message, params.subagent_type, resolved_model
));
return Err(anyhow::anyhow!(message));
}
};
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point. The goal of this PR is to bound the parent agent’s direct wait and return the child session_id for recovery. Dropping the future is sufficient for that wait-bound behavior here. A harder JoinHandle::abort/cleanup path would be a larger follow-up if we find subagent work continues past cancellation.

Comment thread crates/jcode-app-core/src/tool/task.rs
Comment thread crates/jcode-app-core/src/tool/task.rs
@ritamgh ritamgh force-pushed the fix/bound-subagent-waits branch from 6f9ca6d to a7a74f0 Compare June 2, 2026 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants