openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
324 lines (323 loc) • 13.5 kB
JavaScript
import { o as resolveUserPath } from "./home-dir-CjKH62jd.mjs";
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { y as uniqueStrings } from "./string-normalization-YMwDjufr.mjs";
import { u as withPluginRuntimeRegistryScope } from "./gateway-request-scope-Btza1qbw.mjs";
import "./utils-DUAwk2p5.mjs";
import { i as parseModelCatalogRef } from "./model-catalog-refs-Dd-I0WMI.mjs";
import { a as resolveAgentDir, p as resolveAmbientOwnerAgentId } from "./agent-scope-config-BU4qc9bA.mjs";
import { r as listAgentEntriesWithSource } from "./agent-roster-B45JERT5.mjs";
import { n as defaultSlotIdForKey } from "./slots-CSgIxf6O.mjs";
import { n as DEFAULT_MODEL, r as DEFAULT_PROVIDER } from "./defaults-BbU4k6fu.mjs";
import { N as listModelRefsFromConfigValue } from "./runtime-snapshot-CifcQ780.mjs";
import { i as normalizeEmbeddedAgentRuntime } from "./agent-runtime-id-D81oHQZ0.mjs";
import { t as acquirePluginRegistryForInspection } from "./loader-runtime-load-lwNg_N_C.mjs";
import { n as getContextEngineRegistration, s as resolveContextEngine } from "./registry-Il2ImH3E.mjs";
import { t as resolveAgentHarnessPolicy } from "./policy-tSS-HDQS.mjs";
import { n as getRegisteredAgentHarness } from "./registry-DT4lh31t.mjs";
import { i as resolveCliBackendConfig } from "./cli-backends-CxUCbGNt.mjs";
import "./loader-DPiNpDHK.mjs";
import { a as evaluateContextEngineHostSupport, i as buildGenericCliContextEngineHostSupport, n as OPENCLAW_EMBEDDED_CONTEXT_ENGINE_HOST, t as CODEX_APP_SERVER_CONTEXT_ENGINE_HOST } from "./host-compat-xESS3bi6.mjs";
import { t as ensureContextEnginesInitialized } from "./init-C0_4CXw5.mjs";
//#region src/commands/doctor/shared/context-engine-host-compat.ts
function normalizeRuntimeId(value) {
if (typeof value !== "string") return;
return normalizeEmbeddedAgentRuntime(value.trim().toLowerCase()) || void 0;
}
function parseModelRef(value) {
return typeof value === "string" ? parseModelCatalogRef(value) ?? void 0 : void 0;
}
function collectExplicitRuntimeRefs(cfg) {
const refs = [];
const push = (runtime, path) => {
const runtimeId = normalizeRuntimeId(runtime);
if (runtimeId && runtimeId !== "default") refs.push({
runtimeId,
path
});
};
for (const [providerId, providerConfig] of Object.entries(cfg.models?.providers ?? {})) {
push(providerConfig?.agentRuntime?.id, `models.providers.${providerId}.agentRuntime.id`);
providerConfig?.models?.forEach((modelConfig, index) => {
push(modelConfig?.agentRuntime?.id, `models.providers.${providerId}.models[${index}].agentRuntime.id`);
});
}
for (const [modelRef, modelConfig] of Object.entries(cfg.agents?.defaults?.models ?? {})) push(modelConfig?.agentRuntime?.id, `agents.defaults.models.${modelRef}.agentRuntime.id`);
for (const { entry: agent, source } of listAgentEntriesWithSource(cfg)) {
const path = source.kind === "entries" ? `agents.entries.${source.key}` : `agents.list.${source.index}`;
for (const [modelRef, modelConfig] of Object.entries(agent.models ?? {})) push(modelConfig?.agentRuntime?.id, `${path}.models.${modelRef}.agentRuntime.id`);
}
return refs;
}
function collectSelectedModelRefs(cfg) {
const refs = [];
const pushModel = (value, path, agentId) => {
for (const ref of listModelRefsFromConfigValue(value)) {
const modelRef = ref.trim();
if (modelRef) refs.push({
modelRef,
path,
...agentId ? { agentId } : {}
});
}
};
const pushModelMap = (models, path, agentId) => {
if (!isRecord(models)) return;
for (const modelRef of Object.keys(models)) refs.push({
modelRef,
path: `${path}.${modelRef}`,
...agentId ? { agentId } : {}
});
};
if (cfg.agents?.defaults?.model !== void 0) pushModel(cfg.agents.defaults.model, "agents.defaults.model");
else refs.push({
modelRef: `${DEFAULT_PROVIDER}/${DEFAULT_MODEL}`,
path: "agents.defaults.model (default)"
});
pushModelMap(cfg.agents?.defaults?.models, "agents.defaults.models");
for (const { entry: agent, source } of listAgentEntriesWithSource(cfg)) {
const agentId = agent.id;
const path = source.kind === "entries" ? `agents.entries.${source.key}` : `agents.list.${source.index}`;
pushModel(agent.model ?? cfg.agents?.defaults?.model, `${path}.model`, agentId);
pushModelMap(agent.models, `${path}.models`, agentId);
}
return refs;
}
function runtimeHostCandidate(params) {
const runtimeId = normalizeRuntimeId(params.runtimeId) ?? params.runtimeId;
if (runtimeId === "openclaw" || runtimeId === "auto") return {
runtimeId,
host: OPENCLAW_EMBEDDED_CONTEXT_ENGINE_HOST,
paths: params.paths
};
if (runtimeId === "codex") return {
runtimeId,
host: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
paths: params.paths
};
const harness = getRegisteredAgentHarness(runtimeId)?.harness;
if (harness) return {
runtimeId,
host: {
id: `harness:${harness.id}`,
label: `${harness.label} harness`,
capabilities: harness.contextEngineHostCapabilities ?? []
},
paths: params.paths
};
const cliBackend = resolveCliBackendConfig(runtimeId, params.cfg);
return {
runtimeId,
host: buildGenericCliContextEngineHostSupport({
backendId: cliBackend?.id ?? runtimeId,
capabilities: cliBackend?.contextEngineHostCapabilities
}),
paths: params.paths
};
}
/** Collect effective agent-run host candidates from provider/model runtime policy. */
function collectConfiguredContextEngineAgentRunHosts(params) {
const runtimePaths = /* @__PURE__ */ new Map();
const push = (runtimeId, path) => {
if (!runtimeId) return;
const normalized = normalizeRuntimeId(runtimeId) ?? runtimeId;
const paths = runtimePaths.get(normalized) ?? [];
paths.push(path);
runtimePaths.set(normalized, paths);
};
for (const ref of collectExplicitRuntimeRefs(params.cfg)) push(ref.runtimeId, ref.path);
for (const model of collectSelectedModelRefs(params.cfg)) {
const parsed = parseModelRef(model.modelRef);
if (!parsed) continue;
push(resolveAgentHarnessPolicy({
config: params.cfg,
provider: parsed.provider,
modelId: parsed.modelId,
agentId: model.agentId
}).runtime, model.path);
}
return [...runtimePaths.entries()].map(([runtimeId, paths]) => runtimeHostCandidate({
cfg: params.cfg,
runtimeId,
paths
}));
}
function selectedContextEngineSlotId(cfg) {
const slotValue = cfg.plugins?.slots?.contextEngine;
return typeof slotValue === "string" && slotValue.trim() ? slotValue.trim() : defaultSlotIdForKey("contextEngine");
}
async function resolveSelectedContextEngineInfo(params) {
const engineId = selectedContextEngineSlotId(params.cfg);
if (engineId === defaultSlotIdForKey("contextEngine") || engineId === "none") return {
info: {
id: engineId,
name: engineId
},
warnings: []
};
ensureContextEnginesInitialized();
let pluginRegistry;
let inspection;
let engine;
let outcome;
try {
let inspectionWarning;
if (getContextEngineRegistration(engineId)?.lifecycle !== "runtime") {
try {
inspection = await acquirePluginRegistryForInspection({
config: params.cfg,
env: params.env,
onlyPluginIds: [engineId]
});
pluginRegistry = inspection.registry;
} catch (error) {
inspectionWarning = `- plugins.slots.contextEngine: could not inspect context engine "${engineId}" host requirements because its plugin failed to load: ${error instanceof Error ? error.message : String(error)}`;
}
if (pluginRegistry) {
const registration = pluginRegistry.contextEngines.get(engineId);
if (registration?.lifecycle === "readOnlyDiscovery") inspectionWarning = `- plugins.slots.contextEngine: context engine "${engineId}" is registered for read-only discovery; offline host compatibility inspection is unavailable. This does not indicate a missing runtime registration in the Gateway.`;
else if (registration?.lifecycle !== "runtime") inspectionWarning = `- plugins.slots.contextEngine: could not inspect context engine "${engineId}" host requirements because it is not registered.`;
}
}
if (inspectionWarning) outcome = {
ok: true,
result: { warnings: [inspectionWarning] }
};
else {
const agentId = resolveAmbientOwnerAgentId(params.cfg, void 0, {
surface: "context-engine Doctor checks",
hint: "Set agents.defaults.systemAgent.agentId before running Doctor."
});
const resolve = () => resolveContextEngine(params.cfg, {
agentDir: resolveAgentDir(params.cfg, agentId, params.env),
workspaceDir: params.cfg.agents?.defaults?.workspace ? resolveUserPath(params.cfg.agents.defaults.workspace, params.env) : void 0
});
engine = await withPluginRuntimeRegistryScope(pluginRegistry, resolve);
const info = engine.info;
const requirements = info.hostRequirements?.["agent-run"];
outcome = {
ok: true,
result: {
info: {
id: info.id,
name: info.name,
hostRequirements: requirements ? { "agent-run": {
requiredCapabilities: [...requirements.requiredCapabilities],
unsupportedMessage: requirements.unsupportedMessage
} } : void 0
},
warnings: []
}
};
}
} catch (error) {
outcome = {
ok: false,
error
};
}
for (const cleanup of [() => engine?.dispose?.(), () => inspection?.release()]) try {
await cleanup();
} catch (error) {
if (outcome.ok) outcome = {
ok: false,
error
};
}
if (!outcome.ok) {
const { error } = outcome;
return { warnings: [`- plugins.slots.contextEngine: could not inspect context engine "${engineId}" host requirements: ${error instanceof Error ? error.message : String(error)}`] };
}
return outcome.result;
}
function collectHostCompatibilityIssues(params) {
return params.hosts.flatMap((candidate) => {
const evaluation = evaluateContextEngineHostSupport({
contextEngineInfo: params.info,
operation: "agent-run",
host: candidate.host
});
if (evaluation.ok) return [];
return [{
candidate,
missingCapabilities: evaluation.missingCapabilities,
requiredCapabilities: evaluation.requirements.requiredCapabilities
}];
});
}
function formatPaths(paths) {
const unique = uniqueStrings(paths);
if (unique.length <= 2) return unique.join(", ");
return `${unique.slice(0, 2).join(", ")}, and ${unique.length - 2} more`;
}
function formatHostCapabilities(capabilities) {
return capabilities.length > 0 ? capabilities.join(", ") : "(none)";
}
function formatCompatibilityWarnings(params) {
if (params.issues.length === 0) return [];
const lines = params.issues.map((issue) => {
const paths = formatPaths(issue.candidate.paths);
return `- plugins.slots.contextEngine: context engine "${params.info.id}" is incompatible with ${issue.candidate.host.label} (${paths}). Missing host capabilities: ${issue.missingCapabilities.join(", ")}. Required capabilities: ${issue.requiredCapabilities.join(", ")}. Host capabilities: ${formatHostCapabilities(issue.candidate.host.capabilities)}.`;
});
const incompatibleAllHosts = params.issues.length === params.hostCount;
lines.push(incompatibleAllHosts ? `- Run "${params.doctorFixCommand}" to remove the plugins.slots.contextEngine override and restore the default "legacy", or configure a compatible runtime/harness for agent runs.` : `- Some configured runtimes support context engine "${params.info.id}" and others do not; doctor will not rewrite the global contextEngine slot automatically. Configure unsupported models to use a compatible runtime/harness or set plugins.slots.contextEngine to "legacy".`);
return [lines.join("\n")];
}
/** Collect doctor warnings for context engines that cannot run under configured hosts. */
async function collectContextEngineHostCompatibilityWarnings(params) {
const resolved = await resolveSelectedContextEngineInfo(params);
if (!resolved.info) return resolved.warnings;
const hosts = collectConfiguredContextEngineAgentRunHosts(params);
const issues = collectHostCompatibilityIssues({
info: resolved.info,
hosts
});
return [...resolved.warnings, ...formatCompatibilityWarnings({
info: resolved.info,
issues,
hostCount: hosts.length,
doctorFixCommand: params.doctorFixCommand
})];
}
/** Repair a globally incompatible context engine by falling back to legacy. */
async function maybeRepairContextEngineHostCompatibility(params) {
const resolved = await resolveSelectedContextEngineInfo(params);
if (!resolved.info) return {
config: params.cfg,
changes: [],
warnings: resolved.warnings
};
const hosts = collectConfiguredContextEngineAgentRunHosts(params);
const issues = collectHostCompatibilityIssues({
info: resolved.info,
hosts
});
if (issues.length === 0) return {
config: params.cfg,
changes: [],
warnings: resolved.warnings
};
const warnings = formatCompatibilityWarnings({
info: resolved.info,
issues,
hostCount: hosts.length,
doctorFixCommand: params.doctorFixCommand
});
if (issues.length !== hosts.length) return {
config: params.cfg,
changes: [],
warnings: [...resolved.warnings, ...warnings]
};
const next = structuredClone(params.cfg);
const slots = next.plugins?.slots;
if (slots) {
delete slots.contextEngine;
if (Object.keys(slots).length === 0) delete next.plugins?.slots;
}
return {
config: next,
changes: [`Reset plugins.slots.contextEngine to the default "legacy" because context engine "${resolved.info.id}" is incompatible with every configured agent-run host.`],
warnings: resolved.warnings
};
}
//#endregion
export { maybeRepairContextEngineHostCompatibility as n, collectContextEngineHostCompatibilityWarnings as t };