Files
openclaw/src/commands/agent-command.test-mocks.ts
Peter Steinberger bb46b79d3c refactor: internalize OpenClaw agent runtime (#85341)
* 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
2026-05-27 19:24:04 +01:00

277 lines
9.2 KiB
TypeScript

import { vi } from "vitest";
import { normalizeStringEntries } from "../shared/string-normalization.js";
vi.mock("../logging/subsystem.js", () => {
const createMockLogger = () => ({
subsystem: "test",
isEnabled: vi.fn(() => true),
trace: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
fatal: vi.fn(),
raw: vi.fn(),
child: vi.fn(() => createMockLogger()),
});
return {
createSubsystemLogger: vi.fn(() => createMockLogger()),
};
});
vi.mock("../cli/deps.js", () => ({
createDefaultDeps: vi.fn(() => ({})),
}));
const acpManagerMock = vi.hoisted(() => ({
current: {
resolveSession: vi.fn(() => null),
} as unknown,
}));
vi.mock("../acp/control-plane/manager.js", () => ({
testing: {
resetAcpSessionManagerForTests: vi.fn(() => {
acpManagerMock.current = {
resolveSession: vi.fn(() => null),
};
}),
setAcpSessionManagerForTests: vi.fn((manager: unknown) => {
acpManagerMock.current = manager;
}),
},
getAcpSessionManager: vi.fn(() => acpManagerMock.current),
}));
vi.mock("../agents/embedded-agent.js", () => ({
abortEmbeddedAgentRun: vi.fn().mockReturnValue(false),
runEmbeddedAgent: vi.fn(),
resolveEmbeddedSessionLane: (key: string) => `session:${key.trim() || "main"}`,
}));
vi.mock("../agents/model-catalog.js", () => ({
loadManifestModelCatalog: vi.fn(() => []),
loadModelCatalog: vi.fn(),
}));
vi.mock("../agents/model-selection.js", () => {
type ConfigWithModels = {
agents?: {
defaults?: {
model?: string | { primary?: string; fallbacks?: string[] };
models?: Record<string, { params?: { thinking?: string } } | undefined>;
thinkingDefault?: string;
};
};
};
type ModelRef = { provider: string; model: string };
type CatalogEntry = { id?: string; model?: string; name?: string; reasoning?: boolean };
const parseModelRefImpl = (raw: string, defaultProvider = "openai"): ModelRef | null => {
const value = raw.trim();
if (!value) {
return null;
}
const slash = value.indexOf("/");
if (slash >= 0) {
return {
provider: value.slice(0, slash).trim(),
model: value.slice(slash + 1).trim(),
};
}
return { provider: defaultProvider, model: value };
};
const parseModelRef = vi.fn(parseModelRefImpl);
const normalizeModelRef = (provider: string, model: string): ModelRef => ({
provider: provider.trim().toLowerCase(),
model: model.trim(),
});
const modelKey = (provider: string, model: string) =>
`${provider.trim().toLowerCase()}/${model.trim().toLowerCase()}`;
const isModelKeyAllowedBySet = (allowedKeys: ReadonlySet<string>, key: string) => {
if (allowedKeys.has(key)) {
return true;
}
const slash = key.indexOf("/");
return slash > 0 && allowedKeys.has(`${key.slice(0, slash)}/*`);
};
const resolvePrimary = (cfg?: ConfigWithModels): string | undefined => {
const primary = cfg?.agents?.defaults?.model;
if (typeof primary === "string") {
return primary;
}
return primary?.primary;
};
const resolveDefaultRef = (cfg?: ConfigWithModels): ModelRef => {
const parsed = parseModelRefImpl(resolvePrimary(cfg) ?? "openai/gpt-5.5", "openai");
return parsed ?? { provider: "openai", model: "gpt-5.5" };
};
const resolveModelConfig = (cfg: ConfigWithModels | undefined, ref: ModelRef) => {
const models = cfg?.agents?.defaults?.models ?? {};
return models[`${ref.provider}/${ref.model}`] ?? models[modelKey(ref.provider, ref.model)];
};
return {
buildAllowedModelSet: vi.fn(({ cfg }: { cfg?: ConfigWithModels; catalog?: CatalogEntry[] }) => {
const refs = new Set<string>();
const modelConfig = cfg?.agents?.defaults?.models ?? {};
for (const raw of Object.keys(modelConfig)) {
const parsed = parseModelRefImpl(raw, "openai");
if (parsed) {
refs.add(modelKey(parsed.provider, parsed.model));
}
}
const primary = resolveDefaultRef(cfg);
refs.add(modelKey(primary.provider, primary.model));
const fallbackRefs =
typeof cfg?.agents?.defaults?.model === "object"
? (cfg.agents.defaults.model.fallbacks ?? [])
: [];
for (const fallback of fallbackRefs) {
const parsed = parseModelRefImpl(fallback, primary.provider);
if (parsed) {
refs.add(modelKey(parsed.provider, parsed.model));
}
}
return {
allowedKeys: refs,
allowedCatalog: [],
allowAny: Object.keys(modelConfig).length === 0,
};
}),
createModelVisibilityPolicy: vi.fn(
({ cfg, catalog = [] }: { cfg?: ConfigWithModels; catalog?: CatalogEntry[] }) => {
const refs = new Set<string>();
const modelConfig = cfg?.agents?.defaults?.models ?? {};
for (const raw of Object.keys(modelConfig)) {
const parsed = parseModelRefImpl(raw, "openai");
if (parsed) {
refs.add(modelKey(parsed.provider, parsed.model));
}
}
const primary = resolveDefaultRef(cfg);
refs.add(modelKey(primary.provider, primary.model));
const allowAny = Object.keys(modelConfig).length === 0;
const allowsKey = (key: string) => allowAny || isModelKeyAllowedBySet(refs, key);
return {
allowAny,
allowedKeys: refs,
allowedCatalog: catalog,
exactModelRefs: Object.keys(modelConfig).filter((key) => !key.endsWith("/*")),
providerWildcards: new Set(
Object.keys(modelConfig)
.filter((key) => key.endsWith("/*"))
.map((key) => key.slice(0, -2).trim().toLowerCase()),
),
hasConfiguredEntries: Object.keys(modelConfig).length > 0,
hasProviderWildcards: Object.keys(modelConfig).some((key) => key.endsWith("/*")),
allowsKey,
allows: ({ provider, model }: ModelRef) => allowsKey(modelKey(provider, model)),
resolveSelection: ({ provider, model }: ModelRef) => {
const key = modelKey(provider, model);
if (allowsKey(key)) {
return { provider, model };
}
const fallback = catalog[0];
return fallback?.id ? { provider: "openai", model: fallback.id } : null;
},
visibleCatalog: ({ catalog: visibleCatalog }: { catalog: CatalogEntry[] }) =>
visibleCatalog,
};
},
),
buildConfiguredModelCatalog: vi.fn(() => []),
isModelKeyAllowedBySet,
isCliProvider: vi.fn(() => false),
modelKey,
normalizeModelRef,
parseModelRef,
resolveConfiguredModelRef: vi.fn(
({ cfg }: { cfg?: ConfigWithModels; defaultProvider?: string; defaultModel?: string }) =>
resolveDefaultRef(cfg),
),
resolveDefaultModelForAgent: vi.fn(({ cfg }: { cfg?: ConfigWithModels }) =>
resolveDefaultRef(cfg),
),
resolveThinkingDefault: vi.fn(
({
cfg,
provider,
model,
catalog,
}: {
cfg?: ConfigWithModels;
provider: string;
model: string;
catalog?: CatalogEntry[];
}) => {
const ref = normalizeModelRef(provider, model);
const modelThinking = resolveModelConfig(cfg, ref)?.params?.thinking;
if (modelThinking) {
return modelThinking;
}
const defaultThinking = cfg?.agents?.defaults?.thinkingDefault;
if (defaultThinking) {
return defaultThinking;
}
const entry = catalog?.find((item) => item.id === model || item.model === model);
if (entry?.reasoning && entry.name?.includes("4.6")) {
return "adaptive";
}
return entry?.reasoning ? "low" : "off";
},
),
};
});
vi.mock("../agents/subagent-announce.js", () => ({
runSubagentAnnounceFlow: vi.fn(),
}));
vi.mock("../gateway/call.js", () => ({
callGateway: vi.fn(),
}));
vi.mock("../agents/workspace.js", () => ({
DEFAULT_AGENT_WORKSPACE_DIR: "/tmp/openclaw-workspace",
DEFAULT_AGENTS_FILENAME: "AGENTS.md",
DEFAULT_IDENTITY_FILENAME: "IDENTITY.md",
resolveDefaultAgentWorkspaceDir: () => "/tmp/openclaw-workspace",
ensureAgentWorkspace: vi.fn(async ({ dir }: { dir: string }) => ({ dir })),
}));
vi.mock("../agents/skills.js", () => ({
buildWorkspaceSkillSnapshot: vi.fn(() => undefined),
loadWorkspaceSkillEntries: vi.fn(() => []),
}));
vi.mock("../agents/skills/refresh.js", () => ({
getSkillsSnapshotVersion: vi.fn(() => 0),
}));
vi.mock("../agents/skills/refresh-state.js", () => ({
getSkillsSnapshotVersion: vi.fn(() => 0),
shouldRefreshSnapshotForVersion: vi.fn(() => false),
}));
vi.mock("../agents/skills/filter.js", () => ({
normalizeSkillFilter: vi.fn((skillFilter?: ReadonlyArray<unknown>) =>
skillFilter ? normalizeStringEntries(skillFilter) : undefined,
),
normalizeSkillFilterForComparison: vi.fn((skillFilter?: ReadonlyArray<unknown>) =>
skillFilter
?.map((entry) => String(entry).trim())
.filter(Boolean)
.toSorted(),
),
matchesSkillFilter: vi.fn(() => true),
}));
vi.mock("../agents/exec-defaults.js", () => ({
canExecRequestNode: vi.fn(() => false),
}));
vi.mock("../infra/skills-remote.js", () => ({
getRemoteSkillEligibility: vi.fn(() => undefined),
}));