











@@ -555,23 +555,12 @@ async function buildDynamicToolsForTest(
555555});
556556}
557557558-async function createCodexToolBridgeForTest(
558+function createCodexToolBridgeForTest(
559559params: EmbeddedRunAttemptParams,
560-workspaceDir: string,
561-options: Partial<
562-Pick<
563-Parameters<typeof testing.buildDynamicTools>[0],
564-"forceHeartbeatTool" | "ignoreRuntimePlan"
565->
566-> = {},
560+tools: RuntimeDynamicToolForTest[],
561+registeredTools: RuntimeDynamicToolForTest[] = tools,
567562) {
568563const signal = new AbortController().signal;
569-const tools = await buildDynamicToolsForTest(params, workspaceDir, options);
570-const registeredTools = await buildDynamicToolsForTest(params, workspaceDir, {
571- ...options,
572-forceHeartbeatTool: true,
573-ignoreRuntimePlan: true,
574-});
575564return createCodexDynamicToolBridge({
576565 tools,
577566 registeredTools,
@@ -2383,18 +2372,8 @@ describe("runCodexAppServerAttempt", () => {
23832372 ? [createRuntimeDynamicTool("heartbeat_respond")]
23842373 : []),
23852374]);
2386-const request = vi.fn(async (method: string, _params?: unknown) => {
2387-if (method === "thread/start") {
2388-return threadStartResult("thread-1");
2389-}
2390-if (method === "thread/resume") {
2391-return threadStartResult("thread-1");
2392-}
2393-throw new Error(`unexpected method: ${method}`);
2394-});
23952375const sessionFile = path.join(tempDir, "session.jsonl");
23962376const workspaceDir = path.join(tempDir, "workspace");
2397-const appServer = createThreadLifecycleAppServerOptions();
23982377const createRunParams = (trigger?: EmbeddedRunAttemptParams["trigger"]) => {
23992378const params = createParams(sessionFile, workspaceDir);
24002379params.disableTools = false;
@@ -2407,47 +2386,43 @@ describe("runCodexAppServerAttempt", () => {
24072386}
24082387return params;
24092388};
2410-const startOrResume = async (params: EmbeddedRunAttemptParams) => {
2411-const toolBridge = await createCodexToolBridgeForTest(params, workspaceDir);
2412-return startOrResumeThread({
2413-client: { request } as never,
2414- params,
2415-cwd: workspaceDir,
2416-dynamicTools: toolBridge.specs,
2417- appServer,
2418-developerInstructions: testing.buildDeveloperInstructions(params, {
2419-dynamicTools: toolBridge.availableSpecs,
2420-}),
2421-nativeCodeModeEnabled: true,
2422-userMcpServersEnabled: false,
2423-});
2424-};
242523892426-await startOrResume(createRunParams());
2427-2428-const startRequest = request.mock.calls.find(([method]) => method === "thread/start");
2429-const startParams = startRequest?.[1] as
2430-| { dynamicTools?: Array<{ name?: string }>; developerInstructions?: string }
2431-| undefined;
2432-const registeredToolNames = startParams?.dynamicTools?.map((tool) => tool.name) ?? [];
2390+const registeredTools = [
2391+createRuntimeDynamicTool("message"),
2392+createRuntimeDynamicTool("heartbeat_respond"),
2393+];
2394+const normalBridge = createCodexToolBridgeForTest(
2395+createRunParams(),
2396+[createRuntimeDynamicTool("message")],
2397+registeredTools,
2398+);
2399+const normalInstructions = testing.buildDeveloperInstructions(createRunParams(), {
2400+dynamicTools: normalBridge.availableSpecs,
2401+});
2402+const registeredToolNames = normalBridge.specs.map((tool) => tool.name);
2433240324342404expect(registeredToolNames).toContain("message");
24352405expect(registeredToolNames).toContain("heartbeat_respond");
2436-expect(startParams?.developerInstructions).toContain(
2406+expect(normalInstructions).toContain(
24372407"Deferred searchable OpenClaw dynamic tools available: message.",
24382408);
2439-expect(startParams?.developerInstructions).not.toContain(
2409+expect(normalInstructions).not.toContain(
24402410"Deferred searchable OpenClaw dynamic tools available: heartbeat_respond",
24412411);
244224122443-await startOrResume(createRunParams("heartbeat"));
2444-await startOrResume(createRunParams());
2413+const heartbeatBridge = createCodexToolBridgeForTest(
2414+createRunParams("heartbeat"),
2415+[createRuntimeDynamicTool("message"), createRuntimeDynamicTool("heartbeat_respond")],
2416+registeredTools,
2417+);
2418+const nextNormalBridge = createCodexToolBridgeForTest(
2419+createRunParams(),
2420+[createRuntimeDynamicTool("message")],
2421+registeredTools,
2422+);
244524232446-expect(request.mock.calls.map(([method]) => method)).toEqual([
2447-"thread/start",
2448-"thread/resume",
2449-"thread/resume",
2450-]);
2424+expect(heartbeatBridge.specs.map((tool) => tool.name)).toEqual(registeredToolNames);
2425+expect(nextNormalBridge.specs.map((tool) => tool.name)).toEqual(registeredToolNames);
24512426});
2452242724532428it("keeps the persistent dynamic schema stable across heartbeat-only turns", async () => {
@@ -2458,18 +2433,8 @@ describe("runCodexAppServerAttempt", () => {
24582433 ? [createRuntimeDynamicTool("heartbeat_respond")]
24592434 : []),
24602435]);
2461-const request = vi.fn(async (method: string, _params?: unknown) => {
2462-if (method === "thread/start") {
2463-return threadStartResult("thread-1");
2464-}
2465-if (method === "thread/resume") {
2466-return threadStartResult("thread-1");
2467-}
2468-throw new Error(`unexpected method: ${method}`);
2469-});
24702436const sessionFile = path.join(tempDir, "session.jsonl");
24712437const workspaceDir = path.join(tempDir, "workspace");
2472-const appServer = createThreadLifecycleAppServerOptions();
24732438const createRunParams = (trigger?: EmbeddedRunAttemptParams["trigger"]) => {
24742439const params = createParams(sessionFile, workspaceDir);
24752440params.disableTools = false;
@@ -2489,31 +2454,34 @@ describe("runCodexAppServerAttempt", () => {
24892454}
24902455return params;
24912456};
2492-const startOrResume = async (params: EmbeddedRunAttemptParams) => {
2493-const toolBridge = await createCodexToolBridgeForTest(params, workspaceDir);
2494-return startOrResumeThread({
2495-client: { request } as never,
2496- params,
2497-cwd: workspaceDir,
2498-dynamicTools: toolBridge.specs,
2499- appServer,
2500-developerInstructions: testing.buildDeveloperInstructions(params, {
2501-dynamicTools: toolBridge.availableSpecs,
2502-}),
2503-nativeCodeModeEnabled: true,
2504-userMcpServersEnabled: false,
2505-});
2506-};
2507-2508-await startOrResume(createRunParams());
2509-await startOrResume(createRunParams("heartbeat"));
2510-await startOrResume(createRunParams());
2457+const registeredTools = [
2458+createRuntimeDynamicTool("message"),
2459+createRuntimeDynamicTool("web_search"),
2460+createRuntimeDynamicTool("heartbeat_respond"),
2461+];
2462+const normalBridge = createCodexToolBridgeForTest(
2463+createRunParams(),
2464+registeredTools,
2465+registeredTools,
2466+);
2467+const heartbeatBridge = createCodexToolBridgeForTest(
2468+createRunParams("heartbeat"),
2469+[createRuntimeDynamicTool("heartbeat_respond")],
2470+registeredTools,
2471+);
2472+const nextNormalBridge = createCodexToolBridgeForTest(
2473+createRunParams(),
2474+registeredTools,
2475+registeredTools,
2476+);
251124772512-expect(
2513-request.mock.calls
2514-.map(([method]) => method)
2515-.filter((method) => method === "thread/start" || method === "thread/resume"),
2516-).toEqual(["thread/start", "thread/resume", "thread/resume"]);
2478+expect(heartbeatBridge.availableSpecs.map((tool) => tool.name)).toEqual(["heartbeat_respond"]);
2479+expect(heartbeatBridge.specs.map((tool) => tool.name)).toEqual(
2480+normalBridge.specs.map((tool) => tool.name),
2481+);
2482+expect(nextNormalBridge.specs.map((tool) => tool.name)).toEqual(
2483+normalBridge.specs.map((tool) => tool.name),
2484+);
25172485});
2518248625192487it("disables Codex native tool surfaces when runtime toolsAllow is empty", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。