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:
5
.changeset/fn-6042-security-dependencies.md
Normal file
5
.changeset/fn-6042-security-dependencies.md
Normal 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.
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## Essential rules
|
## 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
|
### 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/*`.
|
- 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/*`.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ COPY packages/dashboard/package.json ./packages/dashboard/package.json
|
|||||||
COPY packages/desktop/package.json ./packages/desktop/package.json
|
COPY packages/desktop/package.json ./packages/desktop/package.json
|
||||||
COPY packages/droid-cli/package.json ./packages/droid-cli/package.json
|
COPY packages/droid-cli/package.json ./packages/droid-cli/package.json
|
||||||
COPY packages/engine/package.json ./packages/engine/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/mobile/package.json ./packages/mobile/package.json
|
||||||
COPY packages/pi-claude-cli/package.json ./packages/pi-claude-cli/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
|
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-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-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/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-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-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
|
COPY plugins/fusion-plugin-cli-printing-press/package.json ./plugins/fusion-plugin-cli-printing-press/package.json
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ A comprehensive guide to creating Fusion plugins that extend the task board with
|
|||||||
14. [Example Plugins](#14-example-plugins)
|
14. [Example Plugins](#14-example-plugins)
|
||||||
15. [Registering Skills](#15-registering-skills)
|
15. [Registering Skills](#15-registering-skills)
|
||||||
16. [Registering Workflow Steps](#16-registering-workflow-steps)
|
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)
|
17. [Contributing Prompt Modifications](#17-contributing-prompt-modifications)
|
||||||
18. [Plugin Binary Setup Hooks](#18-plugin-binary-setup-hooks)
|
18. [Plugin Binary Setup Hooks](#18-plugin-binary-setup-hooks)
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,8 @@
|
|||||||
"protobufjs"
|
"protobufjs"
|
||||||
],
|
],
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/node": "^25.5.2"
|
"@types/node": "^25.5.2",
|
||||||
|
"protobufjs": "^7.5.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"@types/react": "^19.2.0",
|
"@types/react": "^19.2.0",
|
||||||
"@types/ws": "^8.5.0",
|
"@types/ws": "^8.5.0",
|
||||||
"@vitest/coverage-v8": "^3.1.0",
|
"@vitest/coverage-v8": "^4.1.0",
|
||||||
"cross-env": "^7.0.0",
|
"cross-env": "^7.0.0",
|
||||||
"esbuild": "^0.25.12",
|
"esbuild": "^0.25.12",
|
||||||
"ink-testing-library": "^4.0.0",
|
"ink-testing-library": "^4.0.0",
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
"tsx": "^4.19.0",
|
"tsx": "^4.19.0",
|
||||||
"typebox": "^1.0.0",
|
"typebox": "^1.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.1.0",
|
"vitest": "^4.1.0",
|
||||||
"yaml": "^2.8.3"
|
"yaml": "^2.8.3"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
coverage: {
|
coverage: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@@ -40,9 +40,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dockerode": "^3.3.41",
|
"@types/dockerode": "^3.3.41",
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.5.0",
|
||||||
"@vitest/coverage-v8": "^3.1.0",
|
"@vitest/coverage-v8": "^4.1.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.5.0"
|
"node": ">=22.5.0"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default defineConfig({
|
|||||||
// and cannot safely share mutable process state under worker_threads.
|
// and cannot safely share mutable process state under worker_threads.
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
// Core runs a large SQLite-heavy suite while other workspace packages test concurrently.
|
// Core runs a large SQLite-heavy suite while other workspace packages test concurrently.
|
||||||
// Use a slightly higher timeout to reduce nondeterministic slow-machine flakes.
|
// Use a slightly higher timeout to reduce nondeterministic slow-machine flakes.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function getFallback(name: string): AnyFn {
|
|||||||
return fallbackFns.get(name)!;
|
return fallbackFns.get(name)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dashboardApiMocks = {
|
export const dashboardApiMocks: Record<string, AnyFn> = {
|
||||||
fetchTasks: vi.fn(async () => []),
|
fetchTasks: vi.fn(async () => []),
|
||||||
fetchSettings: vi.fn(async () => ({})),
|
fetchSettings: vi.fn(async () => ({})),
|
||||||
updateSettings: vi.fn(async () => ({})),
|
updateSettings: vi.fn(async () => ({})),
|
||||||
@@ -34,7 +34,7 @@ export const dashboardApiMocks = {
|
|||||||
fetchTaskDetail: vi.fn(),
|
fetchTaskDetail: vi.fn(),
|
||||||
fetchTaskReview: vi.fn(),
|
fetchTaskReview: vi.fn(),
|
||||||
fetchUnreadCount: vi.fn(async () => ({ unreadCount: 0 })),
|
fetchUnreadCount: vi.fn(async () => ({ unreadCount: 0 })),
|
||||||
} satisfies Record<string, AnyFn>;
|
};
|
||||||
|
|
||||||
export async function createDashboardApiMock(
|
export async function createDashboardApiMock(
|
||||||
importActual: () => Promise<AnyModule>,
|
importActual: () => Promise<AnyModule>,
|
||||||
|
|||||||
@@ -147,12 +147,12 @@
|
|||||||
"@types/react-dom": "^19.0.0",
|
"@types/react-dom": "^19.0.0",
|
||||||
"@types/ws": "^8.5.0",
|
"@types/ws": "^8.5.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"@vitest/coverage-v8": "^3.1.0",
|
"@vitest/coverage-v8": "^4.1.0",
|
||||||
"jsdom": "^29.0.1",
|
"jsdom": "^29.0.1",
|
||||||
"tinyrainbow": "^2.0.0",
|
"tinyrainbow": "^2.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ export default defineConfig({
|
|||||||
// React suites; forks duplicated the entire renderer per worker (~500MB).
|
// React suites; forks duplicated the entire renderer per worker (~500MB).
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
isolate: true,
|
isolate: true,
|
||||||
// Dashboard route and integration-heavy suites can exceed the Vitest
|
// Dashboard route and integration-heavy suites can exceed the Vitest
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-dom": "^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": "^35.0.0",
|
||||||
"electron-builder": "^26.0.0",
|
"electron-builder": "^26.0.0",
|
||||||
"esbuild": "^0.25.12",
|
"esbuild": "^0.25.12",
|
||||||
@@ -57,6 +57,6 @@
|
|||||||
"tsx": "^4.19.0",
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
|||||||
hookTimeout: 30_000,
|
hookTimeout: 30_000,
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
passWithNoTests: true,
|
passWithNoTests: true,
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run --reporter=dot",
|
"test": "vitest run --reporter=dot",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: "v8",
|
provider: "v8",
|
||||||
|
|||||||
@@ -52,9 +52,9 @@
|
|||||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.5.0",
|
||||||
"@types/proper-lockfile": "^4.1.4",
|
"@types/proper-lockfile": "^4.1.4",
|
||||||
"@vitest/coverage-v8": "^3.1.0",
|
"@vitest/coverage-v8": "^4.1.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
|
import type { Mock } from "vitest";
|
||||||
import { installTaskWorktreeIdentityGuard } from "../worktree-hooks.js";
|
import { installTaskWorktreeIdentityGuard } from "../worktree-hooks.js";
|
||||||
|
|
||||||
// Mock external dependencies
|
// Mock external dependencies
|
||||||
@@ -220,16 +221,18 @@ vi.mock("node:fs", () => ({
|
|||||||
realpathSync: vi.fn((path: string) => path),
|
realpathSync: vi.fn((path: string) => path),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const mockExecuteAll = vi.fn().mockResolvedValue([]);
|
export const mockExecuteAll: Mock<() => Promise<unknown[]>> = vi.fn().mockResolvedValue([]);
|
||||||
export const mockTerminateAllSessions = vi.fn().mockResolvedValue(undefined);
|
export const mockTerminateAllSessions: Mock<() => Promise<void>> = vi.fn().mockResolvedValue(undefined);
|
||||||
export const mockCleanup = vi.fn().mockResolvedValue(undefined);
|
export const mockCleanup: Mock<() => Promise<void>> = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
vi.mock("../step-session-executor.js", () => ({
|
vi.mock("../step-session-executor.js", () => ({
|
||||||
StepSessionExecutor: vi.fn().mockImplementation(() => ({
|
StepSessionExecutor: vi.fn().mockImplementation(function () {
|
||||||
executeAll: mockExecuteAll,
|
return {
|
||||||
terminateAllSessions: mockTerminateAllSessions,
|
executeAll: mockExecuteAll,
|
||||||
cleanup: mockCleanup,
|
terminateAllSessions: mockTerminateAllSessions,
|
||||||
})),
|
cleanup: mockCleanup,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../rate-limit-retry.js", () => ({
|
vi.mock("../rate-limit-retry.js", () => ({
|
||||||
@@ -257,10 +260,12 @@ vi.mock("@earendil-works/pi-coding-agent", () => {
|
|||||||
open: vi.fn().mockReturnValue(mockSessionManager),
|
open: vi.fn().mockReturnValue(mockSessionManager),
|
||||||
inMemory: vi.fn().mockReturnValue(mockSessionManager),
|
inMemory: vi.fn().mockReturnValue(mockSessionManager),
|
||||||
},
|
},
|
||||||
ModelRegistry: vi.fn().mockImplementation(() => ({
|
ModelRegistry: vi.fn().mockImplementation(function () {
|
||||||
find: vi.fn(),
|
return {
|
||||||
refresh: vi.fn(),
|
find: vi.fn(),
|
||||||
})),
|
refresh: vi.fn(),
|
||||||
|
};
|
||||||
|
}),
|
||||||
AuthStorage: {
|
AuthStorage: {
|
||||||
create: vi.fn().mockReturnValue({}),
|
create: vi.fn().mockReturnValue({}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||||
import type { Settings, Task, TaskStore } from "@fusion/core";
|
import type { Settings, Task, TaskStore } from "@fusion/core";
|
||||||
import { GridlockDetector } from "../gridlock-detector.js";
|
import { GridlockDetector } from "../gridlock-detector.js";
|
||||||
|
import type { GridlockEvent } from "../gridlock-detector.js";
|
||||||
|
|
||||||
function createTask(id: string, overrides: Partial<Task> = {}): Task {
|
function createTask(id: string, overrides: Partial<Task> = {}): Task {
|
||||||
return {
|
return {
|
||||||
@@ -34,8 +35,8 @@ describe("GridlockDetector", () => {
|
|||||||
let tasks: Task[];
|
let tasks: Task[];
|
||||||
let settings: Settings;
|
let settings: Settings;
|
||||||
let scopes: Record<string, string[]>;
|
let scopes: Record<string, string[]>;
|
||||||
let onGridlock: ReturnType<typeof vi.fn>;
|
let onGridlock: ReturnType<typeof vi.fn<(event: GridlockEvent) => void>>;
|
||||||
let onGridlockCleared: ReturnType<typeof vi.fn>;
|
let onGridlockCleared: ReturnType<typeof vi.fn<() => void>>;
|
||||||
let store: TaskStore;
|
let store: TaskStore;
|
||||||
let detector: GridlockDetector;
|
let detector: GridlockDetector;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
HeartbeatMonitor,
|
HeartbeatMonitor,
|
||||||
HeartbeatTriggerScheduler,
|
HeartbeatTriggerScheduler,
|
||||||
} from "../agent-heartbeat.js";
|
} from "../agent-heartbeat.js";
|
||||||
|
import type { TriggerCallback } from "../agent-heartbeat.js";
|
||||||
import type { AgentStore, TaskStore, Agent } from "@fusion/core";
|
import type { AgentStore, TaskStore, Agent } from "@fusion/core";
|
||||||
import { createBudgetStatus } from "./heartbeat-test-helpers.js";
|
import { createBudgetStatus } from "./heartbeat-test-helpers.js";
|
||||||
vi.mock("../logger.js", async () => {
|
vi.mock("../logger.js", async () => {
|
||||||
@@ -18,7 +19,7 @@ import { heartbeatLog } from "../logger.js";
|
|||||||
|
|
||||||
describe("HeartbeatTriggerScheduler", () => {
|
describe("HeartbeatTriggerScheduler", () => {
|
||||||
let store: AgentStore;
|
let store: AgentStore;
|
||||||
let callback: ReturnType<typeof vi.fn>;
|
let callback: ReturnType<typeof vi.fn<TriggerCallback>>;
|
||||||
let scheduler: import("../agent-heartbeat.js").HeartbeatTriggerScheduler;
|
let scheduler: import("../agent-heartbeat.js").HeartbeatTriggerScheduler;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -52,21 +52,27 @@ vi.mock("../logger.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../stale-task-reporter.js", () => ({
|
vi.mock("../stale-task-reporter.js", () => ({
|
||||||
StaleTaskReporter: vi.fn().mockImplementation(() => ({
|
StaleTaskReporter: vi.fn().mockImplementation(function () {
|
||||||
report: staleReporterReportMock,
|
return {
|
||||||
})),
|
report: staleReporterReportMock,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../backlog-pressure-reporter.js", () => ({
|
vi.mock("../backlog-pressure-reporter.js", () => ({
|
||||||
BacklogPressureReporter: vi.fn().mockImplementation(() => ({
|
BacklogPressureReporter: vi.fn().mockImplementation(function () {
|
||||||
report: backlogPressureReporterReportMock,
|
return {
|
||||||
})),
|
report: backlogPressureReporterReportMock,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../unlinked-missions-advisory-reporter.js", () => ({
|
vi.mock("../unlinked-missions-advisory-reporter.js", () => ({
|
||||||
UnlinkedMissionsAdvisoryReporter: vi.fn().mockImplementation(() => ({
|
UnlinkedMissionsAdvisoryReporter: vi.fn().mockImplementation(function () {
|
||||||
report: unlinkedMissionsAdvisoryReporterReportMock,
|
return {
|
||||||
})),
|
report: unlinkedMissionsAdvisoryReporterReportMock,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Helper to create mock tasks
|
// Helper to create mock tasks
|
||||||
|
|||||||
@@ -7891,6 +7891,11 @@ describe("SelfHealingManager reclaimSelfOwnedBranchConflicts", () => {
|
|||||||
mockedIsUsableTaskWorktree.mockResolvedValue(true);
|
mockedIsUsableTaskWorktree.mockResolvedValue(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
manager.stop();
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it("reclaims fully-subsumed branch conflicts and emits subsumed audit metadata", async () => {
|
it("reclaims fully-subsumed branch conflicts and emits subsumed audit metadata", async () => {
|
||||||
store = createMockStore({
|
store = createMockStore({
|
||||||
getSettings: vi.fn().mockResolvedValue({ globalPause: false, enginePaused: false } as any),
|
getSettings: vi.fn().mockResolvedValue({ globalPause: false, enginePaused: false } as any),
|
||||||
|
|||||||
@@ -9,5 +9,6 @@
|
|||||||
"node-pty": ["./src/types/node-pty/index.d.ts"]
|
"node-pty": ["./src/types/node-pty/index.d.ts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"]
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["src/__tests__/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
// Enable isolate to allow parallel execution of tests with conflicting mocks
|
// Enable isolate to allow parallel execution of tests with conflicting mocks
|
||||||
isolate: true,
|
isolate: true,
|
||||||
@@ -117,7 +117,9 @@ export default defineConfig({
|
|||||||
// SQLite rowid interleaving (e.g. FN-5521 hit
|
// SQLite rowid interleaving (e.g. FN-5521 hit
|
||||||
// `expected 24 to be less than 19` in merge-reuse-task-worktree).
|
// `expected 24 to be less than 19` in merge-reuse-task-worktree).
|
||||||
// Serialize at the file level; within-file order is already linear.
|
// 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
|
// and inflating wall time further. Excluded from the default
|
||||||
// `pnpm test` lane; run via `pnpm test:slow` / `pnpm test:all`.
|
// `pnpm test` lane; run via `pnpm test:slow` / `pnpm test:all`.
|
||||||
include: ["src/**/*.slow.test.ts"],
|
include: ["src/**/*.slow.test.ts"],
|
||||||
poolOptions: { threads: { singleThread: true } },
|
minWorkers: 1,
|
||||||
|
maxWorkers: 1,
|
||||||
|
fileParallelism: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.5.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.5.0"
|
"node": ">=22.5.0"
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { defineConfig } from "vitest/config";
|
import { defineConfig } from "vitest/config";
|
||||||
|
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
|
||||||
|
|
||||||
|
const maxWorkers = computeMaxWorkers();
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -10,5 +13,9 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
include: ["src/**/*.test.ts"],
|
include: ["src/**/*.test.ts"],
|
||||||
|
setupFiles: ["../core/src/__test-utils__/vitest-setup.ts"],
|
||||||
|
globalSetup: ["../core/src/__test-utils__/vitest-teardown.ts"],
|
||||||
|
maxWorkers,
|
||||||
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,6 +36,6 @@
|
|||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"tsx": "^4.19.0",
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run --reporter=dot",
|
"test": "vitest run --reporter=dot",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: "v8",
|
provider: "v8",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run --reporter=dot",
|
"test": "vitest run --reporter=dot",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
fileParallelism: true,
|
fileParallelism: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.5.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.1.0"
|
"vitest": "^4.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fusion/core": "workspace:*"
|
"@fusion/core": "workspace:*"
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,6 +38,6 @@
|
|||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,6 +32,6 @@
|
|||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|||||||
|
|
||||||
/** A factory exposing the onProgress hook and a controllable nextEvent. */
|
/** A factory exposing the onProgress hook and a controllable nextEvent. */
|
||||||
function progressFactory(nextEvent: () => Promise<InteractiveAiSessionEvent>) {
|
function progressFactory(nextEvent: () => Promise<InteractiveAiSessionEvent>) {
|
||||||
const captured: { progress?: (e: InteractiveAiSessionProgressEvent) => void; dispose: ReturnType<typeof vi.fn> } = {
|
const captured: { progress?: (e: InteractiveAiSessionProgressEvent) => void; dispose: ReturnType<typeof vi.fn<() => void>> } = {
|
||||||
dispose: vi.fn(),
|
dispose: vi.fn<() => void>(),
|
||||||
};
|
};
|
||||||
const factory: CreateInteractiveAiSessionFactory = vi.fn(async (opts) => {
|
const factory: CreateInteractiveAiSessionFactory = vi.fn(async (opts) => {
|
||||||
captured.progress = opts.onProgress;
|
captured.progress = opts.onProgress;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
extends: true,
|
extends: true,
|
||||||
|
|||||||
@@ -33,6 +33,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,6 @@
|
|||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,6 +33,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,6 +28,6 @@
|
|||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"better-sqlite3": "^12.4.1",
|
"better-sqlite3": "^12.4.1",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,6 +27,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,6 +27,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,6 @@
|
|||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ describe("runReviewPanel", () => {
|
|||||||
if (systemPrompt.includes("Operations")) {
|
if (systemPrompt.includes("Operations")) {
|
||||||
return {
|
return {
|
||||||
session: {
|
session: {
|
||||||
prompt: vi.fn(async () => new Promise(() => {})),
|
prompt: vi.fn(async () => new Promise<void>(() => {})),
|
||||||
state: { messages: [] },
|
state: { messages: [] },
|
||||||
dispose: vi.fn(),
|
dispose: vi.fn<() => void>(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ describe("runReviewPanel", () => {
|
|||||||
session: {
|
session: {
|
||||||
prompt: vi.fn(async () => {}),
|
prompt: vi.fn(async () => {}),
|
||||||
state: { messages: [{ role: "assistant", content: JSON.stringify({ verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [] }) }] },
|
state: { messages: [{ role: "assistant", content: JSON.stringify({ verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [] }) }] },
|
||||||
dispose: vi.fn(),
|
dispose: vi.fn<() => void>(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
extends: true,
|
extends: true,
|
||||||
|
|||||||
@@ -43,6 +43,6 @@
|
|||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
extends: true,
|
extends: true,
|
||||||
|
|||||||
@@ -29,6 +29,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ export default defineConfig({
|
|||||||
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
globalSetup: [fileURLToPath(new URL("../../packages/core/src/__test-utils__/vitest-teardown.ts", import.meta.url))],
|
||||||
pool: "threads",
|
pool: "threads",
|
||||||
maxWorkers,
|
maxWorkers,
|
||||||
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers }, forks: { minForks: 1, maxForks: maxWorkers } },
|
minWorkers: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
626
pnpm-lock.yaml
generated
626
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
95
scripts/__tests__/dependency-security-floor.test.mjs
Normal file
95
scripts/__tests__/dependency-security-floor.test.mjs
Normal 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user