diff --git a/scripts/check-architecture-smells.mjs b/scripts/check-architecture-smells.mjs index c10973355bc..1fb74428793 100644 --- a/scripts/check-architecture-smells.mjs +++ b/scripts/check-architecture-smells.mjs @@ -256,17 +256,27 @@ function formatInventoryHuman(inventory) { return lines.join("\n"); } -export async function main(argv = process.argv.slice(2)) { +function writeLine(stream, text) { + stream.write(`${text}\n`); +} + +export async function runArchitectureSmellsCheck(argv = process.argv.slice(2), io) { + const streams = io ?? { stdout: process.stdout, stderr: process.stderr }; const json = argv.includes("--json"); const inventory = await collectArchitectureSmells(); if (json) { - process.stdout.write(`${JSON.stringify(inventory, null, 2)}\n`); - return; + writeLine(streams.stdout, JSON.stringify(inventory, null, 2)); + return 0; } - console.log(formatInventoryHuman(inventory)); - console.log(`${inventory.length} smell${inventory.length === 1 ? "" : "s"} found.`); + writeLine(streams.stdout, formatInventoryHuman(inventory)); + writeLine(streams.stdout, `${inventory.length} smell${inventory.length === 1 ? "" : "s"} found.`); + return 0; +} + +export async function main(argv = process.argv.slice(2), io) { + return await runArchitectureSmellsCheck(argv, io); } runAsScript(import.meta.url, main); diff --git a/scripts/check-extension-plugin-sdk-boundary.mjs b/scripts/check-extension-plugin-sdk-boundary.mjs index 91ed44230fc..4e21b86021f 100644 --- a/scripts/check-extension-plugin-sdk-boundary.mjs +++ b/scripts/check-extension-plugin-sdk-boundary.mjs @@ -35,6 +35,9 @@ const baselinePathByMode = { ), }; +const inventoryPromiseByMode = new Map(); +let parsedExtensionSourceFilesPromise; + const ruleTextByMode = { "src-outside-plugin-sdk": "Rule: production extensions/** must not import src/** outside src/plugin-sdk/**", @@ -87,6 +90,34 @@ async function collectExtensionSourceFiles(rootDir) { return out.toSorted((left, right) => normalizePath(left).localeCompare(normalizePath(right))); } +async function collectParsedExtensionSourceFiles() { + if (!parsedExtensionSourceFilesPromise) { + parsedExtensionSourceFilesPromise = (async () => { + const files = await collectExtensionSourceFiles(extensionsRoot); + return await Promise.all( + files.map(async (filePath) => { + const source = await fs.readFile(filePath, "utf8"); + const scriptKind = + filePath.endsWith(".tsx") || filePath.endsWith(".jsx") + ? ts.ScriptKind.TSX + : ts.ScriptKind.TS; + return { + filePath, + sourceFile: ts.createSourceFile( + filePath, + source, + ts.ScriptTarget.Latest, + true, + scriptKind, + ), + }; + }), + ); + })(); + } + return await parsedExtensionSourceFilesPromise; +} + function toLine(sourceFile, node) { return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1; } @@ -216,22 +247,19 @@ export async function collectExtensionPluginSdkBoundaryInventory(mode) { if (!MODES.has(mode)) { throw new Error(`Unknown mode: ${mode}`); } - const files = await collectExtensionSourceFiles(extensionsRoot); - const inventory = []; - for (const filePath of files) { - const source = await fs.readFile(filePath, "utf8"); - const scriptKind = - filePath.endsWith(".tsx") || filePath.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS; - const sourceFile = ts.createSourceFile( - filePath, - source, - ts.ScriptTarget.Latest, - true, - scriptKind, - ); - inventory.push(...collectFromSourceFile(mode, sourceFile, filePath)); + let pending = inventoryPromiseByMode.get(mode); + if (!pending) { + pending = (async () => { + const files = await collectParsedExtensionSourceFiles(); + const inventory = []; + for (const { filePath, sourceFile } of files) { + inventory.push(...collectFromSourceFile(mode, sourceFile, filePath)); + } + return inventory.toSorted(compareEntries); + })(); + inventoryPromiseByMode.set(mode, pending); } - return inventory.toSorted(compareEntries); + return await pending; } export async function readExpectedInventory(mode) { @@ -286,7 +314,12 @@ function formatInventoryHuman(mode, inventory) { return lines.join("\n"); } -export async function main(argv = process.argv.slice(2)) { +function writeLine(stream, text) { + stream.write(`${text}\n`); +} + +export async function runExtensionPluginSdkBoundaryCheck(argv = process.argv.slice(2), io) { + const streams = io ?? { stdout: process.stdout, stderr: process.stderr }; const json = argv.includes("--json"); const modeArg = argv.find((arg) => arg.startsWith("--mode=")); const mode = modeArg?.slice("--mode=".length) ?? "src-outside-plugin-sdk"; @@ -296,30 +329,38 @@ export async function main(argv = process.argv.slice(2)) { const actual = await collectExtensionPluginSdkBoundaryInventory(mode); if (json) { - process.stdout.write(`${JSON.stringify(actual, null, 2)}\n`); - return; + writeLine(streams.stdout, JSON.stringify(actual, null, 2)); + return 0; } const expected = await readExpectedInventory(mode); const diff = diffInventory(expected, actual); - console.log(formatInventoryHuman(mode, actual)); + writeLine(streams.stdout, formatInventoryHuman(mode, actual)); if (diff.missing.length === 0 && diff.unexpected.length === 0) { - console.log(`Baseline matches (${actual.length} entries).`); - return; + writeLine(streams.stdout, `Baseline matches (${actual.length} entries).`); + return 0; } if (diff.missing.length > 0) { - console.error(`Missing baseline entries (${diff.missing.length}):`); + writeLine(streams.stderr, `Missing baseline entries (${diff.missing.length}):`); for (const entry of diff.missing) { - console.error(` - ${entry.file}:${entry.line} ${entry.reason}`); + writeLine(streams.stderr, ` - ${entry.file}:${entry.line} ${entry.reason}`); } } if (diff.unexpected.length > 0) { - console.error(`Unexpected inventory entries (${diff.unexpected.length}):`); + writeLine(streams.stderr, `Unexpected inventory entries (${diff.unexpected.length}):`); for (const entry of diff.unexpected) { - console.error(` - ${entry.file}:${entry.line} ${entry.reason}`); + writeLine(streams.stderr, ` - ${entry.file}:${entry.line} ${entry.reason}`); } } - process.exitCode = 1; + return 1; +} + +export async function main(argv = process.argv.slice(2), io) { + const exitCode = await runExtensionPluginSdkBoundaryCheck(argv, io); + if (!io) { + process.exitCode = exitCode; + } + return exitCode; } if (path.resolve(process.argv[1] ?? "") === fileURLToPath(import.meta.url)) { diff --git a/scripts/check-plugin-extension-import-boundary.mjs b/scripts/check-plugin-extension-import-boundary.mjs index ac9c5e178a4..827abb5d171 100644 --- a/scripts/check-plugin-extension-import-boundary.mjs +++ b/scripts/check-plugin-extension-import-boundary.mjs @@ -266,7 +266,12 @@ function formatEntry(entry) { return `${entry.file}:${entry.line} [${entry.kind}] ${entry.reason} (${entry.specifier} -> ${entry.resolvedPath})`; } -export async function main(argv = process.argv.slice(2)) { +function writeLine(stream, text) { + stream.write(`${text}\n`); +} + +export async function runPluginExtensionImportBoundaryCheck(argv = process.argv.slice(2), io) { + const streams = io ?? { stdout: process.stdout, stderr: process.stderr }; const json = argv.includes("--json"); const actual = await collectPluginExtensionImportBoundaryInventory(); const expected = await readExpectedInventory(); @@ -274,33 +279,43 @@ export async function main(argv = process.argv.slice(2)) { const matchesBaseline = missing.length === 0 && unexpected.length === 0; if (json) { - process.stdout.write(`${JSON.stringify(actual, null, 2)}\n`); + writeLine(streams.stdout, JSON.stringify(actual, null, 2)); } else { - console.log(formatInventoryHuman(actual)); - console.log( + writeLine(streams.stdout, formatInventoryHuman(actual)); + writeLine( + streams.stdout, matchesBaseline ? `Baseline matches (${actual.length} entries).` : `Baseline mismatch (${unexpected.length} unexpected, ${missing.length} missing).`, ); if (!matchesBaseline) { if (unexpected.length > 0) { - console.error("Unexpected entries:"); + writeLine(streams.stderr, "Unexpected entries:"); for (const entry of unexpected) { - console.error(`- ${formatEntry(entry)}`); + writeLine(streams.stderr, `- ${formatEntry(entry)}`); } } if (missing.length > 0) { - console.error("Missing baseline entries:"); + writeLine(streams.stderr, "Missing baseline entries:"); for (const entry of missing) { - console.error(`- ${formatEntry(entry)}`); + writeLine(streams.stderr, `- ${formatEntry(entry)}`); } } } } if (!matchesBaseline) { - process.exit(1); + return 1; } + return 0; +} + +export async function main(argv = process.argv.slice(2), io) { + const exitCode = await runPluginExtensionImportBoundaryCheck(argv, io); + if (!io && exitCode !== 0) { + process.exit(exitCode); + } + return exitCode; } runAsScript(import.meta.url, main); diff --git a/scripts/check-web-search-provider-boundaries.mjs b/scripts/check-web-search-provider-boundaries.mjs index 2ba31b465c0..97496323935 100644 --- a/scripts/check-web-search-provider-boundaries.mjs +++ b/scripts/check-web-search-provider-boundaries.mjs @@ -255,7 +255,12 @@ function formatEntry(entry) { return `${entry.provider} ${entry.file}:${entry.line} ${entry.reason}`; } -export async function main(argv = process.argv.slice(2)) { +function writeLine(stream, text) { + stream.write(`${text}\n`); +} + +export async function runWebSearchProviderBoundaryCheck(argv = process.argv.slice(2), io) { + const streams = io ?? { stdout: process.stdout, stderr: process.stderr }; const json = argv.includes("--json"); const actual = await collectWebSearchProviderBoundaryInventory(); const expected = await readExpectedInventory(); @@ -263,33 +268,43 @@ export async function main(argv = process.argv.slice(2)) { const matchesBaseline = missing.length === 0 && unexpected.length === 0; if (json) { - process.stdout.write(`${JSON.stringify(actual, null, 2)}\n`); + writeLine(streams.stdout, JSON.stringify(actual, null, 2)); } else { - console.log(formatInventoryHuman(actual)); - console.log( + writeLine(streams.stdout, formatInventoryHuman(actual)); + writeLine( + streams.stdout, matchesBaseline ? `Baseline matches (${actual.length} entries).` : `Baseline mismatch (${unexpected.length} unexpected, ${missing.length} missing).`, ); if (!matchesBaseline) { if (unexpected.length > 0) { - console.error("Unexpected entries:"); + writeLine(streams.stderr, "Unexpected entries:"); for (const entry of unexpected) { - console.error(`- ${formatEntry(entry)}`); + writeLine(streams.stderr, `- ${formatEntry(entry)}`); } } if (missing.length > 0) { - console.error("Missing baseline entries:"); + writeLine(streams.stderr, "Missing baseline entries:"); for (const entry of missing) { - console.error(`- ${formatEntry(entry)}`); + writeLine(streams.stderr, `- ${formatEntry(entry)}`); } } } } if (!matchesBaseline) { - process.exit(1); + return 1; } + return 0; +} + +export async function main(argv = process.argv.slice(2), io) { + const exitCode = await runWebSearchProviderBoundaryCheck(argv, io); + if (!io && exitCode !== 0) { + process.exit(exitCode); + } + return exitCode; } runAsScript(import.meta.url, main); diff --git a/src/daemon/schtasks.stop.test.ts b/src/daemon/schtasks.stop.test.ts index 04e5f1fced1..fe05df20008 100644 --- a/src/daemon/schtasks.stop.test.ts +++ b/src/daemon/schtasks.stop.test.ts @@ -13,11 +13,24 @@ import { const findVerifiedGatewayListenerPidsOnPortSync = vi.hoisted(() => vi.fn<(port: number) => number[]>(() => []), ); +const timeState = vi.hoisted(() => ({ now: 0 })); +const sleepMock = vi.hoisted(() => + vi.fn(async (ms: number) => { + timeState.now += ms; + }), +); vi.mock("../infra/gateway-processes.js", () => ({ findVerifiedGatewayListenerPidsOnPortSync: (port: number) => findVerifiedGatewayListenerPidsOnPortSync(port), })); +vi.mock("../utils.js", async () => { + const actual = await vi.importActual("../utils.js"); + return { + ...actual, + sleep: (ms: number) => sleepMock(ms), + }; +}); const { restartScheduledTask, stopScheduledTask } = await import("./schtasks.js"); const GATEWAY_PORT = 18789; @@ -81,6 +94,12 @@ beforeEach(() => { resetSchtasksBaseMocks(); findVerifiedGatewayListenerPidsOnPortSync.mockReset(); findVerifiedGatewayListenerPidsOnPortSync.mockReturnValue([]); + timeState.now = 0; + vi.spyOn(Date, "now").mockImplementation(() => timeState.now); + sleepMock.mockReset(); + sleepMock.mockImplementation(async (ms: number) => { + timeState.now += ms; + }); inspectPortUsage.mockResolvedValue(freePortUsage()); }); diff --git a/src/infra/outbound/agent-delivery.test.ts b/src/infra/outbound/agent-delivery.test.ts index 88b6776105e..96278e9c3da 100644 --- a/src/infra/outbound/agent-delivery.test.ts +++ b/src/infra/outbound/agent-delivery.test.ts @@ -13,14 +13,10 @@ vi.mock("./targets.js", async () => { }); import type { OpenClawConfig } from "../../config/config.js"; -type AgentDeliveryModule = typeof import("./agent-delivery.js"); +import { resolveAgentDeliveryPlan, resolveAgentOutboundTarget } from "./agent-delivery.js"; -let resolveAgentDeliveryPlan: AgentDeliveryModule["resolveAgentDeliveryPlan"]; -let resolveAgentOutboundTarget: AgentDeliveryModule["resolveAgentOutboundTarget"]; - -beforeEach(async () => { - vi.resetModules(); - ({ resolveAgentDeliveryPlan, resolveAgentOutboundTarget } = await import("./agent-delivery.js")); +beforeEach(() => { + mocks.resolveOutboundTarget.mockClear(); }); describe("agent delivery helpers", () => { diff --git a/src/infra/outbound/message.test.ts b/src/infra/outbound/message.test.ts index 47a43eb8437..200d4d587e1 100644 --- a/src/infra/outbound/message.test.ts +++ b/src/infra/outbound/message.test.ts @@ -46,13 +46,10 @@ vi.mock("./deliver.js", () => ({ import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { createTestRegistry } from "../../test-utils/channel-plugins.js"; - -let sendMessage: typeof import("./message.js").sendMessage; +import { sendMessage } from "./message.js"; describe("sendMessage", () => { - beforeEach(async () => { - vi.resetModules(); - ({ sendMessage } = await import("./message.js")); + beforeEach(() => { setActivePluginRegistry(createTestRegistry([])); mocks.getChannelPlugin.mockClear(); mocks.resolveOutboundTarget.mockClear(); diff --git a/src/infra/restart.test.ts b/src/infra/restart.test.ts index fe6e760041b..30d21414c79 100644 --- a/src/infra/restart.test.ts +++ b/src/infra/restart.test.ts @@ -16,25 +16,31 @@ vi.mock("../config/paths.js", () => ({ resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args), })); -let __testing: typeof import("./restart-stale-pids.js").__testing; -let cleanStaleGatewayProcessesSync: typeof import("./restart-stale-pids.js").cleanStaleGatewayProcessesSync; -let findGatewayPidsOnPortSync: typeof import("./restart-stale-pids.js").findGatewayPidsOnPortSync; +import { + __testing, + cleanStaleGatewayProcessesSync, + findGatewayPidsOnPortSync, +} from "./restart-stale-pids.js"; -beforeEach(async () => { - vi.resetModules(); - ({ __testing, cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } = - await import("./restart-stale-pids.js")); +let currentTimeMs = 0; + +beforeEach(() => { spawnSyncMock.mockReset(); resolveLsofCommandSyncMock.mockReset(); resolveGatewayPortMock.mockReset(); + currentTimeMs = 0; resolveLsofCommandSyncMock.mockReturnValue("/usr/sbin/lsof"); resolveGatewayPortMock.mockReturnValue(18789); - __testing.setSleepSyncOverride(() => {}); + __testing.setSleepSyncOverride((ms) => { + currentTimeMs += ms; + }); + __testing.setDateNowOverride(() => currentTimeMs); }); afterEach(() => { __testing.setSleepSyncOverride(null); + __testing.setDateNowOverride(null); vi.restoreAllMocks(); }); @@ -79,11 +85,17 @@ describe.runIf(process.platform !== "win32")("findGatewayPidsOnPortSync", () => describe.runIf(process.platform !== "win32")("cleanStaleGatewayProcessesSync", () => { it("kills stale gateway pids discovered on the gateway port", () => { - spawnSyncMock.mockReturnValue({ - error: undefined, - status: 0, - stdout: ["p6001", "copenclaw", "p6002", "copenclaw-gateway"].join("\n"), - }); + spawnSyncMock + .mockReturnValueOnce({ + error: undefined, + status: 0, + stdout: ["p6001", "copenclaw", "p6002", "copenclaw-gateway"].join("\n"), + }) + .mockReturnValue({ + error: undefined, + status: 1, + stdout: "", + }); const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true); const killed = cleanStaleGatewayProcessesSync(); @@ -97,11 +109,17 @@ describe.runIf(process.platform !== "win32")("cleanStaleGatewayProcessesSync", ( }); it("uses explicit port override when provided", () => { - spawnSyncMock.mockReturnValue({ - error: undefined, - status: 0, - stdout: ["p7001", "copenclaw"].join("\n"), - }); + spawnSyncMock + .mockReturnValueOnce({ + error: undefined, + status: 0, + stdout: ["p7001", "copenclaw"].join("\n"), + }) + .mockReturnValue({ + error: undefined, + status: 1, + stdout: "", + }); const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true); const killed = cleanStaleGatewayProcessesSync(19999); diff --git a/src/media-understanding/runner.vision-skip.test.ts b/src/media-understanding/runner.vision-skip.test.ts index 97f1a0cd77c..1b00d285c61 100644 --- a/src/media-understanding/runner.vision-skip.test.ts +++ b/src/media-understanding/runner.vision-skip.test.ts @@ -1,6 +1,12 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { MsgContext } from "../auto-reply/templating.js"; import type { OpenClawConfig } from "../config/config.js"; +import { + buildProviderRegistry, + createMediaAttachmentCache, + normalizeMediaAttachments, + runCapability, +} from "./runner.js"; const catalog = [ { @@ -23,20 +29,9 @@ vi.mock("../agents/model-catalog.js", async () => { }; }); -let buildProviderRegistry: typeof import("./runner.js").buildProviderRegistry; -let createMediaAttachmentCache: typeof import("./runner.js").createMediaAttachmentCache; -let normalizeMediaAttachments: typeof import("./runner.js").normalizeMediaAttachments; -let runCapability: typeof import("./runner.js").runCapability; - describe("runCapability image skip", () => { - beforeEach(async () => { - vi.resetModules(); - ({ - buildProviderRegistry, - createMediaAttachmentCache, - normalizeMediaAttachments, - runCapability, - } = await import("./runner.js")); + beforeEach(() => { + loadModelCatalog.mockClear(); }); it("skips image understanding when the active model supports vision", async () => { diff --git a/src/plugin-sdk/index.bundle.test.ts b/src/plugin-sdk/index.bundle.test.ts index 0d790399c49..e7140856187 100644 --- a/src/plugin-sdk/index.bundle.test.ts +++ b/src/plugin-sdk/index.bundle.test.ts @@ -4,21 +4,27 @@ import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { describe, expect, it } from "vitest"; -import { - buildPluginSdkEntrySources, - buildPluginSdkPackageExports, - buildPluginSdkSpecifiers, - pluginSdkEntrypoints, -} from "./entrypoints.js"; +import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "./entrypoints.js"; -const pluginSdkSpecifiers = buildPluginSdkSpecifiers(); const require = createRequire(import.meta.url); const tsdownModuleUrl = pathToFileURL(require.resolve("tsdown")).href; +const bundledSmokeEntrypoints = [ + "index", + "core", + "runtime", + "channel-runtime", + "provider-setup", + "setup", + "matrix-runtime-heavy", + "windows-spawn", + "gateway-runtime", + "plugin-runtime", + "testing", +] as const; describe("plugin-sdk bundled exports", () => { it("emits importable bundled subpath entries", { timeout: 240_000 }, async () => { const outDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-plugin-sdk-build-")); - const fixtureDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-plugin-sdk-consumer-")); try { const { build } = await import(tsdownModuleUrl); @@ -45,52 +51,20 @@ describe("plugin-sdk bundled exports", () => { }), ); - const packageDir = path.join(fixtureDir, "openclaw"); - const consumerDir = path.join(fixtureDir, "consumer"); - const consumerEntry = path.join(consumerDir, "import-plugin-sdk.mjs"); - - await fs.mkdir(path.join(packageDir, "dist"), { recursive: true }); - await fs.symlink(outDir, path.join(packageDir, "dist", "plugin-sdk"), "dir"); - // Mirror the installed package layout so subpaths can resolve root deps. - await fs.symlink( - path.join(process.cwd(), "node_modules"), - path.join(packageDir, "node_modules"), - "dir", + // Export list and package-specifier coverage already live in + // package-contract-guardrails.test.ts and subpaths.test.ts. Keep this file + // focused on the expensive part: can tsdown emit working bundle artifacts? + const importResults = await Promise.all( + bundledSmokeEntrypoints.map(async (entry) => [ + entry, + typeof (await import(pathToFileURL(path.join(outDir, `${entry}.js`)).href)), + ]), ); - await fs.writeFile( - path.join(packageDir, "package.json"), - JSON.stringify( - { - exports: buildPluginSdkPackageExports(), - name: "openclaw", - type: "module", - }, - null, - 2, - ), - ); - - await fs.mkdir(path.join(consumerDir, "node_modules"), { recursive: true }); - await fs.symlink(packageDir, path.join(consumerDir, "node_modules", "openclaw"), "dir"); - await fs.writeFile( - consumerEntry, - [ - `const specifiers = ${JSON.stringify(pluginSdkSpecifiers)};`, - "const results = {};", - "for (const specifier of specifiers) {", - " results[specifier] = typeof (await import(specifier));", - "}", - "export default results;", - ].join("\n"), - ); - - const { default: importResults } = await import(pathToFileURL(consumerEntry).href); - expect(importResults).toEqual( - Object.fromEntries(pluginSdkSpecifiers.map((specifier: string) => [specifier, "object"])), + expect(Object.fromEntries(importResults)).toEqual( + Object.fromEntries(bundledSmokeEntrypoints.map((entry) => [entry, "object"])), ); } finally { await fs.rm(outDir, { recursive: true, force: true }); - await fs.rm(fixtureDir, { recursive: true, force: true }); } }); }); diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 75ab0fd489d..5b1716f6a85 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import { emitDiagnosticEvent, resetDiagnosticEventsForTest } from "../infra/diagnostic-events.js"; import { withEnv } from "../test-utils/env.js"; +import { clearPluginCommands, getPluginCommandSpecs } from "./commands.js"; async function importFreshPluginTestModules() { vi.resetModules(); @@ -1009,8 +1010,6 @@ module.exports = { id: "skipped-scoped-only", register() { throw new Error("skip }, };`, }); - const { clearPluginCommands, getPluginCommandSpecs } = await import("./commands.js"); - clearPluginCommands(); const scoped = loadOpenClawPlugins({ @@ -1395,6 +1394,8 @@ module.exports = { id: "skipped-scoped-only", register() { throw new Error("skip filename: "cache-eviction.cjs", body: `module.exports = { id: "cache-eviction", register() {} };`, }); + const previousCacheCap = __testing.maxPluginRegistryCacheEntries; + __testing.setMaxPluginRegistryCacheEntriesForTest(4); const stateDirs = Array.from({ length: __testing.maxPluginRegistryCacheEntries + 1 }, () => makeTempDir(), ); @@ -1416,17 +1417,21 @@ module.exports = { id: "skipped-scoped-only", register() { throw new Error("skip }, }); - const first = loadWithStateDir(stateDirs[0] ?? makeTempDir()); - const second = loadWithStateDir(stateDirs[1] ?? makeTempDir()); + try { + const first = loadWithStateDir(stateDirs[0] ?? makeTempDir()); + const second = loadWithStateDir(stateDirs[1] ?? makeTempDir()); - expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first); + expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first); - for (const stateDir of stateDirs.slice(2)) { - loadWithStateDir(stateDir); + for (const stateDir of stateDirs.slice(2)) { + loadWithStateDir(stateDir); + } + + expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first); + expect(loadWithStateDir(stateDirs[1] ?? makeTempDir())).not.toBe(second); + } finally { + __testing.setMaxPluginRegistryCacheEntriesForTest(previousCacheCap); } - - expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first); - expect(loadWithStateDir(stateDirs[1] ?? makeTempDir())).not.toBe(second); }); it("normalizes bundled plugin env overrides against the provided env", () => { diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index dca0ddd14c2..632d4442a9b 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -101,6 +101,7 @@ type CachedPluginState = { }; const MAX_PLUGIN_REGISTRY_CACHE_ENTRIES = 128; +let pluginRegistryCacheEntryCap = MAX_PLUGIN_REGISTRY_CACHE_ENTRIES; const registryCache = new Map(); const openAllowlistWarningCache = new Set(); const LAZY_RUNTIME_REFLECTION_KEYS = [ @@ -139,7 +140,15 @@ export const __testing = { resolvePluginSdkAliasFile, resolvePluginRuntimeModulePath, shouldPreferNativeJiti, - maxPluginRegistryCacheEntries: MAX_PLUGIN_REGISTRY_CACHE_ENTRIES, + get maxPluginRegistryCacheEntries() { + return pluginRegistryCacheEntryCap; + }, + setMaxPluginRegistryCacheEntriesForTest(value?: number) { + pluginRegistryCacheEntryCap = + typeof value === "number" && Number.isFinite(value) && value > 0 + ? Math.max(1, Math.floor(value)) + : MAX_PLUGIN_REGISTRY_CACHE_ENTRIES; + }, }; function getCachedPluginRegistry(cacheKey: string): CachedPluginState | undefined { @@ -158,7 +167,7 @@ function setCachedPluginRegistry(cacheKey: string, state: CachedPluginState): vo registryCache.delete(cacheKey); } registryCache.set(cacheKey, state); - while (registryCache.size > MAX_PLUGIN_REGISTRY_CACHE_ENTRIES) { + while (registryCache.size > pluginRegistryCacheEntryCap) { const oldestKey = registryCache.keys().next().value; if (!oldestKey) { break; diff --git a/test/architecture-smells.test.ts b/test/architecture-smells.test.ts index ebc9c5bf7b4..c64a4cdc363 100644 --- a/test/architecture-smells.test.ts +++ b/test/architecture-smells.test.ts @@ -1,10 +1,26 @@ -import { execFileSync } from "node:child_process"; -import path from "node:path"; import { describe, expect, it } from "vitest"; -import { collectArchitectureSmells } from "../scripts/check-architecture-smells.mjs"; +import { collectArchitectureSmells, main } from "../scripts/check-architecture-smells.mjs"; -const repoRoot = process.cwd(); -const scriptPath = path.join(repoRoot, "scripts", "check-architecture-smells.mjs"); +function createCapturedIo() { + let stdout = ""; + let stderr = ""; + return { + io: { + stdout: { + write(chunk) { + stdout += String(chunk); + }, + }, + stderr: { + write(chunk) { + stderr += String(chunk); + }, + }, + }, + readStdout: () => stdout, + readStderr: () => stderr, + }; +} describe("architecture smell inventory", () => { it("produces stable sorted output", async () => { @@ -26,11 +42,11 @@ describe("architecture smell inventory", () => { }); it("script json output matches the collector", async () => { - const stdout = execFileSync(process.execPath, [scriptPath, "--json"], { - cwd: repoRoot, - encoding: "utf8", - }); + const captured = createCapturedIo(); + const exitCode = await main(["--json"], captured.io); - expect(JSON.parse(stdout)).toEqual(await collectArchitectureSmells()); + expect(exitCode).toBe(0); + expect(captured.readStderr()).toBe(""); + expect(JSON.parse(captured.readStdout())).toEqual(await collectArchitectureSmells()); }); }); diff --git a/test/extension-plugin-sdk-boundary.test.ts b/test/extension-plugin-sdk-boundary.test.ts index 5a7325077c7..ebd02eaa2e5 100644 --- a/test/extension-plugin-sdk-boundary.test.ts +++ b/test/extension-plugin-sdk-boundary.test.ts @@ -1,11 +1,12 @@ -import { execFileSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { collectExtensionPluginSdkBoundaryInventory } from "../scripts/check-extension-plugin-sdk-boundary.mjs"; +import { + collectExtensionPluginSdkBoundaryInventory, + main, +} from "../scripts/check-extension-plugin-sdk-boundary.mjs"; const repoRoot = process.cwd(); -const scriptPath = path.join(repoRoot, "scripts", "check-extension-plugin-sdk-boundary.mjs"); const relativeOutsidePackageBaselinePath = path.join( repoRoot, "test", @@ -13,6 +14,27 @@ const relativeOutsidePackageBaselinePath = path.join( "extension-relative-outside-package-inventory.json", ); +function createCapturedIo() { + let stdout = ""; + let stderr = ""; + return { + io: { + stdout: { + write(chunk) { + stdout += String(chunk); + }, + }, + stderr: { + write(chunk) { + stderr += String(chunk); + }, + }, + }, + readStdout: () => stdout, + readStderr: () => stderr, + }; +} + describe("extension src outside plugin-sdk boundary inventory", () => { it("is currently empty", async () => { const inventory = await collectExtensionPluginSdkBoundaryInventory("src-outside-plugin-sdk"); @@ -38,17 +60,13 @@ describe("extension src outside plugin-sdk boundary inventory", () => { ).toEqual(first); }); - it("script json output is empty", () => { - const stdout = execFileSync( - process.execPath, - [scriptPath, "--mode=src-outside-plugin-sdk", "--json"], - { - cwd: repoRoot, - encoding: "utf8", - }, - ); + it("script json output is empty", async () => { + const captured = createCapturedIo(); + const exitCode = await main(["--mode=src-outside-plugin-sdk", "--json"], captured.io); - expect(JSON.parse(stdout)).toEqual([]); + expect(exitCode).toBe(0); + expect(captured.readStderr()).toBe(""); + expect(JSON.parse(captured.readStdout())).toEqual([]); }); }); @@ -59,17 +77,13 @@ describe("extension plugin-sdk-internal boundary inventory", () => { expect(inventory).toEqual([]); }); - it("script json output is empty", () => { - const stdout = execFileSync( - process.execPath, - [scriptPath, "--mode=plugin-sdk-internal", "--json"], - { - cwd: repoRoot, - encoding: "utf8", - }, - ); + it("script json output is empty", async () => { + const captured = createCapturedIo(); + const exitCode = await main(["--mode=plugin-sdk-internal", "--json"], captured.io); - expect(JSON.parse(stdout)).toEqual([]); + expect(exitCode).toBe(0); + expect(captured.readStderr()).toBe(""); + expect(JSON.parse(captured.readStdout())).toEqual([]); }); }); @@ -81,17 +95,13 @@ describe("extension relative-outside-package boundary inventory", () => { expect(inventory).toEqual(expected); }); - it("script json output matches the checked-in baseline", () => { - const stdout = execFileSync( - process.execPath, - [scriptPath, "--mode=relative-outside-package", "--json"], - { - cwd: repoRoot, - encoding: "utf8", - }, - ); + it("script json output matches the checked-in baseline", async () => { + const captured = createCapturedIo(); + const exitCode = await main(["--mode=relative-outside-package", "--json"], captured.io); const expected = JSON.parse(fs.readFileSync(relativeOutsidePackageBaselinePath, "utf8")); - expect(JSON.parse(stdout)).toEqual(expected); + expect(exitCode).toBe(0); + expect(captured.readStderr()).toBe(""); + expect(JSON.parse(captured.readStdout())).toEqual(expected); }); }); diff --git a/test/fixtures/test-timings.unit.json b/test/fixtures/test-timings.unit.json index bf8e78f61b8..2484cdbb091 100644 --- a/test/fixtures/test-timings.unit.json +++ b/test/fixtures/test-timings.unit.json @@ -1,1031 +1,1031 @@ { "config": "vitest.unit.config.ts", - "generatedAt": "2026-03-21T18:55:37.147Z", + "generatedAt": "2026-03-21T23:20:17.389Z", "defaultDurationMs": 250, "files": { "src/plugins/loader.test.ts": { - "durationMs": 10349.788330078125, - "testCount": 53 - }, - "test/extension-plugin-sdk-boundary.test.ts": { - "durationMs": 8856.68896484375, - "testCount": 7 - }, - "src/media/fetch.telegram-network.test.ts": { - "durationMs": 6647.10498046875, - "testCount": 5 + "durationMs": 4426.110107421875, + "testCount": 56 }, "src/plugin-sdk/index.bundle.test.ts": { - "durationMs": 6294.27294921875, + "durationMs": 4297.02197265625, "testCount": 1 }, - "src/config/doc-baseline.integration.test.ts": { - "durationMs": 6183.0947265625, + "src/security/audit.test.ts": { + "durationMs": 3373.431884765625, + "testCount": 65 + }, + "test/extension-plugin-sdk-boundary.test.ts": { + "durationMs": 2000.430419921875, "testCount": 7 }, - "src/tts/edge-tts-validation.test.ts": { - "durationMs": 6078.44921875, - "testCount": 2 - }, - "src/daemon/schtasks.stop.test.ts": { - "durationMs": 5794.84912109375, - "testCount": 4 - }, - "src/memory/manager.read-file.test.ts": { - "durationMs": 5783.181640625, - "testCount": 4 - }, - "src/memory/manager.readonly-recovery.test.ts": { - "durationMs": 5782.993408203125, - "testCount": 4 - }, - "src/memory/manager.sync-errors-do-not-crash.test.ts": { - "durationMs": 5771.345703125, - "testCount": 1 - }, - "src/context-engine/context-engine.test.ts": { - "durationMs": 5692.849365234375, - "testCount": 32 - }, - "src/channels/plugins/setup-wizard-helpers.test.ts": { - "durationMs": 5644.7509765625, - "testCount": 83 - }, - "src/infra/restart.test.ts": { - "durationMs": 4152.199951171875, - "testCount": 5 - }, - "src/channels/plugins/contracts/registry.contract.test.ts": { - "durationMs": 3771.96826171875, - "testCount": 10 - }, "test/web-search-provider-boundary.test.ts": { - "durationMs": 2874.2265625, + "durationMs": 1541.68359375, "testCount": 4 }, - "src/infra/outbound/message.test.ts": { - "durationMs": 2655.238037109375, - "testCount": 3 - }, - "src/infra/provider-usage.auth.plugin.test.ts": { - "durationMs": 2518.3916015625, - "testCount": 1 - }, - "src/index.test.ts": { - "durationMs": 2497.32275390625, - "testCount": 2 - }, - "src/infra/provider-usage.load.plugin.test.ts": { - "durationMs": 2477.368896484375, - "testCount": 1 - }, - "src/media-understanding/runner.vision-skip.test.ts": { - "durationMs": 2464.70703125, - "testCount": 1 - }, - "src/infra/outbound/agent-delivery.test.ts": { - "durationMs": 2451.758056640625, - "testCount": 6 - }, "src/plugins/install.test.ts": { - "durationMs": 1821.473876953125, + "durationMs": 1353.5146484375, "testCount": 34 }, "src/config/plugin-auto-enable.test.ts": { - "durationMs": 1287.16845703125, + "durationMs": 1124.0224609375, "testCount": 25 }, + "src/config/doc-baseline.integration.test.ts": { + "durationMs": 1026.2373046875, + "testCount": 7 + }, "src/plugin-sdk/channel-import-guardrails.test.ts": { - "durationMs": 1260.345703125, + "durationMs": 1014.92724609375, "testCount": 9 }, "src/hooks/install.test.ts": { - "durationMs": 1005.34716796875, + "durationMs": 939.1748046875, "testCount": 15 }, - "test/plugin-extension-import-boundary.test.ts": { - "durationMs": 963.47021484375, - "testCount": 5 - }, "src/hooks/bundled/session-memory/handler.test.ts": { - "durationMs": 896.0625, + "durationMs": 856.34912109375, "testCount": 17 }, - "src/infra/fs-safe.test.ts": { - "durationMs": 718.878173828125, - "testCount": 27 + "test/plugin-extension-import-boundary.test.ts": { + "durationMs": 698.7822265625, + "testCount": 5 }, "src/cron/isolated-agent.model-formatting.test.ts": { - "durationMs": 702.48046875, + "durationMs": 688.40087890625, "testCount": 22 }, - "test/architecture-smells.test.ts": { - "durationMs": 695.52783203125, - "testCount": 2 - }, - "src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts": { - "durationMs": 692.59814453125, - "testCount": 15 - }, - "src/memory/qmd-manager.test.ts": { - "durationMs": 635.891845703125, - "testCount": 57 - }, - "src/infra/git-commit.test.ts": { - "durationMs": 623.604736328125, - "testCount": 13 - }, - "src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts": { - "durationMs": 618.68505859375, - "testCount": 18 - }, - "src/security/temp-path-guard.test.ts": { - "durationMs": 602.46484375, - "testCount": 3 + "src/infra/fs-safe.test.ts": { + "durationMs": 682.8740234375, + "testCount": 27 }, "src/hooks/loader.test.ts": { - "durationMs": 582.092041015625, + "durationMs": 668.00830078125, "testCount": 14 }, - "test/scripts/committer.test.ts": { - "durationMs": 578.763671875, - "testCount": 3 - }, - "src/config/config.plugin-validation.test.ts": { - "durationMs": 513.0751953125, - "testCount": 15 - }, - "src/infra/provider-usage.test.ts": { - "durationMs": 497.81884765625, - "testCount": 11 - }, - "src/infra/provider-usage.auth.normalizes-keys.test.ts": { - "durationMs": 461.506103515625, - "testCount": 19 - }, - "src/cron/service.issue-regressions.test.ts": { - "durationMs": 459.50244140625, - "testCount": 38 - }, - "src/infra/host-env-security.test.ts": { - "durationMs": 431.400390625, + "src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts": { + "durationMs": 577.583984375, "testCount": 18 }, + "src/config/config.plugin-validation.test.ts": { + "durationMs": 536.86669921875, + "testCount": 15 + }, + "src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts": { + "durationMs": 527.092529296875, + "testCount": 15 + }, + "test/scripts/committer.test.ts": { + "durationMs": 509.810302734375, + "testCount": 3 + }, + "src/infra/provider-usage.test.ts": { + "durationMs": 508.7373046875, + "testCount": 11 + }, + "src/memory/manager.sync-errors-do-not-crash.test.ts": { + "durationMs": 481.74560546875, + "testCount": 1 + }, + "src/infra/provider-usage.auth.plugin.test.ts": { + "durationMs": 456.6474609375, + "testCount": 1 + }, + "test/architecture-smells.test.ts": { + "durationMs": 436.11083984375, + "testCount": 2 + }, + "src/cron/service.issue-regressions.test.ts": { + "durationMs": 405.372314453125, + "testCount": 38 + }, + "src/infra/provider-usage.auth.normalizes-keys.test.ts": { + "durationMs": 397.945068359375, + "testCount": 19 + }, "src/infra/fs-pinned-write-helper.test.ts": { - "durationMs": 346.49560546875, + "durationMs": 389.989990234375, "testCount": 3 }, "src/infra/archive.test.ts": { - "durationMs": 313.887451171875, + "durationMs": 324.909423828125, "testCount": 15 }, - "src/memory/manager.get-concurrency.test.ts": { - "durationMs": 286.710205078125, - "testCount": 3 - }, "src/infra/archive-staging.test.ts": { - "durationMs": 282.27490234375, + "durationMs": 308.276611328125, "testCount": 7 }, - "src/security/audit.test.ts": { - "durationMs": 274.441162109375, - "testCount": 65 - }, - "src/secrets/audit.test.ts": { - "durationMs": 254.64794921875, - "testCount": 18 - }, - "src/cli/program/preaction.test.ts": { - "durationMs": 252.990478515625, - "testCount": 7 - }, - "src/memory/index.test.ts": { - "durationMs": 248.887451171875, - "testCount": 21 - }, - "src/memory/embeddings.test.ts": { - "durationMs": 241.66162109375, - "testCount": 19 - }, - "src/secrets/runtime.integration.test.ts": { - "durationMs": 231.779052734375, - "testCount": 5 - }, - "test/scripts/test-extension.test.ts": { - "durationMs": 223.220703125, - "testCount": 8 - }, - "src/cron/isolated-agent.delivers-response-has-heartbeat-ok-but-includes.test.ts": { - "durationMs": 214.800048828125, - "testCount": 6 - }, - "src/tui/gateway-chat.test.ts": { - "durationMs": 205.42236328125, - "testCount": 14 - }, - "src/install-sh-version.test.ts": { - "durationMs": 203.495361328125, + "src/memory/manager.get-concurrency.test.ts": { + "durationMs": 279.231201171875, "testCount": 3 }, - "src/secrets/apply.test.ts": { - "durationMs": 196.867919921875, - "testCount": 15 + "src/cli/program/preaction.test.ts": { + "durationMs": 251.61328125, + "testCount": 7 }, - "src/channels/plugins/plugins-core.test.ts": { - "durationMs": 192.526123046875, - "testCount": 39 - }, - "src/entry.version-fast-path.test.ts": { - "durationMs": 192.364013671875, - "testCount": 2 + "src/memory/embeddings.test.ts": { + "durationMs": 241.597412109375, + "testCount": 19 }, "src/hooks/plugin-hooks.test.ts": { - "durationMs": 185.23828125, + "durationMs": 228.1298828125, "testCount": 4 }, - "src/cli/daemon-cli/install.integration.test.ts": { - "durationMs": 179.0986328125, - "testCount": 2 + "test/scripts/test-extension.test.ts": { + "durationMs": 225.09619140625, + "testCount": 8 }, - "src/plugins/loader.git-path-regression.test.ts": { - "durationMs": 175.49853515625, + "src/infra/provider-usage.load.plugin.test.ts": { + "durationMs": 221.7685546875, "testCount": 1 }, - "src/cron/isolated-agent.subagent-model.test.ts": { - "durationMs": 163.193359375, - "testCount": 4 + "src/acp/control-plane/manager.test.ts": { + "durationMs": 220.148193359375, + "testCount": 35 + }, + "src/memory/qmd-manager.test.ts": { + "durationMs": 214.2646484375, + "testCount": 57 + }, + "src/secrets/audit.test.ts": { + "durationMs": 209.112548828125, + "testCount": 18 + }, + "src/cron/isolated-agent.delivers-response-has-heartbeat-ok-but-includes.test.ts": { + "durationMs": 207.84912109375, + "testCount": 6 + }, + "src/secrets/apply.test.ts": { + "durationMs": 204.255615234375, + "testCount": 15 + }, + "src/tui/gateway-chat.test.ts": { + "durationMs": 199.711669921875, + "testCount": 14 + }, + "src/secrets/runtime.integration.test.ts": { + "durationMs": 197.940185546875, + "testCount": 5 + }, + "src/infra/git-commit.test.ts": { + "durationMs": 194.261962890625, + "testCount": 13 + }, + "src/entry.version-fast-path.test.ts": { + "durationMs": 191.978515625, + "testCount": 2 + }, + "src/memory/index.test.ts": { + "durationMs": 186.47119140625, + "testCount": 21 + }, + "src/plugins/loader.git-path-regression.test.ts": { + "durationMs": 177.200927734375, + "testCount": 1 + }, + "src/install-sh-version.test.ts": { + "durationMs": 173.1142578125, + "testCount": 3 + }, + "src/cli/daemon-cli/install.integration.test.ts": { + "durationMs": 169.927490234375, + "testCount": 2 + }, + "src/channels/plugins/plugins-core.test.ts": { + "durationMs": 169.484130859375, + "testCount": 39 + }, + "src/infra/host-env-security.test.ts": { + "durationMs": 167.84375, + "testCount": 18 + }, + "src/plugins/manifest-registry.test.ts": { + "durationMs": 165.427734375, + "testCount": 21 }, "src/acp/server.startup.test.ts": { - "durationMs": 160.4287109375, + "durationMs": 159.467529296875, "testCount": 4 }, "src/cron/isolated-agent.direct-delivery-core-channels.test.ts": { - "durationMs": 158.726318359375, + "durationMs": 159.3427734375, "testCount": 4 }, + "src/cron/isolated-agent.subagent-model.test.ts": { + "durationMs": 158.701416015625, + "testCount": 4 + }, + "src/security/temp-path-guard.test.ts": { + "durationMs": 154.90576171875, + "testCount": 3 + }, "src/plugins/bundle-mcp.test.ts": { - "durationMs": 158.040283203125, + "durationMs": 154.823486328125, "testCount": 3 }, - "src/plugins/sdk-alias.test.ts": { - "durationMs": 138.389892578125, - "testCount": 24 - }, - "src/media-understanding/apply.test.ts": { - "durationMs": 137.737548828125, - "testCount": 32 - }, "src/daemon/schtasks.startup-fallback.test.ts": { - "durationMs": 137.69873046875, + "durationMs": 141.314453125, "testCount": 6 }, - "src/plugins/manifest-registry.test.ts": { - "durationMs": 137.384521484375, - "testCount": 21 - }, - "src/infra/device-pairing.test.ts": { - "durationMs": 134.861328125, - "testCount": 19 - }, - "ui/src/ui/views/chat.test.ts": { - "durationMs": 126.7685546875, - "testCount": 29 - }, - "src/secrets/resolve.test.ts": { - "durationMs": 124.764404296875, - "testCount": 17 - }, - "src/config/schema.hints.test.ts": { - "durationMs": 123.307861328125, - "testCount": 7 - }, - "src/infra/heartbeat-runner.returns-default-unset.test.ts": { - "durationMs": 118.725341796875, - "testCount": 25 - }, - "src/memory/manager.batch.test.ts": { - "durationMs": 117.29296875, - "testCount": 3 - }, - "src/cron/isolated-agent.lane.test.ts": { - "durationMs": 115.09423828125, - "testCount": 3 - }, - "src/security/windows-acl.test.ts": { - "durationMs": 114.22412109375, - "testCount": 48 - }, - "src/media/store.outside-workspace.test.ts": { - "durationMs": 113.27978515625, - "testCount": 1 - }, - "src/infra/run-node.test.ts": { - "durationMs": 111.617919921875, - "testCount": 12 - }, - "src/config/config.nix-integration-u3-u5-u9.test.ts": { - "durationMs": 110.521728515625, - "testCount": 19 - }, - "src/node-host/invoke-system-run-plan.test.ts": { - "durationMs": 109.346435546875, - "testCount": 41 - }, - "src/node-host/invoke-system-run.test.ts": { - "durationMs": 106.599365234375, - "testCount": 37 - }, - "src/acp/control-plane/manager.test.ts": { - "durationMs": 106.168701171875, - "testCount": 33 - }, - "test/scripts/ios-team-id.test.ts": { - "durationMs": 104.40087890625, - "testCount": 3 - }, - "src/infra/system-presence.version.test.ts": { - "durationMs": 101.902587890625, - "testCount": 5 - }, - "src/cron/isolated-agent/run.owner-auth.test.ts": { - "durationMs": 100.63330078125, - "testCount": 1 - }, - "src/media/read-response-with-limit.test.ts": { - "durationMs": 100.395263671875, - "testCount": 5 - }, - "test/git-hooks-pre-commit.test.ts": { - "durationMs": 98.15380859375, - "testCount": 1 - }, - "src/infra/matrix-legacy-crypto.test.ts": { - "durationMs": 97.4072265625, - "testCount": 8 - }, - "src/infra/gateway-lock.test.ts": { - "durationMs": 96.080810546875, - "testCount": 9 - }, - "src/media-understanding/apply.echo-transcript.test.ts": { - "durationMs": 95.3515625, - "testCount": 10 - }, - "src/infra/update-runner.test.ts": { - "durationMs": 95.1650390625, - "testCount": 20 - }, - "src/cli/config-cli.integration.test.ts": { - "durationMs": 94.6064453125, - "testCount": 4 - }, - "src/config/io.write-config.test.ts": { - "durationMs": 91.735107421875, - "testCount": 16 - }, - "src/pairing/pairing-store.test.ts": { - "durationMs": 90.6376953125, - "testCount": 17 - }, - "src/cron/isolated-agent.direct-delivery-forum-topics.test.ts": { - "durationMs": 88.56201171875, - "testCount": 2 - }, - "src/config/config.web-search-provider.test.ts": { - "durationMs": 88.09130859375, + "src/plugins/sdk-alias.test.ts": { + "durationMs": 138.323974609375, "testCount": 24 }, - "src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts": { - "durationMs": 87.196044921875, - "testCount": 12 + "src/plugin-sdk/subpaths.test.ts": { + "durationMs": 137.55322265625, + "testCount": 50 + }, + "src/media/store.outside-workspace.test.ts": { + "durationMs": 133.3681640625, + "testCount": 1 + }, + "src/config/schema.hints.test.ts": { + "durationMs": 127.630126953125, + "testCount": 7 + }, + "ui/src/ui/views/chat.test.ts": { + "durationMs": 126.7119140625, + "testCount": 29 + }, + "src/media-understanding/apply.test.ts": { + "durationMs": 125.6669921875, + "testCount": 32 + }, + "src/infra/heartbeat-runner.returns-default-unset.test.ts": { + "durationMs": 119.38134765625, + "testCount": 25 + }, + "src/secrets/resolve.test.ts": { + "durationMs": 116.83056640625, + "testCount": 17 + }, + "src/config/config.nix-integration-u3-u5-u9.test.ts": { + "durationMs": 115.965576171875, + "testCount": 19 + }, + "src/cron/isolated-agent.lane.test.ts": { + "durationMs": 112.56396484375, + "testCount": 3 + }, + "src/node-host/invoke-system-run-plan.test.ts": { + "durationMs": 111.2958984375, + "testCount": 41 + }, + "test/scripts/ios-team-id.test.ts": { + "durationMs": 105.57080078125, + "testCount": 3 + }, + "src/memory/manager.batch.test.ts": { + "durationMs": 104.496337890625, + "testCount": 3 }, "src/plugins/marketplace.test.ts": { - "durationMs": 86.336669921875, + "durationMs": 103.127197265625, + "testCount": 3 + }, + "src/infra/run-node.test.ts": { + "durationMs": 102.4814453125, + "testCount": 12 + }, + "src/media/read-response-with-limit.test.ts": { + "durationMs": 101.84716796875, + "testCount": 5 + }, + "src/infra/gateway-lock.test.ts": { + "durationMs": 101.620849609375, + "testCount": 9 + }, + "src/infra/device-pairing.test.ts": { + "durationMs": 98.19140625, + "testCount": 19 + }, + "src/cron/isolated-agent/run.owner-auth.test.ts": { + "durationMs": 97.31201171875, + "testCount": 1 + }, + "src/infra/system-presence.version.test.ts": { + "durationMs": 93.771240234375, + "testCount": 5 + }, + "src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts": { + "durationMs": 92.437744140625, + "testCount": 12 + }, + "src/cli/config-cli.integration.test.ts": { + "durationMs": 92.197021484375, + "testCount": 4 + }, + "src/pairing/pairing-store.test.ts": { + "durationMs": 91.511962890625, + "testCount": 17 + }, + "src/media/input-files.fetch-guard.test.ts": { + "durationMs": 91.102294921875, + "testCount": 10 + }, + "src/plugins/copy-bundled-plugin-metadata.test.ts": { + "durationMs": 90.367431640625, + "testCount": 8 + }, + "src/infra/outbound/outbound.test.ts": { + "durationMs": 90.14208984375, + "testCount": 65 + }, + "src/cron/isolated-agent.direct-delivery-forum-topics.test.ts": { + "durationMs": 89.577880859375, + "testCount": 2 + }, + "src/security/windows-acl.test.ts": { + "durationMs": 87.017822265625, + "testCount": 48 + }, + "test/git-hooks-pre-commit.test.ts": { + "durationMs": 86.962158203125, + "testCount": 1 + }, + "src/config/io.write-config.test.ts": { + "durationMs": 83.775146484375, + "testCount": 16 + }, + "src/plugin-sdk/channel-lifecycle.test.ts": { + "durationMs": 82.317138671875, + "testCount": 6 + }, + "src/media-understanding/runner.skip-tiny-audio.test.ts": { + "durationMs": 82.166259765625, + "testCount": 3 + }, + "src/plugins/contracts/auth-choice.contract.test.ts": { + "durationMs": 81.44873046875, "testCount": 3 }, "src/pairing/setup-code.test.ts": { - "durationMs": 85.327392578125, + "durationMs": 81.33056640625, "testCount": 15 }, - "src/plugin-sdk/subpaths.test.ts": { - "durationMs": 85.2998046875, - "testCount": 49 - }, - "src/media-understanding/runner.auto-audio.test.ts": { - "durationMs": 84.716064453125, - "testCount": 4 - }, - "src/plugins/contracts/auth-choice.contract.test.ts": { - "durationMs": 84.557373046875, - "testCount": 3 - }, - "src/media-understanding/runner.skip-tiny-audio.test.ts": { - "durationMs": 84.239990234375, - "testCount": 3 - }, - "src/media/input-files.fetch-guard.test.ts": { - "durationMs": 82.484130859375, - "testCount": 10 - }, - "src/plugin-sdk/channel-lifecycle.test.ts": { - "durationMs": 82.316650390625, - "testCount": 6 - }, - "src/infra/outbound/outbound.test.ts": { - "durationMs": 82.1455078125, - "testCount": 65 - }, - "src/plugins/bundle-commands.test.ts": { - "durationMs": 79.72412109375, - "testCount": 1 - }, - "src/config/sessions/targets.test.ts": { - "durationMs": 79.463623046875, - "testCount": 13 - }, - "src/process/command-queue.test.ts": { - "durationMs": 78.396728515625, - "testCount": 17 - }, - "src/config/sessions/sessions.test.ts": { - "durationMs": 77.7880859375, - "testCount": 23 - }, "src/cron/isolated-agent/delivery-dispatch.double-announce.test.ts": { - "durationMs": 77.459716796875, + "durationMs": 80.12109375, "testCount": 15 }, - "src/plugins/stage-bundled-plugin-runtime.test.ts": { - "durationMs": 76.509521484375, - "testCount": 7 + "src/config/sessions/sessions.test.ts": { + "durationMs": 78.61669921875, + "testCount": 23 }, - "src/media-understanding/runner.proxy.test.ts": { - "durationMs": 73.716796875, - "testCount": 3 - }, - "src/plugins/status.test.ts": { - "durationMs": 72.9384765625, - "testCount": 9 - }, - "src/wizard/setup.gateway-config.test.ts": { - "durationMs": 69.90869140625, - "testCount": 7 - }, - "src/plugins/discovery.test.ts": { - "durationMs": 69.2861328125, - "testCount": 24 - }, - "src/canvas-host/server.test.ts": { - "durationMs": 68.94140625, - "testCount": 6 - }, - "src/cli/program.smoke.test.ts": { - "durationMs": 68.869873046875, - "testCount": 4 - }, - "src/tts/tts.test.ts": { - "durationMs": 68.49755859375, - "testCount": 27 - }, - "src/infra/outbound/delivery-queue.test.ts": { - "durationMs": 68.437744140625, - "testCount": 36 - }, - "src/media-understanding/runner.deepgram.test.ts": { - "durationMs": 67.562255859375, - "testCount": 1 - }, - "src/media-understanding/runtime.test.ts": { - "durationMs": 65.036865234375, - "testCount": 2 - }, - "src/cron/service.failure-alert.test.ts": { - "durationMs": 64.65673828125, - "testCount": 4 - }, - "src/config/sessions.test.ts": { - "durationMs": 64.11376953125, + "src/node-host/invoke-system-run.test.ts": { + "durationMs": 77.802490234375, "testCount": 37 }, - "src/infra/matrix-plugin-helper.test.ts": { - "durationMs": 63.89306640625, - "testCount": 4 - }, - "src/config/config.legacy-config-detection.accepts-imessage-dmpolicy.test.ts": { - "durationMs": 62.707275390625, - "testCount": 30 - }, - "src/infra/session-maintenance-warning.test.ts": { - "durationMs": 62.460205078125, - "testCount": 5 - }, - "src/infra/heartbeat-runner.ghost-reminder.test.ts": { - "durationMs": 61.06640625, - "testCount": 6 - }, "src/hooks/hooks-install.test.ts": { - "durationMs": 60.970947265625, + "durationMs": 77.791015625, "testCount": 1 }, "src/cli/pairing-cli.test.ts": { - "durationMs": 60.802734375, + "durationMs": 77.739501953125, "testCount": 12 }, - "src/plugins/copy-bundled-plugin-metadata.test.ts": { - "durationMs": 60.125244140625, - "testCount": 8 + "src/plugins/status.test.ts": { + "durationMs": 77.654541015625, + "testCount": 9 }, - "src/secrets/runtime.test.ts": { - "durationMs": 59.844970703125, - "testCount": 55 + "src/process/command-queue.test.ts": { + "durationMs": 77.4296875, + "testCount": 17 }, - "src/infra/jsonl-socket.test.ts": { - "durationMs": 59.01025390625, - "testCount": 2 - }, - "src/media/store.test.ts": { - "durationMs": 58.599609375, + "src/plugins/discovery.test.ts": { + "durationMs": 77.133544921875, "testCount": 24 }, - "src/cron/isolated-agent.auth-profile-propagation.test.ts": { - "durationMs": 58.552734375, - "testCount": 1 - }, - "src/plugins/web-search-providers.test.ts": { - "durationMs": 57.475830078125, - "testCount": 7 - }, - "src/infra/device-bootstrap.test.ts": { - "durationMs": 56.591064453125, + "src/media-understanding/apply.echo-transcript.test.ts": { + "durationMs": 76.621826171875, "testCount": 10 }, - "src/plugins/tools.optional.test.ts": { - "durationMs": 56.499267578125, - "testCount": 8 + "src/config/config.web-search-provider.test.ts": { + "durationMs": 76.143310546875, + "testCount": 24 }, - "src/config/schema.test.ts": { - "durationMs": 56.23974609375, - "testCount": 22 + "src/plugins/bundle-commands.test.ts": { + "durationMs": 74.98974609375, + "testCount": 1 }, - "src/plugin-sdk/keyed-async-queue.test.ts": { - "durationMs": 55.49609375, - "testCount": 4 + "src/tts/tts.test.ts": { + "durationMs": 73.42138671875, + "testCount": 27 }, - "src/plugins/conversation-binding.test.ts": { - "durationMs": 55.274169921875, - "testCount": 15 + "src/wizard/setup.gateway-config.test.ts": { + "durationMs": 72.30078125, + "testCount": 7 }, - "src/infra/boundary-path.test.ts": { - "durationMs": 53.925537109375, - "testCount": 5 - }, - "src/channels/plugins/acp-bindings.test.ts": { - "durationMs": 53.187744140625, - "testCount": 6 - }, - "src/infra/heartbeat-runner.model-override.test.ts": { - "durationMs": 52.96875, - "testCount": 8 - }, - "src/media-understanding/providers/index.test.ts": { - "durationMs": 52.26025390625, + "src/media-understanding/runner.proxy.test.ts": { + "durationMs": 69.77685546875, "testCount": 3 }, - "src/security/fix.test.ts": { - "durationMs": 51.88134765625, + "src/cli/program.smoke.test.ts": { + "durationMs": 68.246826171875, + "testCount": 4 + }, + "src/media-understanding/runner.auto-audio.test.ts": { + "durationMs": 67.60791015625, + "testCount": 4 + }, + "src/canvas-host/server.test.ts": { + "durationMs": 67.552734375, + "testCount": 6 + }, + "src/config/sessions.test.ts": { + "durationMs": 67.53515625, + "testCount": 37 + }, + "src/media-understanding/runner.deepgram.test.ts": { + "durationMs": 67.04638671875, + "testCount": 1 + }, + "src/infra/update-runner.test.ts": { + "durationMs": 66.3642578125, + "testCount": 20 + }, + "src/secrets/runtime.test.ts": { + "durationMs": 66.31298828125, + "testCount": 55 + }, + "src/plugins/bundled-web-search.test.ts": { + "durationMs": 66.30419921875, + "testCount": 3 + }, + "src/cron/service.failure-alert.test.ts": { + "durationMs": 66.09130859375, + "testCount": 4 + }, + "src/infra/device-bootstrap.test.ts": { + "durationMs": 65.17578125, + "testCount": 10 + }, + "src/infra/matrix-legacy-crypto.test.ts": { + "durationMs": 63.92236328125, + "testCount": 8 + }, + "src/config/config.legacy-config-detection.accepts-imessage-dmpolicy.test.ts": { + "durationMs": 62.4326171875, + "testCount": 30 + }, + "src/context-engine/context-engine.test.ts": { + "durationMs": 61.626220703125, + "testCount": 30 + }, + "src/infra/outbound/outbound-send-service.test.ts": { + "durationMs": 61.38720703125, + "testCount": 9 + }, + "src/plugins/conversation-binding.test.ts": { + "durationMs": 61.240478515625, + "testCount": 15 + }, + "src/infra/jsonl-socket.test.ts": { + "durationMs": 60.9248046875, + "testCount": 2 + }, + "src/cron/isolated-agent.auth-profile-propagation.test.ts": { + "durationMs": 59.379638671875, + "testCount": 1 + }, + "src/infra/session-maintenance-warning.test.ts": { + "durationMs": 58.95166015625, "testCount": 5 }, - "src/cron/service.store.migration.test.ts": { - "durationMs": 51.132568359375, + "src/plugins/web-search-providers.test.ts": { + "durationMs": 58.458740234375, "testCount": 7 }, - "src/plugins/providers.test.ts": { - "durationMs": 50.559326171875, - "testCount": 7 + "src/config/schema.test.ts": { + "durationMs": 58.256591796875, + "testCount": 22 + }, + "src/channels/plugins/acp-bindings.test.ts": { + "durationMs": 57.998046875, + "testCount": 6 + }, + "src/media/store.test.ts": { + "durationMs": 57.622802734375, + "testCount": 24 + }, + "src/media-understanding/runner.vision-skip.test.ts": { + "durationMs": 57.560546875, + "testCount": 1 + }, + "src/infra/heartbeat-runner.ghost-reminder.test.ts": { + "durationMs": 56.308837890625, + "testCount": 6 + }, + "src/plugins/tools.optional.test.ts": { + "durationMs": 55.556396484375, + "testCount": 8 + }, + "src/infra/outbound/delivery-queue.test.ts": { + "durationMs": 55.4453125, + "testCount": 36 + }, + "src/plugins/web-search-providers.runtime.test.ts": { + "durationMs": 55.43701171875, + "testCount": 2 + }, + "src/plugins/contracts/registry.contract.test.ts": { + "durationMs": 55.233154296875, + "testCount": 19 + }, + "src/infra/ports.test.ts": { + "durationMs": 54.990966796875, + "testCount": 5 + }, + "src/config/sessions/targets.test.ts": { + "durationMs": 54.558837890625, + "testCount": 13 + }, + "src/plugin-sdk/keyed-async-queue.test.ts": { + "durationMs": 54.40087890625, + "testCount": 4 + }, + "src/media-understanding/providers/index.test.ts": { + "durationMs": 54.11083984375, + "testCount": 3 }, "src/config/sessions/store.pruning.integration.test.ts": { - "durationMs": 50.111083984375, + "durationMs": 53.06689453125, + "testCount": 10 + }, + "src/media-understanding/runtime.test.ts": { + "durationMs": 51.96337890625, + "testCount": 2 + }, + "src/infra/heartbeat-runner.model-override.test.ts": { + "durationMs": 51.758544921875, + "testCount": 8 + }, + "src/plugins/providers.test.ts": { + "durationMs": 51.7294921875, + "testCount": 7 + }, + "src/cli/update-cli/restart-helper.test.ts": { + "durationMs": 50.244140625, + "testCount": 20 + }, + "src/security/fix.test.ts": { + "durationMs": 49.827392578125, + "testCount": 5 + }, + "src/cli/plugin-registry.test.ts": { + "durationMs": 49.33544921875, + "testCount": 2 + }, + "src/cron/service.persists-delivered-status.test.ts": { + "durationMs": 48.68798828125, + "testCount": 6 + }, + "src/config/io.runtime-snapshot-write.test.ts": { + "durationMs": 47.719482421875, + "testCount": 6 + }, + "src/memory/manager.embedding-batches.test.ts": { + "durationMs": 47.244140625, + "testCount": 5 + }, + "src/plugins/stage-bundled-plugin-runtime.test.ts": { + "durationMs": 46.9541015625, + "testCount": 7 + }, + "src/infra/boundary-path.test.ts": { + "durationMs": 46.826904296875, + "testCount": 5 + }, + "src/infra/update-startup.test.ts": { + "durationMs": 46.8212890625, "testCount": 10 }, "src/cron/service.restart-catchup.test.ts": { - "durationMs": 50.01611328125, + "durationMs": 46.452392578125, "testCount": 8 }, - "src/config/io.runtime-snapshot-write.test.ts": { - "durationMs": 49.573486328125, - "testCount": 6 - }, - "src/infra/ports.test.ts": { - "durationMs": 49.525390625, - "testCount": 5 - }, - "src/cron/service.store-migration.test.ts": { - "durationMs": 49.196533203125, - "testCount": 5 - }, - "src/memory/manager.embedding-batches.test.ts": { - "durationMs": 49.182861328125, - "testCount": 5 - }, - "src/plugins/bundled-web-search.test.ts": { - "durationMs": 48.532958984375, + "src/logging/log-file-size-cap.test.ts": { + "durationMs": 46.26904296875, "testCount": 3 }, - "src/infra/outbound/outbound-send-service.test.ts": { - "durationMs": 48.320068359375, - "testCount": 9 + "src/memory/batch-gemini.test.ts": { + "durationMs": 45.881103515625, + "testCount": 1 }, - "src/plugins/contracts/registry.contract.test.ts": { - "durationMs": 48.1787109375, - "testCount": 19 - }, - "src/cron/service.persists-delivered-status.test.ts": { - "durationMs": 48.072509765625, - "testCount": 6 - }, - "src/channels/plugins/whatsapp-heartbeat.test.ts": { - "durationMs": 46.382568359375, - "testCount": 8 - }, - "src/config/config.backup-rotation.test.ts": { - "durationMs": 46.346435546875, - "testCount": 4 - }, - "src/cron/service.every-jobs-fire.test.ts": { - "durationMs": 45.6865234375, - "testCount": 3 - }, - "src/config/config.pruning-defaults.test.ts": { - "durationMs": 45.49609375, + "src/cron/service.store.migration.test.ts": { + "durationMs": 45.3388671875, "testCount": 7 }, - "src/routing/resolve-route.test.ts": { - "durationMs": 45.240234375, - "testCount": 41 - }, - "src/plugins/web-search-providers.runtime.test.ts": { - "durationMs": 44.73388671875, - "testCount": 2 - }, - "src/cli/plugin-registry.test.ts": { - "durationMs": 44.709228515625, - "testCount": 2 - }, - "src/cli/route.test.ts": { - "durationMs": 44.515380859375, + "src/cron/service.every-jobs-fire.test.ts": { + "durationMs": 44.91748046875, "testCount": 3 }, + "src/cli/route.test.ts": { + "durationMs": 44.565185546875, + "testCount": 3 + }, + "src/infra/install-package-dir.test.ts": { + "durationMs": 44.29150390625, + "testCount": 5 + }, + "src/routing/resolve-route.test.ts": { + "durationMs": 43.66748046875, + "testCount": 41 + }, + "src/memory/embeddings-voyage.test.ts": { + "durationMs": 43.4365234375, + "testCount": 4 + }, + "src/config/schema.help.quality.test.ts": { + "durationMs": 43.33740234375, + "testCount": 20 + }, + "src/plugins/contracts/discovery.contract.test.ts": { + "durationMs": 43.250244140625, + "testCount": 15 + }, + "src/infra/matrix-plugin-helper.test.ts": { + "durationMs": 43.179443359375, + "testCount": 4 + }, + "src/memory/internal.test.ts": { + "durationMs": 43.138916015625, + "testCount": 18 + }, + "src/config/config.pruning-defaults.test.ts": { + "durationMs": 42.963134765625, + "testCount": 7 + }, + "src/infra/matrix-legacy-state.test.ts": { + "durationMs": 41.80126953125, + "testCount": 6 + }, "src/process/supervisor/supervisor.pty-command.test.ts": { - "durationMs": 44.046142578125, + "durationMs": 40.801513671875, + "testCount": 2 + }, + "src/config/config.identity-defaults.test.ts": { + "durationMs": 40.382568359375, + "testCount": 7 + }, + "src/config/env-preserve-io.test.ts": { + "durationMs": 40.336181640625, + "testCount": 4 + }, + "src/config/config.multi-agent-agentdir-validation.test.ts": { + "durationMs": 40.139892578125, "testCount": 2 }, "src/cron/run-log.test.ts": { - "durationMs": 44.02197265625, + "durationMs": 40.08837890625, "testCount": 11 }, - "src/logging/log-file-size-cap.test.ts": { - "durationMs": 43.52294921875, - "testCount": 3 - }, - "src/config/env-preserve-io.test.ts": { - "durationMs": 42.47412109375, - "testCount": 4 - }, - "src/infra/matrix-legacy-state.test.ts": { - "durationMs": 42.006103515625, - "testCount": 6 - }, - "src/infra/install-package-dir.test.ts": { - "durationMs": 40.187744140625, - "testCount": 5 - }, - "src/infra/matrix-migration-snapshot.test.ts": { - "durationMs": 39.780029296875, - "testCount": 7 - }, - "src/memory/embeddings-voyage.test.ts": { - "durationMs": 39.38671875, - "testCount": 4 - }, - "src/cron/service.runs-one-shot-main-job-disables-it.test.ts": { - "durationMs": 39.27001953125, - "testCount": 11 - }, - "src/infra/update-startup.test.ts": { - "durationMs": 37.697021484375, - "testCount": 10 - }, - "src/memory/internal.test.ts": { - "durationMs": 37.352294921875, - "testCount": 18 - }, - "src/infra/net/proxy-fetch.test.ts": { - "durationMs": 37.226318359375, - "testCount": 10 - }, - "src/cli/program/config-guard.test.ts": { - "durationMs": 37.146728515625, - "testCount": 8 - }, "src/process/exec.windows.test.ts": { - "durationMs": 36.946044921875, + "durationMs": 39.901123046875, "testCount": 2 }, - "src/plugins/hook-runner-global.test.ts": { - "durationMs": 36.940673828125, - "testCount": 2 - }, - "src/infra/state-migrations.test.ts": { - "durationMs": 36.804931640625, - "testCount": 2 - }, - "src/config/config.compaction-settings.test.ts": { - "durationMs": 36.283203125, - "testCount": 5 - }, - "src/infra/json-files.test.ts": { - "durationMs": 36.281005859375, - "testCount": 5 - }, - "src/config/mcp-config.test.ts": { - "durationMs": 35.782470703125, - "testCount": 2 - }, - "src/cron/service.issue-16156-list-skips-cron.test.ts": { - "durationMs": 35.733642578125, - "testCount": 3 - }, - "src/media/fetch.test.ts": { - "durationMs": 34.879638671875, - "testCount": 6 + "src/channels/plugins/whatsapp-heartbeat.test.ts": { + "durationMs": 38.927978515625, + "testCount": 8 }, "src/cron/isolated-agent/run.skill-filter.test.ts": { - "durationMs": 34.52734375, + "durationMs": 38.463623046875, "testCount": 13 }, - "src/infra/transport-ready.test.ts": { - "durationMs": 34.49560546875, - "testCount": 6 - }, - "src/cli/config-cli.test.ts": { - "durationMs": 34.365966796875, - "testCount": 48 - }, - "src/process/exec.no-output-timer.test.ts": { - "durationMs": 34.100341796875, - "testCount": 1 - }, - "src/config/config.legacy-config-detection.rejects-routing-allowfrom.test.ts": { - "durationMs": 34.00390625, - "testCount": 28 - }, - "src/config/plugins-runtime-boundary.test.ts": { - "durationMs": 33.844970703125, - "testCount": 3 - }, - "src/plugin-sdk/persistent-dedupe.test.ts": { - "durationMs": 33.832275390625, - "testCount": 6 - }, - "src/media/server.test.ts": { - "durationMs": 33.778564453125, - "testCount": 9 - }, - "src/cron/service.read-ops-nonblocking.test.ts": { - "durationMs": 33.532470703125, - "testCount": 3 - }, - "src/security/skill-scanner.test.ts": { - "durationMs": 33.52099609375, - "testCount": 27 - }, - "src/config/config.identity-defaults.test.ts": { - "durationMs": 33.500732421875, - "testCount": 7 - }, - "src/acp/persistent-bindings.lifecycle.test.ts": { - "durationMs": 33.449462890625, - "testCount": 1 - }, - "src/infra/outbound/targets.channel-resolution.test.ts": { - "durationMs": 33.4482421875, + "src/plugins/hook-runner-global.test.ts": { + "durationMs": 38.257080078125, "testCount": 2 }, - "src/plugins/schema-validator.test.ts": { - "durationMs": 33.44189453125, - "testCount": 7 + "src/cron/service.store-migration.test.ts": { + "durationMs": 38.186279296875, + "testCount": 5 }, "src/infra/heartbeat-runner.sender-prefers-delivery-target.test.ts": { - "durationMs": 32.837646484375, + "durationMs": 37.928955078125, "testCount": 1 }, - "src/config/schema.help.quality.test.ts": { - "durationMs": 32.271240234375, - "testCount": 20 + "src/infra/matrix-migration-snapshot.test.ts": { + "durationMs": 37.8251953125, + "testCount": 7 }, - "src/memory/manager.atomic-reindex.test.ts": { - "durationMs": 32.11962890625, - "testCount": 1 + "src/cron/service.runs-one-shot-main-job-disables-it.test.ts": { + "durationMs": 37.491455078125, + "testCount": 11 }, - "src/cron/isolated-agent/run.message-tool-policy.test.ts": { - "durationMs": 31.809326171875, + "src/infra/net/proxy-fetch.test.ts": { + "durationMs": 37.133056640625, + "testCount": 10 + }, + "src/infra/outbound/targets.channel-resolution.test.ts": { + "durationMs": 36.54296875, + "testCount": 2 + }, + "src/cron/service.read-ops-nonblocking.test.ts": { + "durationMs": 35.898681640625, "testCount": 3 }, - "src/cron/isolated-agent/run.cron-model-override.test.ts": { - "durationMs": 31.62109375, + "src/process/exec.no-output-timer.test.ts": { + "durationMs": 34.91845703125, + "testCount": 1 + }, + "src/cron/service.issue-16156-list-skips-cron.test.ts": { + "durationMs": 34.7041015625, + "testCount": 3 + }, + "src/config/mcp-config.test.ts": { + "durationMs": 34.5478515625, + "testCount": 2 + }, + "src/acp/persistent-bindings.lifecycle.test.ts": { + "durationMs": 34.266845703125, + "testCount": 1 + }, + "src/infra/exec-approvals-store.test.ts": { + "durationMs": 33.988525390625, + "testCount": 8 + }, + "src/media/fetch.test.ts": { + "durationMs": 33.756103515625, "testCount": 6 }, "src/cron/service.session-reaper-in-finally.test.ts": { - "durationMs": 31.396484375, + "durationMs": 33.66015625, "testCount": 3 }, - "src/memory/manager.async-search.test.ts": { - "durationMs": 31.3916015625, - "testCount": 2 - }, - "src/config/config.talk-validation.test.ts": { - "durationMs": 31.235595703125, - "testCount": 5 - }, - "src/infra/provider-usage.fetch.claude.test.ts": { - "durationMs": 31.22900390625, - "testCount": 13 - }, - "src/infra/exec-approvals-store.test.ts": { - "durationMs": 31.08544921875, - "testCount": 8 - }, - "src/infra/outbound/channel-resolution.test.ts": { - "durationMs": 30.5302734375, + "src/cron/isolated-agent/run.cron-model-override.test.ts": { + "durationMs": 33.645751953125, "testCount": 6 }, - "src/infra/provider-usage.fetch.shared.test.ts": { - "durationMs": 30.34716796875, - "testCount": 9 - }, - "src/daemon/service-audit.test.ts": { - "durationMs": 30.04296875, - "testCount": 16 - }, - "src/cron/isolated-agent/run.fast-mode.test.ts": { - "durationMs": 29.775390625, + "src/config/plugins-runtime-boundary.test.ts": { + "durationMs": 33.640380859375, "testCount": 3 }, - "src/cron/isolated-agent/run.interim-retry.test.ts": { - "durationMs": 29.641845703125, - "testCount": 3 - }, - "src/cron/isolated-agent/run.payload-fallbacks.test.ts": { - "durationMs": 29.556640625, - "testCount": 3 - }, - "src/infra/provider-usage.fetch.zai.test.ts": { - "durationMs": 29.4599609375, + "src/config/config.compaction-settings.test.ts": { + "durationMs": 33.577880859375, "testCount": 5 }, - "src/cron/session-reaper.test.ts": { - "durationMs": 29.3984375, - "testCount": 16 + "src/config/config-misc.test.ts": { + "durationMs": 33.1669921875, + "testCount": 38 }, - "src/config/io.compat.test.ts": { - "durationMs": 29.25, - "testCount": 7 + "src/config/config.legacy-config-detection.rejects-routing-allowfrom.test.ts": { + "durationMs": 33.0146484375, + "testCount": 28 }, - "src/infra/heartbeat-runner.transcript-prune.test.ts": { - "durationMs": 29.059326171875, - "testCount": 2 - }, - "src/memory/batch-gemini.test.ts": { - "durationMs": 28.962890625, - "testCount": 1 - }, - "src/infra/infra-runtime.test.ts": { - "durationMs": 28.714599609375, - "testCount": 11 - }, - "src/infra/push-apns.store.test.ts": { - "durationMs": 28.333740234375, - "testCount": 7 - }, - "src/infra/provider-usage.fetch.minimax.test.ts": { - "durationMs": 28.28271484375, - "testCount": 10 - }, - "src/memory/manager.mistral-provider.test.ts": { - "durationMs": 28.19580078125, - "testCount": 3 - }, - "src/infra/provider-usage.fetch.codex.test.ts": { - "durationMs": 28.186279296875, - "testCount": 8 - }, - "src/config/sessions.cache.test.ts": { - "durationMs": 28.095947265625, + "src/media/server.test.ts": { + "durationMs": 32.695068359375, "testCount": 9 }, + "src/cron/isolated-agent/run.message-tool-policy.test.ts": { + "durationMs": 32.653564453125, + "testCount": 3 + }, + "src/infra/outbound/channel-resolution.test.ts": { + "durationMs": 32.214111328125, + "testCount": 6 + }, + "src/cli/program/config-guard.test.ts": { + "durationMs": 32.14794921875, + "testCount": 8 + }, + "src/infra/transport-ready.test.ts": { + "durationMs": 31.7958984375, + "testCount": 6 + }, + "src/security/skill-scanner.test.ts": { + "durationMs": 31.746826171875, + "testCount": 27 + }, + "src/config/config.backup-rotation.test.ts": { + "durationMs": 31.5751953125, + "testCount": 4 + }, + "src/plugins/schema-validator.test.ts": { + "durationMs": 31.465576171875, + "testCount": 7 + }, + "src/infra/json-files.test.ts": { + "durationMs": 31.266845703125, + "testCount": 5 + }, + "src/cron/isolated-agent/run.fast-mode.test.ts": { + "durationMs": 30.658935546875, + "testCount": 3 + }, + "src/infra/matrix-migration-config.test.ts": { + "durationMs": 30.35595703125, + "testCount": 7 + }, + "src/infra/provider-usage.fetch.shared.test.ts": { + "durationMs": 30.326904296875, + "testCount": 9 + }, + "src/infra/provider-usage.fetch.claude.test.ts": { + "durationMs": 29.79833984375, + "testCount": 13 + }, + "src/infra/state-migrations.test.ts": { + "durationMs": 29.6259765625, + "testCount": 2 + }, "src/cli/daemon-cli.coverage.test.ts": { - "durationMs": 27.56591796875, + "durationMs": 29.58935546875, "testCount": 5 }, "src/infra/outbound/deliver.test.ts": { - "durationMs": 27.35498046875, - "testCount": 43 + "durationMs": 29.402099609375, + "testCount": 44 }, - "src/infra/fetch.test.ts": { - "durationMs": 27.31005859375, + "src/cli/config-cli.test.ts": { + "durationMs": 29.34619140625, + "testCount": 48 + }, + "src/channels/plugins/contracts/session-binding.contract.test.ts": { + "durationMs": 29.2939453125, "testCount": 16 }, - "src/cron/store.test.ts": { - "durationMs": 27.124267578125, - "testCount": 11 - }, - "src/infra/session-cost-usage.test.ts": { - "durationMs": 27.038818359375, - "testCount": 9 - }, - "src/infra/matrix-migration-config.test.ts": { - "durationMs": 26.976318359375, + "src/config/io.compat.test.ts": { + "durationMs": 29.235107421875, "testCount": 7 }, - "src/infra/outbound/message-action-runner.plugin-dispatch.test.ts": { - "durationMs": 26.87646484375, + "src/daemon/service-audit.test.ts": { + "durationMs": 29.21240234375, + "testCount": 16 + }, + "src/config/config.talk-validation.test.ts": { + "durationMs": 29.14306640625, + "testCount": 5 + }, + "src/cron/isolated-agent/run.interim-retry.test.ts": { + "durationMs": 28.907470703125, + "testCount": 3 + }, + "src/infra/push-apns.store.test.ts": { + "durationMs": 28.8544921875, + "testCount": 7 + }, + "src/cron/isolated-agent/run.payload-fallbacks.test.ts": { + "durationMs": 28.58935546875, + "testCount": 3 + }, + "src/infra/session-cost-usage.test.ts": { + "durationMs": 28.518310546875, + "testCount": 9 + }, + "src/infra/fetch.test.ts": { + "durationMs": 28.30419921875, + "testCount": 16 + }, + "src/media-understanding/runner.video.test.ts": { + "durationMs": 28.2119140625, + "testCount": 2 + }, + "src/infra/heartbeat-runner.transcript-prune.test.ts": { + "durationMs": 28.03564453125, + "testCount": 2 + }, + "src/cron/service.main-job-passes-heartbeat-target-last.test.ts": { + "durationMs": 27.652099609375, + "testCount": 2 + }, + "src/config/sessions.cache.test.ts": { + "durationMs": 27.560302734375, + "testCount": 9 + }, + "src/infra/provider-usage.fetch.zai.test.ts": { + "durationMs": 27.3720703125, + "testCount": 5 + }, + "src/infra/provider-usage.fetch.minimax.test.ts": { + "durationMs": 27.30126953125, "testCount": 10 }, "src/infra/provider-usage.fetch.gemini.test.ts": { - "durationMs": 26.228271484375, + "durationMs": 27.2841796875, "testCount": 4 }, - "src/config/config-misc.test.ts": { - "durationMs": 25.92041015625, - "testCount": 38 - }, - "src/infra/install-source-utils.test.ts": { - "durationMs": 25.84375, - "testCount": 16 - }, - "src/cli/memory-cli.test.ts": { - "durationMs": 25.3857421875, - "testCount": 24 - }, - "src/plugin-sdk/fetch-auth.test.ts": { - "durationMs": 25.314453125, - "testCount": 5 - }, - "src/infra/outbound/message-action-runner.media.test.ts": { - "durationMs": 24.552734375, - "testCount": 7 - }, - "src/plugins/bundle-manifest.test.ts": { - "durationMs": 24.4482421875, + "src/infra/provider-usage.fetch.codex.test.ts": { + "durationMs": 27.250244140625, "testCount": 8 }, - "src/channels/plugins/contracts/inbound.contract.test.ts": { - "durationMs": 24.302734375, - "testCount": 5 - }, - "src/channels/plugins/contracts/session-binding.contract.test.ts": { - "durationMs": 24.275390625, + "src/cron/session-reaper.test.ts": { + "durationMs": 26.843505859375, "testCount": 16 }, - "src/node-host/invoke-browser.test.ts": { - "durationMs": 24.2431640625, - "testCount": 4 + "src/plugin-sdk/persistent-dedupe.test.ts": { + "durationMs": 26.835205078125, + "testCount": 6 }, - "src/cli/daemon-cli/restart-health.test.ts": { - "durationMs": 24.205322265625, + "src/infra/infra-runtime.test.ts": { + "durationMs": 26.794921875, + "testCount": 11 + }, + "src/logging/logger.browser-import.test.ts": { + "durationMs": 26.61572265625, + "testCount": 2 + }, + "src/infra/outbound/message-action-runner.plugin-dispatch.test.ts": { + "durationMs": 26.400634765625, "testCount": 10 }, - "src/memory/manager.watcher-config.test.ts": { - "durationMs": 24.20263671875, - "testCount": 2 + "src/infra/outbound/message-action-runner.media.test.ts": { + "durationMs": 26.33740234375, + "testCount": 7 }, - "src/config/sessions/store.session-key-normalization.test.ts": { - "durationMs": 23.89453125, - "testCount": 4 + "src/plugin-sdk/fetch-auth.test.ts": { + "durationMs": 26.331298828125, + "testCount": 5 + }, + "src/cron/store.test.ts": { + "durationMs": 25.98681640625, + "testCount": 11 }, "src/memory/post-json.test.ts": { - "durationMs": 23.77587890625, + "durationMs": 25.880615234375, "testCount": 2 }, - "src/cli/mcp-cli.test.ts": { - "durationMs": 23.469482421875, + "src/memory/manager.async-search.test.ts": { + "durationMs": 25.531494140625, "testCount": 2 }, - "src/image-generation/providers/google.test.ts": { - "durationMs": 23.42578125, - "testCount": 4 + "src/cli/memory-cli.test.ts": { + "durationMs": 25.39306640625, + "testCount": 24 + }, + "src/cli/daemon-cli/restart-health.test.ts": { + "durationMs": 25.20361328125, + "testCount": 10 }, "src/cli/run-main.profile-env.test.ts": { - "durationMs": 23.318359375, + "durationMs": 25.12646484375, "testCount": 1 }, + "src/plugin-sdk/runtime-api-guardrails.test.ts": { + "durationMs": 25.1103515625, + "testCount": 1 + }, + "src/plugins/bundle-manifest.test.ts": { + "durationMs": 25.057373046875, + "testCount": 8 + }, + "src/config/sessions/store.session-key-normalization.test.ts": { + "durationMs": 24.89111328125, + "testCount": 4 + }, "src/config/logging.test.ts": { - "durationMs": 22.7255859375, + "durationMs": 24.798095703125, + "testCount": 2 + }, + "src/infra/install-source-utils.test.ts": { + "durationMs": 24.661865234375, + "testCount": 16 + }, + "src/memory/manager.mistral-provider.test.ts": { + "durationMs": 24.02294921875, + "testCount": 3 + }, + "src/cli/mcp-cli.test.ts": { + "durationMs": 23.84375, "testCount": 2 }, "src/cron/service.delivery-plan.test.ts": { - "durationMs": 22.681884765625, + "durationMs": 23.5244140625, "testCount": 3 + }, + "src/channels/plugins/contracts/inbound.contract.test.ts": { + "durationMs": 23.243896484375, + "testCount": 5 + }, + "src/memory/manager.atomic-reindex.test.ts": { + "durationMs": 23.026611328125, + "testCount": 1 + }, + "src/cli/prompt.test.ts": { + "durationMs": 22.975341796875, + "testCount": 2 + }, + "src/config/config.allowlist-requires-allowfrom.test.ts": { + "durationMs": 22.65576171875, + "testCount": 14 + }, + "src/config/io.validation-fails-closed.test.ts": { + "durationMs": 22.468505859375, + "testCount": 2 + }, + "src/node-host/invoke-browser.test.ts": { + "durationMs": 22.24267578125, + "testCount": 4 + }, + "src/cli/nodes-camera.test.ts": { + "durationMs": 22.165283203125, + "testCount": 17 + }, + "src/image-generation/providers/google.test.ts": { + "durationMs": 22.115234375, + "testCount": 4 + }, + "src/process/supervisor/supervisor.test.ts": { + "durationMs": 21.928466796875, + "testCount": 5 } } } diff --git a/test/plugin-extension-import-boundary.test.ts b/test/plugin-extension-import-boundary.test.ts index bef7bb57838..ef86549dca1 100644 --- a/test/plugin-extension-import-boundary.test.ts +++ b/test/plugin-extension-import-boundary.test.ts @@ -1,14 +1,13 @@ -import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { collectPluginExtensionImportBoundaryInventory, diffInventory, + main, } from "../scripts/check-plugin-extension-import-boundary.mjs"; const repoRoot = process.cwd(); -const scriptPath = path.join(repoRoot, "scripts", "check-plugin-extension-import-boundary.mjs"); const baselinePath = path.join( repoRoot, "test", @@ -20,6 +19,27 @@ function readBaseline() { return JSON.parse(readFileSync(baselinePath, "utf8")); } +function createCapturedIo() { + let stdout = ""; + let stderr = ""; + return { + io: { + stdout: { + write(chunk) { + stdout += String(chunk); + }, + }, + stderr: { + write(chunk) { + stderr += String(chunk); + }, + }, + }, + readStdout: () => stdout, + readStderr: () => stderr, + }; +} + describe("plugin extension import boundary inventory", () => { it("keeps dedicated web-search registry shims out of the remaining inventory", async () => { const inventory = await collectPluginExtensionImportBoundaryInventory(); @@ -65,12 +85,12 @@ describe("plugin extension import boundary inventory", () => { expect(diffInventory(expected, actual)).toEqual({ missing: [], unexpected: [] }); }); - it("script json output matches the baseline exactly", () => { - const stdout = execFileSync(process.execPath, [scriptPath, "--json"], { - cwd: repoRoot, - encoding: "utf8", - }); + it("script json output matches the baseline exactly", async () => { + const captured = createCapturedIo(); + const exitCode = await main(["--json"], captured.io); - expect(JSON.parse(stdout)).toEqual(readBaseline()); + expect(exitCode).toBe(0); + expect(captured.readStderr()).toBe(""); + expect(JSON.parse(captured.readStdout())).toEqual(readBaseline()); }); }); diff --git a/test/web-search-provider-boundary.test.ts b/test/web-search-provider-boundary.test.ts index f211a262ca3..cb309459712 100644 --- a/test/web-search-provider-boundary.test.ts +++ b/test/web-search-provider-boundary.test.ts @@ -1,10 +1,29 @@ -import { execFileSync } from "node:child_process"; -import path from "node:path"; import { describe, expect, it } from "vitest"; -import { collectWebSearchProviderBoundaryInventory } from "../scripts/check-web-search-provider-boundaries.mjs"; +import { + collectWebSearchProviderBoundaryInventory, + main, +} from "../scripts/check-web-search-provider-boundaries.mjs"; -const repoRoot = process.cwd(); -const scriptPath = path.join(repoRoot, "scripts", "check-web-search-provider-boundaries.mjs"); +function createCapturedIo() { + let stdout = ""; + let stderr = ""; + return { + io: { + stdout: { + write(chunk) { + stdout += String(chunk); + }, + }, + stderr: { + write(chunk) { + stderr += String(chunk); + }, + }, + }, + readStdout: () => stdout, + readStderr: () => stderr, + }; +} describe("web search provider boundary inventory", () => { it("has no remaining production inventory in core", async () => { @@ -35,12 +54,12 @@ describe("web search provider boundary inventory", () => { ).toEqual(first); }); - it("script json output is empty", () => { - const stdout = execFileSync(process.execPath, [scriptPath, "--json"], { - cwd: repoRoot, - encoding: "utf8", - }); + it("script json output is empty", async () => { + const captured = createCapturedIo(); + const exitCode = await main(["--json"], captured.io); - expect(JSON.parse(stdout)).toEqual([]); + expect(exitCode).toBe(0); + expect(captured.readStderr()).toBe(""); + expect(JSON.parse(captured.readStdout())).toEqual([]); }); });