From 708632c4510f50460e6d511f7d7c19bfe8b24c52 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 26 Aug 2026 00:39:38 -0700 Subject: [PATCH] perf: stop provider policy loads from compiling the transport graph (#129652) * perf(plugins): keep provider policy artifacts on leaf module graphs Provider policy artifacts (provider-policy-api.js) load eagerly whenever a provider is resolved, but five of them imported the provider-model-shared barrel at runtime, dragging the transports/compat/state graph into every policy load. In contexts without a native TS require hook (Vitest workers, non-tsx source runs) jiti compiled that whole graph: ~65s of event-loop starvation on the first embedded run, which is what pushed run.session-permissions.test.ts past its 120s timeout before #129582. Add openclaw/plugin-sdk/claude-model-runtime, a narrow family-level and local-only subpath re-exporting the Claude identity/thinking helpers from their leaf owners (@openclaw/llm-core, plugins/provider-claude-thinking). Switch anthropic, anthropic-vertex, and opencode policy artifacts to it, and amazon-bedrock plus ollama to the already-plugin-visible @openclaw/model-catalog-core leaves. The barrel keeps re-exporting the same symbols, so no existing consumer changes. Measured on the embedded-runner host route (first run, Vitest worker): 65540ms -> 6627ms; jiti self-time 23.4s -> 1.3s, statSync 18.6s -> 0.7s. run.shared-integration.test.ts drops from 167s to 65s as a side effect. Also pin run.inherited-auth-owner.test.ts to the mocked plugin-harness route (its assertions are provider-agnostic; 37.6s -> sub-second test time) and document the no-provider default-route trap on overflowBaseRunParams. Follow-up to #129582. * chore(plugins): register claude-model-runtime boundary aliases The extension package boundary contract requires every local-only plugin-sdk entrypoint to carry a d.ts path alias in the shared boundary map and xai's derived override set; CI's contracts-plugin lane caught the missing entries. * chore(release): exclude claude-model-runtime declarations from the pack Local-only plugin-sdk entrypoints ship runtime .js only; the release check derives the required pack exclusion from the local-only registry and CI's core-tooling lane caught the missing package.json files entry. * test(agents): assert the mocked harness route in auth-owner proof ClawSweeper P2: without the agentHarnessId assertion a silent fall-back to the built-in host harness would still pass the auth-owner assertions while proving the wrong route; fail loudly like run.session-permissions.test.ts. --- .../amazon-bedrock/provider-policy-api.ts | 2 +- .../anthropic-vertex/provider-policy-api.ts | 2 +- extensions/anthropic/provider-policy-api.ts | 2 +- extensions/ollama/provider-policy-api.ts | 3 ++- extensions/opencode/provider-policy-api.ts | 2 +- .../tsconfig.package-boundary.paths.json | 3 +++ extensions/xai/tsconfig.json | 3 +++ package.json | 4 ++++ scripts/lib/plugin-sdk-entrypoints.json | 1 + ...lugin-sdk-private-local-only-subpaths.json | 1 + .../run.inherited-auth-owner.test.ts | 23 ++++++++++++++----- .../run.overflow-compaction.harness.ts | 4 ++++ src/plugin-sdk/claude-model-runtime.ts | 11 +++++++++ 13 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 src/plugin-sdk/claude-model-runtime.ts diff --git a/extensions/amazon-bedrock/provider-policy-api.ts b/extensions/amazon-bedrock/provider-policy-api.ts index d9f56ad1bd49..db16b19cc154 100644 --- a/extensions/amazon-bedrock/provider-policy-api.ts +++ b/extensions/amazon-bedrock/provider-policy-api.ts @@ -2,7 +2,7 @@ * Provider-policy API for Amazon Bedrock. Core asks this plugin for thinking * profiles without importing provider registration or streaming code. */ -import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared"; +import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { resolveBedrockClaudeThinkingProfile } from "./thinking-policy.js"; /** Resolve the Bedrock thinking profile for a provider/model pair. */ diff --git a/extensions/anthropic-vertex/provider-policy-api.ts b/extensions/anthropic-vertex/provider-policy-api.ts index 278297448dba..69220a9c4680 100644 --- a/extensions/anthropic-vertex/provider-policy-api.ts +++ b/extensions/anthropic-vertex/provider-policy-api.ts @@ -2,7 +2,7 @@ * Provider-policy API for Anthropic Vertex. Core asks for thinking profiles * without importing the provider entry or stream runtime. */ -import { resolveClaudeThinkingProfile } from "openclaw/plugin-sdk/provider-model-shared"; +import { resolveClaudeThinkingProfile } from "openclaw/plugin-sdk/claude-model-runtime"; /** Resolve Anthropic Vertex thinking profile for a provider/model pair. */ export function resolveThinkingProfile(params: { diff --git a/extensions/anthropic/provider-policy-api.ts b/extensions/anthropic/provider-policy-api.ts index dca81eb88f0b..ff988e77e88d 100644 --- a/extensions/anthropic/provider-policy-api.ts +++ b/extensions/anthropic/provider-policy-api.ts @@ -6,7 +6,7 @@ import { resolveClaudeModelIdentity, resolveClaudeMythos5ModelIdentity, resolveClaudeThinkingProfile, -} from "openclaw/plugin-sdk/provider-model-shared"; +} from "openclaw/plugin-sdk/claude-model-runtime"; import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types"; import { CLAUDE_CLI_OFF_THINKING_PROFILE, CLAUDE_CLI_PROFILE_ID } from "./cli-constants.js"; import { diff --git a/extensions/ollama/provider-policy-api.ts b/extensions/ollama/provider-policy-api.ts index 9c0e0e141457..02458a6f3a5e 100644 --- a/extensions/ollama/provider-policy-api.ts +++ b/extensions/ollama/provider-policy-api.ts @@ -1,10 +1,11 @@ +import { isCloudModelRef } from "@openclaw/model-catalog-core/model-catalog-refs"; +import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; // Ollama API module exposes the plugin public contract. import type { ProviderDefaultThinkingPolicyContext, ProviderNormalizeResolvedModelContext, ProviderThinkingProfile, } from "openclaw/plugin-sdk/plugin-entry"; -import { isCloudModelRef, normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared"; import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types"; import { OLLAMA_CLOUD_PROVIDER_ID, OLLAMA_DEFAULT_BASE_URL } from "./src/defaults.js"; import { supportsOllamaCloudFullThinkingEffort } from "./src/model-reasoning.js"; diff --git a/extensions/opencode/provider-policy-api.ts b/extensions/opencode/provider-policy-api.ts index 20d468f3573b..5de30933539c 100644 --- a/extensions/opencode/provider-policy-api.ts +++ b/extensions/opencode/provider-policy-api.ts @@ -1,9 +1,9 @@ +import { resolveClaudeThinkingProfile } from "openclaw/plugin-sdk/claude-model-runtime"; // Opencode API module exposes the plugin public contract. import type { ProviderDefaultThinkingPolicyContext, ProviderThinkingProfile, } from "openclaw/plugin-sdk/plugin-entry"; -import { resolveClaudeThinkingProfile } from "openclaw/plugin-sdk/provider-model-shared"; const FIXED_REASONING_PROFILE = { levels: [{ id: "off", label: "always on" }], diff --git a/extensions/tsconfig.package-boundary.paths.json b/extensions/tsconfig.package-boundary.paths.json index 376aa4d1275a..38c09c26e52d 100644 --- a/extensions/tsconfig.package-boundary.paths.json +++ b/extensions/tsconfig.package-boundary.paths.json @@ -134,6 +134,9 @@ "openclaw/plugin-sdk/cli-backend": [ "../packages/plugin-sdk/dist/src/plugin-sdk/cli-backend.d.ts" ], + "openclaw/plugin-sdk/claude-model-runtime": [ + "../packages/plugin-sdk/dist/src/plugin-sdk/claude-model-runtime.d.ts" + ], "openclaw/plugin-sdk/codex-mcp-projection": [ "../packages/plugin-sdk/dist/src/plugin-sdk/codex-mcp-projection.d.ts" ], diff --git a/extensions/xai/tsconfig.json b/extensions/xai/tsconfig.json index 4662f1971ee7..b699a2ef9afe 100644 --- a/extensions/xai/tsconfig.json +++ b/extensions/xai/tsconfig.json @@ -128,6 +128,9 @@ "openclaw/plugin-sdk/cli-backend": [ "../../packages/plugin-sdk/dist/src/plugin-sdk/cli-backend.d.ts" ], + "openclaw/plugin-sdk/claude-model-runtime": [ + "../../packages/plugin-sdk/dist/src/plugin-sdk/claude-model-runtime.d.ts" + ], "openclaw/plugin-sdk/codex-mcp-projection": [ "../../packages/plugin-sdk/dist/src/plugin-sdk/codex-mcp-projection.d.ts" ], diff --git a/package.json b/package.json index 918ca5f1041f..47a01e091600 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "!dist/plugin-sdk/channel-test-helpers.d.ts", "!dist/plugin-sdk/chat-channel-ids.d.ts", "!dist/plugin-sdk/cli-backend.d.ts", + "!dist/plugin-sdk/claude-model-runtime.d.ts", "!dist/plugin-sdk/cli-runtime.d.ts", "!dist/plugin-sdk/codex-mcp-projection.d.ts", "!dist/plugin-sdk/codex-session-transcript-runtime.d.ts", @@ -785,6 +786,9 @@ "./plugin-sdk/cli-backend": { "default": "./dist/plugin-sdk/cli-backend.js" }, + "./plugin-sdk/claude-model-runtime": { + "default": "./dist/plugin-sdk/claude-model-runtime.js" + }, "./plugin-sdk/codex-mcp-projection": { "default": "./dist/plugin-sdk/codex-mcp-projection.js" }, diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index 717aa626aeff..3b7b0ed3a394 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -111,6 +111,7 @@ "cli-argv", "cli-runtime", "cli-backend", + "claude-model-runtime", "codex-mcp-projection", "native-hook-relay-runtime", "codex-session-transcript-runtime", diff --git a/scripts/lib/plugin-sdk-private-local-only-subpaths.json b/scripts/lib/plugin-sdk-private-local-only-subpaths.json index dacdd4a760a3..909380cd03ee 100644 --- a/scripts/lib/plugin-sdk-private-local-only-subpaths.json +++ b/scripts/lib/plugin-sdk-private-local-only-subpaths.json @@ -28,6 +28,7 @@ "channel-targets", "channel-test-helpers", "chat-channel-ids", + "claude-model-runtime", "cli-backend", "cli-runtime", "codex-mcp-projection", diff --git a/src/agents/embedded-agent-runner/run.inherited-auth-owner.test.ts b/src/agents/embedded-agent-runner/run.inherited-auth-owner.test.ts index 13fd27a42766..9a1376b06870 100644 --- a/src/agents/embedded-agent-runner/run.inherited-auth-owner.test.ts +++ b/src/agents/embedded-agent-runner/run.inherited-auth-owner.test.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import { beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { listAgentIds } from "../agent-scope-config.js"; import { makeAttemptResult } from "./run.overflow-compaction.fixture.js"; @@ -10,7 +10,7 @@ import { mockedRunEmbeddedAttempt, overflowBaseRunParams, resetSharedRunIntegrationHarnessMocks, - warmRunOverflowCompactionHarness, + useOpenAIPlatformAuthFixture, } from "./run.overflow-compaction.harness.js"; const { runEmbeddedAgent } = await loadRunOverflowCompactionHarness(); @@ -29,12 +29,13 @@ function projectSetupExecutionConfig(source: OpenClawConfig): OpenClawConfig { } describe("embedded setup inference inherited auth owner", () => { - beforeAll(async () => { - await warmRunOverflowCompactionHarness(runEmbeddedAgent); + // Provider-pinned runs stay on the mocked plugin harness, so no host-route + // warmup is needed here; see overflowBaseRunParams for the route trap. + beforeEach(() => { + resetSharedRunIntegrationHarnessMocks(); + useOpenAIPlatformAuthFixture(); }); - beforeEach(resetSharedRunIntegrationHarnessMocks); - it.each([ { name: "a pre-roster config", source: {} }, { name: "a sole-agent config", source: { agents: { entries: { main: {} } } } }, @@ -49,6 +50,11 @@ describe("embedded setup inference inherited auth owner", () => { await runEmbeddedAgent({ ...overflowBaseRunParams, + // Auth-owner resolution is provider-agnostic. Route through the mocked + // plugin harness so this shard does not compile the bundled Anthropic + // provider policy from source just to assert an agent directory. + provider: "openai", + model: "gpt-5.6-luna", agentId: "main", config, runId: `run-setup-inference-owner-${name}`, @@ -60,6 +66,11 @@ describe("embedded setup inference inherited auth owner", () => { value.endsWith(path.join("agents", "main", "agent")), ); expect(mockedRunEmbeddedAttempt).toHaveBeenCalledOnce(); + // A silent fall-back to the built-in host harness would still pass the + // auth-owner assertions; fail loudly on the route instead. + expect(mockedRunEmbeddedAttempt).toHaveBeenCalledWith( + expect.objectContaining({ agentHarnessId: "codex" }), + ); }, ); }); diff --git a/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts b/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts index 777bdabc7818..135156bc4593 100644 --- a/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts +++ b/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts @@ -442,6 +442,10 @@ const mockedHasUsableCustomProviderApiKey = vi.fn(() => false); const mockedMarkAuthProfileSuccess = vi.fn(async () => {}); const mockedShouldPreferExplicitConfigApiKeyAuth = vi.fn(() => false); +// No provider here means model resolution defaults to anthropic/test-model, which +// the mocked codex harness does not claim: such runs select the built-in openclaw +// host harness and pay its one-time source-compile cost. Suites proving plugin +// harness behavior must pin provider "openai" (see run.session-permissions.test.ts). export const overflowBaseRunParams = { agentId: "main", sessionId: "test-session", diff --git a/src/plugin-sdk/claude-model-runtime.ts b/src/plugin-sdk/claude-model-runtime.ts new file mode 100644 index 000000000000..b3a5e71daf11 --- /dev/null +++ b/src/plugin-sdk/claude-model-runtime.ts @@ -0,0 +1,11 @@ +/** + * Claude model-family policy helpers for provider policy artifacts. + * + * Provider policy artifacts (`provider-policy-api.js`) load eagerly whenever a + * provider is resolved, so their module graph must stay leaf-light. This + * subpath re-exports the Claude identity and thinking helpers from their leaf + * owners; importing them through `provider-model-shared` drags the transport + * and compat graph into every policy load (~60s under jiti in source checkouts). + */ +export { resolveClaudeModelIdentity, resolveClaudeMythos5ModelIdentity } from "@openclaw/llm-core"; +export { resolveClaudeThinkingProfile } from "../plugins/provider-claude-thinking.js";