





















@@ -6,11 +6,13 @@ import {
66extractToolResultMediaArtifact,
77filterToolResultMediaUrls,
88HEARTBEAT_RESPONSE_TOOL_NAME,
9+embeddedAgentLog,
910type EmbeddedRunAttemptParams,
1011isToolWrappedWithBeforeToolCallHook,
1112isMessagingTool,
1213isMessagingToolSendAction,
1314normalizeHeartbeatToolResponse,
15+projectRuntimeToolInputSchema,
1416runAgentHarnessAfterToolCallHook,
1517setBeforeToolCallDiagnosticsEnabled,
1618type AnyAgentTool,
@@ -46,6 +48,16 @@ type CodexDynamicToolHookContext = {
46484749type CodexToolResultHookContext = Omit<CodexDynamicToolHookContext, "config">;
485051+type ProjectedCodexDynamicTool = {
52+tool: AnyAgentTool;
53+inputSchema: JsonValue;
54+};
55+56+type CodexDynamicToolSchemaQuarantine = {
57+tool: string;
58+violations: readonly string[];
59+};
60+4961export type CodexDynamicToolBridge = {
5062availableSpecs: CodexDynamicToolSpec[];
5163specs: CodexDynamicToolSpec[];
@@ -63,6 +75,7 @@ export type CodexDynamicToolBridge = {
6375toolMediaUrls: string[];
6476toolAudioAsVoice: boolean;
6577successfulCronAdds?: number;
78+quarantinedTools: CodexDynamicToolSchemaQuarantine[];
6679};
6780};
6881@@ -83,16 +96,28 @@ export function createCodexDynamicToolBridge(params: {
8396}): CodexDynamicToolBridge {
8497const toolResultHookContext = toToolResultHookContext(params.hookContext);
8598const toolResultMaxChars = resolveCodexDynamicToolResultMaxChars(params.hookContext);
86-const tools = params.tools.map((tool) => {
99+const availableProjection = projectCodexDynamicTools(params.tools);
100+const registeredProjection = params.registeredTools
101+ ? projectCodexDynamicTools(params.registeredTools)
102+ : availableProjection;
103+const availableTools = availableProjection.tools.map(({ tool, inputSchema }) => {
87104if (isToolWrappedWithBeforeToolCallHook(tool)) {
88105setBeforeToolCallDiagnosticsEnabled(tool, false);
89-return tool;
106+return { tool, inputSchema };
90107}
91-return wrapToolWithBeforeToolCallHook(tool, params.hookContext, { emitDiagnostics: false });
108+return {
109+tool: wrapToolWithBeforeToolCallHook(tool, params.hookContext, { emitDiagnostics: false }),
110+ inputSchema,
111+};
92112});
93-const toolMap = new Map(tools.map((tool) => [tool.name, tool]));
94-const registeredTools = params.registeredTools ?? tools;
113+const toolMap = new Map(availableTools.map(({ tool }) => [tool.name, tool]));
114+const registeredTools = registeredProjection.tools.map(({ tool }) => tool);
95115const registeredToolNames = new Set(registeredTools.map((tool) => tool.name));
116+const quarantinedTools = dedupeQuarantinedDynamicTools([
117+ ...availableProjection.quarantinedTools,
118+ ...registeredProjection.quarantinedTools,
119+]);
120+warnQuarantinedDynamicTools(quarantinedTools);
96121const telemetry: CodexDynamicToolBridge["telemetry"] = {
97122didSendViaMessagingTool: false,
98123messagingToolSentTexts: [],
@@ -101,6 +126,7 @@ export function createCodexDynamicToolBridge(params: {
101126messagingToolSourceReplyPayloads: [],
102127toolMediaUrls: [],
103128toolAudioAsVoice: false,
129+ quarantinedTools,
104130};
105131const middlewareRunner = createAgentToolResultMiddlewareRunner({
106132runtime: "codex",
@@ -114,16 +140,18 @@ export function createCodexDynamicToolBridge(params: {
114140]);
115141116142return {
117-availableSpecs: tools.map((tool) =>
143+availableSpecs: availableTools.map(({ tool, inputSchema }) =>
118144createCodexDynamicToolSpec({
119145 tool,
146+ inputSchema,
120147loading: params.loading ?? "searchable",
121148 directToolNames,
122149}),
123150),
124-specs: registeredTools.map((tool) =>
151+specs: registeredProjection.tools.map(({ tool, inputSchema }) =>
125152createCodexDynamicToolSpec({
126153 tool,
154+ inputSchema,
127155loading: params.loading ?? "searchable",
128156 directToolNames,
129157}),
@@ -257,13 +285,14 @@ export function createCodexDynamicToolBridge(params: {
257285258286function createCodexDynamicToolSpec(params: {
259287tool: AnyAgentTool;
288+inputSchema: JsonValue;
260289loading: CodexDynamicToolsLoading;
261290directToolNames: ReadonlySet<string>;
262291}): CodexDynamicToolSpec {
263292const base = {
264293name: params.tool.name,
265294description: params.tool.description,
266-inputSchema: toJsonValue(params.tool.parameters),
295+inputSchema: params.inputSchema,
267296};
268297if (params.loading === "direct" || params.directToolNames.has(params.tool.name)) {
269298return base;
@@ -274,6 +303,55 @@ function createCodexDynamicToolSpec(params: {
274303deferLoading: true,
275304};
276305}
306+307+function projectCodexDynamicTools(tools: readonly AnyAgentTool[]): {
308+tools: ProjectedCodexDynamicTool[];
309+quarantinedTools: CodexDynamicToolSchemaQuarantine[];
310+} {
311+const projectedTools: ProjectedCodexDynamicTool[] = [];
312+const quarantinedTools: CodexDynamicToolSchemaQuarantine[] = [];
313+for (const tool of tools) {
314+const projection = projectRuntimeToolInputSchema(tool.parameters, `${tool.name}.inputSchema`);
315+if (projection.violations.length > 0) {
316+quarantinedTools.push({ tool: tool.name, violations: projection.violations });
317+continue;
318+}
319+projectedTools.push({ tool, inputSchema: projection.schema as JsonValue });
320+}
321+return { tools: projectedTools, quarantinedTools };
322+}
323+324+function warnQuarantinedDynamicTools(tools: readonly CodexDynamicToolSchemaQuarantine[]): void {
325+if (tools.length === 0) {
326+return;
327+}
328+const unique = new Map<string, readonly string[]>();
329+for (const tool of tools) {
330+unique.set(tool.tool, tool.violations);
331+}
332+embeddedAgentLog.warn(
333+`codex app-server quarantined ${unique.size} dynamic ${unique.size === 1 ? "tool" : "tools"} with unsupported input schemas: ${[...unique.keys()].join(", ")}`,
334+{
335+tools: [...unique.entries()].map(([tool, violations]) => ({ tool, violations })),
336+},
337+);
338+}
339+340+function dedupeQuarantinedDynamicTools(
341+tools: readonly CodexDynamicToolSchemaQuarantine[],
342+): CodexDynamicToolSchemaQuarantine[] {
343+return [
344+ ...new Map(
345+tools.map((tool) => [
346+tool.tool,
347+{
348+tool: tool.tool,
349+violations: tool.violations,
350+},
351+]),
352+).values(),
353+];
354+}
277355function toToolResultHookContext(
278356ctx: CodexDynamicToolHookContext | undefined,
279357): CodexToolResultHookContext {
@@ -634,18 +712,6 @@ function convertToolContent(
634712];
635713}
636714637-function toJsonValue(value: unknown): JsonValue {
638-try {
639-const text = JSON.stringify(value);
640-if (!text) {
641-return {};
642-}
643-return JSON.parse(text) as JsonValue;
644-} catch {
645-return {};
646-}
647-}
648-649715function jsonObjectToRecord(value: JsonValue | undefined): Record<string, unknown> {
650716if (!value || typeof value !== "object" || Array.isArray(value)) {
651717return {};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。