






















@@ -594,10 +594,36 @@ describe("Codex app-server dynamic tool build", () => {
594594]);
595595});
596596597-it("keeps OpenClaw shell tools for node-targeted Codex app-server runs", async () => {
597+it("exposes pinned node shell tools for node-targeted Codex app-server runs", async () => {
598+const execTool = {
599+ ...createRuntimeDynamicTool("exec"),
600+parameters: {
601+type: "object",
602+properties: {
603+command: { type: "string" },
604+workdir: { type: "string" },
605+host: { type: "string" },
606+security: { type: "string" },
607+ask: { type: "string" },
608+node: { type: "string" },
609+},
610+required: ["command", "host", "node"],
611+additionalProperties: false,
612+},
613+};
614+vi.mocked(execTool.execute).mockResolvedValueOnce({
615+content: [
616+{
617+type: "text",
618+text: "Command still running (session exec-1, pid 123). Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.",
619+},
620+],
621+details: { status: "running" },
622+});
623+const processTool = createRuntimeDynamicTool("process");
598624setOpenClawCodingToolsFactoryForTests(() => [
599-createRuntimeDynamicTool("exec"),
600-createRuntimeDynamicTool("process"),
625+execTool,
626+processTool,
601627createRuntimeDynamicTool("message"),
602628]);
603629const sessionFile = path.join(tempDir, "session.jsonl");
@@ -613,10 +639,50 @@ describe("Codex app-server dynamic tool build", () => {
613639};
614640615641const tools = await buildDynamicToolsForTest(params, workspaceDir, {
616-nativeToolSurfaceEnabled: false,
642+nativeToolSurfaceEnabled: true,
617643});
618644619-expect(tools.map((tool) => tool.name)).toEqual(["message", "exec", "process"]);
645+expect(tools.map((tool) => tool.name)).toEqual(["message", "node_exec", "node_process"]);
646+const nodeExec = tools.find((tool) => tool.name === "node_exec");
647+const nodeProcess = tools.find((tool) => tool.name === "node_process");
648+expect(nodeExec?.description).toContain("host=node internally");
649+expect(nodeProcess?.description).toContain("node_exec sessions");
650+expect(nodeExec?.parameters).toEqual({
651+type: "object",
652+properties: {
653+command: { type: "string" },
654+workdir: { type: "string" },
655+},
656+required: ["command"],
657+additionalProperties: false,
658+});
659+const result = await nodeExec?.execute(
660+"call-1",
661+{
662+command: "pwd",
663+host: "gateway",
664+node: "model-selected-node",
665+security: "full",
666+ask: "off",
667+},
668+undefined,
669+);
670+expect(execTool.execute).toHaveBeenCalledWith(
671+"call-1",
672+{
673+command: "pwd",
674+host: "node",
675+node: "mac-mini",
676+},
677+undefined,
678+undefined,
679+);
680+expect(result?.content).toEqual([
681+{
682+type: "text",
683+text: "Command still running (session exec-1, pid 123). Use node_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.",
684+},
685+]);
620686621687const runtimePolicySessionFile = path.join(tempDir, "runtime-policy-session.jsonl");
622688const runtimePolicyParams = createParams(runtimePolicySessionFile, workspaceDir);
@@ -634,11 +700,15 @@ describe("Codex app-server dynamic tool build", () => {
634700} as never;
635701const runtimePolicyTools = await buildDynamicToolsForTest(runtimePolicyParams, workspaceDir, {
636702sandboxSessionKey: "agent:policy:session-1",
637-nativeToolSurfaceEnabled: false,
703+nativeToolSurfaceEnabled: true,
638704sessionAgentId: "policy",
639705});
640706641-expect(runtimePolicyTools.map((tool) => tool.name)).toEqual(["message", "exec", "process"]);
707+expect(runtimePolicyTools.map((tool) => tool.name)).toEqual([
708+"message",
709+"node_exec",
710+"node_process",
711+]);
642712});
643713644714it("exposes Docker sandbox shell tools when native Code Mode cannot honor sandbox paths", async () => {
@@ -1003,15 +1073,30 @@ describe("Codex app-server dynamic tool build", () => {
10031073});
1004107410051075it("normalizes Codex dynamic toolsAllow entries before filtering", () => {
1006-const tools = ["exec", "sandbox_exec", "sandbox_process", "apply_patch", "read", "message"].map(
1007-(name) => ({ name }),
1008-);
1076+const tools = [
1077+"exec",
1078+"sandbox_exec",
1079+"sandbox_process",
1080+"node_exec",
1081+"node_process",
1082+"apply_patch",
1083+"read",
1084+"message",
1085+].map((name) => ({ name }));
1009108610101087expect(
10111088filterCodexDynamicToolsForAllowlist(tools, [" BASH ", "apply-patch", "READ"]).map(
10121089(tool) => tool.name,
10131090),
1014-).toEqual(["exec", "sandbox_exec", "sandbox_process", "apply_patch", "read"]);
1091+).toEqual([
1092+"exec",
1093+"sandbox_exec",
1094+"sandbox_process",
1095+"node_exec",
1096+"node_process",
1097+"apply_patch",
1098+"read",
1099+]);
10151100});
1016110110171102it("treats an explicit empty Codex dynamic toolsAllow as no tools", () => {
@@ -1044,7 +1129,7 @@ describe("Codex app-server dynamic tool build", () => {
10441129expect(shouldEnableCodexAppServerNativeToolSurface(params)).toBe(false);
10451130});
104611311047-it("disables Codex native tool surfaces when the effective exec target is node", () => {
1132+it("keeps Codex native tool surfaces when the effective exec target is node", () => {
10481133const workspaceDir = path.join(tempDir, "workspace");
10491134const sessionParams = createParams(path.join(tempDir, "session.jsonl"), workspaceDir);
10501135sessionParams.disableTools = false;
@@ -1055,16 +1140,16 @@ describe("Codex app-server dynamic tool build", () => {
10551140ask: "off",
10561141};
105711421058-expect(shouldEnableCodexAppServerNativeToolSurface(sessionParams)).toBe(false);
1143+expect(shouldEnableCodexAppServerNativeToolSurface(sessionParams)).toBe(true);
1059114410601145sessionParams.toolsAllow = ["*"];
1061-expect(shouldEnableCodexAppServerNativeToolSurface(sessionParams)).toBe(false);
1146+expect(shouldEnableCodexAppServerNativeToolSurface(sessionParams)).toBe(true);
1062114710631148const globalParams = createParams(path.join(tempDir, "global-session.jsonl"), workspaceDir);
10641149globalParams.disableTools = false;
10651150globalParams.config = { tools: { exec: { host: "node" } } } as never;
106611511067-expect(shouldEnableCodexAppServerNativeToolSurface(globalParams)).toBe(false);
1152+expect(shouldEnableCodexAppServerNativeToolSurface(globalParams)).toBe(true);
1068115310691154const autoOverrideParams = createParams(
10701155path.join(tempDir, "auto-override-session.jsonl"),
@@ -1088,7 +1173,7 @@ describe("Codex app-server dynamic tool build", () => {
10881173shouldEnableCodexAppServerNativeToolSurface(agentParams, undefined, {
10891174agentId: "main",
10901175}),
1091-).toBe(false);
1176+).toBe(true);
1092117710931178const runtimePolicyParams = createParams(
10941179path.join(tempDir, "runtime-policy-session.jsonl"),
@@ -1106,7 +1191,7 @@ describe("Codex app-server dynamic tool build", () => {
11061191},
11071192} as never;
110811931109-expect(shouldEnableCodexAppServerNativeToolSurface(runtimePolicyParams)).toBe(false);
1194+expect(shouldEnableCodexAppServerNativeToolSurface(runtimePolicyParams)).toBe(true);
11101195});
1111119611121197it("disables Codex native tool surfaces whenever an OpenClaw sandbox is active", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。