From 0caa8e22d717faef53505174616537ff2c0530b2 Mon Sep 17 00:00:00 2001 From: Shakker Date: Thu, 7 May 2026 06:13:23 +0100 Subject: [PATCH] fix: thread registry model workspace --- src/agents/pi-embedded-runner/model.test.ts | 61 ++++++++++++++++++++- src/agents/pi-embedded-runner/model.ts | 23 ++++---- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/agents/pi-embedded-runner/model.test.ts b/src/agents/pi-embedded-runner/model.test.ts index c256d1c970f..7e947c5f813 100644 --- a/src/agents/pi-embedded-runner/model.test.ts +++ b/src/agents/pi-embedded-runner/model.test.ts @@ -157,7 +157,12 @@ vi.mock("./openrouter-model-capabilities.js", () => ({ import type { OpenClawConfig } from "../../config/config.js"; import { getModelProviderRequestTransport } from "../provider-request-config.js"; import { buildForwardCompatTemplate } from "./model.forward-compat.test-support.js"; -import { buildInlineProviderModels, resolveModel, resolveModelAsync } from "./model.js"; +import { + buildInlineProviderModels, + resolveModel, + resolveModelAsync, + resolveModelWithRegistry, +} from "./model.js"; import { buildOpenAICodexForwardCompatExpectation, makeModel, @@ -1952,6 +1957,60 @@ describe("resolveModel", () => { }); }); + it("passes configured workspaceDir through direct registry dynamic hooks", () => { + const runProviderDynamicModel = vi.fn( + (params: { + workspaceDir?: string; + context: { workspaceDir?: string; provider: string; modelId: string }; + }) => + params.workspaceDir === "/tmp/workspace" && + params.context.workspaceDir === "/tmp/workspace" && + params.context.provider === "openai-codex" && + params.context.modelId === "gpt-5.4" + ? ({ + ...buildOpenAICodexForwardCompatExpectation("gpt-5.4"), + name: "GPT-5.4", + } as ReturnType) + : undefined, + ); + const runtimeHooks = { + ...createRuntimeHooks(), + runProviderDynamicModel, + }; + const cfg = { + agents: { + defaults: { + workspace: "/tmp/workspace", + }, + }, + } as OpenClawConfig; + + const result = resolveModelWithRegistry({ + provider: "openai-codex", + modelId: "gpt-5.4", + agentDir: "/tmp/agent-state", + cfg, + modelRegistry: discoverModels({ mocked: true } as never, "/tmp/agent-state"), + runtimeHooks, + }); + + expect(runProviderDynamicModel).toHaveBeenCalledWith( + expect.objectContaining({ + workspaceDir: "/tmp/workspace", + context: expect.objectContaining({ + workspaceDir: "/tmp/workspace", + agentDir: "/tmp/agent-state", + modelId: "gpt-5.4", + provider: "openai-codex", + }), + }), + ); + expect(result).toMatchObject({ + provider: "openai-codex", + id: "gpt-5.4", + }); + }); + it("resolves discovered openai-codex gpt-5.4-mini rows", () => { mockDiscoveredModel(discoverModels, { provider: "openai-codex", diff --git a/src/agents/pi-embedded-runner/model.ts b/src/agents/pi-embedded-runner/model.ts index 08a31b40f45..cdc9d85a867 100644 --- a/src/agents/pi-embedded-runner/model.ts +++ b/src/agents/pi-embedded-runner/model.ts @@ -965,38 +965,39 @@ export function resolveModelWithRegistry(params: { const runtimeHooks = params.runtimeHooks ?? DEFAULT_PROVIDER_RUNTIME_HOOKS; const workspaceDir = normalizedParams.workspaceDir ?? normalizedParams.cfg?.agents?.defaults?.workspace; - const explicitModel = resolveExplicitModelWithRegistry(normalizedParams); + const scopedParams = { + ...normalizedParams, + ...(workspaceDir !== undefined ? { workspaceDir } : {}), + }; + const explicitModel = resolveExplicitModelWithRegistry(scopedParams); if (explicitModel?.kind === "suppressed") { return undefined; } if (explicitModel?.kind === "resolved") { if ( !shouldCompareProviderRuntimeResolvedModel({ - provider: normalizedParams.provider, - modelId: normalizedParams.modelId, - cfg: normalizedParams.cfg, - agentDir: normalizedParams.agentDir, + provider: scopedParams.provider, + modelId: scopedParams.modelId, + cfg: scopedParams.cfg, + agentDir: scopedParams.agentDir, workspaceDir, runtimeHooks, }) ) { return explicitModel.model; } - const pluginDynamicModel = resolvePluginDynamicModelWithRegistry({ - ...normalizedParams, - workspaceDir, - }); + const pluginDynamicModel = resolvePluginDynamicModelWithRegistry(scopedParams); return preferProviderRuntimeResolvedModel({ explicitModel: explicitModel.model, runtimeResolvedModel: pluginDynamicModel, }); } - const pluginDynamicModel = resolvePluginDynamicModelWithRegistry(normalizedParams); + const pluginDynamicModel = resolvePluginDynamicModelWithRegistry(scopedParams); if (pluginDynamicModel) { return pluginDynamicModel; } - return resolveConfiguredFallbackModel(normalizedParams); + return resolveConfiguredFallbackModel(scopedParams); } export function resolveModel(