
























@@ -1,6 +1,13 @@
11import type { AgentToolResult } from "@mariozechner/pi-agent-core";
22import type { AnyAgentTool } from "openclaw/plugin-sdk/agent-harness";
3-import { describe, expect, it, vi } from "vitest";
3+import { afterEach, describe, expect, it, vi } from "vitest";
4+import {
5+initializeGlobalHookRunner,
6+resetGlobalHookRunner,
7+} from "../../../../src/plugins/hook-runner-global.js";
8+import { createMockPluginRegistry } from "../../../../src/plugins/hooks.test-helpers.js";
9+import { createEmptyPluginRegistry } from "../../../../src/plugins/registry.js";
10+import { setActivePluginRegistry } from "../../../../src/plugins/runtime.js";
411import { createCodexDynamicToolBridge } from "./dynamic-tools.js";
512import type { JsonValue } from "./protocol.js";
613@@ -58,6 +65,11 @@ async function handleMessageToolCall(
5865});
5966}
606768+afterEach(() => {
69+resetGlobalHookRunner();
70+setActivePluginRegistry(createEmptyPluginRegistry());
71+});
72+6173describe("createCodexDynamicToolBridge", () => {
6274it.each([
6375{ toolName: "tts", mediaUrl: "/tmp/reply.opus", audioAsVoice: true },
@@ -152,4 +164,82 @@ describe("createCodexDynamicToolBridge", () => {
152164messagingToolSentTargets: [],
153165});
154166});
167+168+it("applies codex app-server tool_result extensions from the active plugin registry", async () => {
169+const registry = createEmptyPluginRegistry();
170+const factory = async (codex: {
171+on: (
172+event: "tool_result",
173+handler: (event: any) => Promise<{ result: AgentToolResult<unknown> }>,
174+) => void;
175+}) => {
176+codex.on("tool_result", async (event) => ({
177+result: {
178+ ...event.result,
179+content: [{ type: "text", text: `${event.toolName} compacted` }],
180+},
181+}));
182+};
183+registry.codexAppServerExtensionFactories.push({
184+pluginId: "tokenjuice",
185+pluginName: "Tokenjuice",
186+rawFactory: factory,
187+ factory,
188+source: "test",
189+});
190+setActivePluginRegistry(registry);
191+192+const bridge = createBridgeWithToolResult("exec", {
193+content: [{ type: "text", text: "raw output" }],
194+details: {},
195+});
196+197+const result = await bridge.handleToolCall({
198+threadId: "thread-1",
199+turnId: "turn-1",
200+callId: "call-1",
201+tool: "exec",
202+arguments: { command: "git status" },
203+});
204+205+expect(result).toEqual(expectInputText("exec compacted"));
206+});
207+208+it("fires after_tool_call for successful codex tool executions", async () => {
209+const afterToolCall = vi.fn();
210+initializeGlobalHookRunner(
211+createMockPluginRegistry([{ hookName: "after_tool_call", handler: afterToolCall }]),
212+);
213+214+const bridge = createBridgeWithToolResult("exec", {
215+content: [{ type: "text", text: "done" }],
216+details: {},
217+});
218+219+await bridge.handleToolCall({
220+threadId: "thread-1",
221+turnId: "turn-1",
222+callId: "call-1",
223+tool: "exec",
224+arguments: { command: "pwd" },
225+});
226+227+await vi.waitFor(() => {
228+expect(afterToolCall).toHaveBeenCalledWith(
229+expect.objectContaining({
230+toolName: "exec",
231+toolCallId: "call-1",
232+params: { command: "pwd" },
233+result: expect.objectContaining({
234+content: [{ type: "text", text: "done" }],
235+details: {},
236+}),
237+}),
238+expect.objectContaining({
239+toolName: "exec",
240+toolCallId: "call-1",
241+}),
242+);
243+});
244+});
155245});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。