mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 23:46:48 +00:00
* refactor: extract agent core package Introduce packages/agent-core as the OpenClaw-owned home for reusable agent loop, harness, session, prompt, and runtime dependency contracts. * refactor: extract shared llm runtime Move provider model registries, stream wrappers, OAuth helpers, and LLM utilities into src/llm with plugin-sdk barrels instead of depending on the old embedded runtime layout. * refactor: remove pi runtime internals Rename remaining Pi-shaped agent surfaces to OpenClaw agent runtime names, delete obsolete Pi docs and package graph checks, and add the third-party notice for incorporated code. * refactor: tighten agent session runtime Make agent-core/runtime dependencies explicit, consolidate compaction and session transcript helpers, and move model/session helpers behind OpenClaw-owned contracts. * refactor: remove static model and pi auth paths Drop static model catalogs and Pi auth bridges, move model/provider facts to manifest-owned runtime contracts, and harden internal embedded-agent utilities. * refactor: remove legacy provider compat paths * docs: remove agent parity notes * fix: skip provider wildcard metadata parsing * refactor: share session extension sdk loading * refactor: inline acpx proxy error formatter * refactor: fold edit recovery into edit tool * fix: accept extension batch separator * test: align startup provider plugin expectations * fix: restore provider-scoped release discovery * test: align static asset packaging expectations * fix: run static provider catalogs during scoped discovery * fix: add provider entry catalogs for scoped live discovery * fix: load lightweight provider catalog entries * fix: refresh provider-scoped plugin metadata * fix: keep provider catalog entries on release live path * fix: keep static manifest models in release live checks * fix: harden release model discovery * fix: reduce OpenAI live cache probe reasoning * fix: disable OpenAI cache probe reasoning * ci: extend OpenAI gateway live timeout * fix: extend live gateway model budget * fix: stabilize release validation regressions * fix: honor provider aliases in model rows * fix: stabilize release validation lanes * fix: stabilize release memory qa * ci: stabilize release validation lanes * ci: prefer ipv4 for live docker node calls * fix: restore shared tool-call stream wrapper * ci: remove legacy pi test shard alias * fix: clean up embedded agent test drift * fix: stabilize runtime alias status * fix: clean up embedded agent ci drift * fix: restore release ci invariants * fix: clean up post-rebase runtime drift * fix: restore release ci checks * fix: restore release ci after rebase * fix: remove stale pi runtime path * test: align compaction runtime expectations * test: update plugin prerelease expectations * fix: handle claude live tool approvals * fix: stabilize release validation gates * fix: finish agent runtime import * test: finish post-rebase agent runtime mocks * fix: keep codex compaction native * fix: stabilize codex app-server hook tests * test: isolate codex diagnostic active run * test: remove codex diagnostic completion race # Conflicts: # extensions/codex/src/app-server/run-attempt.test.ts * ci: fix full release manifest performance run id * refactor: narrow llm plugin sdk boundary * chore: drop generated google boundary stamps * fix: repair rebase fallout * fix: clean up rebased runtime references * fix: decode codex jwt payloads as base64url * fix: preserve shipped pi runtime alias * fix: add scoped sdk virtual modules * fix: decode llm codex oauth jwt as base64url * fix: avoid stale vertex adc negative cache * fix: harden tool arg decoding and codeql path * fix: keep vertex adc negative checks live * refactor: consolidate codex jwt and edit helpers * fix: await codex oauth node runtime imports * fix: preserve sdk tool and notice contracts * fix: preserve shipped compat config boundaries * fix: align codex oauth callback host * fix: terminate agent-core loop streams on failure * fix: keep codex oauth callback alive during fallback * ci: include session tools in critical codeql scans * fix: keep Cloudflare Anthropic provider auth header * docs: redirect legacy pi runtime pages * fix: honor bundled web provider compat discovery * fix: protect session output spill files * fix: keep legacy agent dir env blocked * fix: contain auto-discovered skill symlinks * fix: harden agent core sdk proxy surfaces * fix: restore approval reaction sdk compat * fix: keep live docker runs bounded * fix: keep codex oauth redirect host aligned * fix: resolve post-rebase agent runtime drift * fix: redact anthropic oauth parse failures * fix: preserve responses strict tool shaping * fix: repair agent runtime rebase cleanup * docs: redirect retired parity pages * fix: bound auto-discovered resources to roots * fix: repair post-rebase agent test drift * fix: preserve bundled provider allowlist migration * fix: preserve manifest-owned provider aliases * fix: declare photon image dependency * fix: keep provider headers out of proxy body * fix: preserve shipped env aliases * fix: refresh control ui i18n generated state * fix: quote read fallback paths * fix: preview edits through configured backend * test: satisfy core test typecheck * fix: preserve ZAI usage auth fallback * test: repair codex diagnostic test * fix: repair agent runtime rebase drift * test: finish embedded runner import rename * fix: repair agent runtime rebase integrations * test: align compaction oauth fallback expectations * fix: allow sdk-auth session models * fix: update doctor tool schema import * fix: preserve bedrock plugin region * fix: stream harmony-like prose immediately * ci: include session runtime in codeql shards * fix: repair latest rebase integrations * fix: honor explicit codex websocket transport * fix: keep openai-compatible credentials provider-scoped * fix: refresh sdk api baseline after rebase * fix: route cli runtime aliases through openclaw harness * test: rename stale harness mock expectation * test: rename embedded agent overflow calls * test: clean embedded auth test wording * test: use openclaw stream types in deepinfra cache test * fix: refresh sdk api baseline on latest main * fix: honor bundled discovery compat allowlists * fix: refresh sdk api baseline after latest rebase * fix: remove stale rebase imports * test: rename stale model catalog mock * test: mock renamed doctor runtime modules * fix: map canonical kimi env auth * fix: use internal model registry in bench script * fix: migrate deepinfra provider catalog entry * fix: enforce builtin tool suppression * fix: route compaction auth and proxy payloads safely * refactor: prune unused llm registry leftovers * test: update codex hooks session import * test: fix model picker ci coverage * test: align model picker auth mock types
435 lines
14 KiB
TypeScript
435 lines
14 KiB
TypeScript
import { resolveModelCatalogScope } from "../agents/model-catalog-scope.js";
|
|
import { normalizeProviderId } from "../agents/provider-id.js";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import {
|
|
normalizeLowercaseStringOrEmpty,
|
|
normalizeOptionalString,
|
|
} from "../shared/string-coerce.js";
|
|
import { getLoadedRuntimePluginRegistry } from "./active-runtime-registry.js";
|
|
import {
|
|
PluginLruCache,
|
|
resolveConfigScopedRuntimeCacheValue,
|
|
type ConfigScopedRuntimeCache,
|
|
} from "./plugin-cache-primitives.js";
|
|
import { resolvePluginControlPlaneFingerprint } from "./plugin-control-plane-context.js";
|
|
import { resolveProviderConfigApiOwnerHint } from "./provider-config-owner.js";
|
|
import { isPluginProvidersLoadInFlight, resolvePluginProviders } from "./providers.runtime.js";
|
|
import type { PluginRegistry } from "./registry-types.js";
|
|
import {
|
|
getActivePluginRegistryWorkspaceDirFromState,
|
|
getPluginRegistryState,
|
|
} from "./runtime-state.js";
|
|
import type {
|
|
ProviderPlugin,
|
|
ProviderExtraParamsForTransportContext,
|
|
ProviderPrepareExtraParamsContext,
|
|
ProviderResolveAuthProfileIdContext,
|
|
ProviderFollowupFallbackRouteContext,
|
|
ProviderFollowupFallbackRouteResult,
|
|
ProviderWrapStreamFnContext,
|
|
} from "./types.js";
|
|
|
|
let providerRuntimePluginCache: ConfigScopedRuntimeCache<ProviderPlugin | null> = new WeakMap();
|
|
const defaultProviderRuntimePluginCache = new PluginLruCache<ProviderPlugin | null>(128);
|
|
const PREPARED_PROVIDER_RUNTIME_SURFACES = ["channel"] as const;
|
|
|
|
export type ProviderRuntimePluginLookupParams = {
|
|
provider: string;
|
|
modelId?: string | null;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
applyAutoEnable?: boolean;
|
|
bundledProviderVitestCompat?: boolean;
|
|
};
|
|
|
|
export type ProviderRuntimePluginHandle = ProviderRuntimePluginLookupParams & {
|
|
plugin?: ProviderPlugin;
|
|
};
|
|
|
|
export type ProviderRuntimePluginHandleParams = ProviderRuntimePluginLookupParams & {
|
|
runtimeHandle?: ProviderRuntimePluginHandle;
|
|
};
|
|
|
|
export function clearProviderRuntimePluginCacheForTest(): void {
|
|
providerRuntimePluginCache = new WeakMap();
|
|
defaultProviderRuntimePluginCache.clear();
|
|
}
|
|
|
|
function matchesProviderId(provider: ProviderPlugin, providerId: string): boolean {
|
|
const normalized = normalizeProviderId(providerId);
|
|
if (!normalized) {
|
|
return false;
|
|
}
|
|
if (normalizeProviderId(provider.id) === normalized) {
|
|
return true;
|
|
}
|
|
return [...(provider.aliases ?? []), ...(provider.hookAliases ?? [])].some(
|
|
(alias) => normalizeProviderId(alias) === normalized,
|
|
);
|
|
}
|
|
|
|
function resolveProviderRuntimePluginCacheKey(
|
|
params: ProviderRuntimePluginLookupParams,
|
|
registryState = getPluginRegistryState(),
|
|
): string {
|
|
return JSON.stringify({
|
|
provider: normalizeLowercaseStringOrEmpty(params.provider),
|
|
modelId: resolveProviderRuntimeLookupModelId(params) ?? null,
|
|
pluginControlPlane: resolvePluginControlPlaneFingerprint({
|
|
config: params.config,
|
|
env: params.env,
|
|
workspaceDir: params.workspaceDir,
|
|
}),
|
|
plugins: params.config?.plugins,
|
|
models: params.config?.models?.providers,
|
|
workspaceDir: params.workspaceDir ?? "",
|
|
applyAutoEnable: params.applyAutoEnable ?? null,
|
|
bundledProviderVitestCompat: params.bundledProviderVitestCompat ?? null,
|
|
pluginRegistryKey: registryState?.key ?? null,
|
|
pluginRegistryVersion: registryState?.activeVersion ?? null,
|
|
});
|
|
}
|
|
|
|
function matchesProviderLiteralId(provider: ProviderPlugin, providerId: string): boolean {
|
|
const normalized = normalizeLowercaseStringOrEmpty(providerId);
|
|
return !!normalized && normalizeLowercaseStringOrEmpty(provider.id) === normalized;
|
|
}
|
|
|
|
function resolveProviderRuntimeLookupModelId(
|
|
params: ProviderRuntimePluginLookupParams & { context?: { modelId?: unknown } },
|
|
): string | undefined {
|
|
return normalizeOptionalString(
|
|
params.modelId ??
|
|
(typeof params.context?.modelId === "string" ? params.context.modelId : undefined),
|
|
);
|
|
}
|
|
|
|
function resolveProviderRuntimeLookupScope(
|
|
params: ProviderRuntimePluginLookupParams,
|
|
apiOwnerHint?: string,
|
|
): {
|
|
providerRefs: string[];
|
|
modelRefs?: string[];
|
|
} {
|
|
const providerRefs = apiOwnerHint ? [params.provider, apiOwnerHint] : [params.provider];
|
|
const modelId = resolveProviderRuntimeLookupModelId(params);
|
|
if (!modelId) {
|
|
return { providerRefs };
|
|
}
|
|
return {
|
|
providerRefs,
|
|
modelRefs: resolveModelCatalogScope({
|
|
cfg: params.config,
|
|
provider: params.provider,
|
|
model: modelId,
|
|
}).modelRefs,
|
|
};
|
|
}
|
|
|
|
function findProviderRuntimePluginInLoadedRegistries(params: {
|
|
lookup: ProviderRuntimePluginLookupParams;
|
|
apiOwnerHint?: string;
|
|
}): ProviderPlugin | undefined {
|
|
const activeRegistry = getLoadedRuntimePluginRegistry({
|
|
env: params.lookup.env,
|
|
workspaceDir: params.lookup.workspaceDir,
|
|
});
|
|
const activePlugin = activeRegistry
|
|
? findProviderRuntimePluginInRegistry({
|
|
registry: activeRegistry,
|
|
provider: params.lookup.provider,
|
|
apiOwnerHint: params.apiOwnerHint,
|
|
})
|
|
: undefined;
|
|
if (activePlugin) {
|
|
return activePlugin;
|
|
}
|
|
for (const surface of PREPARED_PROVIDER_RUNTIME_SURFACES) {
|
|
const registry = getLoadedRuntimePluginRegistry({
|
|
env: params.lookup.env,
|
|
workspaceDir: params.lookup.workspaceDir,
|
|
surface,
|
|
});
|
|
const plugin = registry
|
|
? findProviderRuntimePluginInRegistry({
|
|
registry,
|
|
provider: params.lookup.provider,
|
|
apiOwnerHint: params.apiOwnerHint,
|
|
})
|
|
: undefined;
|
|
if (plugin) {
|
|
return plugin;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function findProviderRuntimePluginInRegistry(params: {
|
|
registry: PluginRegistry;
|
|
provider: string;
|
|
apiOwnerHint?: string;
|
|
}): ProviderPlugin | undefined {
|
|
return params.registry.providers
|
|
.map((entry) => Object.assign({}, entry.provider, { pluginId: entry.pluginId }))
|
|
.find((plugin) => {
|
|
if (params.apiOwnerHint) {
|
|
return (
|
|
matchesProviderLiteralId(plugin, params.provider) ||
|
|
matchesProviderId(plugin, params.apiOwnerHint)
|
|
);
|
|
}
|
|
return matchesProviderId(plugin, params.provider);
|
|
});
|
|
}
|
|
|
|
export function resolveProviderPluginsForHooks(params: {
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
onlyPluginIds?: string[];
|
|
providerRefs?: readonly string[];
|
|
modelRefs?: readonly string[];
|
|
applyAutoEnable?: boolean;
|
|
bundledProviderVitestCompat?: boolean;
|
|
}): ProviderPlugin[] {
|
|
const env = params.env ?? process.env;
|
|
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState();
|
|
if (
|
|
isPluginProvidersLoadInFlight({
|
|
...params,
|
|
workspaceDir,
|
|
env,
|
|
activate: false,
|
|
applyAutoEnable: params.applyAutoEnable,
|
|
bundledProviderVitestCompat: params.bundledProviderVitestCompat ?? true,
|
|
})
|
|
) {
|
|
return [];
|
|
}
|
|
const resolved = resolvePluginProviders({
|
|
...params,
|
|
workspaceDir,
|
|
env,
|
|
activate: false,
|
|
applyAutoEnable: params.applyAutoEnable,
|
|
bundledProviderVitestCompat: params.bundledProviderVitestCompat ?? true,
|
|
});
|
|
return resolved;
|
|
}
|
|
|
|
export function resolveProviderRuntimePlugin(
|
|
params: ProviderRuntimePluginLookupParams,
|
|
): ProviderPlugin | undefined {
|
|
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState();
|
|
const env = params.env ?? process.env;
|
|
const lookup = { ...params, workspaceDir, env };
|
|
const apiOwnerHint = resolveProviderConfigApiOwnerHint({
|
|
provider: params.provider,
|
|
config: params.config,
|
|
});
|
|
const providerRefs = apiOwnerHint ? [params.provider, apiOwnerHint] : [params.provider];
|
|
const loadedPlugin = findProviderRuntimePluginInLoadedRegistries({
|
|
lookup,
|
|
apiOwnerHint,
|
|
});
|
|
if (loadedPlugin) {
|
|
return loadedPlugin;
|
|
}
|
|
if (
|
|
isPluginProvidersLoadInFlight({
|
|
...params,
|
|
workspaceDir,
|
|
env,
|
|
providerRefs,
|
|
activate: false,
|
|
applyAutoEnable: params.applyAutoEnable,
|
|
bundledProviderVitestCompat: params.bundledProviderVitestCompat ?? true,
|
|
})
|
|
) {
|
|
return undefined;
|
|
}
|
|
const cacheConfig = params.env && params.env !== process.env ? undefined : params.config;
|
|
const registryState = getPluginRegistryState();
|
|
const cacheKey = resolveProviderRuntimePluginCacheKey(lookup, registryState);
|
|
const load = () => {
|
|
const lookupScope = resolveProviderRuntimeLookupScope(params, apiOwnerHint);
|
|
return (
|
|
resolveProviderPluginsForHooks({
|
|
config: params.config,
|
|
workspaceDir,
|
|
env,
|
|
providerRefs: lookupScope.providerRefs,
|
|
modelRefs: lookupScope.modelRefs,
|
|
applyAutoEnable: params.applyAutoEnable,
|
|
bundledProviderVitestCompat: params.bundledProviderVitestCompat,
|
|
}).find((plugin) => {
|
|
if (apiOwnerHint) {
|
|
return (
|
|
matchesProviderLiteralId(plugin, params.provider) ||
|
|
matchesProviderId(plugin, apiOwnerHint)
|
|
);
|
|
}
|
|
return matchesProviderId(plugin, params.provider);
|
|
}) ?? null
|
|
);
|
|
};
|
|
const plugin = cacheConfig
|
|
? resolveConfigScopedRuntimeCacheValue({
|
|
cache: providerRuntimePluginCache,
|
|
config: cacheConfig,
|
|
key: cacheKey,
|
|
load,
|
|
})
|
|
: !registryState?.key
|
|
? load()
|
|
: (() => {
|
|
const cached = defaultProviderRuntimePluginCache.getResult(cacheKey);
|
|
if (cached.hit) {
|
|
return cached.value;
|
|
}
|
|
const loaded = load();
|
|
defaultProviderRuntimePluginCache.set(cacheKey, loaded);
|
|
return loaded;
|
|
})();
|
|
return plugin ?? undefined;
|
|
}
|
|
|
|
export function resolveLoadedProviderRuntimePlugin(
|
|
params: ProviderRuntimePluginLookupParams,
|
|
): ProviderPlugin | undefined {
|
|
const apiOwnerHint = resolveProviderConfigApiOwnerHint({
|
|
provider: params.provider,
|
|
config: params.config,
|
|
});
|
|
return findProviderRuntimePluginInLoadedRegistries({
|
|
lookup: params,
|
|
apiOwnerHint,
|
|
});
|
|
}
|
|
|
|
export function resolveProviderHookPlugin(params: {
|
|
provider: string;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}): ProviderPlugin | undefined {
|
|
return (
|
|
resolveProviderRuntimePlugin(params) ??
|
|
resolveProviderPluginsForHooks({
|
|
config: params.config,
|
|
workspaceDir: params.workspaceDir,
|
|
env: params.env,
|
|
}).find((candidate) => matchesProviderId(candidate, params.provider))
|
|
);
|
|
}
|
|
|
|
export function resolveProviderRuntimePluginHandle(
|
|
params: ProviderRuntimePluginLookupParams,
|
|
): ProviderRuntimePluginHandle {
|
|
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState();
|
|
const env = params.env;
|
|
const runtimePlugin = resolveProviderRuntimePlugin({
|
|
...params,
|
|
workspaceDir,
|
|
env,
|
|
});
|
|
|
|
return {
|
|
...params,
|
|
workspaceDir,
|
|
env,
|
|
plugin: runtimePlugin,
|
|
};
|
|
}
|
|
|
|
export function ensureProviderRuntimePluginHandle(
|
|
params: ProviderRuntimePluginHandleParams,
|
|
): ProviderRuntimePluginHandle {
|
|
const modelId = resolveProviderRuntimeLookupModelId(params);
|
|
if (
|
|
!params.runtimeHandle ||
|
|
(modelId && !params.runtimeHandle.plugin && params.runtimeHandle.modelId !== modelId)
|
|
) {
|
|
return resolveProviderRuntimePluginHandle({
|
|
provider: params.provider,
|
|
modelId,
|
|
config: params.config ?? params.runtimeHandle?.config,
|
|
workspaceDir: params.workspaceDir ?? params.runtimeHandle?.workspaceDir,
|
|
env: params.env ?? params.runtimeHandle?.env,
|
|
applyAutoEnable: params.runtimeHandle?.applyAutoEnable,
|
|
bundledProviderVitestCompat: params.runtimeHandle?.bundledProviderVitestCompat,
|
|
});
|
|
}
|
|
return params.runtimeHandle;
|
|
}
|
|
|
|
export function prepareProviderExtraParams(params: {
|
|
provider: string;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
runtimeHandle?: ProviderRuntimePluginHandle;
|
|
context: ProviderPrepareExtraParamsContext;
|
|
}) {
|
|
return (
|
|
ensureProviderRuntimePluginHandle(params).plugin?.prepareExtraParams?.(params.context) ??
|
|
undefined
|
|
);
|
|
}
|
|
|
|
export function resolveProviderExtraParamsForTransport(params: {
|
|
provider: string;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
runtimeHandle?: ProviderRuntimePluginHandle;
|
|
context: ProviderExtraParamsForTransportContext;
|
|
}) {
|
|
return (
|
|
ensureProviderRuntimePluginHandle(params).plugin?.extraParamsForTransport?.(params.context) ??
|
|
undefined
|
|
);
|
|
}
|
|
|
|
export function resolveProviderAuthProfileId(params: {
|
|
provider: string;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
runtimeHandle?: ProviderRuntimePluginHandle;
|
|
context: ProviderResolveAuthProfileIdContext;
|
|
}): string | undefined {
|
|
const resolved = ensureProviderRuntimePluginHandle(params).plugin?.resolveAuthProfileId?.(
|
|
params.context,
|
|
);
|
|
return typeof resolved === "string" && resolved.trim() ? resolved.trim() : undefined;
|
|
}
|
|
|
|
export function resolveProviderFollowupFallbackRoute(params: {
|
|
provider: string;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
runtimeHandle?: ProviderRuntimePluginHandle;
|
|
context: ProviderFollowupFallbackRouteContext;
|
|
}): ProviderFollowupFallbackRouteResult | undefined {
|
|
return (
|
|
ensureProviderRuntimePluginHandle(params).plugin?.followupFallbackRoute?.(params.context) ??
|
|
undefined
|
|
);
|
|
}
|
|
|
|
export function wrapProviderStreamFn(params: {
|
|
provider: string;
|
|
config?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
runtimeHandle?: ProviderRuntimePluginHandle;
|
|
context: ProviderWrapStreamFnContext;
|
|
}) {
|
|
return (
|
|
ensureProviderRuntimePluginHandle(params).plugin?.wrapStreamFn?.(params.context) ?? undefined
|
|
);
|
|
}
|