tool.call.progress is declared in crates/stella-core/src/bus/names.rs and emitted nowhere. It cannot be emitted: Tool::execute(&self, input, root) (crates/stella-tools/src/registry.rs) has no event handle, so only the registry itself can author events onto the stream.
This is #2716 §4, deferred from PR #3281.
What to build
Change Tool::execute to take a ToolCtx carrying the workspace root and an event emitter:
async fn execute(&self, input: &Value, ctx: &ToolCtx) -> ToolOutput;
Hard constraint
Event emission must never ride ToolOutput. The loop detector keys on output bytes, so a timing or progress marker in the output string makes identical calls look distinct and defeats it. This has bitten before.
Scope note
Mechanical but wide: every Tool impl in stella-tools changes signature, plus the CLI's session-layer tools. Post-#3244 that is twelve built-ins rather than seventy-nine, so this is materially cheaper than it once was.
Witness
A test that runs a tool which emits progress and asserts the event reaches the bus, plus one asserting an undeclared event name is dropped rather than forwarded.
Refs #2716, #2694.
tool.call.progressis declared incrates/stella-core/src/bus/names.rsand emitted nowhere. It cannot be emitted:Tool::execute(&self, input, root)(crates/stella-tools/src/registry.rs) has no event handle, so only the registry itself can author events onto the stream.This is #2716 §4, deferred from PR #3281.
What to build
Change
Tool::executeto take aToolCtxcarrying the workspace root and an event emitter:events: Vec<String>field tostella_protocol::ToolContractdeclaring what a tool may emit; the registry drops undeclared names, so the contract is the allowlist. (The field was deliberately omitted from feat(stella-tools): ToolContract, AuthzGate port and the gate decorator — the governance half of tool-first #3281 rather than shipped unread — add it here, with this as its consumer.)tool.call.progressfrom at least one long-running built-in and assert it in a test.Hard constraint
Event emission must never ride
ToolOutput. The loop detector keys on output bytes, so a timing or progress marker in the output string makes identical calls look distinct and defeats it. This has bitten before.Scope note
Mechanical but wide: every
Toolimpl instella-toolschanges signature, plus the CLI's session-layer tools. Post-#3244 that is twelve built-ins rather than seventy-nine, so this is materially cheaper than it once was.Witness
A test that runs a tool which emits progress and asserts the event reaches the bus, plus one asserting an undeclared event name is dropped rather than forwarded.
Refs #2716, #2694.