Skip to content

feat(core): emit SubagentProgress for child tool/turn milestones#36

Closed
ZhiXiao-Lin wants to merge 1 commit into
feat/subagent-task-trackerfrom
feat/subagent-progress-events
Closed

feat(core): emit SubagentProgress for child tool/turn milestones#36
ZhiXiao-Lin wants to merge 1 commit into
feat/subagent-task-trackerfrom
feat/subagent-progress-events

Conversation

@ZhiXiao-Lin
Copy link
Copy Markdown
Contributor

Stacked on #35. Merge that one first; the base of this PR will retarget to `main` automatically once #35 lands.

Summary

The SubagentProgress event variant existed but was never emitted — the tracker from #35 had no mid-task signal to update from. This PR fills that gap so dashboards see meaningful intermediate state, not just Start → End.

Adds synthesize_subagent_progress() in tools/task.rs, called by the mpsc → broadcast forwarder inside TaskExecutor::execute_with_task_id. It translates two child-loop events into compact progress milestones on the parent broadcast:

  • ToolEndstatus = \"tool_completed\", metadata = { tool, exit_code, output_bytes, error_kind? }
  • TurnEndstatus = \"turn_completed\", metadata = { turn, total_tokens, prompt_tokens, completion_tokens }

Noisy events (TextDelta, ToolStart, ToolOutputDelta, nested Subagent*) are intentionally not translated — consumers needing token-level streaming should subscribe to the raw event stream.

The original child events are still forwarded unchanged; progress is additive.

What this unlocks

let snap = session.subagent_task(\"task-abc\").await.unwrap();
for entry in &snap.progress {
    println!(\"{} - {}\", entry.timestamp_ms, entry.status);
    // e.g. \"1731234567000 - tool_completed\" with metadata { tool: \"bash\", exit_code: 0 }
}
println!(\"last update: {}\", snap.updated_ms);

Test plan

  • 4 new unit tests for synthesize_subagent_progress cover ToolEnd, ToolEnd with error_kind, TurnEnd, and the negative cases (TextDelta / ToolStart / TurnStart / nested SubagentStart).
  • Extended parallel_task_executor_emits_subagent_events_for_each_child to assert at least 2 turn_completed progress events arrive on the broadcast for a 2-task parallel run.
  • New integration test subagent_progress_events_accumulate_in_tracker verifies progress events flow through RuntimeEventSink into the tracker's progress Vec.
  • cargo test --lib -p a3s-code-core: 1656 passed / 0 failed (4 new).
  • cargo test --tests -p a3s-code-core: all integration binaries green.
  • cargo clippy --lib --tests -p a3s-code-core: no new warnings.
  • cargo fmt clean.

Out of scope (follow-ups)

The `SubagentProgress` event variant exists but until now was never sent —
the subagent task tracker introduced in the previous commit observed
Start and End but had no mid-task signal to update from.

Adds a `synthesize_subagent_progress()` helper called by the mpsc → broadcast
forwarder inside `TaskExecutor::execute_with_task_id`. It translates two
child-loop events into compact progress milestones on the parent broadcast:

- `ToolEnd` → `status = "tool_completed"`, metadata `{ tool, exit_code,
  output_bytes, error_kind? }`
- `TurnEnd` → `status = "turn_completed"`, metadata `{ turn, total_tokens,
  prompt_tokens, completion_tokens }`

Noisy events (TextDelta / ToolStart / ToolOutputDelta / nested Subagent*)
are intentionally not translated — consumers needing token-level detail
should subscribe to the raw event stream directly.

The original child events are still forwarded unchanged; progress is
additive, so downstream consumers see both the synthesized milestone
and the underlying event.
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