Files
openclaw/scripts/lib/ci-node-test-plan.mjs
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

462 lines
14 KiB
JavaScript

import { relative } from "node:path";
import { commandsLightTestFiles } from "../../test/vitest/vitest.commands-light-paths.mjs";
import { fullSuiteVitestShards } from "../../test/vitest/vitest.test-shards.mjs";
import { listTrackedTestFiles } from "./list-test-files.mjs";
const EXCLUDED_FULL_SUITE_SHARDS = new Set([
"test/vitest/vitest.full-core-contracts.config.ts",
"test/vitest/vitest.full-core-bundled.config.ts",
"test/vitest/vitest.full-extensions.config.ts",
]);
const EXCLUDED_PROJECT_CONFIGS = new Set(["test/vitest/vitest.channels.config.ts"]);
const RELEASE_ONLY_PLUGIN_SHARDS = new Set(["agentic-plugins"]);
function listTestFiles(rootDir) {
return listTrackedTestFiles(rootDir);
}
function createAutoReplyReplySplitShards() {
const files = listTestFiles("src/auto-reply/reply");
const groups = {
"auto-reply-reply-agent-runner": [],
"auto-reply-reply-commands": [],
"auto-reply-reply-dispatch": [],
"auto-reply-reply-session": [],
"auto-reply-reply-state-routing": [],
};
for (const file of files) {
const name = relative("src/auto-reply/reply", file).replaceAll("\\", "/");
if (
name.startsWith("agent-runner") ||
name.startsWith("acp-") ||
name === "abort.test.ts" ||
name === "bash-command.stop.test.ts" ||
name.startsWith("block-")
) {
groups["auto-reply-reply-agent-runner"].push(file);
} else if (name.startsWith("commands")) {
groups["auto-reply-reply-commands"].push(file);
} else if (
name.startsWith("directive-") ||
name.startsWith("dispatch") ||
name.startsWith("followup-") ||
name.startsWith("get-reply")
) {
groups["auto-reply-reply-dispatch"].push(file);
} else if (name.startsWith("session")) {
groups["auto-reply-reply-session"].push(file);
} else {
groups["auto-reply-reply-state-routing"].push(file);
}
}
return Object.entries(groups)
.map(([groupName, includePatterns]) => ({
configs: ["test/vitest/vitest.auto-reply-reply.config.ts"],
includePatterns,
requiresDist: false,
shardName: groupName,
}))
.filter((shard) => shard.includePatterns.length > 0);
}
function resolveCommandShardName(file) {
const name = relative("src/commands", file).replaceAll("\\", "/");
if (name.startsWith("agent") || name.startsWith("channel") || name === "message.test.ts") {
return "agentic-commands-agent-channel";
}
if (name.startsWith("doctor")) {
if (name.startsWith("doctor/shared/") || name.startsWith("doctor/")) {
return "agentic-commands-doctor-shared";
}
return "agentic-commands-doctor";
}
if (
name.startsWith("auth-choice") ||
name.startsWith("configure") ||
name.startsWith("onboard") ||
name === "setup.test.ts"
) {
return "agentic-commands-onboard-config";
}
if (
name.startsWith("models/") ||
name === "model-picker.test.ts" ||
name === "openai-model-default.test.ts"
) {
return "agentic-commands-models";
}
return "agentic-commands-status-tools";
}
function createAgenticCommandSplitShards() {
const commandsLightTests = new Set(commandsLightTestFiles);
const groups = new Map();
for (const file of listTestFiles("src/commands")) {
if (commandsLightTests.has(file)) {
continue;
}
const shardName = resolveCommandShardName(file);
groups.set(shardName, [...(groups.get(shardName) ?? []), file]);
}
return [
"agentic-commands-agent-channel",
"agentic-commands-doctor",
"agentic-commands-doctor-shared",
"agentic-commands-models",
"agentic-commands-onboard-config",
"agentic-commands-status-tools",
]
.map((shardName) => ({
configs: ["test/vitest/vitest.commands.config.ts"],
includePatterns: groups.get(shardName) ?? [],
requiresDist: false,
shardName,
}))
.filter((shard) => shard.includePatterns.length > 0);
}
const GATEWAY_SERVER_BACKED_HTTP_TESTS = new Set([
"src/gateway/embeddings-http.test.ts",
"src/gateway/models-http.test.ts",
"src/gateway/openai-http.test.ts",
"src/gateway/openresponses-http.test.ts",
"src/gateway/probe.auth.integration.test.ts",
]);
const GATEWAY_SERVER_EXCLUDED_TESTS = new Set([
"src/gateway/gateway.test.ts",
"src/gateway/server.startup-matrix-migration.integration.test.ts",
"src/gateway/sessions-history-http.test.ts",
]);
function isGatewayServerTestFile(file) {
return (
file.startsWith("src/gateway/") &&
!file.startsWith("src/gateway/server-methods/") &&
!GATEWAY_SERVER_EXCLUDED_TESTS.has(file) &&
(file.includes("server") || GATEWAY_SERVER_BACKED_HTTP_TESTS.has(file))
);
}
function resolveGatewayServerShardName(file) {
const name = relative("src/gateway", file).replaceAll("\\", "/");
if (
GATEWAY_SERVER_BACKED_HTTP_TESTS.has(file) ||
name.startsWith("server.models") ||
name.startsWith("server.talk")
) {
return "agentic-control-plane-http-models";
}
if (
name.startsWith("server.agent") ||
name.startsWith("server.chat") ||
name.startsWith("server.sessions")
) {
return "agentic-control-plane-agent-chat";
}
if (
name.includes("auth") ||
name.includes("device") ||
name.includes("node") ||
name.includes("roles") ||
name.includes("silent") ||
name.includes("preauth") ||
name.includes("control-plane-rate-limit")
) {
return "agentic-control-plane-auth-node";
}
if (
name.startsWith("server-startup") ||
name.startsWith("server-restart") ||
name.startsWith("server-runtime") ||
name.startsWith("server.lazy") ||
name.startsWith("server.health") ||
name.startsWith("server/health-state") ||
name.startsWith("server/readiness") ||
name === "server-close.test.ts"
) {
return "agentic-control-plane-startup-runtime";
}
if (
name.includes("plugin") ||
name.includes("hooks") ||
name.includes("http") ||
name.includes("ws-connection")
) {
return "agentic-control-plane-http-plugin-ws";
}
return "agentic-control-plane-runtime";
}
function createGatewayServerSplitShards() {
const groups = new Map();
for (const file of listTestFiles("src/gateway").filter(isGatewayServerTestFile)) {
const shardName = resolveGatewayServerShardName(file);
groups.set(shardName, [...(groups.get(shardName) ?? []), file]);
}
return [
"agentic-control-plane-agent-chat",
"agentic-control-plane-auth-node",
"agentic-control-plane-http-models",
"agentic-control-plane-http-plugin-ws",
"agentic-control-plane-runtime",
"agentic-control-plane-startup-runtime",
]
.map((shardName) => ({
configs: ["test/vitest/vitest.gateway-server.config.ts"],
includePatterns: groups.get(shardName) ?? [],
requiresDist: false,
runner: "blacksmith-4vcpu-ubuntu-2404",
shardName,
}))
.filter((shard) => shard.includePatterns.length > 0);
}
function resolveCronShardName(file) {
const name = relative("src/cron", file).replaceAll("\\", "/");
if (name.startsWith("isolated-agent")) {
return "core-runtime-cron-isolated-agent";
}
if (name.startsWith("service")) {
return "core-runtime-cron-service";
}
return "core-runtime-cron-core";
}
function createCronSplitShards() {
const groups = new Map();
for (const file of listTestFiles("src/cron")) {
const shardName = resolveCronShardName(file);
groups.set(shardName, [...(groups.get(shardName) ?? []), file]);
}
return ["core-runtime-cron-core", "core-runtime-cron-isolated-agent", "core-runtime-cron-service"]
.map((shardName) => ({
configs: ["test/vitest/vitest.cron.config.ts"],
includePatterns: groups.get(shardName) ?? [],
requiresDist: false,
shardName,
}))
.filter((shard) => shard.includePatterns.length > 0);
}
const SPLIT_NODE_SHARDS = new Map([
[
"core-unit-fast",
[
{
shardName: "core-unit-fast",
configs: ["test/vitest/vitest.unit-fast.config.ts"],
requiresDist: false,
},
],
],
[
"core-unit-src",
[
{
shardName: "core-unit-src-security",
configs: [
"test/vitest/vitest.unit-src.config.ts",
"test/vitest/vitest.unit-security.config.ts",
],
includeExternalConfigs: true,
requiresDist: false,
},
],
],
["core-unit-security", []],
[
"core-unit-support",
[
{
shardName: "core-unit-support",
configs: ["test/vitest/vitest.unit-support.config.ts"],
requiresDist: false,
},
],
],
[
"core-runtime",
[
{
shardName: "core-runtime-infra-state",
configs: [
"test/vitest/vitest.infra.config.ts",
"test/vitest/vitest.hooks.config.ts",
"test/vitest/vitest.secrets.config.ts",
],
requiresDist: false,
runner: "blacksmith-4vcpu-ubuntu-2404",
},
{
shardName: "core-runtime-infra-process",
configs: [
"test/vitest/vitest.logging.config.ts",
"test/vitest/vitest.process.config.ts",
"test/vitest/vitest.runtime-config.config.ts",
],
requiresDist: false,
runner: "blacksmith-4vcpu-ubuntu-2404",
},
{
shardName: "core-runtime-media-ui",
configs: [
"test/vitest/vitest.media.config.ts",
"test/vitest/vitest.media-understanding.config.ts",
"test/vitest/vitest.tui.config.ts",
"test/vitest/vitest.ui.config.ts",
"test/vitest/vitest.wizard.config.ts",
],
requiresDist: false,
},
{
shardName: "core-runtime-shared",
configs: [
"test/vitest/vitest.acp.config.ts",
"test/vitest/vitest.shared-core.config.ts",
"test/vitest/vitest.tasks.config.ts",
"test/vitest/vitest.utils.config.ts",
],
requiresDist: false,
},
...createCronSplitShards(),
],
],
[
"auto-reply",
[
{
shardName: "auto-reply-core-top-level",
configs: [
"test/vitest/vitest.auto-reply-core.config.ts",
"test/vitest/vitest.auto-reply-top-level.config.ts",
],
requiresDist: false,
},
...createAutoReplyReplySplitShards(),
],
],
[
"agentic",
[
...createGatewayServerSplitShards(),
{
shardName: "agentic-cli",
configs: ["test/vitest/vitest.cli.config.ts"],
requiresDist: false,
},
{
shardName: "agentic-command-support",
configs: [
"test/vitest/vitest.commands-light.config.ts",
"test/vitest/vitest.daemon.config.ts",
],
requiresDist: false,
},
...createAgenticCommandSplitShards(),
{
shardName: "agentic-agents",
configs: [
"test/vitest/vitest.agents-core.config.ts",
"test/vitest/vitest.agents-embedded-agent.config.ts",
"test/vitest/vitest.agents-support.config.ts",
"test/vitest/vitest.agents-tools.config.ts",
],
requiresDist: false,
},
{
shardName: "agentic-gateway-core",
configs: [
"test/vitest/vitest.gateway-core.config.ts",
"test/vitest/vitest.gateway-client.config.ts",
],
requiresDist: false,
},
{
shardName: "agentic-gateway-methods",
configs: ["test/vitest/vitest.gateway-methods.config.ts"],
requiresDist: false,
},
{
shardName: "agentic-plugin-sdk",
configs: [
"test/vitest/vitest.plugin-sdk-light.config.ts",
"test/vitest/vitest.plugin-sdk.config.ts",
],
requiresDist: false,
},
{
shardName: "agentic-plugins",
configs: ["test/vitest/vitest.plugins.config.ts"],
requiresDist: false,
},
],
],
]);
const DIST_DEPENDENT_NODE_SHARD_NAMES = new Set(["core-support-boundary"]);
function formatNodeTestShardCheckName(shardName) {
const normalizedShardName = shardName.startsWith("core-unit-")
? `core-${shardName.slice("core-unit-".length)}`
: shardName;
return `checks-node-${normalizedShardName}`;
}
export function createNodeTestShards(options = {}) {
const includeReleaseOnlyPluginShards = options.includeReleaseOnlyPluginShards ?? true;
return fullSuiteVitestShards.flatMap((shard) => {
if (EXCLUDED_FULL_SUITE_SHARDS.has(shard.config)) {
return [];
}
const configs = shard.projects.filter((config) => !EXCLUDED_PROJECT_CONFIGS.has(config));
if (configs.length === 0) {
return [];
}
const splitShards = SPLIT_NODE_SHARDS.get(shard.name);
if (splitShards) {
return splitShards.flatMap((splitShard) => {
if (
RELEASE_ONLY_PLUGIN_SHARDS.has(splitShard.shardName) &&
!includeReleaseOnlyPluginShards
) {
return [];
}
const splitConfigs = splitShard.includeExternalConfigs
? splitShard.configs
: splitShard.configs.filter((config) => configs.includes(config));
if (splitConfigs.length === 0) {
return [];
}
return [
{
checkName: formatNodeTestShardCheckName(splitShard.shardName),
shardName: splitShard.shardName,
configs: splitConfigs,
...(splitShard.includePatterns ? { includePatterns: splitShard.includePatterns } : {}),
...(splitShard.runner ? { runner: splitShard.runner } : {}),
requiresDist: splitShard.requiresDist,
},
];
});
}
return [
{
checkName: formatNodeTestShardCheckName(shard.name),
shardName: shard.name,
configs,
requiresDist: DIST_DEPENDENT_NODE_SHARD_NAMES.has(shard.name),
},
];
});
}