import type {
AgentTool,
AgentToolResult,
AgentToolUpdateCallback,
} from "@mariozechner/pi-agent-core"; import type { ToolDefinition } from "@mariozechner/pi-coding-agent"; import { logDebug, logError } from "../logger.js"; import { redactToolDetail } from "../logging/redact.js"; import { isPlainObject } from "../utils.js"; import { sanitizeForConsole } from "./console-sanitize.js"; import type { ClientToolDefinition } from "./pi-embedded-runner/run/params.js"; import type { HookContext } from "./pi-tools.before-tool-call.js"; import {
isToolWrappedWithBeforeToolCallHook,
runBeforeToolCallHook,
} from "./pi-tools.before-tool-call.js"; import { normalizeToolName } from "./tool-policy.js"; import { jsonResult, payloadTextResult } from "./tools/common.js";
/** * Coerce tool-call params into a plain object. * * Some providers (e.g. Gemini) stream tool-call arguments as incremental * string deltas. By the time the framework invokes the tool's `execute` * callback the accumulated value may still be a JSON **string** rather than * a parsed object. `isPlainObject()` returns `false` for strings, which * caused the params to be silently replaced with `{}`. * * This helper tries `JSON.parse` when the value is a string and falls back * to an empty object only when parsing genuinely fails.
*/ function coerceParamsRecord(value: unknown): Record<string, unknown> { if (isPlainObject(value)) { return value;
} if (typeof value === "string") { const trimmed = value.trim(); if (trimmed.length > 0) { try { const parsed: unknown = JSON.parse(trimmed); if (isPlainObject(parsed)) { return parsed;
}
} catch { // not valid JSON – fall through to empty object
}
}
} return {};
}
// Convert client tools (OpenResponses hosted tools) to ToolDefinition format // These tools are intercepted to return a "pending" result instead of executing
export function toClientToolDefinitions(
tools: ClientToolDefinition[],
onClientToolCall?: (toolName: string, params: Record<string, unknown>) => void,
hookContext?: HookContext,
): ToolDefinition[] { return tools.map((tool) => { const func = tool.function; return {
name: func.name,
label: func.name,
description: func.description ?? "",
parameters: func.parameters as ToolDefinition["parameters"],
execute: async (...args: ToolExecuteArgs): Promise<AgentToolResult<unknown>> => { const { toolCallId, params } = splitToolExecuteArgs(args); const outcome = await runBeforeToolCallHook({
toolName: func.name,
params,
toolCallId,
ctx: hookContext,
}); if (outcome.blocked) { thrownew Error(outcome.reason);
} const adjustedParams = outcome.params; const paramsRecord = coerceParamsRecord(adjustedParams); // Notify handler that a client tool was called if (onClientToolCall) {
onClientToolCall(func.name, paramsRecord);
} // Return a pending result - the client will execute this tool return jsonResult({
status: "pending",
tool: func.name,
message: "Tool execution delegated to client",
});
},
} satisfies ToolDefinition;
});
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-05-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.