FN-6042: raise dependency security floors

Harden dependency floors and update the Vitest toolchain to patched releases.

- upgrade workspace vitest and @vitest/coverage-v8 dependencies to the 4.1 line across packages and plugins
- pin transitive protobufjs via pnpm overrides and lockfile updates to patched versions
- adapt Vitest configs, engine test helpers, and security-floor coverage for the new dependency baselines
- add the published CLI changeset and related workspace/package metadata updates included in the task branch

Files changed:
 .changeset/fn-6042-security-dependencies.md        |   5 +
 AGENTS.md                                          |   4 +
 Dockerfile                                         |   3 +
 docs/PLUGIN_AUTHORING.md                           |   2 -
 package.json                                       |   3 +-
 packages/cli/package.json                          |   4 +-
 packages/cli/vitest.config.ts                      |   2 +-
 packages/core/package.json                         |   4 +-
 packages/core/vitest.config.ts                     |   2 +-
 packages/dashboard/app/test/mockApi.ts             |   4 +-
 packages/dashboard/package.json                    |   4 +-
 packages/dashboard/vitest.config.ts                |   2 +-
 packages/desktop/package.json                      |   4 +-
 packages/desktop/vitest.config.ts                  |   2 +-
 packages/droid-cli/package.json                    |   2 +-
 packages/droid-cli/vitest.config.ts                |   2 +-
 packages/engine/package.json                       |   4 +-
 .../engine/src/__tests__/executor-test-helpers.ts  |  29 +-
 .../engine/src/__tests__/gridlock-detector.test.ts |   5 +-
 .../src/__tests__/heartbeat-scheduler.test.ts      |   3 +-
 packages/engine/src/__tests__/scheduler.test.ts    |  24 +-
 packages/engine/src/__tests__/self-healing.test.ts |   5 +
 packages/engine/tsconfig.json                      |   3 +-
 packages/engine/vitest.config.ts                   |  10 +-
 packages/i18n/package.json                         |   2 +-
 packages/i18n/vitest.config.ts                     |   7 +
 packages/mobile/package.json                       |   2 +-
 packages/mobile/vitest.config.ts                   |   2 +-
 packages/pi-claude-cli/package.json                |   2 +-
 packages/pi-claude-cli/vitest.config.ts            |   2 +-
 packages/pi-llama-cpp/package.json                 |   2 +-
 packages/pi-llama-cpp/vitest.config.ts             |   2 +-
 packages/plugin-sdk/package.json                   |   2 +-
 packages/plugin-sdk/vitest.config.ts               |   2 +-
 .../examples/fusion-plugin-auto-label/package.json |   2 +-
 .../fusion-plugin-auto-label/vitest.config.ts      |   2 +-
 .../examples/fusion-plugin-ci-status/package.json  |   2 +-
 .../fusion-plugin-ci-status/vitest.config.ts       |   2 +-
 .../fusion-plugin-notification/package.json        |   2 +-
 .../fusion-plugin-notification/vitest.config.ts    |   2 +-
 .../fusion-plugin-settings-demo/package.json       |   2 +-
 .../fusion-plugin-settings-demo/vitest.config.ts   |   2 +-
 plugins/fusion-plugin-acp-runtime/package.json     |   2 +-
 plugins/fusion-plugin-acp-runtime/vitest.config.ts |   2 +-
 plugins/fusion-plugin-agent-browser/package.json   |   2 +-
 .../fusion-plugin-agent-browser/vitest.config.ts   |   2 +-
 .../fusion-plugin-cli-printing-press/package.json  |   2 +-
 .../vitest.config.ts                               |   2 +-
 .../package.json                                   |   2 +-
 .../src/__tests__/orchestrator-live-output.test.ts |   4 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-cursor-runtime/package.json  |   4 +-
 .../fusion-plugin-dependency-graph/package.json    |   4 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-droid-runtime/package.json   |   2 +-
 .../fusion-plugin-droid-runtime/vitest.config.ts   |   2 +-
 plugins/fusion-plugin-even-cards/package.json      |   2 +-
 plugins/fusion-plugin-even-cards/vitest.config.ts  |   2 +-
 .../package.json                                   |   2 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-hermes-runtime/package.json  |   2 +-
 .../fusion-plugin-hermes-runtime/vitest.config.ts  |   2 +-
 .../fusion-plugin-openclaw-runtime/package.json    |   2 +-
 .../vitest.config.ts                               |   2 +-
 .../fusion-plugin-paperclip-runtime/package.json   |   2 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-reports/package.json         |   2 +-
 .../src/__tests__/review-panel.test.ts             |   6 +-
 plugins/fusion-plugin-reports/vitest.config.ts     |   2 +-
 plugins/fusion-plugin-roadmap/package.json         |   2 +-
 plugins/fusion-plugin-roadmap/vitest.config.ts     |   2 +-
 plugins/fusion-plugin-whatsapp-chat/package.json   |   2 +-
 .../fusion-plugin-whatsapp-chat/vitest.config.ts   |   2 +-
 pnpm-lock.yaml                                     | 626 ++++++++-------------
 .../__tests__/dependency-security-floor.test.mjs   |  95 ++++
 75 files changed, 475 insertions(+), 491 deletions(-)

Fusion-Task-Id: FN-6042

Fusion-Task-Lineage: fff6a1cb-8937-435c-9a91-b7c7a59cc80e
This commit is contained in:
gsxdsm
2026-06-08 15:19:27 -07:00
parent 558720b4e5
commit 8bc3d7b0a5
75 changed files with 475 additions and 491 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Harden dependency security floors by forcing protobufjs resolutions to patched versions and upgrading Vitest tooling to the patched 4.1 line.

View File

@@ -2,6 +2,10 @@
## Essential rules
### STANDING DIRECTIVE: Buttons Are Frozen
Buttons Are Frozen (2026-05-13): do not rename, remove, reorder, or restyle established dashboard action buttons unless the task explicitly targets that button contract. Preserve icon affordances and width-aware labels when making adjacent UI changes.
### Spec Generation Hygiene
- Do not cite `.fusion/tasks/<id>/<file>` paths in Context/Steps/File Scope unless the file already exists, is explicitly created as a `(new)` Artifact, or is sibling `PROMPT.md`/`task.json`/`attachments/*`.

View File

@@ -17,6 +17,7 @@ COPY packages/dashboard/package.json ./packages/dashboard/package.json
COPY packages/desktop/package.json ./packages/desktop/package.json
COPY packages/droid-cli/package.json ./packages/droid-cli/package.json
COPY packages/engine/package.json ./packages/engine/package.json
COPY packages/i18n/package.json ./packages/i18n/package.json
COPY packages/mobile/package.json ./packages/mobile/package.json
COPY packages/pi-claude-cli/package.json ./packages/pi-claude-cli/package.json
COPY packages/pi-llama-cpp/package.json ./packages/pi-llama-cpp/package.json
@@ -25,6 +26,8 @@ COPY plugins/examples/fusion-plugin-auto-label/package.json ./plugins/examples/f
COPY plugins/examples/fusion-plugin-ci-status/package.json ./plugins/examples/fusion-plugin-ci-status/package.json
COPY plugins/examples/fusion-plugin-notification/package.json ./plugins/examples/fusion-plugin-notification/package.json
COPY plugins/examples/fusion-plugin-settings-demo/package.json ./plugins/examples/fusion-plugin-settings-demo/package.json
COPY plugins/fusion-plugin-acp-runtime/package.json ./plugins/fusion-plugin-acp-runtime/package.json
COPY plugins/fusion-plugin-compound-engineering/package.json ./plugins/fusion-plugin-compound-engineering/package.json
COPY plugins/fusion-plugin-paperclip-runtime/package.json ./plugins/fusion-plugin-paperclip-runtime/package.json
COPY plugins/fusion-plugin-dependency-graph/package.json ./plugins/fusion-plugin-dependency-graph/package.json
COPY plugins/fusion-plugin-cli-printing-press/package.json ./plugins/fusion-plugin-cli-printing-press/package.json

View File

@@ -20,8 +20,6 @@ A comprehensive guide to creating Fusion plugins that extend the task board with
14. [Example Plugins](#14-example-plugins)
15. [Registering Skills](#15-registering-skills)
16. [Registering Workflow Steps](#16-registering-workflow-steps)
16.5. [Contributing Column Traits](#165-contributing-column-traits)
16.6. [Contributing Workflow Extensions](#166-contributing-workflow-extensions)
17. [Contributing Prompt Modifications](#17-contributing-prompt-modifications)
18. [Plugin Binary Setup Hooks](#18-plugin-binary-setup-hooks)

View File

@@ -85,7 +85,8 @@
"protobufjs"
],
"overrides": {
"@types/node": "^25.5.2"
"@types/node": "^25.5.2",
"protobufjs": "^7.5.8"
}
},
"devDependencies": {

View File

@@ -98,7 +98,7 @@
"@types/node": "^22.0.0",
"@types/react": "^19.2.0",
"@types/ws": "^8.5.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"cross-env": "^7.0.0",
"esbuild": "^0.25.12",
"ink-testing-library": "^4.0.0",
@@ -106,7 +106,7 @@
"tsx": "^4.19.0",
"typebox": "^1.0.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0",
"vitest": "^4.1.0",
"yaml": "^2.8.3"
},
"repository": {

View File

@@ -52,7 +52,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
coverage: {
enabled: false,

View File

@@ -40,9 +40,9 @@
"devDependencies": {
"@types/dockerode": "^3.3.41",
"@types/node": "^25.5.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"engines": {
"node": ">=22.5.0"

View File

@@ -28,7 +28,7 @@ export default defineConfig({
// and cannot safely share mutable process state under worker_threads.
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
// Core runs a large SQLite-heavy suite while other workspace packages test concurrently.
// Use a slightly higher timeout to reduce nondeterministic slow-machine flakes.

View File

@@ -24,7 +24,7 @@ function getFallback(name: string): AnyFn {
return fallbackFns.get(name)!;
}
export const dashboardApiMocks = {
export const dashboardApiMocks: Record<string, AnyFn> = {
fetchTasks: vi.fn(async () => []),
fetchSettings: vi.fn(async () => ({})),
updateSettings: vi.fn(async () => ({})),
@@ -34,7 +34,7 @@ export const dashboardApiMocks = {
fetchTaskDetail: vi.fn(),
fetchTaskReview: vi.fn(),
fetchUnreadCount: vi.fn(async () => ({ unreadCount: 0 })),
} satisfies Record<string, AnyFn>;
};
export async function createDashboardApiMock(
importActual: () => Promise<AnyModule>,

View File

@@ -147,12 +147,12 @@
"@types/react-dom": "^19.0.0",
"@types/ws": "^8.5.0",
"@vitejs/plugin-react": "^4.3.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"jsdom": "^29.0.1",
"tinyrainbow": "^2.0.0",
"typescript": "^5.7.0",
"vite": "^6.0.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"private": true
}

View File

@@ -316,7 +316,7 @@ export default defineConfig({
// React suites; forks duplicated the entire renderer per worker (~500MB).
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
isolate: true,
// Dashboard route and integration-heavy suites can exceed the Vitest

View File

@@ -46,7 +46,7 @@
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"electron": "^35.0.0",
"electron-builder": "^26.0.0",
"esbuild": "^0.25.12",
@@ -57,6 +57,6 @@
"tsx": "^4.19.0",
"typescript": "^5.7.0",
"vite": "^6.0.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
}
}

View File

@@ -20,7 +20,7 @@ export default defineConfig({
hookTimeout: 30_000,
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
passWithNoTests: true,
projects: [

View File

@@ -31,7 +31,7 @@
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
"vitest": "^4.1.0"
},
"scripts": {
"test": "vitest run --reporter=dot",

View File

@@ -14,7 +14,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
coverage: {
provider: "v8",

View File

@@ -52,9 +52,9 @@
"@modelcontextprotocol/sdk": "^1.0.0",
"@types/node": "^25.5.0",
"@types/proper-lockfile": "^4.1.4",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"private": true
}

View File

@@ -1,4 +1,5 @@
import { vi } from "vitest";
import type { Mock } from "vitest";
import { installTaskWorktreeIdentityGuard } from "../worktree-hooks.js";
// Mock external dependencies
@@ -220,16 +221,18 @@ vi.mock("node:fs", () => ({
realpathSync: vi.fn((path: string) => path),
}));
export const mockExecuteAll = vi.fn().mockResolvedValue([]);
export const mockTerminateAllSessions = vi.fn().mockResolvedValue(undefined);
export const mockCleanup = vi.fn().mockResolvedValue(undefined);
export const mockExecuteAll: Mock<() => Promise<unknown[]>> = vi.fn().mockResolvedValue([]);
export const mockTerminateAllSessions: Mock<() => Promise<void>> = vi.fn().mockResolvedValue(undefined);
export const mockCleanup: Mock<() => Promise<void>> = vi.fn().mockResolvedValue(undefined);
vi.mock("../step-session-executor.js", () => ({
StepSessionExecutor: vi.fn().mockImplementation(() => ({
executeAll: mockExecuteAll,
terminateAllSessions: mockTerminateAllSessions,
cleanup: mockCleanup,
})),
StepSessionExecutor: vi.fn().mockImplementation(function () {
return {
executeAll: mockExecuteAll,
terminateAllSessions: mockTerminateAllSessions,
cleanup: mockCleanup,
};
}),
}));
vi.mock("../rate-limit-retry.js", () => ({
@@ -257,10 +260,12 @@ vi.mock("@earendil-works/pi-coding-agent", () => {
open: vi.fn().mockReturnValue(mockSessionManager),
inMemory: vi.fn().mockReturnValue(mockSessionManager),
},
ModelRegistry: vi.fn().mockImplementation(() => ({
find: vi.fn(),
refresh: vi.fn(),
})),
ModelRegistry: vi.fn().mockImplementation(function () {
return {
find: vi.fn(),
refresh: vi.fn(),
};
}),
AuthStorage: {
create: vi.fn().mockReturnValue({}),
},

View File

@@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import type { Settings, Task, TaskStore } from "@fusion/core";
import { GridlockDetector } from "../gridlock-detector.js";
import type { GridlockEvent } from "../gridlock-detector.js";
function createTask(id: string, overrides: Partial<Task> = {}): Task {
return {
@@ -34,8 +35,8 @@ describe("GridlockDetector", () => {
let tasks: Task[];
let settings: Settings;
let scopes: Record<string, string[]>;
let onGridlock: ReturnType<typeof vi.fn>;
let onGridlockCleared: ReturnType<typeof vi.fn>;
let onGridlock: ReturnType<typeof vi.fn<(event: GridlockEvent) => void>>;
let onGridlockCleared: ReturnType<typeof vi.fn<() => void>>;
let store: TaskStore;
let detector: GridlockDetector;

View File

@@ -4,6 +4,7 @@ import {
HeartbeatMonitor,
HeartbeatTriggerScheduler,
} from "../agent-heartbeat.js";
import type { TriggerCallback } from "../agent-heartbeat.js";
import type { AgentStore, TaskStore, Agent } from "@fusion/core";
import { createBudgetStatus } from "./heartbeat-test-helpers.js";
vi.mock("../logger.js", async () => {
@@ -18,7 +19,7 @@ import { heartbeatLog } from "../logger.js";
describe("HeartbeatTriggerScheduler", () => {
let store: AgentStore;
let callback: ReturnType<typeof vi.fn>;
let callback: ReturnType<typeof vi.fn<TriggerCallback>>;
let scheduler: import("../agent-heartbeat.js").HeartbeatTriggerScheduler;
beforeEach(() => {

View File

@@ -52,21 +52,27 @@ vi.mock("../logger.js", () => ({
}));
vi.mock("../stale-task-reporter.js", () => ({
StaleTaskReporter: vi.fn().mockImplementation(() => ({
report: staleReporterReportMock,
})),
StaleTaskReporter: vi.fn().mockImplementation(function () {
return {
report: staleReporterReportMock,
};
}),
}));
vi.mock("../backlog-pressure-reporter.js", () => ({
BacklogPressureReporter: vi.fn().mockImplementation(() => ({
report: backlogPressureReporterReportMock,
})),
BacklogPressureReporter: vi.fn().mockImplementation(function () {
return {
report: backlogPressureReporterReportMock,
};
}),
}));
vi.mock("../unlinked-missions-advisory-reporter.js", () => ({
UnlinkedMissionsAdvisoryReporter: vi.fn().mockImplementation(() => ({
report: unlinkedMissionsAdvisoryReporterReportMock,
})),
UnlinkedMissionsAdvisoryReporter: vi.fn().mockImplementation(function () {
return {
report: unlinkedMissionsAdvisoryReporterReportMock,
};
}),
}));
// Helper to create mock tasks

View File

@@ -7891,6 +7891,11 @@ describe("SelfHealingManager reclaimSelfOwnedBranchConflicts", () => {
mockedIsUsableTaskWorktree.mockResolvedValue(true);
});
afterEach(() => {
manager.stop();
vi.restoreAllMocks();
});
it("reclaims fully-subsumed branch conflicts and emits subsumed audit metadata", async () => {
store = createMockStore({
getSettings: vi.fn().mockResolvedValue({ globalPause: false, enginePaused: false } as any),

View File

@@ -9,5 +9,6 @@
"node-pty": ["./src/types/node-pty/index.d.ts"]
}
},
"include": ["src/**/*"]
"include": ["src/**/*"],
"exclude": ["src/__tests__/**/*"]
}

View File

@@ -21,7 +21,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
// Enable isolate to allow parallel execution of tests with conflicting mocks
isolate: true,
@@ -117,7 +117,9 @@ export default defineConfig({
// SQLite rowid interleaving (e.g. FN-5521 hit
// `expected 24 to be less than 19` in merge-reuse-task-worktree).
// Serialize at the file level; within-file order is already linear.
poolOptions: { threads: { singleThread: true } },
minWorkers: 1,
maxWorkers: 1,
fileParallelism: false,
},
},
{
@@ -130,7 +132,9 @@ export default defineConfig({
// and inflating wall time further. Excluded from the default
// `pnpm test` lane; run via `pnpm test:slow` / `pnpm test:all`.
include: ["src/**/*.slow.test.ts"],
poolOptions: { threads: { singleThread: true } },
minWorkers: 1,
maxWorkers: 1,
fileParallelism: false,
},
},
],

View File

@@ -28,7 +28,7 @@
"devDependencies": {
"@types/node": "^25.5.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"engines": {
"node": ">=22.5.0"

View File

@@ -1,5 +1,8 @@
import { resolve } from "node:path";
import { defineConfig } from "vitest/config";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
const maxWorkers = computeMaxWorkers();
export default defineConfig({
resolve: {
@@ -10,5 +13,9 @@ export default defineConfig({
},
test: {
include: ["src/**/*.test.ts"],
setupFiles: ["../core/src/__test-utils__/vitest-setup.ts"],
globalSetup: ["../core/src/__test-utils__/vitest-teardown.ts"],
maxWorkers,
minWorkers: 1,
},
});

View File

@@ -36,6 +36,6 @@
"@types/node": "^22.0.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
}
}

View File

@@ -11,7 +11,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
},
});

View File

@@ -26,7 +26,7 @@
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
"vitest": "^4.1.0"
},
"scripts": {
"test": "vitest run --reporter=dot",

View File

@@ -14,7 +14,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
coverage: {
provider: "v8",

View File

@@ -27,7 +27,7 @@
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
"vitest": "^4.1.0"
},
"scripts": {
"test": "vitest run --reporter=dot",

View File

@@ -11,7 +11,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
},
});

View File

@@ -32,7 +32,7 @@
"devDependencies": {
"@types/node": "^25.5.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"dependencies": {
"@fusion/core": "workspace:*"

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -23,6 +23,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -23,6 +23,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -23,6 +23,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -23,6 +23,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -37,6 +37,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -20,6 +20,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -38,6 +38,6 @@
"@types/node": "^25.5.2",
"@types/react": "^19.0.0",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -32,6 +32,6 @@
"@types/react": "^19.0.0",
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -43,8 +43,8 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
/** A factory exposing the onProgress hook and a controllable nextEvent. */
function progressFactory(nextEvent: () => Promise<InteractiveAiSessionEvent>) {
const captured: { progress?: (e: InteractiveAiSessionProgressEvent) => void; dispose: ReturnType<typeof vi.fn> } = {
dispose: vi.fn(),
const captured: { progress?: (e: InteractiveAiSessionProgressEvent) => void; dispose: ReturnType<typeof vi.fn<() => void>> } = {
dispose: vi.fn<() => void>(),
};
const factory: CreateInteractiveAiSessionFactory = vi.fn(async (opts) => {
captured.progress = opts.onProgress;

View File

@@ -38,7 +38,7 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
projects: [
{
extends: true,

View File

@@ -33,6 +33,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}
}

View File

@@ -31,6 +31,6 @@
"react": "^19.0.0",
"react-dom": "^19.2.4",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}
}

View File

@@ -34,6 +34,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -33,6 +33,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -20,6 +20,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -18,6 +18,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -28,6 +28,6 @@
"@types/node": "^25.5.2",
"better-sqlite3": "^12.4.1",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -18,6 +18,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -27,6 +27,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -27,6 +27,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -24,6 +24,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

View File

@@ -37,6 +37,6 @@
"@types/react": "^19.0.0",
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -83,9 +83,9 @@ describe("runReviewPanel", () => {
if (systemPrompt.includes("Operations")) {
return {
session: {
prompt: vi.fn(async () => new Promise(() => {})),
prompt: vi.fn(async () => new Promise<void>(() => {})),
state: { messages: [] },
dispose: vi.fn(),
dispose: vi.fn<() => void>(),
},
};
}
@@ -93,7 +93,7 @@ describe("runReviewPanel", () => {
session: {
prompt: vi.fn(async () => {}),
state: { messages: [{ role: "assistant", content: JSON.stringify({ verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [] }) }] },
dispose: vi.fn(),
dispose: vi.fn<() => void>(),
},
};
});

View File

@@ -25,7 +25,7 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
projects: [
{
extends: true,

View File

@@ -43,6 +43,6 @@
"@types/node": "^25.5.2",
"@types/react": "^19.0.0",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -16,7 +16,7 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
projects: [
{
extends: true,

View File

@@ -29,6 +29,6 @@
"devDependencies": {
"@types/node": "^25.5.2",
"typescript": "^5.7.0",
"vitest": "^3.2.4"
"vitest": "^4.1.0"
}
}

View File

@@ -18,6 +18,6 @@ export default defineConfig({
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
},
});

626
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
import assert from "node:assert/strict";
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import test from "node:test";
import YAML from "yaml";
const root = process.cwd();
const dependencyFloors = [
{ name: "protobufjs", minimum: "7.5.5", manifestSections: ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"], lockfileMatcher: /^protobufjs@(.*)$/ },
{ name: "vitest", minimum: "4.1.0", manifestSections: ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"], lockfileMatcher: /^vitest@(.*)$/ },
{ name: "@vitest/coverage-v8", minimum: "4.1.0", manifestSections: ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"], lockfileMatcher: /^@vitest\/coverage-v8@(.*)$/ },
];
const manifestRoots = ["packages", "plugins"];
const ignoredPathParts = new Set(["node_modules", "dist", "build", "coverage", ".turbo"]);
function compareVersions(a, b) {
const pa = a.split(".").map((part) => Number.parseInt(part, 10));
const pb = b.split(".").map((part) => Number.parseInt(part, 10));
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
const ai = Number.isFinite(pa[i]) ? pa[i] : 0;
const bi = Number.isFinite(pb[i]) ? pb[i] : 0;
if (ai !== bi) return ai - bi;
}
return 0;
}
function minVersionFromRange(range) {
const version = String(range).match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/u)?.[0];
return version?.split(/[+-]/u)[0] ?? null;
}
function assertRangeMeetsFloor({ location, name, range, minimum }) {
const minVersion = minVersionFromRange(range);
assert.ok(minVersion, `${location}: ${name} range ${range} must include an explicit semver version`);
assert.ok(compareVersions(minVersion, minimum) >= 0, `${location}: ${name} range ${range} is below required floor ${minimum}`);
}
async function collectPackageManifests(dir) {
const entries = await readdir(dir, { withFileTypes: true });
const manifests = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (ignoredPathParts.has(entry.name)) continue;
if (entry.isDirectory()) {
manifests.push(...(await collectPackageManifests(fullPath)));
} else if (entry.name === "package.json") {
manifests.push(fullPath);
}
}
return manifests;
}
async function sourceManifestPaths() {
const manifests = [path.join(root, "package.json")];
for (const manifestRoot of manifestRoots) {
manifests.push(...(await collectPackageManifests(path.join(root, manifestRoot))));
}
return manifests.sort();
}
test("source manifests keep vulnerable dependency floors out", async () => {
for (const manifestPath of await sourceManifestPaths()) {
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
const relativePath = path.relative(root, manifestPath);
for (const floor of dependencyFloors) {
for (const section of floor.manifestSections) {
const range = manifest[section]?.[floor.name];
if (range) assertRangeMeetsFloor({ location: `${relativePath} ${section}`, name: floor.name, range, minimum: floor.minimum });
}
}
}
});
test("pnpm overrides pin transitive protobufjs to a safe floor", async () => {
const manifest = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
assertRangeMeetsFloor({ location: "package.json pnpm.overrides", name: "protobufjs", range: manifest.pnpm?.overrides?.protobufjs, minimum: "7.5.5" });
});
test("lockfile resolutions satisfy dependency security floors", async () => {
const lockfile = YAML.parse(await readFile(path.join(root, "pnpm-lock.yaml"), "utf8"));
const packageKeys = Object.keys(lockfile.packages ?? {});
for (const floor of dependencyFloors) {
const matches = packageKeys
.map((key) => key.replace(/^\//u, ""))
.map((key) => key.match(floor.lockfileMatcher)?.[1])
.filter(Boolean)
.map((suffix) => suffix.split("(")[0]);
assert.ok(matches.length > 0, `pnpm-lock.yaml must include at least one ${floor.name} resolution`);
for (const version of matches) {
assert.ok(compareVersions(version, floor.minimum) >= 0, `pnpm-lock.yaml resolves ${floor.name}@${version}, below required floor ${floor.minimum}`);
}
}
});