chore: common tool event logging, reduce per-AgentRuntime code

This commit is contained in:
David Garson 2026-01-28 22:54:57 -07:00
parent 800c211582
commit b927361650
7 changed files with 51 additions and 19 deletions

View File

@ -252,11 +252,7 @@
"@sinclair/typebox": "0.34.47", "@sinclair/typebox": "0.34.47",
"hono": "4.11.4", "hono": "4.11.4",
"tar": "7.5.4" "tar": "7.5.4"
}, }
"ignoredBuiltDependencies": [
"core-js",
"node-llama-cpp"
]
}, },
"vitest": { "vitest": {
"coverage": { "coverage": {

View File

@ -930,15 +930,6 @@ export async function runSdkAgent(params: SdkRunnerParams): Promise<SdkRunnerRes
undefined) undefined)
: undefined; : undefined;
// Log tool errors at warn level so they appear in logs
if (isError && phase === "result") {
log.warn("Tool execution failed", {
tool: normalizedName.name,
toolCallId,
error: toolText?.slice(0, 500) ?? "Unknown error",
});
}
emitEvent("tool", { emitEvent("tool", {
phase, phase,
name: normalizedName.name, name: normalizedName.name,

View File

@ -207,10 +207,6 @@ export function handleToolExecutionEnd(
}, },
}); });
ctx.log.debug(
`embedded run tool end: runId=${ctx.params.runId} tool=${toolName} toolCallId=${toolCallId}`,
);
if (ctx.params.onToolResult && ctx.shouldEmitToolOutput()) { if (ctx.params.onToolResult && ctx.shouldEmitToolOutput()) {
const outputText = extractToolResultText(sanitizedResult); const outputText = extractToolResultText(sanitizedResult);
if (outputText) { if (outputText) {

View File

@ -0,0 +1,43 @@
import { createSubsystemLogger } from "../logging/subsystem.js";
const log = createSubsystemLogger("agent/tools");
export type AgentEventData = { stream: string; data: Record<string, unknown> };
/**
* Handle tool events from onAgentEvent callback.
* Call this from onAgentEvent handlers to get consistent tool logging.
*/
export function logToolEvent(evt: AgentEventData, runId?: string): void {
if (evt.stream !== "tool") return;
const phase = evt.data.phase as string | undefined;
const toolName = String(evt.data.name ?? "unknown");
const toolCallId = String(evt.data.toolCallId ?? "");
switch (phase) {
case "start":
log.debug(`tool start: ${toolName}`, {
runId,
toolCallId,
args: evt.data.args,
});
break;
case "result": {
const isError = Boolean(evt.data.isError);
if (isError) {
const result = evt.data.result ?? evt.data.resultText;
const errorPreview =
typeof result === "string" ? result.slice(0, 500) : String(result).slice(0, 500);
log.warn(`tool failed: ${toolName}`, {
runId,
toolCallId,
error: errorPreview,
});
} else {
log.debug(`tool complete: ${toolName}`, { runId, toolCallId });
}
break;
}
}
}

View File

@ -336,7 +336,7 @@ export async function runAgentWithUnifiedFailover(
: { enforceFinalTag: effectiveEnforceFinalTag }, : { enforceFinalTag: effectiveEnforceFinalTag },
}); });
log.debug("Agent run completed", { log.info("Agent run completed", {
sessionId: params.sessionId, sessionId: params.sessionId,
runId: params.runId, runId: params.runId,
runtime: runtimeKind, runtime: runtimeKind,

View File

@ -5,6 +5,7 @@ import { runCliAgent } from "../../agents/cli-runner.js";
import { getCliSessionId } from "../../agents/cli-session.js"; import { getCliSessionId } from "../../agents/cli-session.js";
import { isCliProvider } from "../../agents/model-selection.js"; import { isCliProvider } from "../../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js"; import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js";
import { logToolEvent } from "../../agents/tool-event-logger.js";
import { runAgentWithUnifiedFailover } from "../../agents/unified-agent-runner.js"; import { runAgentWithUnifiedFailover } from "../../agents/unified-agent-runner.js";
import { import {
isCompactionFailureError, isCompactionFailureError,
@ -362,6 +363,8 @@ export async function runAgentTurnWithFallback(params: {
} }
: undefined, : undefined,
onAgentEvent: async (evt) => { onAgentEvent: async (evt) => {
// Log tool events centrally.
logToolEvent(evt, runId);
// Trigger typing when tools start executing. // Trigger typing when tools start executing.
if (evt.stream === "tool") { if (evt.stream === "tool") {
const phase = typeof evt.data.phase === "string" ? evt.data.phase : ""; const phase = typeof evt.data.phase === "string" ? evt.data.phase : "";

View File

@ -18,6 +18,7 @@ import {
resolveThinkingDefault, resolveThinkingDefault,
} from "../agents/model-selection.js"; } from "../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js"; import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
import { logToolEvent } from "../agents/tool-event-logger.js";
import { import {
runAgentWithUnifiedFailover, runAgentWithUnifiedFailover,
type UnifiedAgentRunResult, type UnifiedAgentRunResult,
@ -456,6 +457,8 @@ export async function agentCommand(
// Callbacks // Callbacks
onAgentEvent: (evt) => { onAgentEvent: (evt) => {
// Log tool events centrally.
logToolEvent(evt, runId);
// Track lifecycle end for fallback emission below. // Track lifecycle end for fallback emission below.
if ( if (
evt.stream === "lifecycle" && evt.stream === "lifecycle" &&