Skip to content

fix(subagent): inherit parent tools and abort on Ctrl+C - #350

Merged
F16shen merged 2 commits into
AI-Shell-Team:mainfrom
F16shen:fix/subagent-tool-inherit-and-cancel
Jul 13, 2026
Merged

fix(subagent): inherit parent tools and abort on Ctrl+C#350
F16shen merged 2 commits into
AI-Shell-Team:mainfrom
F16shen:fix/subagent-tool-inherit-and-cancel

Conversation

@F16shen

@F16shen F16shen commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Spawn inherits filtered parent Tool Arcs (Claude Code–style) instead of re-registering per-type factories, so allowlist agents keep skill/WebFetch when present and bash binds the child cancel token.
  • Raw-mode Ctrl+C (0x03) marks PTY cancel as user interrupt; bash cancels the session and short-circuits, the tool loop stops before another LLM turn, and the shell prints a single 已中断.

Test plan

  • cargo test -p aish-tools --lib -- test_user_interrupt_cancels_session
  • cargo test -p aish-tools --test agent_tool_test
  • cargo test -p aish-llm --lib -- test_loop_stops_after_tool_cancels
  • Manual: ;必须用 Agent(subagent_type=explore) 委派。子 agent 只用 bash 执行:sleep 90。 → wait for 🔧 bash (sleep 90) → Ctrl+C once → expect one 已中断, no further explore/main “timeout” narrative

Summary by CodeRabbit

  • New Features
    • Sub-agents now automatically inherit the parent session’s permitted tools, with support for tools to customize what gets created for sub-sessions.
    • Tools can run in sub-agent sessions while preserving shared resources like PTYs and secrets.
  • Bug Fixes
    • Tool execution loops now stop immediately when cancellation occurs during a tool run.
    • Ctrl+C is now treated as a user interruption consistently, producing consistent “interrupted/cancelled” outcomes without extra follow-up processing.
  • Improvements
    • Cancellation messaging is now localized to avoid duplicate or hard-coded text.

Spawn filtered parent Tool Arcs instead of re-registering factories, and
propagate raw-mode Ctrl+C (0x03) into session cancel so explore stops
instead of inventing a timeout narrative.
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the pull request. A maintainer will review it when available.

Please keep the PR focused, explain the why in the description, and make sure local checks pass before requesting review.

Contribution guide: https://github.com/AI-Shell-Team/aish/blob/main/CONTRIBUTING.md

@github-actions

Copy link
Copy Markdown
Contributor

This pull request description looks incomplete. Please update the missing sections below before review.

Missing items:

  • User-visible Changes
  • Compatibility
  • Testing
  • Change Type
  • Scope

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 97009806-fe71-42fb-9718-4422a5f0c38a

📥 Commits

Reviewing files that changed from the base of the PR and between 19f7ab8 and e6fdd2e.

📒 Files selected for processing (2)
  • crates/aish-shell/src/app.rs
  • crates/aish-tools/src/bash/bash.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/aish-shell/src/app.rs
  • crates/aish-tools/src/bash/bash.rs

📝 Walkthrough

Walkthrough

Sub-agent spawning now inherits filtered parent tools through shared handles, with optional child-specific tool instances. Cancellation state distinguishes user interrupts, propagates through PTY and Bash execution, stops further LLM turns, and produces localized interruption output.

Changes

Sub-agent tools and cancellation

Layer / File(s) Summary
Interrupt state and PTY propagation
crates/aish-pty/src/types.rs, crates/aish-pty/src/executor.rs, crates/aish-pty/src/persistent.rs
CancelToken records user interrupts, and Ctrl-C paths mark that state before interrupting child processes.
Shared tool contracts and inheritance
crates/aish-llm/src/agents/*, crates/aish-llm/src/session.rs, crates/aish-llm/src/types.rs
spawn_builtin inherits filtered parent tools, optionally adapts them for child sessions, and tests verify inherited tool sets and hooks.
Child Bash cancellation
crates/aish-tools/src/bash/bash.rs, crates/aish-llm/src/session.rs
BashTool shares PTY resources with child-scoped cancellation and returns short-circuit results for user interrupts.
Agent loop and shell handling
crates/aish-tools/src/agent_tool/*, crates/aish-llm/src/agents/tool_loop.rs, crates/aish-shell/src/app.rs, crates/aish-tools/tests/agent_tool_test.rs
Cancelled sub-agent results propagate to the parent, tool loops stop before another model turn, and shell output uses one localized interruption message.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AishShell
  participant AgentTool
  participant BashTool
  participant LlmSession
  User->>AishShell: Press Ctrl-C
  AishShell->>BashTool: Cancel PTY command
  BashTool-->>LlmSession: Return user_cancelled short-circuit
  LlmSession-->>AgentTool: Report cancelled result
  AgentTool-->>AishShell: Cancel parent session
  AishShell-->>User: Render shell.interrupted
Loading

Possibly related PRs

Poem

I’m a bunny with tools in a child-session nest,
Shared arcs and soft cancels now handle the rest.
Ctrl-C leaves a clear little trail,
No extra thinking when interrupts prevail.
“shell.interrupted!” I cheer with delight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main changes: subagent tool inheritance and Ctrl+C cancellation handling.
Docstring Coverage ✅ Passed Docstring coverage is 93.55% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
crates/aish-llm/src/agents/tool_loop.rs (1)

425-457: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that the follow-up response remains queued.

The current assertions do not prove that another model turn was avoided. Consume the test queue after cancellation and verify the follow-up response is still present.

Proposed test assertion
         assert!(
             session.cancellation_token().is_cancelled(),
             "session token must stay cancelled"
         );
+
+        let remaining = session
+            .chat_completion_raw(&[], None, false, None, None)
+            .await
+            .expect("follow-up response must remain queued");
+        let LlmResponse::Json(json) = remaining else {
+            panic!("expected queued JSON response");
+        };
+        let (content, _, _, _) = StreamParser::parse_response(&json);
+        assert_eq!(
+            content.as_deref(),
+            Some("sleep was interrupted by built-in timeout")
+        );
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aish-llm/src/agents/tool_loop.rs` around lines 425 - 457, Update
test_loop_stops_after_tool_cancels_session to consume the session’s queued
responses after asserting cancellation and verify the follow-up
mock_text_response remains unconsumed. Use the session’s existing response-queue
access mechanism and assert that the remaining response is the expected “sleep
was interrupted by built-in timeout” message, proving no additional model turn
occurred.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/aish-llm/src/agents/spawn.rs`:
- Around line 132-136: Validate the tool returned by for_sub_session before
registering it: compare its identity/name against the inherited allowlist, and
reject or skip it when the adapted tool is not allowlisted. Apply this
fail-closed check in the inherited_tools loop before sub.register_shared_tool,
preserving the original allowlist rather than trusting the adapted tool’s name.

In `@crates/aish-shell/src/app.rs`:
- Around line 2021-2025: Short-circuit cancelled successful results before
normal completion handling. In the response-processing branches around the
cancellation checks near the existing
`self.ai_handler.cancellation_token().is_cancelled()` logic, return or otherwise
exit the turn after printing the interrupted message so execution cannot reach
plan approval, success handling, or `record_history(input, 0)`; apply the same
fix to both affected branches.

In `@crates/aish-tools/src/bash/bash.rs`:
- Around line 417-419: Before the cancellation early return in the command
execution flow, synchronize the persistent PTY working directory using the same
cwd-update logic used on normal completion. Update the branch around
`self.outcome_if_cancelled(&cancel_token)` so cancellation preserves the shell’s
latest cwd before returning.

---

Nitpick comments:
In `@crates/aish-llm/src/agents/tool_loop.rs`:
- Around line 425-457: Update test_loop_stops_after_tool_cancels_session to
consume the session’s queued responses after asserting cancellation and verify
the follow-up mock_text_response remains unconsumed. Use the session’s existing
response-queue access mechanism and assert that the remaining response is the
expected “sleep was interrupted by built-in timeout” message, proving no
additional model turn occurred.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5dd84e65-73a0-4972-adf1-badabfa1c41a

📥 Commits

Reviewing files that changed from the base of the PR and between 3d289e2 and 19f7ab8.

📒 Files selected for processing (13)
  • crates/aish-llm/src/agents/registry.rs
  • crates/aish-llm/src/agents/spawn.rs
  • crates/aish-llm/src/agents/tool_loop.rs
  • crates/aish-llm/src/session.rs
  • crates/aish-llm/src/types.rs
  • crates/aish-pty/src/executor.rs
  • crates/aish-pty/src/persistent.rs
  • crates/aish-pty/src/types.rs
  • crates/aish-shell/src/app.rs
  • crates/aish-tools/src/agent_tool/agent_tool.rs
  • crates/aish-tools/src/bash/bash.rs
  • crates/aish-tools/src/lib.rs
  • crates/aish-tools/tests/agent_tool_test.rs

Comment thread crates/aish-llm/src/agents/spawn.rs
Comment thread crates/aish-shell/src/app.rs Outdated
Comment thread crates/aish-tools/src/bash/bash.rs
Cancelled Ok("") must not run plan approval or record_history as success,
and persistent-PTY cancel should still apply the command's cwd.
@F16shen
F16shen merged commit e345a3e into AI-Shell-Team:main Jul 13, 2026
8 checks passed
@F16shen
F16shen deleted the fix/subagent-tool-inherit-and-cancel branch July 13, 2026 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant