Skip to content

ductor v0.10.0

Choose a tag to compare

@PleasePrompto PleasePrompto released this 01 Mar 08:38
· 262 commits to main since this release

ductor v0.10.0

Async sub-agent results now flow back into the main agent's conversation — with full context.

How /agents works now

Each sub-agent is a full agent with its own Telegram chat — you can open it and talk to it directly, just like your main agent. It has its own workspace, memory, and provider configuration.

The new part: when the main agent delegates a task to a sub-agent, the result flows back into your main conversation with full context. Direct interaction with a sub-agent stays in its own chat.

Two ways to use sub-agents

1. Direct chat — open the sub-agent's Telegram bot and talk to it like any other agent. Own context, own conversation.

2. Delegation from main agent — the main agent sends a task, the result comes back into your main chat:

Synchronous delegation (blocking)

You: "Ask codex-agent to check the API tests"
  → Main agent calls sub-agent (blocks)
  → Sub-agent executes, returns result as tool output
  → Main agent processes result in the SAME conversation turn
  → Main agent responds to you with full context

Asynchronous delegation (non-blocking) — fixed in v0.10.0

You: "Give codex-agent a task: migrate the database schema"
  → Main agent fires off task, keeps chatting with you
  → Sub-agent works in the background (can take minutes)
  → Sub-agent finishes
  → Result is injected into the main agent's CURRENT active session
  → Main agent processes with full conversation context
  → Main agent reports the result to you

Before v0.10.0: The async result arrived in a new, context-less session. The main agent didn't know what you originally asked or why it delegated the task. It responded blindly.

Now: The result is injected into whatever session is currently active (even if you did /new or switched providers in the meantime). The prompt is self-contained — it includes the original task description and the sub-agent's response. The main agent always has enough context to give you a meaningful answer.

What happens under the hood

  1. When the main agent sends an async task, the full task message is preserved
  2. When the result arrives, the per-chat lock is acquired (no concurrent session access)
  3. The current active session is resolved via get_active() — always the latest, not the original
  4. A self-contained prompt is built: original task + sub-agent result + session hint
  5. The CLI resumes the current session with --resume
  6. Session metrics (cost, tokens, session ID) are updated after execution
  7. Telegram shows "typing..." while the main agent processes the result

/session vs /agents — when to use which

/session — independent conversations with their own context

Named sessions let you start separate conversations inside the same Telegram chat, each with its own context. They are completely independent from your main chat — the main agent doesn't know about them and they don't share context with it.

/session Fix the login bug              → starts session "firmowl" (own context)
/session @codex Refactor the parser     → starts session "pureray" on Codex (own context)
@firmowl Also check the tests          → follow-up in firmowl's context
/sessions                               → list/manage all active sessions

/agents — full agents you can talk to directly or delegate to

Sub-agents have their own Telegram chat — use them directly for independent work, or let the main agent delegate tasks. Delegated results flow back into the main chat with full context.

# Direct chat (own context):
Open codex-agent's Telegram bot → "Refactor the parser module"

# Delegation (main chat context):
Main chat → "Ask codex-agent to write tests for the API module"
  → Result flows back into main agent's session
  → Main agent tells you the result
/session (Named Sessions) /agents (Sub-Agents)
What it is Independent conversation with its own context Full agent with its own Telegram chat
Context Own context, independent from main chat Own context in direct chat — delegation flows back into main chat context
Workspace Same workspace as main agent Own workspace, own memory
Provider Any provider/model per session Own default provider/model
Result Telegram reply (standalone) Direct chat: standalone. Delegation: injected into main agent's session.
Use case Parallel side-conversations you manage yourself Use directly or let the main agent orchestrate
Setup None — just /session <prompt> ductor agents add <name> + BotFather token

Rule of thumb: Use /session when you want a separate conversation on the side. Use sub-agents when you want a dedicated agent you can either talk to directly or let the main agent delegate to.

Technical changes

  • bus.py: AsyncInterAgentResult now carries original_message — the full task text sent to the sub-agent, populated in all code paths (success, timeout, error)
  • app.py: on_async_interagent_result() acquires the per-chat sequential lock and shows a typing indicator while the result is being processed
  • core.py: handle_async_interagent_result() resumes the current active session, builds a self-contained prompt, and updates session metrics after execution
  • test_interagent.py: Updated tests for new API + 2 new tests (original message in prompt, resume of active session)
  • README.md: Added "Sessions vs. Sub-Agents" comparison table

Upgrade

ductor upgrade
# or
pip install --upgrade ductor