




















@@ -646,6 +646,21 @@ describe("runCodexAppServerAttempt", () => {
646646).toEqual(["message"]);
647647});
648648649+it("exposes app-server-owned tools directly for forced private QA Codex runtime", () => {
650+const tools = ["read", "write", "image_generate", "message"].map((name) => ({ name }));
651+const privateQaCodexEnv = {
652+OPENCLAW_BUILD_PRIVATE_QA: "1",
653+OPENCLAW_QA_FORCE_RUNTIME: "codex",
654+};
655+656+expect(
657+__testing
658+.filterCodexDynamicTools(tools, {}, privateQaCodexEnv)
659+.map((tool) => tool.name),
660+).toEqual(["read", "write", "image_generate", "message"]);
661+expect(__testing.resolveCodexDynamicToolsLoading({}, privateQaCodexEnv)).toBe("direct");
662+});
663+649664it("starts Codex threads without duplicate OpenClaw workspace tools by default", async () => {
650665const sessionFile = path.join(tempDir, "session.jsonl");
651666const workspaceDir = path.join(tempDir, "workspace");
@@ -897,6 +912,38 @@ describe("runCodexAppServerAttempt", () => {
897912expect((factoryOptions[0] as { modelApi?: unknown }).modelApi).toBe("openai-responses");
898913});
899914915+it("enables gateway subagent binding for forced private QA Codex runs", async () => {
916+vi.stubEnv("OPENCLAW_BUILD_PRIVATE_QA", "1");
917+vi.stubEnv("OPENCLAW_QA_FORCE_RUNTIME", "codex");
918+const sessionFile = path.join(tempDir, "session.jsonl");
919+const workspaceDir = path.join(tempDir, "workspace");
920+const params = createParams(sessionFile, workspaceDir);
921+params.disableTools = false;
922+params.runtimePlan = createCodexRuntimePlanFixture();
923+const factoryOptions: unknown[] = [];
924+__testing.setOpenClawCodingToolsFactoryForTests((options) => {
925+factoryOptions.push(options);
926+return [createRuntimeDynamicTool("sessions_spawn")];
927+});
928+929+const tools = await __testing.buildDynamicTools({
930+ params,
931+resolvedWorkspace: workspaceDir,
932+effectiveWorkspace: workspaceDir,
933+sandboxSessionKey: params.sessionKey!,
934+sandbox: null as never,
935+runAbortController: new AbortController(),
936+sessionAgentId: "main",
937+pluginConfig: {},
938+onYieldDetected: () => undefined,
939+});
940+941+expect(factoryOptions).toHaveLength(1);
942+const factoryOption = factoryOptions[0] as { allowGatewaySubagentBinding?: unknown };
943+expect(factoryOption.allowGatewaySubagentBinding).toBe(true);
944+expect(tools.map((tool) => tool.name)).toEqual(["sessions_spawn"]);
945+});
946+900947it("normalizes Codex dynamic toolsAllow entries before filtering", () => {
901948const tools = ["exec", "apply_patch", "read", "message"].map((name) => ({ name }));
902949此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。