feat(agent): add real-time streaming updates to exec tool

Utilize the onUpdate callback from pi-agent-core's AgentTool interface
to stream output updates in real-time while commands are executing.

- Accept onUpdate as 4th parameter in execute function
- Emit tailBuffer updates on each stdout/stderr data event
- Stop emitting updates once command is backgrounded (yielded)

This enables UI to show live command output progress via the
tool_execution_update agent event.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jiayuan 2026-01-31 18:28:58 +08:00
parent cbb13b26d1
commit 14bcebee2a

View file

@ -47,7 +47,7 @@ export function createExecTool(defaultCwd?: string): AgentTool<typeof ExecSchema
description:
"Execute a shell command. If the command doesn't complete within yieldMs (default 10s), it automatically runs in background and returns a process ID with any output collected so far. Use 'process output <id>' to check output, 'process status <id>' to check status, 'process stop <id>' to terminate.",
parameters: ExecSchema,
execute: async (_toolCallId, args, signal) => {
execute: async (_toolCallId, args, signal, onUpdate) => {
const { command, cwd, timeoutMs, yieldMs = DEFAULT_YIELD_MS } = args as ExecArgs;
const effectiveCwd = cwd || defaultCwd;
@ -67,6 +67,29 @@ export function createExecTool(defaultCwd?: string): AgentTool<typeof ExecSchema
// This ensures output is captured even before yield timeout
const processId = registerProcess(child, command, effectiveCwd, "exec");
// Stream output updates via onUpdate callback
// Note: appendOutput is already called by registerProcess, we just emit updates here
const emitUpdate = () => {
if (!onUpdate || yielded) return;
const entry = PROCESS_REGISTRY.get(processId);
if (!entry) return;
onUpdate({
content: [{ type: "text", text: entry.tailBuffer || "(running...)" }],
details: {
output: entry.tailBuffer,
exitCode: null,
truncated: false,
processId,
},
});
};
// Listen to stdout/stderr to trigger onUpdate (data collection is handled by registerProcess)
if (onUpdate) {
child.stdout?.on("data", emitUpdate);
child.stderr?.on("data", emitUpdate);
}
// Timeout handling (hard kill)
if (timeoutMs && timeoutMs > 0) {
timeout = setTimeout(() => {