Skip to content

feat: delegate tasks to external AI CLIs (Claude, Gemini)#60

Merged
priyanshujain merged 19 commits intomasterfrom
feat-delegate-task
Mar 11, 2026
Merged

feat: delegate tasks to external AI CLIs (Claude, Gemini)#60
priyanshujain merged 19 commits intomasterfrom
feat-delegate-task

Conversation

@priyanshujain
Copy link
Copy Markdown
Collaborator

Summary

  • Add delegate_task tool that delegates complex tasks to external AI CLIs (Claude, Gemini, Codex) with user approval
  • Auto-detects installed CLIs via exec.LookPath at registration time — no config needed
  • Supports sync and async (background) execution with check_task for retrieving results
  • Async mode uses streaming output with throttled progress notifications (max 1 per 30s)
  • Structured spec support: steps, output format, and max budget (Claude --max-budget-usd)
  • In-memory TaskTracker with max 3 concurrent background tasks
  • Strips CLAUDECODE env var for Claude CLI to avoid nested session detection

New files

File Purpose
agent/tools/agent_runner.go CLI detection + execution
agent/tools/agent_stream.go Streaming NDJSON runner for progress
agent/tools/delegate_task.go delegate_task tool (sync/async, approval, specs)
agent/tools/task_tracker.go In-memory background task state
agent/tools/check_task.go check_task tool (read-only)

Modified files

File Change
agent/tools/prompt.go Conditional "Task Delegation" system prompt section
internal/cli/chat.go Register delegate_task + check_task in CLI
channel/telegram/session.go Register delegate_task + check_task in Telegram

Test plan

  • 59 new tests, all passing with -race detector
  • Streaming path tested via mock StreamRunnerInterface
  • Progress throttling verified (50 rapid events → 1 notification)
  • Async approval, denial, timeout, max concurrent all tested
  • Edge cases: malformed JSON, empty task, unknown agent, no agents available
  • Integration tests for real Claude/Gemini CLIs (gated with t.Skip)

TaskTracker was being recreated per message since registerDelegateTool
is called from newAgent() which runs per message. Moved tracker to
SessionManager struct so async task state persists across messages.
StreamRunnerInterface.RunStream() was missing RunOption variadic param,
so max_budget_usd was silently dropped in the async streaming path.
Added mutex to mockInteractor and a linkCh channel so
TestGWSExecute_MissingScopeSignaled no longer polls shared state.
@priyanshujain priyanshujain merged commit 99f9890 into master Mar 11, 2026
10 checks passed
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.

1 participant