mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
Revert "refactor: move runtime state to SQLite"
This reverts commit f91de52f0d.
This commit is contained in:
15
test/fixtures/talk-config-contract.json
vendored
15
test/fixtures/talk-config-contract.json
vendored
@@ -77,6 +77,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "legacy_payload_fallback",
|
||||
"defaultProvider": "elevenlabs",
|
||||
"payloadValid": true,
|
||||
"expectedSelection": {
|
||||
"provider": "elevenlabs",
|
||||
"normalizedPayload": true,
|
||||
"voiceId": "voice-legacy",
|
||||
"apiKey": "xxxxx"
|
||||
},
|
||||
"talk": {
|
||||
"voiceId": "voice-legacy",
|
||||
"apiKey": "xxxxx"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeoutCases": [
|
||||
|
||||
@@ -30,6 +30,7 @@ export const CODEX_MODEL_PROMPT_FIXTURE_DIR =
|
||||
|
||||
const WORKSPACE_DIR = "/tmp/openclaw-happy-path/workspace";
|
||||
const AGENT_DIR = "/tmp/openclaw-happy-path/agent";
|
||||
const SESSION_FILE = "/tmp/openclaw-happy-path/session.jsonl";
|
||||
const MODEL_ID = "gpt-5.5";
|
||||
const CODEX_PROMPT_PERSONALITY = "pragmatic";
|
||||
const CODEX_MODEL_PROMPT_FIXTURE_PATH = path.join(
|
||||
@@ -280,6 +281,7 @@ function createAttempt(params: {
|
||||
agentId: "main",
|
||||
agentDir: AGENT_DIR,
|
||||
workspaceDir: WORKSPACE_DIR,
|
||||
sessionFile: SESSION_FILE,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionId: `session-${params.scenario.id}`,
|
||||
runId: `run-${params.scenario.id}`,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { vi } from "vitest";
|
||||
import { loadPersistedAuthProfileStore } from "../../src/agents/auth-profiles/persisted.js";
|
||||
import type { RuntimeEnv } from "../../src/runtime.js";
|
||||
import { makeTempWorkspace } from "../../src/test-helpers/workspace.js";
|
||||
import { captureEnv } from "../../src/test-utils/env.js";
|
||||
@@ -83,10 +82,11 @@ export function requireOpenClawAgentDir(): string {
|
||||
return agentDir;
|
||||
}
|
||||
|
||||
export async function readAuthProfilesForAgent<T>(agentDir: string): Promise<T> {
|
||||
const store = loadPersistedAuthProfileStore(agentDir);
|
||||
if (!store) {
|
||||
throw new Error(`Expected SQLite auth profile store for ${agentDir}`);
|
||||
}
|
||||
return store as T;
|
||||
function authProfilePathForAgent(agentDir: string): string {
|
||||
return path.join(agentDir, "auth-profiles.json");
|
||||
}
|
||||
|
||||
export async function readAuthProfilesForAgent<T>(agentDir: string): Promise<T> {
|
||||
const raw = await fs.readFile(authProfilePathForAgent(agentDir), "utf8");
|
||||
return JSON.parse(raw) as T;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ afterAll(async () => {
|
||||
export async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const home = join(suiteTempHomeRoot, `case-${++suiteTempHomeId}`);
|
||||
const snapshot = snapshotTempHomeEnv();
|
||||
await fs.mkdir(join(home, ".openclaw", "agents", "main", "agent"), { recursive: true });
|
||||
await fs.mkdir(join(home, ".openclaw", "agents", "main", "sessions"), { recursive: true });
|
||||
setTempHomeEnv(home);
|
||||
|
||||
try {
|
||||
@@ -300,6 +300,7 @@ export function makeCfg(home: string): OpenClawConfig {
|
||||
debounceMs: 0,
|
||||
},
|
||||
},
|
||||
session: { store: join(home, "sessions.json") },
|
||||
} as OpenClawConfig);
|
||||
}
|
||||
|
||||
@@ -318,6 +319,14 @@ export function installTriggerHandlingReplyHarness(
|
||||
installTriggerHandlingE2eTestHooks();
|
||||
}
|
||||
|
||||
export function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||
const storePath = cfg.session?.store;
|
||||
if (!storePath) {
|
||||
throw new Error("expected session store path");
|
||||
}
|
||||
return storePath;
|
||||
}
|
||||
|
||||
export async function expectInlineCommandHandledAndStripped(params: {
|
||||
home: string;
|
||||
getReplyFromConfig: typeof import("../../../src/auto-reply/reply.js").getReplyFromConfig;
|
||||
|
||||
@@ -4,15 +4,14 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest";
|
||||
import { clearAllBootstrapSnapshots } from "../../../src/agents/bootstrap-cache.js";
|
||||
import { clearSessionStoreCacheForTest } from "../../../src/config/sessions/store.js";
|
||||
import { createCronServiceState, type CronServiceDeps } from "../../../src/cron/service/state.js";
|
||||
import { saveCronStore } from "../../../src/cron/store.js";
|
||||
import type { CronJob, CronJobState } from "../../../src/cron/types.js";
|
||||
import { resetAgentRunContextForTest } from "../../../src/infra/agent-events.js";
|
||||
import {
|
||||
resetCommandQueueStateForTest,
|
||||
waitForActiveTasks,
|
||||
} from "../../../src/process/command-queue.js";
|
||||
import { closeOpenClawStateDatabaseForTest } from "../../../src/state/openclaw-state-db.js";
|
||||
import { useFrozenTime, useRealTime } from "../../../src/test-utils/frozen-time.js";
|
||||
|
||||
const TOP_OF_HOUR_STAGGER_MS = 5 * 60 * 1_000;
|
||||
@@ -28,12 +27,9 @@ export const noopLogger = {
|
||||
export function setupCronRegressionFixtures(options?: { prefix?: string; baseTimeIso?: string }) {
|
||||
let fixtureRoot = "";
|
||||
let fixtureCount = 0;
|
||||
let originalOpenClawStateDir: string | undefined;
|
||||
|
||||
beforeAll(async () => {
|
||||
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), options?.prefix ?? "cron-issues-"));
|
||||
originalOpenClawStateDir = process.env.OPENCLAW_STATE_DIR;
|
||||
process.env.OPENCLAW_STATE_DIR = path.join(fixtureRoot, "state");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -47,26 +43,21 @@ export function setupCronRegressionFixtures(options?: { prefix?: string; baseTim
|
||||
useRealTime();
|
||||
await waitForActiveTasks(250);
|
||||
resetCommandQueueStateForTest();
|
||||
clearSessionStoreCacheForTest();
|
||||
resetAgentRunContextForTest();
|
||||
clearAllBootstrapSnapshots();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
closeOpenClawStateDatabaseForTest();
|
||||
if (originalOpenClawStateDir === undefined) {
|
||||
delete process.env.OPENCLAW_STATE_DIR;
|
||||
} else {
|
||||
process.env.OPENCLAW_STATE_DIR = originalOpenClawStateDir;
|
||||
}
|
||||
useRealTime();
|
||||
await waitForActiveTasks(250);
|
||||
await fs.rm(fixtureRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
return {
|
||||
makeStoreKey() {
|
||||
makeStorePath() {
|
||||
return {
|
||||
storeKey: `case-${fixtureCount++}`,
|
||||
storePath: path.join(fixtureRoot, `case-${fixtureCount++}.jobs.json`),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -83,14 +74,14 @@ export function createDeferred<T>() {
|
||||
}
|
||||
|
||||
export function createRunningCronServiceState(params: {
|
||||
storeKey?: string;
|
||||
storePath: string;
|
||||
log: CronServiceDeps["log"];
|
||||
nowMs: () => number;
|
||||
jobs: CronJob[];
|
||||
}) {
|
||||
const state = createCronServiceState({
|
||||
cronEnabled: true,
|
||||
storeKey: params.storeKey ?? "default",
|
||||
storePath: params.storePath,
|
||||
log: params.log,
|
||||
nowMs: params.nowMs,
|
||||
enqueueSystemEvent: vi.fn(),
|
||||
@@ -189,10 +180,10 @@ export function createIsolatedRegressionJob(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export async function writeCronJobs(storeKey: string, jobs: CronJob[]) {
|
||||
await saveCronStore(storeKey, { version: 1, jobs });
|
||||
export async function writeCronJobs(storePath: string, jobs: CronJob[]) {
|
||||
await fs.writeFile(storePath, JSON.stringify({ version: 1, jobs }), "utf-8");
|
||||
}
|
||||
|
||||
export async function writeCronStoreSnapshot(storeKey: string, jobs: unknown[]) {
|
||||
await saveCronStore(storeKey, { version: 1, jobs: jobs as CronJob[] });
|
||||
export async function writeCronStoreSnapshot(storePath: string, jobs: unknown[]) {
|
||||
await fs.writeFile(storePath, JSON.stringify({ version: 1, jobs }), "utf-8");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { request as httpRequest } from "node:http";
|
||||
import path from "node:path";
|
||||
import { GatewayClient } from "../../src/gateway/client.js";
|
||||
import { connectGatewayClient } from "../../src/gateway/test-helpers.e2e.js";
|
||||
import { loadOrCreateDeviceIdentity } from "../../src/infra/device-identity.js";
|
||||
@@ -87,7 +88,8 @@ export async function connectNode(
|
||||
inst: GatewayInstance,
|
||||
label: string,
|
||||
): Promise<{ client: GatewayClient; nodeId: string }> {
|
||||
const deviceIdentity = loadOrCreateDeviceIdentity({ key: `test:${inst.name}:${label}` });
|
||||
const identityPath = path.join(inst.homeDir, `${label}-device.json`);
|
||||
const deviceIdentity = loadOrCreateDeviceIdentity(identityPath);
|
||||
const nodeId = deviceIdentity.deviceId;
|
||||
const client = await connectGatewayClient({
|
||||
url: `ws://127.0.0.1:${inst.port}`,
|
||||
|
||||
@@ -17,19 +17,6 @@ type HeartbeatSendFn = (
|
||||
opts?: Record<string, unknown>,
|
||||
) => Promise<Record<string, unknown>>;
|
||||
|
||||
function parseTelegramMessageThreadId(
|
||||
threadId: string | number | null | undefined,
|
||||
): number | undefined {
|
||||
if (typeof threadId === "number") {
|
||||
return Number.isInteger(threadId) ? threadId : undefined;
|
||||
}
|
||||
if (typeof threadId !== "string" || !threadId.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(threadId);
|
||||
return Number.isInteger(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
function createHeartbeatOutboundAdapter(channelId: HeartbeatSendChannelId): ChannelOutboundAdapter {
|
||||
return {
|
||||
deliveryMode: "direct",
|
||||
@@ -45,14 +32,11 @@ function createHeartbeatOutboundAdapter(channelId: HeartbeatSendChannelId): Chan
|
||||
};
|
||||
const sendOptions =
|
||||
channelId === "telegram"
|
||||
? (() => {
|
||||
const messageThreadId = parseTelegramMessageThreadId(threadId);
|
||||
return {
|
||||
...baseOptions,
|
||||
...(messageThreadId === undefined ? {} : { messageThreadId }),
|
||||
...(typeof replyToId === "string" ? { replyToMessageId: Number(replyToId) } : {}),
|
||||
};
|
||||
})()
|
||||
? {
|
||||
...baseOptions,
|
||||
...(typeof threadId === "number" ? { messageThreadId: threadId } : {}),
|
||||
...(typeof replyToId === "string" ? { replyToMessageId: Number(replyToId) } : {}),
|
||||
}
|
||||
: {
|
||||
...baseOptions,
|
||||
...opts,
|
||||
|
||||
@@ -6,8 +6,6 @@ type PnpmBuildConfig = {
|
||||
allowBuilds?: Record<string, boolean>;
|
||||
blockExoticSubdeps?: boolean;
|
||||
ignoredBuiltDependencies?: string[];
|
||||
minimumReleaseAgeIgnoreMissingTime?: boolean;
|
||||
minimumReleaseAgeStrict?: boolean;
|
||||
onlyBuiltDependencies?: string[];
|
||||
};
|
||||
|
||||
@@ -31,19 +29,4 @@ describe("package manager build policy", () => {
|
||||
expect(workspace.blockExoticSubdeps).toBe(true);
|
||||
expect(workspace.onlyBuiltDependencies).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps exotic subdependency builds blocked by default", () => {
|
||||
const workspace = parse(fs.readFileSync("pnpm-workspace.yaml", "utf8")) as WorkspaceConfig;
|
||||
|
||||
expect(workspace.allowBuilds?.["baileys"]).toBe(true);
|
||||
expect(workspace.allowBuilds?.["@whiskeysockets/libsignal-node"]).toBeUndefined();
|
||||
expect(workspace.blockExoticSubdeps).toBe(true);
|
||||
});
|
||||
|
||||
it("does not relax release-age installs for missing registry publish metadata", () => {
|
||||
const workspace = parse(fs.readFileSync("pnpm-workspace.yaml", "utf8")) as WorkspaceConfig;
|
||||
|
||||
expect(workspace.minimumReleaseAgeIgnoreMissingTime).toBeUndefined();
|
||||
expect(workspace.minimumReleaseAgeStrict).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,12 +58,6 @@ describe("plugin npm runtime build planning", () => {
|
||||
expect(qqbotRuntimePlan.entry).toEqual({
|
||||
api: path.join(repoRoot, "extensions", "qqbot", "api.ts"),
|
||||
"channel-plugin-api": path.join(repoRoot, "extensions", "qqbot", "channel-plugin-api.ts"),
|
||||
"doctor-legacy-state-api": path.join(
|
||||
repoRoot,
|
||||
"extensions",
|
||||
"qqbot",
|
||||
"doctor-legacy-state-api.ts",
|
||||
),
|
||||
index: path.join(repoRoot, "extensions", "qqbot", "index.ts"),
|
||||
"runtime-api": path.join(repoRoot, "extensions", "qqbot", "runtime-api.ts"),
|
||||
"secret-contract-api": path.join(repoRoot, "extensions", "qqbot", "secret-contract-api.ts"),
|
||||
|
||||
@@ -91,8 +91,8 @@ describe("packed CLI smoke", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps packed completion smoke scoped to one generated shell script", () => {
|
||||
expect(PACKED_COMPLETION_SMOKE_ARGS).toEqual(["completion", "--shell", "zsh"]);
|
||||
it("keeps packed completion smoke scoped to one shell cache", () => {
|
||||
expect(PACKED_COMPLETION_SMOKE_ARGS).toEqual(["completion", "--write-state", "--shell", "zsh"]);
|
||||
});
|
||||
|
||||
it("builds a packed CLI smoke env with packaged-install guardrails", () => {
|
||||
@@ -133,7 +133,7 @@ describe("packed CLI smoke", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("skips plugin command discovery during packed completion smoke", () => {
|
||||
it("skips plugin command discovery during packed completion cache smoke", () => {
|
||||
expect(
|
||||
createPackedCompletionSmokeEnv(
|
||||
{
|
||||
|
||||
@@ -473,6 +473,7 @@ describe("scripts/changed-lanes", () => {
|
||||
"guarded extension wildcard re-exports",
|
||||
"plugin-sdk wildcard re-exports",
|
||||
"duplicate scan target coverage",
|
||||
"dependency pin guard",
|
||||
"typecheck core tests",
|
||||
"lint core",
|
||||
"lint scripts",
|
||||
@@ -752,6 +753,7 @@ describe("scripts/changed-lanes", () => {
|
||||
"lint:extensions:no-guarded-wildcard-reexports",
|
||||
"lint:extensions:no-plugin-sdk-wildcard-reexports",
|
||||
"dup:check:coverage",
|
||||
"deps:pins:check",
|
||||
"release-metadata:check",
|
||||
"ios:version:check",
|
||||
"config:schema:check",
|
||||
@@ -952,6 +954,7 @@ describe("scripts/changed-lanes", () => {
|
||||
args: ["lint:extensions:no-plugin-sdk-wildcard-reexports"],
|
||||
},
|
||||
{ name: "duplicate scan target coverage", args: ["dup:check:coverage"] },
|
||||
{ name: "dependency pin guard", args: ["deps:pins:check"] },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -972,6 +975,7 @@ describe("scripts/changed-lanes", () => {
|
||||
args: ["lint:extensions:no-plugin-sdk-wildcard-reexports"],
|
||||
},
|
||||
{ name: "duplicate scan target coverage", args: ["dup:check:coverage"] },
|
||||
{ name: "dependency pin guard", args: ["deps:pins:check"] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { collectKyselyGuardrailViolations } from "../../scripts/check-kysely-guardrails.mjs";
|
||||
|
||||
function messagesFor(content: string, relativePath = "src/example/store.sqlite.ts"): string[] {
|
||||
return collectKyselyGuardrailViolations(content, relativePath).map(
|
||||
(violation) => violation.message,
|
||||
);
|
||||
}
|
||||
|
||||
describe("Kysely guardrails", () => {
|
||||
it("rejects explicit sync-helper row generics for builder queries", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
import { executeSqliteQuerySync } from "../infra/kysely-sync.js";
|
||||
|
||||
executeSqliteQuerySync<{ id: string }>(db, query);
|
||||
`),
|
||||
).toContain("sync helper row generic at call site; let Kysely infer builder result rows");
|
||||
});
|
||||
|
||||
it("rejects persisted row casts to enum-like types in SQLite stores", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
type TaskStatus = "running" | "succeeded";
|
||||
|
||||
function rowToRecord(row: { status: string }) {
|
||||
return {
|
||||
status: row.status as TaskStatus,
|
||||
};
|
||||
}
|
||||
`),
|
||||
).toContain(
|
||||
"persisted SQLite enum-like values must be parsed through closed validators, not cast",
|
||||
);
|
||||
});
|
||||
|
||||
it("allows explicit local escape hatches for reviewed persisted casts", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
type TaskStatus = "running" | "succeeded";
|
||||
|
||||
function rowToRecord(row: { status: string }) {
|
||||
return {
|
||||
status: row.status as TaskStatus, // sqlite-allow-persisted-cast
|
||||
};
|
||||
}
|
||||
`),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("rejects typed raw SQL outside allowlisted boundaries", () => {
|
||||
expect(
|
||||
messagesFor(
|
||||
`
|
||||
import { sql } from "kysely";
|
||||
|
||||
const count = sql<number>\`COUNT(*)\`;
|
||||
`,
|
||||
"src/example/report.ts",
|
||||
),
|
||||
).toContain("typed raw sql snippet needs a small helper or allowlisted boundary");
|
||||
});
|
||||
|
||||
it("rejects direct raw node:sqlite prepare in new production files", () => {
|
||||
expect(
|
||||
messagesFor(
|
||||
`
|
||||
import { requireNodeSqlite } from "../infra/node-sqlite.js";
|
||||
|
||||
const sqlite = requireNodeSqlite();
|
||||
const db = new sqlite.DatabaseSync(":memory:");
|
||||
db.prepare("select 1").get();
|
||||
`,
|
||||
"src/example/raw-store.ts",
|
||||
),
|
||||
).toContain(
|
||||
"new raw node:sqlite access requires Kysely or an explicit raw SQLite allowlist entry",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps ordinary static Kysely reference strings valid", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
import { executeSqliteQuerySync, getNodeSqliteKysely } from "../infra/kysely-sync.js";
|
||||
|
||||
const query = getNodeSqliteKysely<{ task_runs: { task_id: string } }>(db)
|
||||
.selectFrom("task_runs")
|
||||
.select(["task_id"])
|
||||
.where("task_id", "=", taskId);
|
||||
executeSqliteQuerySync(db, query);
|
||||
`),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -179,8 +179,7 @@ describe("docker build helper", () => {
|
||||
|
||||
expect(runner).toContain("scripts/e2e/lib/plugin-update/unchanged-scenario.sh");
|
||||
expect(probe).toContain("plugin install record changed unexpectedly");
|
||||
expect(probe).toContain("readInstalledPluginRecords()");
|
||||
expect(probe).toContain('records["lossless-claw"] ?? records["@example/lossless-claw"]');
|
||||
expect(probe).toContain("index.installRecords ?? index.records ?? config.plugins?.installs");
|
||||
expect(scenario).toContain("Config changed unexpectedly for modern package");
|
||||
expect(scenario).not.toContain("before_hash");
|
||||
});
|
||||
|
||||
@@ -51,31 +51,15 @@ describe("install.sh", () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-nvm-"));
|
||||
const home = join(tmp, "home");
|
||||
const systemBin = join(tmp, "system-bin");
|
||||
const nvmBin = join(home, ".nvm/versions/node/v24.13.0/bin");
|
||||
const nvmBin = join(home, ".nvm/versions/node/v22.22.1/bin");
|
||||
mkdirSync(systemBin, { recursive: true });
|
||||
mkdirSync(nvmBin, { recursive: true });
|
||||
mkdirSync(join(home, ".nvm"), { recursive: true });
|
||||
|
||||
const systemNode = join(systemBin, "node");
|
||||
const nvmNode = join(nvmBin, "node");
|
||||
writeFileSync(
|
||||
systemNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "8 11"; exit 0; fi',
|
||||
"echo v8.11.3",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(
|
||||
nvmNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "24 13"; exit 0; fi',
|
||||
"echo v24.13.0",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(systemNode, "#!/bin/sh\necho v8.11.3\n");
|
||||
writeFileSync(nvmNode, "#!/bin/sh\necho v22.22.1\n");
|
||||
chmodSync(systemNode, 0o755);
|
||||
chmodSync(nvmNode, 0o755);
|
||||
writeFileSync(
|
||||
@@ -85,7 +69,7 @@ describe("install.sh", () => {
|
||||
"export NVM_DIR",
|
||||
"nvm() {",
|
||||
' if [ "$1" = "use" ]; then',
|
||||
' export PATH="$NVM_DIR/versions/node/v24.13.0/bin:$PATH"',
|
||||
' export PATH="$NVM_DIR/versions/node/v22.22.1/bin:$PATH"',
|
||||
" return 0",
|
||||
" fi",
|
||||
" return 0",
|
||||
@@ -122,7 +106,7 @@ describe("install.sh", () => {
|
||||
const output = result?.stdout ?? "";
|
||||
expect(output).toContain("status=0");
|
||||
expect(output).toContain(`path=${nvmNode}`);
|
||||
expect(output).toContain("version=v24.13.0");
|
||||
expect(output).toContain("version=v22.22.1");
|
||||
});
|
||||
|
||||
it("promotes a supported Linux Node binary over stale PATH entries", () => {
|
||||
@@ -134,24 +118,8 @@ describe("install.sh", () => {
|
||||
|
||||
const staleNode = join(staleBin, "node");
|
||||
const supportedNode = join(supportedBin, "node");
|
||||
writeFileSync(
|
||||
staleNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "20 20"; exit 0; fi',
|
||||
"echo v20.20.0",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(
|
||||
supportedNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "24 13"; exit 0; fi',
|
||||
"echo v24.13.0",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(staleNode, "#!/bin/sh\necho v20.20.0\n");
|
||||
writeFileSync(supportedNode, "#!/bin/sh\necho v22.22.0\n");
|
||||
chmodSync(staleNode, 0o755);
|
||||
chmodSync(supportedNode, 0o755);
|
||||
|
||||
@@ -184,7 +152,7 @@ describe("install.sh", () => {
|
||||
expect(output).toContain("promote=0");
|
||||
expect(output).toContain("active=0");
|
||||
expect(output).toContain(`path=${supportedNode}`);
|
||||
expect(output).toContain("version=v24.13.0");
|
||||
expect(output).toContain("version=v22.22.0");
|
||||
});
|
||||
|
||||
it("persists a supported Linux Node path before noninteractive shell guards", () => {
|
||||
|
||||
@@ -91,10 +91,7 @@ describe("production lint suppressions", () => {
|
||||
"scripts/lib/plugin-npm-release.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/agents/agent-scope.ts|no-control-regex|1",
|
||||
"src/agents/pi-embedded-runner/run/images.ts|no-control-regex|1",
|
||||
"src/agents/runtime-worker.entry.ts|unicorn/require-post-message-target-origin|1",
|
||||
"src/agents/runtime-worker.ts|unicorn/require-post-message-target-origin|1",
|
||||
"src/agents/subagent-attachments.ts|no-control-regex|1",
|
||||
"src/agents/subagent-registry.store.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/agents/subagent-spawn.ts|no-control-regex|1",
|
||||
"src/channels/plugins/channel-runtime-surface.types.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/channels/plugins/contracts/test-helpers.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
@@ -120,7 +117,6 @@ describe("production lint suppressions", () => {
|
||||
"src/plugin-sdk/test-helpers/package-manifest-contract.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugin-sdk/test-helpers/public-surface-loader.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugin-sdk/test-helpers/subagent-hooks.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugins/hook-types.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugins/hooks.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugins/host-hook-runtime.ts|typescript/no-unnecessary-type-parameters|2",
|
||||
"src/plugins/host-hook-state.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
|
||||
@@ -13,10 +13,12 @@ describe("live Docker state staging", () => {
|
||||
expect(script).toContain("--exclude=.artifacts");
|
||||
});
|
||||
|
||||
it("keeps host workspace artifacts out of the container state copy", () => {
|
||||
it("keeps host-only generated registry state out of the container copy", () => {
|
||||
const script = readFileSync(stageScriptPath, "utf8");
|
||||
|
||||
expect(script).toContain("--exclude=workspace");
|
||||
expect(script).toContain("--exclude=sandboxes");
|
||||
expect(script).toContain("--exclude=plugins/installs.json");
|
||||
expect(script).toContain("host-absolute paths");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -345,9 +345,10 @@ console.log(JSON.stringify(result));
|
||||
expect(missingKey.stderr).toContain("PARALLELS_TEST_MISSING_KEY is required");
|
||||
});
|
||||
|
||||
it("seeds configured agent workspace files before OS smoke agent turns", () => {
|
||||
it("seeds agent workspace state before OS smoke agent turns", () => {
|
||||
const workspace = readFileSync(TS_PATHS.agentWorkspace, "utf8");
|
||||
|
||||
expect(workspace).toContain("workspace-state.json");
|
||||
expect(workspace).toContain("IDENTITY.md");
|
||||
expect(workspace).toContain("BOOTSTRAP.md");
|
||||
|
||||
@@ -527,7 +528,7 @@ console.log(JSON.stringify(result));
|
||||
expect(script).toContain("agent turn attempt $attempt failed or finished without OK response");
|
||||
expect(script).not.toContain("$config.models.providers");
|
||||
expect(script).not.toContain("timeoutSeconds = 300");
|
||||
expect(script).not.toContain("$sessionId.jsonl");
|
||||
expect(script).toContain('"$sessionId.jsonl"');
|
||||
});
|
||||
|
||||
it("gives GPT-5.5 enough Parallels model time on slower desktop guests", () => {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const scriptPath = path.join(process.cwd(), "scripts", "pre-commit", "filter-staged-files.mjs");
|
||||
|
||||
function filterFiles(mode: "format" | "lint", files: string[]): string[] {
|
||||
const output = execFileSync(process.execPath, [scriptPath, mode, "--", ...files], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
return output.split("\0").filter(Boolean);
|
||||
}
|
||||
|
||||
describe("pre-commit staged-file filter", () => {
|
||||
it("does not format generated Kysely declaration files", () => {
|
||||
expect(
|
||||
filterFiles("format", [
|
||||
"src/state/openclaw-state-db.generated.d.ts",
|
||||
"src/state/openclaw-state-db.ts",
|
||||
]),
|
||||
).toEqual(["src/state/openclaw-state-db.ts"]);
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,6 @@ import type {
|
||||
import type { OpenClawConfig } from "../src/config/config.js";
|
||||
import type { OutboundSendDeps } from "../src/infra/outbound/deliver.js";
|
||||
import type { PluginRegistry } from "../src/plugins/registry.js";
|
||||
import { closeOpenClawAgentDatabasesForTest } from "../src/state/openclaw-agent-db.js";
|
||||
import { installSharedTestSetup } from "./setup.shared.js";
|
||||
|
||||
installSharedTestSetup();
|
||||
@@ -24,8 +23,14 @@ type WorkerPluginRuntimeHelpers = {
|
||||
setActivePluginRegistry: typeof import("../src/plugins/runtime.js").setActivePluginRegistry;
|
||||
};
|
||||
type WorkerCleanupHelpers = {
|
||||
clearSessionStoreCaches: typeof import("../src/config/sessions/store-cache.js").clearSessionStoreCaches;
|
||||
drainFileLockStateForTest: typeof import("../src/infra/file-lock.js").drainFileLockStateForTest;
|
||||
drainSessionStoreWriterQueuesForTest: typeof import("../src/config/sessions/store-writer-state.js").drainSessionStoreWriterQueuesForTest;
|
||||
drainSessionWriteLockStateForTest: typeof import("../src/agents/session-write-lock.js").drainSessionWriteLockStateForTest;
|
||||
resetContextWindowCacheForTest: typeof import("../src/agents/context-runtime-state.js").resetContextWindowCacheForTest;
|
||||
resetModelCatalogReadyCacheForTest: typeof import("../src/agents/models-config-state.js").resetModelCatalogReadyCacheForTest;
|
||||
resetFileLockStateForTest: typeof import("../src/infra/file-lock.js").resetFileLockStateForTest;
|
||||
resetModelsJsonReadyCacheForTest: typeof import("../src/agents/models-config-state.js").resetModelsJsonReadyCacheForTest;
|
||||
resetSessionWriteLockStateForTest: typeof import("../src/agents/session-write-lock.js").resetSessionWriteLockStateForTest;
|
||||
};
|
||||
|
||||
type ReplyToModeResolver = NonNullable<
|
||||
@@ -69,10 +74,36 @@ function loadWorkerCleanupHelpers(): Promise<WorkerCleanupHelpers> {
|
||||
vi.importActual<typeof import("../src/agents/models-config-state.js")>(
|
||||
"../src/agents/models-config-state.js",
|
||||
),
|
||||
]).then(([contextRuntimeState, modelsConfigState]) => ({
|
||||
resetContextWindowCacheForTest: contextRuntimeState.resetContextWindowCacheForTest,
|
||||
resetModelCatalogReadyCacheForTest: modelsConfigState.resetModelCatalogReadyCacheForTest,
|
||||
}));
|
||||
vi.importActual<typeof import("../src/agents/session-write-lock.js")>(
|
||||
"../src/agents/session-write-lock.js",
|
||||
),
|
||||
vi.importActual<typeof import("../src/config/sessions/store-cache.js")>(
|
||||
"../src/config/sessions/store-cache.js",
|
||||
),
|
||||
vi.importActual<typeof import("../src/config/sessions/store-writer-state.js")>(
|
||||
"../src/config/sessions/store-writer-state.js",
|
||||
),
|
||||
vi.importActual<typeof import("../src/infra/file-lock.js")>("../src/infra/file-lock.js"),
|
||||
]).then(
|
||||
([
|
||||
contextRuntimeState,
|
||||
modelsConfigState,
|
||||
sessionWriteLock,
|
||||
sessionStoreCache,
|
||||
sessionStoreWriterState,
|
||||
fileLock,
|
||||
]) => ({
|
||||
clearSessionStoreCaches: sessionStoreCache.clearSessionStoreCaches,
|
||||
drainFileLockStateForTest: fileLock.drainFileLockStateForTest,
|
||||
drainSessionStoreWriterQueuesForTest:
|
||||
sessionStoreWriterState.drainSessionStoreWriterQueuesForTest,
|
||||
drainSessionWriteLockStateForTest: sessionWriteLock.drainSessionWriteLockStateForTest,
|
||||
resetContextWindowCacheForTest: contextRuntimeState.resetContextWindowCacheForTest,
|
||||
resetFileLockStateForTest: fileLock.resetFileLockStateForTest,
|
||||
resetModelsJsonReadyCacheForTest: modelsConfigState.resetModelsJsonReadyCacheForTest,
|
||||
resetSessionWriteLockStateForTest: sessionWriteLock.resetSessionWriteLockStateForTest,
|
||||
}),
|
||||
);
|
||||
return globalState[WORKER_CLEANUP_HELPERS];
|
||||
}
|
||||
|
||||
@@ -360,14 +391,31 @@ beforeAll(async () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const { resetContextWindowCacheForTest, resetModelCatalogReadyCacheForTest } =
|
||||
await loadWorkerCleanupHelpers();
|
||||
closeOpenClawAgentDatabasesForTest();
|
||||
const {
|
||||
clearSessionStoreCaches,
|
||||
drainFileLockStateForTest,
|
||||
drainSessionStoreWriterQueuesForTest,
|
||||
drainSessionWriteLockStateForTest,
|
||||
resetContextWindowCacheForTest,
|
||||
resetFileLockStateForTest,
|
||||
resetModelsJsonReadyCacheForTest,
|
||||
resetSessionWriteLockStateForTest,
|
||||
} = await loadWorkerCleanupHelpers();
|
||||
await drainSessionStoreWriterQueuesForTest();
|
||||
clearSessionStoreCaches();
|
||||
await drainFileLockStateForTest();
|
||||
await drainSessionWriteLockStateForTest();
|
||||
resetFileLockStateForTest();
|
||||
resetContextWindowCacheForTest();
|
||||
resetModelCatalogReadyCacheForTest();
|
||||
resetModelsJsonReadyCacheForTest();
|
||||
resetSessionWriteLockStateForTest();
|
||||
await installDefaultPluginRegistry();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
closeOpenClawAgentDatabasesForTest();
|
||||
const { clearSessionStoreCaches, drainFileLockStateForTest, drainSessionWriteLockStateForTest } =
|
||||
await loadWorkerCleanupHelpers();
|
||||
clearSessionStoreCaches();
|
||||
await drainFileLockStateForTest();
|
||||
await drainSessionWriteLockStateForTest();
|
||||
});
|
||||
|
||||
@@ -2,11 +2,6 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
loadPersistedAuthProfileStore,
|
||||
savePersistedAuthProfileSecretsStore,
|
||||
} from "../src/agents/auth-profiles/persisted.js";
|
||||
import { closeOpenClawStateDatabaseForTest } from "../src/state/openclaw-state-db.js";
|
||||
import { cleanupTempDirs, makeTempDir } from "./helpers/temp-dir.js";
|
||||
import { installTestEnv } from "./test-env.js";
|
||||
|
||||
@@ -66,7 +61,6 @@ function requireTelegramStreaming(
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
closeOpenClawStateDatabaseForTest();
|
||||
while (cleanupFns.length > 0) {
|
||||
cleanupFns.pop()?.();
|
||||
}
|
||||
@@ -124,29 +118,9 @@ describe("installTestEnv", () => {
|
||||
path.join(realHome, ".openclaw", "external-plugins", "glueclaw", "openclaw.plugin.json"),
|
||||
'{"id":"glueclaw"}\n',
|
||||
);
|
||||
const realStateDir = path.join(realHome, ".openclaw");
|
||||
const realAgentDir = path.join(realStateDir, "agents", "main", "agent");
|
||||
fs.mkdirSync(realAgentDir, { recursive: true });
|
||||
savePersistedAuthProfileSecretsStore(
|
||||
{
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:default": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
key: "sk-test",
|
||||
},
|
||||
},
|
||||
},
|
||||
realAgentDir,
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: realHome,
|
||||
USERPROFILE: realHome,
|
||||
OPENCLAW_STATE_DIR: realStateDir,
|
||||
},
|
||||
},
|
||||
writeFile(
|
||||
path.join(realHome, ".openclaw", "agents", "main", "agent", "auth-profiles.json"),
|
||||
JSON.stringify({ version: 1, profiles: { default: { provider: "openai" } } }, null, 2),
|
||||
);
|
||||
writeFile(path.join(realHome, ".claude", ".credentials.json"), '{"accessToken":"token"}\n');
|
||||
writeFile(path.join(realHome, ".claude", "projects", "old-session.jsonl"), "session\n");
|
||||
@@ -225,12 +199,11 @@ describe("installTestEnv", () => {
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
const tempAgentDir = path.join(testEnv.tempHome, ".openclaw", "agents", "main", "agent");
|
||||
expect(loadPersistedAuthProfileStore(tempAgentDir)?.profiles["openai:default"]).toEqual({
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
key: "sk-test",
|
||||
});
|
||||
expect(
|
||||
fs.existsSync(
|
||||
path.join(testEnv.tempHome, ".openclaw", "agents", "main", "agent", "auth-profiles.json"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(fs.existsSync(path.join(testEnv.tempHome, ".claude", ".credentials.json"))).toBe(true);
|
||||
expect(fs.existsSync(path.join(testEnv.tempHome, ".claude", "projects"))).toBe(false);
|
||||
expect(fs.existsSync(path.join(testEnv.tempHome, ".claude", "settings.local.json"))).toBe(
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import JSON5 from "json5";
|
||||
import {
|
||||
loadPersistedAuthProfileStore,
|
||||
savePersistedAuthProfileSecretsStore,
|
||||
} from "../src/agents/auth-profiles/persisted.js";
|
||||
|
||||
type RestoreEntry = { key: string; value: string | undefined };
|
||||
|
||||
@@ -19,6 +16,13 @@ const LIVE_EXTERNAL_AUTH_FILES = [
|
||||
".codex/auth.json",
|
||||
".codex/config.toml",
|
||||
] as const;
|
||||
const requireFromHere = createRequire(import.meta.url);
|
||||
|
||||
type LegacyConfigCompatApi = typeof import("../src/commands/doctor/shared/legacy-config-compat.js");
|
||||
type ConfigValidationApi = typeof import("../src/config/validation.js");
|
||||
|
||||
let cachedLegacyConfigCompatApi: LegacyConfigCompatApi | undefined;
|
||||
let cachedConfigValidationApi: ConfigValidationApi | undefined;
|
||||
|
||||
function isTruthyEnvValue(value: string | undefined): boolean {
|
||||
if (!value) {
|
||||
@@ -46,6 +50,20 @@ function restoreEnv(entries: RestoreEntry[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
function loadLegacyConfigCompatApi(): LegacyConfigCompatApi {
|
||||
cachedLegacyConfigCompatApi ??= requireFromHere(
|
||||
"../src/commands/doctor/shared/legacy-config-compat.js",
|
||||
) as LegacyConfigCompatApi;
|
||||
return cachedLegacyConfigCompatApi;
|
||||
}
|
||||
|
||||
function loadConfigValidationApi(): ConfigValidationApi {
|
||||
cachedConfigValidationApi ??= requireFromHere(
|
||||
"../src/config/validation.js",
|
||||
) as ConfigValidationApi;
|
||||
return cachedConfigValidationApi;
|
||||
}
|
||||
|
||||
function resolveHomeRelativePath(input: string, homeDir: string): string {
|
||||
const trimmed = input.trim();
|
||||
if (trimmed === "~") {
|
||||
@@ -137,6 +155,10 @@ function resolveRestoreEntries(): RestoreEntry[] {
|
||||
key: "OPENCLAW_ALLOW_SLOW_REPLY_TESTS",
|
||||
value: process.env.OPENCLAW_ALLOW_SLOW_REPLY_TESTS,
|
||||
},
|
||||
{
|
||||
key: "OPENCLAW_LIVE_TEST_NORMALIZE_CONFIG",
|
||||
value: process.env.OPENCLAW_LIVE_TEST_NORMALIZE_CONFIG,
|
||||
},
|
||||
{ key: "HOME", value: process.env.HOME },
|
||||
{ key: "USERPROFILE", value: process.env.USERPROFILE },
|
||||
{ key: "XDG_CONFIG_HOME", value: process.env.XDG_CONFIG_HOME },
|
||||
@@ -306,47 +328,36 @@ function sanitizeLiveConfig(raw: string): string {
|
||||
});
|
||||
}
|
||||
|
||||
return `${JSON.stringify(parsed, null, 2)}\n`;
|
||||
if (!isTruthyEnvValue(process.env.OPENCLAW_LIVE_TEST_NORMALIZE_CONFIG)) {
|
||||
return `${JSON.stringify(parsed, null, 2)}\n`;
|
||||
}
|
||||
|
||||
const { applyLegacyDoctorMigrations } = loadLegacyConfigCompatApi();
|
||||
const migrated = applyLegacyDoctorMigrations(parsed);
|
||||
if (!migrated.next) {
|
||||
return `${JSON.stringify(parsed, null, 2)}\n`;
|
||||
}
|
||||
|
||||
const { validateConfigObjectWithPlugins } = loadConfigValidationApi();
|
||||
const validated = validateConfigObjectWithPlugins(migrated.next);
|
||||
return `${JSON.stringify(validated.ok ? validated.config : migrated.next, null, 2)}\n`;
|
||||
} catch {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
function stageLiveAuthProfiles(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
realHome: string;
|
||||
realStateDir: string;
|
||||
tempHome: string;
|
||||
tempStateDir: string;
|
||||
}): void {
|
||||
const agentsDir = path.join(params.realStateDir, "agents");
|
||||
function copyLiveAuthProfiles(realStateDir: string, tempStateDir: string): void {
|
||||
const agentsDir = path.join(realStateDir, "agents");
|
||||
if (!fs.existsSync(agentsDir)) {
|
||||
return;
|
||||
}
|
||||
const sourceEnv: NodeJS.ProcessEnv = {
|
||||
...params.env,
|
||||
HOME: params.realHome,
|
||||
USERPROFILE: params.realHome,
|
||||
OPENCLAW_STATE_DIR: params.realStateDir,
|
||||
};
|
||||
const targetEnv: NodeJS.ProcessEnv = {
|
||||
...params.env,
|
||||
HOME: params.tempHome,
|
||||
USERPROFILE: params.tempHome,
|
||||
OPENCLAW_STATE_DIR: params.tempStateDir,
|
||||
};
|
||||
for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
const sourceAgentDir = path.join(agentsDir, entry.name, "agent");
|
||||
const store = loadPersistedAuthProfileStore(sourceAgentDir, { env: sourceEnv });
|
||||
if (!store) {
|
||||
continue;
|
||||
}
|
||||
const targetAgentDir = path.join(params.tempStateDir, "agents", entry.name, "agent");
|
||||
fs.mkdirSync(targetAgentDir, { recursive: true });
|
||||
savePersistedAuthProfileSecretsStore(store, targetAgentDir, { env: targetEnv });
|
||||
const sourcePath = path.join(agentsDir, entry.name, "agent", "auth-profiles.json");
|
||||
const targetPath = path.join(tempStateDir, "agents", entry.name, "agent", "auth-profiles.json");
|
||||
copyFileIfExists(sourcePath, targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,13 +401,7 @@ function stageLiveTestState(params: {
|
||||
path.join(realStateDir, "external-plugins"),
|
||||
path.join(tempStateDir, "external-plugins"),
|
||||
);
|
||||
stageLiveAuthProfiles({
|
||||
env: params.env,
|
||||
realHome: params.realHome,
|
||||
realStateDir,
|
||||
tempHome: params.tempHome,
|
||||
tempStateDir,
|
||||
});
|
||||
copyLiveAuthProfiles(realStateDir, tempStateDir);
|
||||
|
||||
for (const authDir of LIVE_EXTERNAL_AUTH_DIRS) {
|
||||
copyDirIfExists(path.join(params.realHome, authDir), path.join(params.tempHome, authDir));
|
||||
|
||||
@@ -62,6 +62,7 @@ describe("unit-fast vitest lane", () => {
|
||||
expect(testConfig.include).toContain("src/commands/status-overview-values.test.ts");
|
||||
expect(testConfig.include).toContain("src/entry.respawn.test.ts");
|
||||
expect(testConfig.include).toContain("src/entry.version-fast-path.test.ts");
|
||||
expect(testConfig.include).toContain("src/flows/doctor-startup-channel-maintenance.test.ts");
|
||||
expect(testConfig.include).toContain("src/crestodian/rescue-policy.test.ts");
|
||||
expect(testConfig.include).toContain("src/crestodian/assistant.configured.test.ts");
|
||||
expect(testConfig.include).toContain("src/flows/search-setup.test.ts");
|
||||
|
||||
@@ -352,6 +352,7 @@ export const sharedVitestConfig = {
|
||||
"src/agents/pi-tool-definition-adapter.ts",
|
||||
"src/agents/tools/discord-actions*.ts",
|
||||
"src/agents/tools/slack-actions.ts",
|
||||
"src/infra/state-migrations.ts",
|
||||
"src/infra/skills-remote.ts",
|
||||
"src/infra/update-check.ts",
|
||||
"src/infra/ports-inspect.ts",
|
||||
|
||||
@@ -63,7 +63,7 @@ export const forcedUnitFastTestFiles = [
|
||||
"packages/memory-host-sdk/src/host/internal.test.ts",
|
||||
"packages/memory-host-sdk/src/host/post-json.test.ts",
|
||||
"packages/memory-host-sdk/src/host/qmd-process.test.ts",
|
||||
"packages/memory-host-sdk/src/host/session-transcripts.test.ts",
|
||||
"packages/memory-host-sdk/src/host/session-files.test.ts",
|
||||
"src/acp/client.test.ts",
|
||||
"src/acp/control-plane/manager.test.ts",
|
||||
"src/acp/session-mapper.test.ts",
|
||||
@@ -102,6 +102,7 @@ export const forcedUnitFastTestFiles = [
|
||||
"src/entry.respawn.test.ts",
|
||||
"src/entry.version-fast-path.test.ts",
|
||||
"src/entry.test.ts",
|
||||
"src/flows/doctor-startup-channel-maintenance.test.ts",
|
||||
"src/flows/search-setup.test.ts",
|
||||
"src/i18n/registry.test.ts",
|
||||
"src/image-generation/openai-compatible-image-provider.test.ts",
|
||||
@@ -178,6 +179,7 @@ export const forcedUnitFastTestFiles = [
|
||||
"src/sessions/transcript-events.test.ts",
|
||||
"src/status/status-message.test.ts",
|
||||
"src/security/windows-acl.test.ts",
|
||||
"src/trajectory/cleanup.test.ts",
|
||||
"src/trajectory/export.test.ts",
|
||||
"src/trajectory/metadata.test.ts",
|
||||
"src/trajectory/runtime.test.ts",
|
||||
|
||||
Reference in New Issue
Block a user