-
Notifications
You must be signed in to change notification settings - Fork 0
Description
SubAgentEventStream.recv() does not forward intermediate events from sub-agents
Environment
- a3s-code version: 1.5.7
- Python: 3.11
- OS: Linux
Description
When using Orchestrator.spawn_subagent() to launch sub-agents, the SubAgentEventStream returned by handle.events() does not deliver intermediate events such as text_delta (LLM thinking text), tool_execution_started, or tool_execution_completed.
Only turn_start events are received. All other calls to stream.recv() return None until handle.state() transitions to Completed, at which point the final output is available via handle.wait().
This makes it impossible to monitor sub-agent reasoning and tool usage in real-time, which is critical for debugging and observability in multi-agent orchestration scenarios.
Minimal Reproduction
from a3s_code import Agent, Orchestrator, SubAgentConfig
agent = Agent.create("config.hcl")
orchestrator = Orchestrator.create(agent=agent)
handle = orchestrator.spawn_subagent(SubAgentConfig(
agent_type="my-scoring-agent",
prompt="Analyze the input and return a JSON result.",
workspace="/path/to/workspace",
permissive=True,
skill_dirs=["./skills"],
agent_dirs=["./agents"],
))
stream = handle.events()
event_count = 0
text_delta_count = 0
tool_event_count = 0
while True:
event = stream.recv(timeout_ms=2000)
if event is None:
st = str(handle.state())
if 'Completed' in st or 'Failed' in st:
print(f"Agent completed. Events: {event_count}, text_delta: {text_delta_count}, tool_events: {tool_event_count}")
break
continue
event_count += 1
etype_outer = event.get('event_type', '')
etype_inner = event.get('type', '')
if etype_inner == 'text_delta':
text_delta_count += 1
elif etype_outer in ('tool_execution_started', 'tool_execution_completed'):
tool_event_count += 1
print(f"Event: event_type={etype_outer!r}, type={etype_inner!r}")
result = handle.wait()
print(f"Final output: {result[:200]}")Actual Output
Event: event_type='sub_agent_internal_event', type='turn_start'
Agent completed. Events: 1, text_delta: 0, tool_events: 0
Final output: {"total_score": 14, ...}
Only 1 event (turn_start) is received. The sub-agent clearly performed LLM reasoning and tool calls (evidenced by the structured output), but none of those intermediate events were forwarded through the event stream.
Expected Output
We expect stream.recv() to deliver detailed intermediate events with full content, enabling real-time observability of sub-agent reasoning and tool usage:
# 1. Thinking starts
Event: {
'event_type': 'sub_agent_internal_event',
'type': 'turn_start',
'turn': 1
}
# 2. LLM reasoning text streamed incrementally
Event: {
'event_type': 'sub_agent_internal_event',
'type': 'text_delta',
'text': 'I will call the scoring-video-adapter Skill with the following parameters...'
}
# 3. Tool call initiated — with tool name and full input arguments
Event: {
'event_type': 'tool_execution_started',
'tool_name': 'Skill',
'tool_id': 'tool_abc123',
'input': {
'name': 'scoring-video-adapter',
'args': 'video_path: /path/to/video.mp4\nscoring_items_file: /path/to/items.json\nkeyframes_dir: /path/to/keyframes'
}
}
# 4. (Optional) Incremental tool output streamed as it's produced
Event: {
'event_type': 'sub_agent_internal_event',
'type': 'tool_output_delta',
'tool_id': 'tool_abc123',
'delta': '{"status": "processing", "progress": 0.5}'
}
# 5. Tool call completed — with full result, duration, and exit status
Event: {
'event_type': 'tool_execution_completed',
'tool_name': 'Skill',
'tool_id': 'tool_abc123',
'exit_code': 0,
'result': '{"status": "success", "findings": [...], "coverage": 1.0, ...}',
'duration_ms': 45000
}
# 6. Post-tool reasoning text
Event: {
'event_type': 'sub_agent_internal_event',
'type': 'text_delta',
'text': 'Based on the analysis results, I will now calculate scores...\n{"total_score": 14, ...}'
}
# 7. Thinking ends
Event: {
'event_type': 'sub_agent_internal_event',
'type': 'turn_end',
'turn': 1,
'usage': {'input_tokens': 5000, 'output_tokens': 2000}
}
Agent completed. Events: 50+, text_delta: 40+, tool_events: 2+
Key requirements for each event type:
| Event | Required Fields | Purpose |
|---|---|---|
text_delta |
text (full incremental content) |
Monitor LLM reasoning in real-time |
tool_execution_started |
tool_name, tool_id, input (full arguments/prompt) |
See what the sub-agent is calling and with what parameters |
tool_output_delta |
tool_id, delta |
Stream long-running tool output incrementally |
tool_execution_completed |
tool_name, tool_id, result (full output), exit_code, duration_ms |
Verify tool success/failure and inspect returned data |
turn_end |
turn, usage (token counts) |
Track resource consumption per reasoning round |
The SDK's own EventType enum already defines TEXT_DELTA, TOOL_START, TOOL_END, and TOOL_OUTPUT_DELTA, suggesting these event types are architecturally supported but not forwarded for sub-agents.
Additional Context
- The same
handle.events()API is used in both dashboard (Rich live display) and file-based logging modes — both receive the same limited events. - A 3-second drain period after detecting
Completedstate yields no additional events. handle.wait()correctly returns the full final output, confirming the sub-agent executed successfully.- This has been observed consistently across multiple sub-agent types and configurations.
Feature Request
Please consider one of the following:
- Enable intermediate event forwarding by default for
SubAgentEventStream - Add a
SubAgentConfigoption (e.g.,forward_events=Trueorevent_detail_level="full") to opt-in to intermediate event forwarding - Document the current limitation if intermediate event forwarding is intentionally disabled for performance or architectural reasons