From dd771f1dc69eb5529e230cd2758ac2aa71b8f858 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 4 Apr 2026 12:57:28 +0100 Subject: [PATCH] fix(ci): repair plugin boundary and bootstrap regressions --- extensions/amazon-bedrock/register.runtime.ts | 33 +++++---- extensions/anthropic/register.runtime.ts | 73 +++++++++++++++---- extensions/memory-core/api.ts | 5 ++ extensions/memory-core/runtime-api.ts | 10 +++ extensions/telegram/channel-config-api.ts | 6 +- src/agents/pi-embedded-runner/run/attempt.ts | 14 ++-- src/agents/subagent-registry-lifecycle.ts | 1 + src/gateway/server-cron.ts | 1 + 8 files changed, 103 insertions(+), 40 deletions(-) diff --git a/extensions/amazon-bedrock/register.runtime.ts b/extensions/amazon-bedrock/register.runtime.ts index b9b349b637b..6c26796310e 100644 --- a/extensions/amazon-bedrock/register.runtime.ts +++ b/extensions/amazon-bedrock/register.runtime.ts @@ -44,18 +44,19 @@ function createGuardrailWrapStreamFn( }; } -const PROVIDER_ID = "amazon-bedrock"; -const CLAUDE_46_MODEL_RE = /claude-(?:opus|sonnet)-4(?:\.|-)6(?:$|[-.])/i; -const ANTHROPIC_BY_MODEL_REPLAY_HOOKS = buildProviderReplayFamilyHooks({ - family: "anthropic-by-model", -}); -const BEDROCK_CONTEXT_OVERFLOW_PATTERNS = [ - /ValidationException.*(?:input is too long|max input token|input token.*exceed)/i, - /ValidationException.*(?:exceeds? the (?:maximum|max) (?:number of )?(?:input )?tokens)/i, - /ModelStreamErrorException.*(?:Input is too long|too many input tokens)/i, -] as const; - export async function registerAmazonBedrockPlugin(api: OpenClawPluginApi): Promise { + // Keep registration-local constants inside the function so partial module + // initialization during test bootstrap cannot trip TDZ reads. + const providerId = "amazon-bedrock"; + const claude46ModelRe = /claude-(?:opus|sonnet)-4(?:\.|-)6(?:$|[-.])/i; + const anthropicByModelReplayHooks = buildProviderReplayFamilyHooks({ + family: "anthropic-by-model", + }); + const bedrockContextOverflowPatterns = [ + /ValidationException.*(?:input is too long|max input token|input token.*exceed)/i, + /ValidationException.*(?:exceeds? the (?:maximum|max) (?:number of )?(?:input )?tokens)/i, + /ModelStreamErrorException.*(?:Input is too long|too many input tokens)/i, + ] as const; const guardrail = (api.pluginConfig as Record | undefined)?.guardrail as | GuardrailConfig | undefined; @@ -69,7 +70,7 @@ export async function registerAmazonBedrockPlugin(api: OpenClawPluginApi): Promi : baseWrapStreamFn; api.registerProvider({ - id: PROVIDER_ID, + id: providerId, label: "Amazon Bedrock", docsPath: "/providers/models", auth: [], @@ -85,17 +86,17 @@ export async function registerAmazonBedrockPlugin(api: OpenClawPluginApi): Promi } return { provider: mergeImplicitBedrockProvider({ - existing: ctx.config.models?.providers?.[PROVIDER_ID], + existing: ctx.config.models?.providers?.[providerId], implicit, }), }; }, }, resolveConfigApiKey: ({ env }) => resolveBedrockConfigApiKey(env), - ...ANTHROPIC_BY_MODEL_REPLAY_HOOKS, + ...anthropicByModelReplayHooks, wrapStreamFn, matchesContextOverflowError: ({ errorMessage }) => - BEDROCK_CONTEXT_OVERFLOW_PATTERNS.some((pattern) => pattern.test(errorMessage)), + bedrockContextOverflowPatterns.some((pattern) => pattern.test(errorMessage)), classifyFailoverReason: ({ errorMessage }) => { if (/ThrottlingException|Too many concurrent requests/i.test(errorMessage)) { return "rate_limit"; @@ -106,6 +107,6 @@ export async function registerAmazonBedrockPlugin(api: OpenClawPluginApi): Promi return undefined; }, resolveDefaultThinkingLevel: ({ modelId }) => - CLAUDE_46_MODEL_RE.test(modelId.trim()) ? "adaptive" : undefined, + claude46ModelRe.test(modelId.trim()) ? "adaptive" : undefined, }); } diff --git a/extensions/anthropic/register.runtime.ts b/extensions/anthropic/register.runtime.ts index 27a7a6b885d..910bb7d96d0 100644 --- a/extensions/anthropic/register.runtime.ts +++ b/extensions/anthropic/register.runtime.ts @@ -6,7 +6,6 @@ import type { ProviderRuntimeModel, } from "openclaw/plugin-sdk/plugin-entry"; import { - CLAUDE_CLI_PROFILE_ID, applyAuthProfileConfig, ensureApiKeyFromOptionEnvOrPrompt, listProfilesForProvider, @@ -16,16 +15,13 @@ import { type ProviderAuthResult, validateApiKeyInput, } from "openclaw/plugin-sdk/provider-auth"; -import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key"; import { cloneFirstTemplateModel } from "openclaw/plugin-sdk/provider-model-shared"; import { fetchClaudeUsage } from "openclaw/plugin-sdk/provider-usage"; -import { buildAnthropicCliBackend } from "./cli-backend.js"; import { buildAnthropicCliMigrationResult, hasClaudeCliAuth } from "./cli-migration.js"; import { applyAnthropicConfigDefaults, normalizeAnthropicProviderConfig, } from "./config-defaults.js"; -import { anthropicMediaUnderstandingProvider } from "./media-understanding-provider.js"; import { buildAnthropicReplayPolicy } from "./replay-policy.js"; import { wrapAnthropicProviderStream, @@ -207,18 +203,61 @@ async function runAnthropicCliMigrationNonInteractive(ctx: { } export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise { - // Be defensive against partially initialized module graphs during test/runtime bootstrap. - const cliBackend = - typeof buildAnthropicCliBackend === "function" ? buildAnthropicCliBackend() : undefined; - if (cliBackend) { - api.registerCliBackend(cliBackend); + const claudeCliProfileId = "anthropic:claude-cli"; + const providerId = "anthropic"; + const defaultAnthropicModel = "anthropic/claude-sonnet-4-6"; + const anthropicOauthAllowlist = [ + "anthropic/claude-sonnet-4-6", + "anthropic/claude-opus-4-6", + "anthropic/claude-opus-4-5", + "anthropic/claude-sonnet-4-5", + "anthropic/claude-haiku-4-5", + ] as const; + let createApiKeyAuthMethod: + | (typeof import("openclaw/plugin-sdk/provider-auth-api-key"))["createProviderApiKeyAuthMethod"] + | undefined; + let mediaUnderstandingProvider: + | (typeof import("./media-understanding-provider.js"))["anthropicMediaUnderstandingProvider"] + | undefined; + + // Avoid touching a partially initialized static binding during cyclic bootstrap. + try { + const cliBackendModule = await import("./cli-backend.js"); + const cliBackend = + typeof cliBackendModule.buildAnthropicCliBackend === "function" + ? cliBackendModule.buildAnthropicCliBackend() + : undefined; + if (cliBackend) { + api.registerCliBackend(cliBackend); + } + } catch { + // Best-effort during test bootstrap; provider registration still proceeds. + } + try { + const providerApiKeyAuthModule = await import("openclaw/plugin-sdk/provider-auth-api-key"); + createApiKeyAuthMethod = + typeof providerApiKeyAuthModule.createProviderApiKeyAuthMethod === "function" + ? providerApiKeyAuthModule.createProviderApiKeyAuthMethod + : undefined; + } catch { + createApiKeyAuthMethod = undefined; + } + if (!createApiKeyAuthMethod) { + return; + } + try { + const mediaUnderstandingModule = await import("./media-understanding-provider.js"); + mediaUnderstandingProvider = + mediaUnderstandingModule.anthropicMediaUnderstandingProvider ?? undefined; + } catch { + mediaUnderstandingProvider = undefined; } api.registerProvider({ - id: PROVIDER_ID, + id: providerId, label: "Anthropic", docsPath: "/providers/models", envVars: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"], - deprecatedProfileIds: [CLAUDE_CLI_PROFILE_ID], + deprecatedProfileIds: [claudeCliProfileId], oauthProfileIdRepairs: [ { legacyProfileId: "anthropic:default", @@ -240,7 +279,7 @@ export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise + allowedKeys: [...anthropicOauthAllowlist].map((model) => model.replace(/^anthropic\//, "claude-cli/"), ), initialSelections: ["claude-cli/claude-sonnet-4-6"], @@ -254,8 +293,8 @@ export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise