diff --git a/.github/workflows/pr-trusted.yml b/.github/workflows/pr-trusted.yml index 1231280351f..18246ba0de7 100644 --- a/.github/workflows/pr-trusted.yml +++ b/.github/workflows/pr-trusted.yml @@ -446,6 +446,9 @@ jobs: - name: Typecheck workspaces whose build scripts skip TypeScript run: pnpm run typecheck:build-gaps + - name: Typecheck test files that no build reads + run: node scripts/typecheck-test-programs.mjs --low-memory + - name: Typecheck the server in low-memory layers env: PAPERCLIP_TYPECHECK_LOW_MEMORY: "1" diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index 054b8317510..5704355c9fc 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -1319,6 +1319,40 @@ that file, and it fails when a referenced config does not exist yet. The program. That program peaks at about 15 GiB at default settings and about 8 GiB on one thread. +#### Typecheck test files outside the build + +Most package tsconfigs exclude tests, and Vitest strips types, so a type error in +one of those tests fails nothing. `scripts/typecheck-test-programs.mjs` reads them +through separate programs, usually a `tsconfig.test.json` beside the package's +`tsconfig.json`: + +```sh +node scripts/typecheck-test-programs.mjs --low-memory +node scripts/typecheck-test-programs.mjs packages/paperclip-runner/tsconfig.test.json +``` + +The first form runs in the required `Typecheck + Release Registry` job. It checks +one program at a time and the largest peaks below 2 GiB. It then fails if a tracked +`*.test.ts(x)` or `*.spec.ts(x)` file is in no program. The compiler's file list +decides that, because `include` silently drops `foo.test.tsx` when `foo.test.ts` +exists. + +To add a test directory, add it to the `include` of the package's +`tsconfig.test.json`. To add a package, add a program to `PROGRAMS` in the script. + +Sandbox providers are outside the root workspace, so the root install has none of +their SDKs. `scripts/typecheck-sandbox-providers` is a small workspace that lists +them and holds one lockfile. Its install replaces any standalone `node_modules` in +those providers. After changing a provider's `package.json`, refresh that lockfile: + +```sh +pnpm -C scripts/typecheck-sandbox-providers install --ignore-scripts --lockfile-only +``` + +Do not commit a lockfile inside a provider. +`scripts/build-standalone-public-packages.mjs` uses a frozen install when one +exists, which changes what a release resolves. + #### Reused Runner build in the server build The server build vendors `packages/paperclip-runner/dist`, so its diff --git a/package.json b/package.json index d05ed21d7ad..8ea83e18e59 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "smoke:notion-generic-live": "node scripts/smoke/notion-generic-live.mjs", "smoke:posthog-live": "node scripts/smoke/posthog-live.mjs", "smoke:terminal-bench-loop-skill": "node scripts/smoke/terminal-bench-loop-skill-smoke.mjs", - "test:release-registry": "node scripts/ci-profile/node-test.mjs scripts/ci-profile/reporters.test.mjs scripts/__tests__/canary-startup-playwright.test.mjs scripts/__tests__/cursor-public-install-sandbox.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/public-install-closure.test.mjs scripts/__tests__/public-install-registry.test.mjs scripts/__tests__/prefetch-child.test.mjs scripts/__tests__/stage-bundled-packages.test.mjs scripts/__tests__/pack-staged-packages.test.mjs scripts/__tests__/retain-runner-qualification-packages.test.mjs scripts/__tests__/runner-release-crate-cache.test.mjs scripts/__tests__/runner-debug-crate-cache.test.mjs scripts/__tests__/vitest-worker-isolation.test.mjs scripts/verify-release-registry-state.test.mjs scripts/release-package-map.test.mjs scripts/check-release-package-bootstrap.test.mjs scripts/check-no-git-push.test.mjs scripts/release-lib.test.mjs scripts/release-registry-versions.test.mjs scripts/link-plugin-dev-sdk.test.js scripts/acpx-patch-packaging.test.mjs scripts/service-onboard-smoke.test.mjs scripts/docker-onboard-smoke.test.mjs scripts/preview-artifacts.test.mjs scripts/cloud-migrator-artifacts.test.mjs scripts/__tests__/local-verify.test.mjs", + "test:release-registry": "node scripts/ci-profile/node-test.mjs scripts/ci-profile/reporters.test.mjs scripts/__tests__/canary-startup-playwright.test.mjs scripts/__tests__/cursor-public-install-sandbox.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/public-install-closure.test.mjs scripts/__tests__/public-install-registry.test.mjs scripts/__tests__/prefetch-child.test.mjs scripts/__tests__/stage-bundled-packages.test.mjs scripts/__tests__/pack-staged-packages.test.mjs scripts/__tests__/retain-runner-qualification-packages.test.mjs scripts/__tests__/runner-release-crate-cache.test.mjs scripts/__tests__/runner-debug-crate-cache.test.mjs scripts/__tests__/vitest-worker-isolation.test.mjs scripts/verify-release-registry-state.test.mjs scripts/release-package-map.test.mjs scripts/check-release-package-bootstrap.test.mjs scripts/check-no-git-push.test.mjs scripts/release-lib.test.mjs scripts/release-registry-versions.test.mjs scripts/link-plugin-dev-sdk.test.js scripts/acpx-patch-packaging.test.mjs scripts/service-onboard-smoke.test.mjs scripts/docker-onboard-smoke.test.mjs scripts/preview-artifacts.test.mjs scripts/cloud-migrator-artifacts.test.mjs scripts/typecheck-test-programs.test.mjs scripts/__tests__/local-verify.test.mjs", "storybook-visual:baseline": "node scripts/storybook-visual-baseline.mjs", "test:storybook-visual": "node scripts/storybook-visual-baseline.mjs download && node scripts/storybook-visual-baseline.mjs verify && pnpm build-storybook && npx playwright test --config tests/storybook-visual/playwright.config.ts", "test:storybook-visual:update": "node scripts/storybook-visual-baseline.mjs download && pnpm build-storybook && npx playwright test --config tests/storybook-visual/playwright.config.ts --update-snapshots && node scripts/storybook-visual-baseline.mjs pack", diff --git a/packages/db/tsconfig.test.json b/packages/db/tsconfig.test.json new file mode 100644 index 00000000000..0308a876ded --- /dev/null +++ b/packages/db/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "." + }, + "include": ["scripts/**/*.test.ts"] +} diff --git a/packages/kv-demo-mcp-server/tsconfig.test.json b/packages/kv-demo-mcp-server/tsconfig.test.json new file mode 100644 index 00000000000..1e36832b11d --- /dev/null +++ b/packages/kv-demo-mcp-server/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "exclude": [] +} diff --git a/packages/paperclip-runner/src/backends/harness-driver-backend.test.ts b/packages/paperclip-runner/src/backends/harness-driver-backend.test.ts index 6791372425a..bc1193167e7 100644 --- a/packages/paperclip-runner/src/backends/harness-driver-backend.test.ts +++ b/packages/paperclip-runner/src/backends/harness-driver-backend.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest"; import type { HarnessDriver, HarnessSession, PersistedHarnessSession } from "../contracts/harness-driver.js"; import { PROVIDER_STATE_INCOMPATIBLE, runnerRecoveryFailureClass } from "../contracts/harness-driver.js"; import type { PrpEvent, PrpStructuredRunResult, PrpTerminalState } from "../protocol/replay-contract.js"; -import { NativeSessionProtocolIntegrityError } from "../contracts/native-session-backend.js"; +import { NativeSessionProtocolIntegrityError, type NativeSession } from "../contracts/native-session-backend.js"; import { HarnessDriverBackend } from "./harness-driver-backend.js"; const result: PrpStructuredRunResult = { @@ -55,8 +55,13 @@ function prpEvent(sourceSeq: number, eventType: PrpEvent["eventType"], payload: const runtimeResolutions: unknown[] = []; +function cancelSession(session: NativeSession, input: Parameters>[0]) { + if (!session.cancel) throw new Error("backend session does not support cancellation"); + return session.cancel(input); +} + class FakeHarnessSession implements HarnessSession { - ids() { return { driverSessionId: "driver-1", providerSessionId: "provider-1" }; } + ids(): ReturnType { return { driverSessionId: "driver-1", providerSessionId: "provider-1" }; } async *events() { yield prpEvent(1, "run.result.proposed", result); yield prpEvent(2, "turn.completed", { status: "completed" }); @@ -942,7 +947,7 @@ describe("HarnessDriverBackend", () => { ); class TerminalThenIntegrityFailure extends FakeHarnessSession { goal = vi.fn(async () => null); - steer = vi.fn(async () => ({ correlationId: "blocked-steer" })); + steer = vi.fn(async () => {}); override async *events() { yield* super.events(); throw fault; @@ -1068,8 +1073,8 @@ describe("HarnessDriverBackend", () => { const session = await backend.openSession({ identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" }, workingDirectory: "/workspace", }); - if (journalOnly) session.revokeTurnPublication(); - else await session.cancel({ reason: "durable governed wait", signal: new AbortController().signal }).cleanup; + if (journalOnly) session.revokeTurnPublication!(); + else await cancelSession(session, { reason: "durable governed wait", signal: new AbortController().signal }).cleanup; const events = []; for await (const event of session.events()) events.push(event); expect(events.filter(event => event.payload.kind === "agentMessage")).toHaveLength(0); @@ -1085,7 +1090,7 @@ describe("HarnessDriverBackend", () => { identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" }, workingDirectory: "/workspace", }); - await session.cancel({ reason: "user stop during startup", signal: new AbortController().signal }).cleanup; + await cancelSession(session, { reason: "user stop during startup", signal: new AbortController().signal }).cleanup; await expect(session.startTurn({ message: { role: "user", text: "cancelled work" } })) .rejects.toThrow("native_session_cancelled"); expect(start).not.toHaveBeenCalled(); @@ -1108,7 +1113,7 @@ describe("HarnessDriverBackend", () => { }); const iterator = session.events()[Symbol.asyncIterator](); await iterator.next(); - await session.cancel({ reason: "operator stop", signal: new AbortController().signal }).cleanup; + await cancelSession(session, { reason: "operator stop", signal: new AbortController().signal }).cleanup; await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.cancelled", sourceSeq: 3 } }); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "turn.failed", sourceSeq: 4 } }); await expect(iterator.next()).resolves.toMatchObject({ done: true }); @@ -1133,7 +1138,7 @@ describe("HarnessDriverBackend", () => { }); const iterator = session.events()[Symbol.asyncIterator](); await iterator.next(); - await expect(session.cancel({ reason: "operator stop", signal: new AbortController().signal }).cleanup).rejects.toThrow("no active provider turn"); + await expect(cancelSession(session, { reason: "operator stop", signal: new AbortController().signal }).cleanup).rejects.toThrow("no active provider turn"); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "turn.cancelled", sourceSeq: 2, payload: { status: "cancelled", providerTerminalState: "completed", reason: "cancelled_after_provider_completed" } } }); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "run.terminal", sourceSeq: 3, @@ -1166,7 +1171,7 @@ describe("HarnessDriverBackend", () => { }); const iterator = session.events()[Symbol.asyncIterator](); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.created" } }); - await session.cancel({ + await cancelSession(session, { reason: "operator cancelled the run", signal: new AbortController().signal, }).cleanup; diff --git a/packages/paperclip-runner/src/backends/native-backend-factory.test.ts b/packages/paperclip-runner/src/backends/native-backend-factory.test.ts index 1b551e20ccf..a0cf6f0e9b5 100644 --- a/packages/paperclip-runner/src/backends/native-backend-factory.test.ts +++ b/packages/paperclip-runner/src/backends/native-backend-factory.test.ts @@ -5,7 +5,7 @@ import { join } from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import * as codexSecurity from "../drivers/codex/codex-security-config.js"; -import { buildNativeModelEnvelope, parseNativeExecutionInput, type NativeExecutionInput } from "../contracts/native-execution.js"; +import { buildNativeModelEnvelope, parseNativeExecutionInput, type NativeExecutionInput, type NativeExecutionInputV1, type NativeExecutionInputV2 } from "../contracts/native-execution.js"; import { NATIVE_RUNTIME_ASSET_SCHEMA, PAPERCLIP_EXECUTION_PROMPT, PAPERCLIP_EXECUTION_PROMPT_REVISION, nativeRuntimePromptDigest, canonicalNativeRuntimeContextDigest, composeNativeSystemInstructions } from "../contracts/runtime-context.js"; const contextRoots: string[] = []; @@ -36,12 +36,12 @@ import { createCodexNativeSessionBackend, createRunnerdNativeSessionBackend } fr import { createOpenCodeNativeSessionBackend } from "./opencode-native-backend.js"; function execution( - provider: NativeExecutionInput["provider"] = { + provider: NativeExecutionInputV1["provider"] = { kind: "codex", model: null, approvalPolicy: "never", }, -): NativeExecutionInput { +): NativeExecutionInputV1 { return { schema: "paperclip.native-execution-input.v1", binding: { @@ -88,7 +88,7 @@ function execution( function acpxExecution( agent: "codex" | "pi" | "claude" = "codex", -): NativeExecutionInput { +): NativeExecutionInputV1 { return { ...execution(), session: { @@ -107,7 +107,7 @@ function acpxExecution( }; } -function opencodeExecution(): NativeExecutionInput { +function opencodeExecution(): NativeExecutionInputV1 { return { ...execution(), session: { @@ -124,7 +124,7 @@ function opencodeExecution(): NativeExecutionInput { }; } -function planningExecution(input: NativeExecutionInput): NativeExecutionInput { +function planningExecution(input: NativeExecutionInputV1): NativeExecutionInputV2 { return { ...input, schema: "paperclip.native-execution-input.v2", @@ -143,7 +143,7 @@ function planningExecution(input: NativeExecutionInput): NativeExecutionInput { function managedExecution( kind: "claude_managed" | "aws_agentcore", -): NativeExecutionInput { +): NativeExecutionInputV1 { if (kind === "claude_managed") { return { ...execution(), @@ -223,7 +223,7 @@ describe("native backend factory", () => { }); try { const backend = createNativeSessionBackend(input, { environment: {} }); - await expect(backend.openSession({ identity: { runId: "run", sessionId: "session", companyId: "company", agentId: "agent" }, + await expect(backend.openSession({ identity: { runId: "run", sessionId: "session", companyId: "company", issueId: "issue", agentId: "agent" }, workingDirectory: WORKSPACE })).rejects.toThrow("offline-stop-before-process-spawn"); expect(generated).toBeDefined(); if (webSearch) expect(generated?.slice(-3)).toEqual(["-c", 'web_search="disabled"', "app-server"]); @@ -404,7 +404,7 @@ describe("native backend factory", () => { expect(recovered).toMatchObject({ recovered: true }); expect(factory).toHaveBeenCalledWith(expect.objectContaining({ baseInstructions: retainedSystemInstructions })); expect(transport.calls.some(call => call.method === "thread/start" || call.method === "turn/start")).toBe(false); - if (recovered.recovered) await recovered.session.close({ reason: "test complete" }); + if (recovered.recovered) await recovered.session!.close({ reason: "test complete" }); }); describe("runner-prepared launch working directory", () => { @@ -420,7 +420,7 @@ describe("native backend factory", () => { return response; } } - const identity = { runId: "run", sessionId: "session", companyId: "company", agentId: "agent" }; + const identity = { runId: "run", sessionId: "session", companyId: "company", issueId: "issue", agentId: "agent" }; const providers = [ ["Codex", execution()], ["OpenCode", opencodeExecution()], @@ -452,8 +452,8 @@ describe("native backend factory", () => { }).recoverSession!({ ...snapshot, activeTurnId: null }, { signal: new AbortController().signal }); expect(recovered).toMatchObject({ recovered: true }); if (recovered.recovered) { - expect((await recovered.session.snapshot()).workingDirectory).toBe(cwd); - await recovered.session.close({ reason: "test complete" }); + expect((await recovered.session!.snapshot()).workingDirectory).toBe(cwd); + await recovered.session!.close({ reason: "test complete" }); } }); @@ -769,6 +769,7 @@ describe("native backend factory", () => { await backend.openSession({ identity: { companyId: "company", + issueId: "issue", agentId: "agent", runId: "run", sessionId: "session", @@ -807,7 +808,7 @@ describe("native backend factory", () => { environment: { PAPERCLIP_WORKSPACE_CWD: WORKSPACE }, }); const session = await backend.openSession({ - identity: { companyId: "company", agentId: "agent", runId: "run", sessionId: "session" }, + identity: { companyId: "company", issueId: "issue", agentId: "agent", runId: "run", sessionId: "session" }, workingDirectory: WORKSPACE, }); const first = JSON.stringify(buildNativeModelEnvelope(input)); diff --git a/packages/paperclip-runner/src/backends/native-instruction-measurement.test.ts b/packages/paperclip-runner/src/backends/native-instruction-measurement.test.ts index 2c5592af47e..ec1f00ec4bb 100644 --- a/packages/paperclip-runner/src/backends/native-instruction-measurement.test.ts +++ b/packages/paperclip-runner/src/backends/native-instruction-measurement.test.ts @@ -21,7 +21,7 @@ import { inspectNativeCompletionSourceMetadata } from "../../../../tests/runner- // It measures complete Paperclip-supplied instruction/tool/message projections, // not provider-owned stock prompts, tokenization, billing or model behavior. const tools = PAPERCLIP_SEMANTIC_ACTION_CATALOG - .filter(tool => tool.placement === "core") + .filter(tool => tool.placement === "always") .map(tool => ({ name: tool.operationId, description: tool.description, inputSchema: tool.inputSchema })); const identity = { companyId: "company", agentId: "agent", issueId: "issue", runId: "run", sessionId: "session" }; const { qualificationModel: _qualificationModel, reportedModelId: _reportedModelId, permissionPolicy: _permissionPolicy, ...profile } = resolveQualifiedAcpxProfile("claude", "claude-sonnet-5"); @@ -182,7 +182,7 @@ describe("direct OpenCode HTTP instruction boundary", () => { try { const checkpoint = await session.snapshot(); const send = async (current: typeof session, phase: string, value: NativeExecutionInput) => { - const envelope = buildNativeModelEnvelope(value, { resumedSession: phase === "continuation" }); + const envelope = phase === "continuation" ? buildNativeModelEnvelope(value, { resumedSession: true }) : buildNativeModelEnvelope(value); if ("requestedSkills" in envelope && backend.preparedTaskConstraints) envelope.constraints = [...backend.preparedTaskConstraints]; await current.startTurn({ message: { role: "user", text: JSON.stringify(envelope) } }); for await (const event of current.events()) { if (event.eventType === "turn.completed") break; if (["turn.failed", "turn.cancelled"].includes(event.eventType)) throw new Error(`Fixture turn failed: ${event.eventType}`); } diff --git a/packages/paperclip-runner/src/browser/client.test.ts b/packages/paperclip-runner/src/browser/client.test.ts index 27795f4dd78..99fdef4fead 100644 --- a/packages/paperclip-runner/src/browser/client.test.ts +++ b/packages/paperclip-runner/src/browser/client.test.ts @@ -54,16 +54,20 @@ describe("public runner browser client", () => { fetchImpl: vi.fn() as unknown as typeof fetch, eventSourceFactory: (nextUrl) => { url = nextUrl; return source; }, }); - const event = { + const event: PrpEvent = { schema: "paperclip.prp.event.v1", sourceEventId: "event-9", - sourceKind: "harness", + sourceKind: "runner", sourceInstanceId: "driver", sourceSeq: 9, + runId: "run-1", + normalizedSessionId: "session/id", + schemaVersion: 1, + priority: 2, emittedAt: "2026-08-08T00:00:00.000Z", eventType: "item.delta", payload: { text: "exact" }, - } as PrpEvent; + }; const onEvent = vi.fn(); const onError = vi.fn(); const handle = client.openEventStream("session/id", 8, { onEvent, onError }); diff --git a/packages/paperclip-runner/src/browser/transcript-model.test.ts b/packages/paperclip-runner/src/browser/transcript-model.test.ts index 2de5010907c..b2fd25af8a2 100644 --- a/packages/paperclip-runner/src/browser/transcript-model.test.ts +++ b/packages/paperclip-runner/src/browser/transcript-model.test.ts @@ -1,17 +1,17 @@ import { describe, expect, it } from "vitest"; -import { LiveConsoleScriptedDriver } from "../mock-core/live-console-scripted-driver"; +import { LiveConsoleScriptedDriver } from "../mock-core/live-console-scripted-driver.js"; import { parseHarnessRuntimeRequestResolution, type HarnessSession, -} from "../contracts/harness-driver"; -import type { PrpEvent } from "../protocol/replay-contract"; +} from "../contracts/harness-driver.js"; +import type { PrpEvent } from "../protocol/replay-contract.js"; import { applyPrpEvent, createSessionSnapshotFromMetadata, reduceSessionEvents, type SessionSnapshot, -} from "../reducer/session-reducer"; +} from "../reducer/session-reducer.js"; import type { RunnerCapabilities } from "./protocol.js"; import { buildTranscript, diff --git a/packages/paperclip-runner/src/catalog/protocol-action-contracts.test.ts b/packages/paperclip-runner/src/catalog/protocol-action-contracts.test.ts index 2ae21e152d5..c399e302439 100644 --- a/packages/paperclip-runner/src/catalog/protocol-action-contracts.test.ts +++ b/packages/paperclip-runner/src/catalog/protocol-action-contracts.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { PAPERCLIP_PROTOCOL_ACTIONS } from "../protocol-actions/index.js"; @@ -50,8 +50,9 @@ describe("canonical Paperclip protocol action contracts", () => { ).toEqual([]); if (action.scenario.descriptor.idempotency === "required") { expect(action.examples).toHaveProperty("scenarioCall"); - expect(scenarioCall.idempotencyKey).toEqual(expect.any(String)); - expect(scenarioCall.idempotencyKey).not.toHaveLength(0); + const { idempotencyKey } = scenarioCall as { idempotencyKey?: string }; + expect(idempotencyKey).toEqual(expect.any(String)); + expect(idempotencyKey).not.toHaveLength(0); } else { expect(scenarioCall).not.toHaveProperty("idempotencyKey"); } diff --git a/packages/paperclip-runner/src/catalog/semantic-action-catalog.test.ts b/packages/paperclip-runner/src/catalog/semantic-action-catalog.test.ts index 992cf522183..afec800cdba 100644 --- a/packages/paperclip-runner/src/catalog/semantic-action-catalog.test.ts +++ b/packages/paperclip-runner/src/catalog/semantic-action-catalog.test.ts @@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { createTaskAction } from "../protocol-actions/create-task.js"; import { createProjectAction } from "../protocol-actions/create-project.js"; diff --git a/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts b/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts index 90d49bb600c..676dabac7cb 100644 --- a/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts +++ b/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts @@ -449,7 +449,7 @@ describe("qualified ACPX runtime sidecar", () => { // The receipt hashes the actual emitted input, not a second normalization. await loadActualToolCommands(tools).resolve({ callId: "3", turnId: "test-turn", result: { accepted: true }, error: null }); } - const body = await (await response).json(); + const body = await (await response).json() as ToolCallResponseBody; const receipt = readNativeSemanticReceipt({ contents: body.result.content }); expect(receipt).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(raw), normalizedInputSha256: mode === "forwarded" ? semanticInputSha256(emitted[0].input) : null, @@ -490,7 +490,7 @@ describe("qualified ACPX runtime sidecar", () => { expect(validatePrpStructuredRunResult(emitted[1].input).ok).toBe(true); await commands.resolve({ callId: "3", turnId: "test-turn", result: { accepted: true }, error: null }); } - const body = await (await response).json(); + const body = await (await response).json() as ToolCallResponseBody; const receipt = readNativeSemanticReceipt({ contents: body.result.content }); expect(receipt).toMatchObject({ inputSha256: semanticInputSha256(raw), normalizedInputSha256: eligible ? semanticInputSha256(emitted[1].input) : null, outcome: eligible ? "returned" : "error" }); @@ -517,7 +517,7 @@ describe("qualified ACPX runtime sidecar", () => { await vi.waitFor(() => expect(emitted).toHaveLength(1)); await expect(handler(call)).rejects.toThrow("duplicated"); } finally { controller.abort(); } - expect((await result).message).toContain("cancelled"); + expect((await result as Error).message).toContain("cancelled"); expect(capture).not.toHaveBeenCalled(); expect(tools.size).toBe(0); expect(emitted[0].operationId).toBe("get_task_context"); @@ -547,7 +547,7 @@ describe("qualified ACPX runtime sidecar", () => { const bridge = await startRunnerToolBridge({ handler, captureSemanticReceipt: () => () => {} }); try { const body = await (await fetch(bridge.url, { method: "POST", headers: { Authorization: `Bearer ${bridge.secret}`, "Content-Type": "application/json" }, - body: JSON.stringify(request) })).json(); + body: JSON.stringify(request) })).json() as ToolCallResponseBody; expect(body.result.isError).toBe(true); expect(body.result.content[0].text).toBe("ACPX semantic tool call exceeds the sidecar frame limit"); expect(readNativeSemanticReceipt({ contents: body.result.content })).toMatchObject({ outcome: "error", normalizedInputSha256: null }); @@ -561,7 +561,7 @@ describe("qualified ACPX runtime sidecar", () => { } finally { await bridge.close(); } }); - it.each(["backpressure", "throws"])("uses actual writer acceptance, preserving %s semantics", async mode => { + it.each(["backpressure", "throws"] as const)("uses actual writer acceptance, preserving %s semantics", async mode => { const wire = loadActualSidecarWriter(mode); const tools = new Map(), emitted: any[] = [], commit = vi.fn(), capture = vi.fn((_input: unknown) => commit); const raw = { reportedWorkDisposition: "done", summary: "Complete", evidence: [], verification: [], @@ -727,7 +727,7 @@ describe("qualified ACPX runtime sidecar", () => { expect(Buffer.byteLength(JSON.stringify(JSON.parse(wire.writes[0]!).payload))).toBeGreaterThan(256 * 1024); expect(Buffer.byteLength(wire.writes[0]!)).toBeLessThan(ACPX_SIDECAR_MAX_FRAME_BYTES); } - const body = await (await response).json(); + const body = await (await response).json() as ToolCallResponseBody; if (mode === "error") { expect(JSON.stringify(body)).not.toContain(credentialCanary); expect(JSON.stringify(body)).toContain("[REDACTED]"); @@ -820,8 +820,9 @@ describe("qualified ACPX runtime sidecar", () => { const sanitize = new Function("boundedOptionalText", "stableProviderIdentity", `return event => {${source.slice(start, end)} return null;}`)( (value: string) => value, (value: string) => `stable-${value}`, ); - const projection = createPiMessageProjection<{ type: string; text: string; stream: string; messageId: string; meta: Record }>(); - const event = (kind: string) => ({ type: "text_delta", text: "", stream: "output", messageId: `pi-message-${"a".repeat(64)}`, + type PiTextFrame = { type: string; text: string; stream: string; messageId: string; meta: Record }; + const projection = createPiMessageProjection(); + const event = (kind: string): PiTextFrame => ({ type: "text_delta", text: "", stream: "output", messageId: `pi-message-${"a".repeat(64)}`, meta: { origin: "pi-native-assistant", source: "pi-rpc-message-v1", kind, secret: "DROP_ME" } }); const startFrame = sanitize(projection.normalize(event("start"))); const endFrame = sanitize(projection.normalize(event("end:toolUse"))); @@ -830,7 +831,7 @@ describe("qualified ACPX runtime sidecar", () => { expect(startFrame.messageId).toBe(endFrame.messageId); expect(JSON.stringify([startFrame, endFrame])).not.toContain("DROP_ME"); projection.settle(); - const loaded = createPiMessageProjection>(); + const loaded = createPiMessageProjection(); const history = sanitize(loaded.normalize({ ...event("history"), messageId: `pi-history-message-${"b".repeat(64)}`, text: "old reply", meta: { origin: "pi-history-assistant", source: "pi-session-history-v1", kind: "history" } })); expect(history).toMatchObject({ text: "old reply", piMessageHistory: true }); loaded.settle(); @@ -1744,6 +1745,8 @@ class SidecarProcess { } } +type ToolCallResponseBody = { result: { content: Array<{ text: string }>; isError?: boolean } }; + function loadWaitForTool(input: { tools: Map; emitted: unknown[]; diff --git a/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts b/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts index 6ebd3259074..8ad2681f63c 100644 --- a/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts +++ b/packages/paperclip-runner/src/cli/acpx-sidecar-input.test.ts @@ -54,7 +54,9 @@ describe("ACPX sidecar input sequencing", () => { async () => { events.push(command); }, - () => events.push(`${command}:error`), + () => { + events.push(`${command}:error`); + }, ); } const shutdown = pending.then(() => events.push("shutdown")); @@ -87,7 +89,9 @@ describe("ACPX sidecar input sequencing", () => { async () => { events.push("recovered"); }, - () => events.push("unexpected"), + () => { + events.push("unexpected"); + }, ); await recovered; @@ -113,7 +117,9 @@ describe("ACPX sidecar input sequencing", () => { async () => { events.push("recovered"); }, - () => events.push("unexpected"), + () => { + events.push("unexpected"); + }, ); await expect( diff --git a/packages/paperclip-runner/src/cli/eval-session-contract.test.ts b/packages/paperclip-runner/src/cli/eval-session-contract.test.ts index 5888e1b9328..0f07e2173ac 100644 --- a/packages/paperclip-runner/src/cli/eval-session-contract.test.ts +++ b/packages/paperclip-runner/src/cli/eval-session-contract.test.ts @@ -123,7 +123,7 @@ describe("eval-session request contract", () => { const value = request({ provider: "acpx", acpxAgent: agent, model: "explicit-provider-model" }); expect(() => parseEvalSessionRequest(value)).toThrow("--candidate-profile"); expect(parseEvalSessionRequest(value, { candidateProfile: agent })).toMatchObject({ acpxAgent: agent, model: "explicit-provider-model" }); - expect(() => parseEvalSessionRequest(value, { candidateProfile: agent === "pi" ? "cursor" : "pi" })).toThrow("must match"); + expect(() => parseEvalSessionRequest(value, { candidateProfile: "pi" })).toThrow("must match"); expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: agent, model: "" }), { candidateProfile: agent })).toThrow("request.model"); expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: "codex", session: { acpxAgent: agent } }))).toThrow("session.acpxAgent must match"); expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: agent, candidateProfile: agent }))).toThrow("--candidate-profile"); @@ -525,10 +525,10 @@ describe("eval-session usage", () => { providerReported: { nanodollars: null, recordedAttempts: 0 }, estimated: { nanodollars: 6_200_000, recordedAttempts: 1 }, }); - expect(snapshot.usageLedger[0]?.costNanodollars).toBe(legacyCost); + expect(snapshot.usageLedger![0]?.costNanodollars).toBe(legacyCost); const limitedRequest = parseEvalSessionRequest(request()); limitedRequest.limits.maxEstimatedCostNanodollars = 10_000_000; - const costlySnapshot = { ...snapshot, usageLedger: [{ ...snapshot.usageLedger[0]!, costNanodollars: 10_000_001 }] }; + const costlySnapshot = { ...snapshot, usageLedger: [{ ...snapshot.usageLedger![0]!, costNanodollars: 10_000_001 }] }; expect(() => boundedEvalSessionUsage(limitedRequest, { turnId: "codex-turn", status: "completed", assistantText: "Completed", snapshot: costlySnapshot, })).toThrow("legacy ledger cost limit exceeded"); @@ -609,7 +609,7 @@ describe("eval-session budget settlement", () => { }); const input = request({ provider: "acpx", acpxAgent: agent, model, runnerd: { path: binary, sha256: createHash("sha256").update("unused fake runner").digest("hex") }, - session: { workingDirectory: workspace, ...(agent === "pi" ? { piThinkingLevel: "low" } : {}) }, limits: { turnTimeoutMs: 1000, maxAgentTurns: 2, maxEstimatedCostNanodollars: 100_000_000 }, + session: { workingDirectory: workspace }, limits: { turnTimeoutMs: 1000, maxAgentTurns: 2, maxEstimatedCostNanodollars: 100_000_000 }, }); const requestPath = join(workspace, "request.json"); const outputPath = join(workspace, "output.json"); diff --git a/packages/paperclip-runner/src/cli/opencode-proxy-input.test.ts b/packages/paperclip-runner/src/cli/opencode-proxy-input.test.ts index 47170c7dbf8..7b3a0a27a7b 100644 --- a/packages/paperclip-runner/src/cli/opencode-proxy-input.test.ts +++ b/packages/paperclip-runner/src/cli/opencode-proxy-input.test.ts @@ -17,14 +17,18 @@ describe("OpenCode proxy input sequencing", () => { await firstBlocked; events.push("initialize:complete"); }, - () => events.push("initialize:error"), + () => { + events.push("initialize:error"); + }, ); pending = enqueueOpenCodeProxyInput( pending, async () => { events.push("thread:start"); }, - () => events.push("thread:error"), + () => { + events.push("thread:error"); + }, ); const shutdown = pending.then(() => { events.push("shutdown"); @@ -50,7 +54,9 @@ describe("OpenCode proxy input sequencing", () => { async () => { throw new Error("initialize failed"); }, - (error) => events.push((error as Error).message), + (error) => { + events.push((error as Error).message); + }, ); await pending; diff --git a/packages/paperclip-runner/src/conformance/local-provider-fault-matrix.test.ts b/packages/paperclip-runner/src/conformance/local-provider-fault-matrix.test.ts index 74419455b09..f69d58d8acb 100644 --- a/packages/paperclip-runner/src/conformance/local-provider-fault-matrix.test.ts +++ b/packages/paperclip-runner/src/conformance/local-provider-fault-matrix.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import type { NativeExecutionInput } from "../contracts/native-execution.js"; +import type { NativeExecutionInput, NativeExecutionInputV1 } from "../contracts/native-execution.js"; import type { NativeRunIdentity, NativeSessionCapabilities, @@ -29,8 +29,8 @@ type ProfileKey = "codex" | "opencode" | "acpx-claude" | "acpx-codex"; interface LocalNativeProfile { key: ProfileKey; - driverKind: NativeExecutionInput["session"]["driverKind"]; - provider: NativeExecutionInput["provider"]; + driverKind: NativeExecutionInputV1["session"]["driverKind"]; + provider: NativeExecutionInputV1["provider"]; } /** diff --git a/packages/paperclip-runner/src/contracts/completion-result.test.ts b/packages/paperclip-runner/src/contracts/completion-result.test.ts index d230ef4ef21..fb166ef36e0 100644 --- a/packages/paperclip-runner/src/contracts/completion-result.test.ts +++ b/packages/paperclip-runner/src/contracts/completion-result.test.ts @@ -1,5 +1,5 @@ import { normalizeLegacyPrpStructuredRunResult } from "../protocol/result-normalization.js"; -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { PRP_BLOCK_RESULT_OUTPUT_SCHEMA, diff --git a/packages/paperclip-runner/src/contracts/provider-trace-file-sink.test.ts b/packages/paperclip-runner/src/contracts/provider-trace-file-sink.test.ts index fc30e120364..f104bc303a3 100644 --- a/packages/paperclip-runner/src/contracts/provider-trace-file-sink.test.ts +++ b/packages/paperclip-runner/src/contracts/provider-trace-file-sink.test.ts @@ -76,5 +76,5 @@ describe("ProviderTraceFileSink", () => { }); function statMode(value: Awaited>): number { - return value.mode & 0o777; + return Number(value.mode) & 0o777; } diff --git a/packages/paperclip-runner/src/contracts/question-set.test.ts b/packages/paperclip-runner/src/contracts/question-set.test.ts index c7860c8bf24..16190c9685d 100644 --- a/packages/paperclip-runner/src/contracts/question-set.test.ts +++ b/packages/paperclip-runner/src/contracts/question-set.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { questionSetSchema } from "../protocol/generated/schema-bundle.js"; import { describe, expect, it } from "vitest"; diff --git a/packages/paperclip-runner/src/control-plane/cursor-cloud-runner-launch.test.ts b/packages/paperclip-runner/src/control-plane/cursor-cloud-runner-launch.test.ts index 14b87373c9c..b38b5099f13 100644 --- a/packages/paperclip-runner/src/control-plane/cursor-cloud-runner-launch.test.ts +++ b/packages/paperclip-runner/src/control-plane/cursor-cloud-runner-launch.test.ts @@ -101,7 +101,7 @@ it("settles a create rejected during real-facade Stop before cold recovery and t PAPERCLIP_CURSOR_CLOUD_RELAY_EXPIRES_AT: String(Date.now() + 60_000) }; const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, - lifecyclePolicy: { mode: "per_turn" as const }, environment }; + lifecyclePolicy: { mode: "per_turn" as const, idleTimeoutMs: null }, environment }; const first = createRunnerdCodexTransport({ ...options, prpIdentity: identity }), bundles = [first]; let stop: Promise | undefined; try { @@ -169,7 +169,7 @@ it("opens and closes the real Cloud facade without a provider prompt", async () runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, - lifecyclePolicy: { mode: "per_turn" }, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, prpIdentity: { runnerInstanceId: "runner-cloud-launch", environmentLeaseId: "lease-cloud-launch", runId: "00000000-0000-4000-8000-000000000761", normalizedSessionId, turnId: "turn-cloud-launch", itemId: "item-cloud-launch" }, environment: { @@ -249,7 +249,7 @@ it("preserves native Cloud tool identity and redacted output through the real Ru provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"), runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, - lifecyclePolicy: { mode: "per_turn" }, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, prpIdentity: { runnerInstanceId: "runner-cloud-tool", environmentLeaseId: "lease-cloud-tool", runId: "00000000-0000-4000-8000-000000000762", normalizedSessionId, turnId: "turn-cloud-tool", itemId: "item-cloud-tool" }, environment: { @@ -343,7 +343,7 @@ async function cloudQuestionHandoff(terminalDelayMs: number, governed: boolean, provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"), runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, - lifecyclePolicy: { mode: "per_turn" }, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, prpIdentity: { runnerInstanceId: "runner-cloud-question", environmentLeaseId: "lease-cloud-question", runId: "00000000-0000-4000-8000-000000000763", normalizedSessionId, turnId: "turn-cloud-question", itemId: "item-cloud-question" }, environment: { @@ -405,7 +405,7 @@ async function cloudQuestionHandoff(terminalDelayMs: number, governed: boolean, const execution = executeNativeSession({ input, backend, runnerInstanceId: "runner-cloud-question", controlPlaneInstanceId: "control-cloud-question", timeoutMs: 120_000, controlPlane: { async openRun() {}, async checkpointSession() {}, completeRun, - async appendEvent(event) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents(replay) { return { events: history.filter(event => event.sourceInstanceId === replay.sourceInstanceId && event.sourceSeq > replay.afterSourceSeq), highestContiguousSourceSeq: 0 }; }, }, resolveGovernedWait: ({ event }) => event.eventType === "item.completed" && event.payload.kind === "dynamicToolCall" ? yielded : null, @@ -520,7 +520,7 @@ async function cloudColdAuthorityRotation(instructions: string, validShutdown = PAPERCLIP_CURSOR_CLOUD_RELAY_URL: `https://relay.example.test/mcp/native-runs/${runId}`, PAPERCLIP_CURSOR_CLOUD_RELAY_EXPIRES_AT: String(Date.now() + 60_000) }); const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, - lifecyclePolicy: { mode: "per_turn" as const } }; + lifecyclePolicy: { mode: "per_turn" as const, idleTimeoutMs: null } }; const mcp = async (token: string, body: Record, sessionId?: string, priorScopeHeader?: string): Promise<{ status: number; sessionId?: string; value: any }> => { const scopeHeader = priorScopeHeader ?? await currentCloudScopeHeader(runtime, normalizedSessionId); return new Promise((accept, reject) => { diff --git a/packages/paperclip-runner/src/control-plane/durable-prp-control-plane.test.ts b/packages/paperclip-runner/src/control-plane/durable-prp-control-plane.test.ts index 13aa10b34d1..0dd2161ca0b 100644 --- a/packages/paperclip-runner/src/control-plane/durable-prp-control-plane.test.ts +++ b/packages/paperclip-runner/src/control-plane/durable-prp-control-plane.test.ts @@ -39,6 +39,8 @@ import { inspectRetainedRunInstructions, inspectStagedRunAttachmentHeader, type DurablePrpCheckpoint, + type DurablePrpControlPlaneStore, + type RunnerProcessHandle, durableRecoveryInternals, SemanticToolNotDispatchedError, inspectWarmRunTransition, @@ -48,6 +50,16 @@ import { } from "./durable-prp-control-plane.js"; import type { DurableRecoveryIdentity } from "./prp-transport-types.js"; +type WritableCoreStore = DurablePrpControlPlaneStore & { + save(): void; + commit(candidate: DurablePrpControlPlaneStore["state"]): void; +}; + +function restartRunner(handle: RunnerProcessHandle, ticket: string): void { + if (!handle.restart) throw new Error("Runner handle does not support restart"); + handle.restart(ticket); +} + const identity: DurableRecoveryIdentity = { runnerInstanceId: "runner-test-1", environmentLeaseId: "environment-test-1", @@ -229,7 +241,7 @@ it("recovers only the matching run's admitted instructions from a valid checkpoi expect(inspectRetainedRunInstructions(core.store.state, { ...targetExpected, runtimeContextDigest: digest })).toBeNull(); expect(inspectRetainedRunInstructions(core.store.state, expected)).toBe(template.provider.instructions); const corrupted = structuredClone(core.store.state); - corrupted.stagedRunAttachment!.templateDigest = `sha256:${"f".repeat(64)}`; + corrupted.stagedRunAttachment = { ...corrupted.stagedRunAttachment!, templateDigest: `sha256:${"f".repeat(64)}` }; expect(inspectRetainedRunInstructions(corrupted, targetExpected)).toBeNull(); } finally { await core.stop(); rmSync(root, { recursive: true, force: true }); } }); @@ -545,7 +557,7 @@ it("excludes streamed output from checkpoints even when the live window is full" anchor.eventType = "turn.started"; (anchor.envelope.payload as Record).eventType = "turn.started"; core.store.state.ackedSourceSeq = 4096; - core.store.save(); + (core.store as WritableCoreStore).save(); const snapshots: DurablePrpCheckpoint[] = []; core.setCheckpointPersistence(async saved => { snapshots.push(saved); }); try { @@ -918,7 +930,7 @@ it.each([false, true])("dispatches a fresh semantic input once when recovery ove if (JSON.parse(saved.records.get("cursor")!).ackedSourceSeq > 0) { writeHeld = true; await writeBarrier; } }); const pending: Promise[] = []; - let client: Awaited>; + let client: AuthenticatedClient | null = null; try { await core.start(); client = (await authenticate(core, core.issueBootstrapTicket()))!; @@ -1149,7 +1161,7 @@ it.each(["event", "progress", "lifecycle", "lifecycle_priority_one", "usage", "c expect(() => core.queueCommand("session.snapshot")).toThrow("indeterminate"); } if (mode === "authority_conflict") { - core.store.commit(structuredClone(core.store.state)); + (core.store as WritableCoreStore).commit(structuredClone(core.store.state)); core.queueCommand("session.snapshot", {}, "command_after_authority"); expect(JSON.parse(readFileSync(core.store.path, "utf8")).ackedSourceSeq).toBe(0); expect(core.store.state.ackedSourceSeq).toBe(0); @@ -1817,7 +1829,7 @@ it("pins the ACPX launch profile in runner startup arguments and restarts", () = }, }); - handle.restart("replacement-ticket"); + restartRunner(handle, "replacement-ticket"); expect(launches).toHaveLength(2); for (const launch of launches) { expect(launch.args).toContain("--acpx-launch-authority-digest"); @@ -1869,7 +1881,7 @@ it("pins the OpenCode launch profile in runner startup arguments and restarts", }, }); - handle.restart("replacement-ticket"); + restartRunner(handle, "replacement-ticket"); expect(launches).toHaveLength(2); for (const launch of launches) { expect(launch.args).toEqual( @@ -2057,7 +2069,7 @@ it("preserves only bounded GitHub credential projection at the runner spawn boun it("passes structured inputs to an external owner and preserves its replacement veto", () => { const process = { child: { exitCode: null, signalCode: null, kill: () => true }, completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }), restart: null }; - const launcher = vi.fn(() => process); + const launcher = vi.fn((_spec: RunnerProcessLaunchSpec) => process); const options = { connection: { mode: "external" as const }, stateDirectory: "/unused", identity, ticket: "synthetic-ticket", maxOutboxBytes: 262144, p0ReserveBytes: 65536, runnerVersion: expectedRunnerVersion, runnerDigest: expectedRunnerDigest, @@ -2065,7 +2077,7 @@ it("passes structured inputs to an external owner and preserves its replacement expect(() => spawnRunner(options)).toThrow("requires a process owner"); expect(spawnRunner({ ...options, processLauncher: launcher })).toBe(process); expect(launcher).toHaveBeenCalledWith(expect.objectContaining({ nativeRunner: expect.objectContaining(options) })); - expect(launcher.mock.calls[0][0].args).not.toContain("--connect-url"); + expect(launcher.mock.calls[0]![0].args).not.toContain("--connect-url"); }); it("preserves the controller-selected ACPX provider package root", () => { @@ -2147,7 +2159,7 @@ it.each([ completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }) }; }, }); - handle.restart("replacement-ticket"); + restartRunner(handle, "replacement-ticket"); for (const launch of launches.splice(0)) { expect(launch.environment[key]).toBe(explicit ? "explicit-fixture-credential" : undefined); const receipt = launch.environment[ACPX_CREDENTIAL_BINDING_ENV]; diff --git a/packages/paperclip-runner/src/devtools/index.test.ts b/packages/paperclip-runner/src/devtools/index.test.ts index 80c591de7cb..f6e118ceb34 100644 --- a/packages/paperclip-runner/src/devtools/index.test.ts +++ b/packages/paperclip-runner/src/devtools/index.test.ts @@ -19,37 +19,41 @@ function snapshot(): CapabilityLiveSessionSnapshot { current.lifecycle = "running"; current.artifacts.push({ id: "artifact-1", - companyId: "company-1", taskId: "task-1", - runId: "run-1", - kind: "text", - name: "result.txt", + filename: "result.txt", + contentType: "text/plain", + byteSize: 6, + sha256: "a".repeat(64), contentRef: "file:///private/result.txt", - metadata: {}, createdAt: "2026-08-09T00:00:01.000Z", }); return { - id: "session-1", + schema: "paperclip.capability.live-session.v1", + sessionId: "session-1", revision: 1, status: "running", activeTurnId: null, providerThreadId: "thread-1", providerSessionId: "provider-session-1", - providerModel: "gpt-test", + providerModel: { id: "gpt-test", provider: "openai" }, config: { seedState: JSON.stringify(seed), workingDirectory: "/private/worktree", - scenario: "hb-baseline", + scenario: { id: "hb-baseline" }, capabilities: ["task:read"], explicitClaims: [], turnTimeoutMs: 1_000, }, authority: { + active: true, companyId: "company-1", actorId: "actor-1", taskId: "task-1", runId: "run-1", sessionId: "session-1", + scenarioId: "hb-baseline", + capabilities: ["task:read"], + explicitClaims: [], }, transcript: [], evidence: [{ @@ -64,8 +68,9 @@ function snapshot(): CapabilityLiveSessionSnapshot { }, }], mockState: JSON.stringify(current), + authorizationRecords: [], process: null, - networkEvidence: [], + networkEvidence: { realPaperclipRequests: 0, childPaperclipEnvironmentKeys: [] }, createdAt: "2026-08-09T00:00:00.000Z", updatedAt: "2026-08-09T00:00:01.000Z", stateHistory: [{ diff --git a/packages/paperclip-runner/src/drivers/acpx/antigravity-acpx-runtime.test.ts b/packages/paperclip-runner/src/drivers/acpx/antigravity-acpx-runtime.test.ts index 93ce3d63825..c8aef50aaa0 100644 --- a/packages/paperclip-runner/src/drivers/acpx/antigravity-acpx-runtime.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/antigravity-acpx-runtime.test.ts @@ -168,5 +168,6 @@ it("rejects an unacknowledged thinking-level change after a native family model const f = await fixture("family-change"), owner = await f.open(); const result = await f.turn(owner); expect(result.status).toBe("failed"); + if (result.status !== "failed") throw new Error("turn did not fail"); expect(result.error?.message).toMatch(/admission/i); }); diff --git a/packages/paperclip-runner/src/drivers/acpx/antigravity-admission.test.ts b/packages/paperclip-runner/src/drivers/acpx/antigravity-admission.test.ts index 728b1fd2a32..343317d0f29 100644 --- a/packages/paperclip-runner/src/drivers/acpx/antigravity-admission.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/antigravity-admission.test.ts @@ -22,7 +22,7 @@ function response(message: ReturnType, changes: Record }, } }; } const prompt = () => ({ id: "turn", method: "session/prompt", params: { sessionId: "native-session", prompt: [{ type: "text", text: "Do the task" }] } }); diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-acpx-driver.test.ts b/packages/paperclip-runner/src/drivers/acpx/codex-acpx-driver.test.ts index f49e42294b7..3fbcc41260e 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-acpx-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-acpx-driver.test.ts @@ -2,7 +2,7 @@ import { appendSemanticToolReceipt } from "../semantic-tool-receipt.js"; import { readFileSync } from "node:fs"; import { createHash } from "node:crypto"; import { cursorToolIdentity } from "./cursor-plan-tool-identity.js"; -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi, type Mock } from "vitest"; import { AcpxProviderProcessLostError } from "./codex-runtime-adapter.js"; import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js"; @@ -31,7 +31,7 @@ describe("Codex ACPX harness driver", () => { it("captures the direct driver's normalized input only when the exact proposal is retained", async () => { const fixture = driverFixture({ agent: "copilot", model: "explicit-test-model", providerPolicy: { readOnly: true } }, { runtimeEvents: [] }); const session = await fixture.driver.openSession({ runId: "run-normalized-receipt", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - await session.startTurn({ message: { text: "Complete" } }); + await session.startTurn({ message: { role: "user", text: "Complete" } }); const { schema: _, attentionRequests: __, artifacts: ___, ...raw } = completedResult(); const commit = vi.fn(), capture = vi.fn((value: unknown) => { expect(commit).not.toHaveBeenCalled(); return commit; }); const handler = fixture.hostOptions!.semanticTools!.handler; @@ -58,7 +58,7 @@ describe("Codex ACPX harness driver", () => { runtimeEvents: Array.from({ length: 8 }, (_, n) => ({ type: "text_delta" as const, stream: "output" as const, text: `bounded-${n}` })), }); const session = await fixture.driver.openSession({ runId: "run-normalized-pressure", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - await session.startTurn({ message: { text: "Complete" } }); + await session.startTurn({ message: { role: "user", text: "Complete" } }); await vi.waitFor(async () => expect((await session.transcript!()).eventCount).toBeGreaterThanOrEqual(4)); const commit = vi.fn(), capture = vi.fn(() => commit); await expect(fixture.hostOptions!.semanticTools!.handler({ tool: PRP_COMPLETION_TOOL_NAME, callId: "finish", arguments: completedResult(), @@ -107,23 +107,23 @@ describe("Codex ACPX harness driver", () => { }, } }; fixture.host.status.mockResolvedValueOnce(before as never).mockResolvedValue(after as never); - await session.startTurn({ message: { text: "fixture" } }); + await session.startTurn({ message: { role: "user", text: "fixture" } }); fixture.finishTurn({ status: "completed", stopReason: "end_turn" }); const events = await collectUntil(session.events(), "turn.completed"); const notices = events.filter(e => e.eventType === "provider.notice.recorded" && e.payload.category === "cursor_native_usage_observed"); expect(notices).toHaveLength(agent === "cursor" ? 1 : 0); - expect(events.filter(e => e.eventType === "usage.updated")).toEqual([]); + expect(events.filter(e => e.eventType === "usage.reported")).toEqual([]); expect(events.at(-1)?.eventType).toBe("turn.completed"); await session.close({ reason: "partial diagnostics verified" }); }); it.each(["tool-1", "tool with spaces", "🧭/plan", "x".repeat(300)])("binds a native Cursor plan request to its actual projected tool identity: %s", async toolCallId => { const fixture = driverFixture({ agent: "cursor", model: "explicit-test-model", mode: "plan", providerPolicy: { readOnly: false } }, { - runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "arbitrary tool display", kind: "execute", status: "pending" }], + runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "arbitrary tool display", kind: "execute", status: "pending", text: "arbitrary tool display" }], }); const session = await fixture.driver.openSession({ runId: "run-plan-identity", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const toolEvents = collectUntil(session.events(), "tool.execution.started"); - const { turnId } = await session.startTurn({ message: { text: "Plan" } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Plan" } }); const tool = (await toolEvents).find(e => e.eventType === "tool.execution.started")!; const created = collectUntil(session.events(), "runtime_request.created"); const callback = fixture.host.startTurn.mock.calls[0]![0].onExtensionRequest!; @@ -145,7 +145,7 @@ describe("Codex ACPX harness driver", () => { ]; const fixture = driverFixture({ agent, model: "explicit-model", providerPolicy: { readOnly: true } }, { runtimeEvents }); const session = await fixture.driver.openSession({ runId: "other-provider-identity", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - const { turnId } = await session.startTurn({ message: { text: "fixture" } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "fixture" } }); fixture.finishTurn({ status: "completed" }); const events = await collectUntil(session.events(), "turn.completed"); const messages = events.filter(event => event.payload.kind === "agentMessage"); @@ -162,7 +162,7 @@ describe("Codex ACPX harness driver", () => { runtimeEvents.push(chunk(undefined, "Info: native session activity")); const fixture = driverFixture({ agent: "copilot", model: "explicit-model", providerPolicy: { readOnly: true } }, { runtimeEvents }); const session = await fixture.driver.openSession({ runId: "run-copilot-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - await session.startTurn({ message: { text: "Answer exactly" } }); + await session.startTurn({ message: { role: "user", text: "Answer exactly" } }); fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" }); const events = await collectUntil(session.events(), scenario === "cancelled" ? "turn.interrupted" : "turn.completed"); const interim = events.find(event => event.eventType === "item.delta" && event.payload.text === "EXACT_MARKER"); @@ -179,7 +179,7 @@ describe("Codex ACPX harness driver", () => { it("keeps Pi retry-only activity as a rich notice without inventing a final answer", async () => { const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] }); const session = await fixture.driver.openSession({ runId: "run-pi-notice", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - await session.startTurn({ message: { text: "Work" } }); + await session.startTurn({ message: { role: "user", text: "Work" } }); const input = fixture.host.startTurn.mock.calls[0]![0]; await input.onExtensionNotification!("paperclip/pi_notice", { sessionId: "backend-1", category: "auto_retry_end", summary: "Retry did not recover.", severity: "warning", details: { attempt: 2, success: false } }); fixture.finishTurn({ status: "completed" }); @@ -201,7 +201,7 @@ describe("Codex ACPX harness driver", () => { } const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents }); const session = await fixture.driver.openSession({ runId: "run-pi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - await session.startTurn({ message: { text: "Answer exactly" } }); + await session.startTurn({ message: { role: "user", text: "Answer exactly" } }); fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" }); const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed"); expect(events.some(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")).toBe(true); @@ -233,7 +233,7 @@ describe("Codex ACPX harness driver", () => { } const fixture = driverFixture({ agent: "kimi", model: "kimi-for-coding", providerPolicy: { readOnly: true } }, { runtimeEvents }); const session = await fixture.driver.openSession({ runId: "run-kimi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - const { turnId } = await session.startTurn({ message: { text: "Answer exactly" } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Answer exactly" } }); fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" }); const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed"); const progress = events.find(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")!; @@ -256,34 +256,34 @@ describe("Codex ACPX harness driver", () => { const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); expect(fixture.hostOptions?.providerPolicy).toEqual({ readOnly: true }); expect(session.turnControlCapabilities?.()).toEqual({ steering: true, queuedFollowUp: true }); - const { turnId } = await session.startTurn({ message: { text: "Work" } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Work" } }); const turnInput = fixture.host.startTurn.mock.calls[0]![0]; const context = { requestId: 0, signal: new AbortController().signal }; await expect(turnInput.onExtensionRequest!("provider/request", { sessionId: "agent-1" }, context)).rejects.toThrow(/session mismatch/); - await session.steer!({ turnId, correlationId: "control-1", message: { text: "Change focus" } }); - await session.steer!({ turnId, correlationId: "control-2", mode: "follow_up", message: { text: "Then validate" } }); + await session.steer!({ turnId, correlationId: "control-1", message: { role: "user", text: "Change focus" } }); + await session.steer!({ turnId, correlationId: "control-2", mode: "follow_up", message: { role: "user", text: "Then validate" } }); expect(fixture.host.steerActiveTurn).toHaveBeenCalledExactlyOnceWith("Change focus", `run-controls:${turnId}`); expect(fixture.host.queueFollowUp).toHaveBeenCalledExactlyOnceWith("Then validate", `run-controls:${turnId}`); expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled(); - await expect(session.steer!({ turnId, correlationId: "control-2", message: { text: "Duplicate" } })).rejects.toThrow(/already attempted/); - await expect(session.steer!({ turnId: "wrong-turn", message: { text: "Stale" } })).rejects.toThrow(); + await expect(session.steer!({ turnId, correlationId: "control-2", message: { role: "user", text: "Duplicate" } })).rejects.toThrow(/already attempted/); + await expect(session.steer!({ turnId: "wrong-turn", message: { role: "user", text: "Stale" } })).rejects.toThrow(); fixture.finishTurn({ status: "completed" }); const events = await collectUntil(session.events(), "turn.completed"); expect(events.filter(event => event.payload.kind === "steering_acknowledgement").map(event => event.payload.mode)).toEqual(["steer", "follow_up"]); for (const event of events) expect(validatePrpEvent(event).ok).toBe(true); - await expect(session.steer!({ turnId, message: { text: "Already settled" } })).rejects.toThrow(); + await expect(session.steer!({ turnId, message: { role: "user", text: "Already settled" } })).rejects.toThrow(); await session.close({ reason: "controls verified" }); }); it("rejects unnegotiated controls and retains ambiguous delivery attempts", async () => { const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] }); const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - const { turnId } = await session.startTurn({ message: { text: "Work" } }); - await expect(session.steer!({ turnId, message: { text: "No handshake" } })).rejects.toThrow(/did not negotiate/); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Work" } }); + await expect(session.steer!({ turnId, message: { role: "user", text: "No handshake" } })).rejects.toThrow(/did not negotiate/); expect(fixture.host.steerActiveTurn).not.toHaveBeenCalled(); fixture.host.steeringCapability.mockReturnValue({ steering: true, queuedFollowUp: false }); fixture.host.steerActiveTurn.mockRejectedValueOnce(new Error("delivery acknowledgement lost")); - const input = { turnId, correlationId: "ambiguous", message: { text: "Maybe delivered" } }; + const input = { turnId, correlationId: "ambiguous", message: { role: "user" as const, text: "Maybe delivered" } }; await expect(session.steer!(input)).rejects.toThrow(/acknowledgement lost/); await expect(session.steer!(input)).rejects.toThrow(/already attempted/); expect(fixture.host.steerActiveTurn).toHaveBeenCalledOnce(); @@ -453,7 +453,7 @@ describe("Codex ACPX harness driver", () => { ], }); const session = await fixture.driver.openSession({ runId: "run-claude-banners", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); - await session.startTurn({ message: { text: "Fix it" } }); + await session.startTurn({ message: { role: "user", text: "Fix it" } }); fixture.finishTurn({ status: "completed" }); const events = await collectUntil(session.events(), "turn.completed"); for (const event of events) validatePrpEvent(event); @@ -713,11 +713,11 @@ describe("Codex ACPX harness driver", () => { signal: new AbortController().signal, }), ).rejects.toThrow("does not match"); - await session.interrupt({ turnId, reason: "user cancelled" }); + await session.interrupt!({ turnId, reason: "user cancelled" }); expect(fixture.host.interruptActiveTurn).toHaveBeenCalledWith( "user cancelled", ); - await expect(session.interrupt({ turnId: "stale-turn" })).rejects.toThrow( + await expect(session.interrupt!({ turnId: "stale-turn" })).rejects.toThrow( "is not the active turn", ); await session.close({ reason: "cancelled" }); @@ -1291,13 +1291,13 @@ describe("Codex ACPX harness driver", () => { const fixture = driverFixture({}, { closeSettlementTimeoutMs: 1 }); const hostA = { ...fixture.host, - close: vi.fn(async () => { + close: vi.fn(async (_input: { reason: string }): Promise => { throw new Error("host A cleanup failed"); }), }; const hostB = { ...fixture.host, - close: vi.fn(async () => { + close: vi.fn(async (_input: { reason: string }): Promise => { throw new Error("host B cleanup failed"); }), }; @@ -1606,7 +1606,7 @@ describe("Codex ACPX harness driver", () => { ).rejects.toThrow("awaiting canonical terminal-event retention"); expect(dynamicToolHandler).not.toHaveBeenCalled(); await expect( - session.interrupt({ turnId, reason: "too late" }), + session.interrupt!({ turnId, reason: "too late" }), ).rejects.toThrow("awaiting canonical terminal-event retention"); expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled(); await expect(session.snapshot()).resolves.toMatchObject({ @@ -1761,7 +1761,7 @@ describe("Codex ACPX harness driver", () => { const fixture = driverFixture({ agent: "copilot", model: "gpt-5.6-luna", providerPolicy: { readOnly: false } }, { eventStreamFailure: failure.promise }); const session = await fixture.driver.openSession({ runId: "run-provider-death", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const created = collectUntil(session.events(), "runtime_request.created"); - const { turnId } = await session.startTurn({ message: { text: "Edit the file" } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Edit the file" } }); const callback = fixture.host.startTurn.mock.calls[0]![0].onPermissionRequest!; const controller = new AbortController(); const response = callback({ inferredKind: "edit", raw: { @@ -1895,11 +1895,11 @@ describe("Codex ACPX harness driver", () => { it.each(["cursor-tool", "native\u0000tool", "native\u007ftool"].flatMap(toolCallId => ["written", "failed"].map(outcome => ({ toolCallId, outcome }))))("binds Cursor denial evidence to its original tool and response write: $outcome/$toolCallId", async ({ outcome, toolCallId }) => { const command = "printf 'sensitive-value' > /workspace/denied.txt"; const fixture = driverFixture({ agent: "cursor", model: "explicit-test-model", providerPolicy: { readOnly: false } }, { - runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "Run command", kind: "execute", status: "pending", rawInput: { command } }], + runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "Run command", kind: "execute", status: "pending", text: "Run command", rawInput: { command } }], }); const session = await fixture.driver.openSession({ runId: "run-cursor-receipt", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const origin = collectUntil(session.events(), "tool.execution.started"); - const { turnId } = await session.startTurn({ message: { text: "Request the command." } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Request the command." } }); const originEvents = await origin; const created = collectUntil(session.events(), "runtime_request.created"); const callback = fixture.host.startTurn.mock.calls[0]![0].onPermissionRequest!; @@ -1942,7 +1942,7 @@ describe("Codex ACPX harness driver", () => { const fixture = driverFixture({ agent, providerPolicy: { readOnly: false } }, { runtimeEvents }); const session = await fixture.driver.openSession({ runId: "run-attached", normalizedSessionId: "session-1", workingDirectory: "/fixture/workspace" }); const completed = collectUntil(session.events(), "turn.completed"); - const { turnId } = await session.startTurn({ message: { text: "Start attached work." } }); + const { turnId } = await session.startTurn({ message: { role: "user", text: "Start attached work." } }); fixture.finishTurn({ status: "completed", stopReason: "end_turn" }); const events = await completed; const evidence = events.filter(event => event.eventType === "provider.notice.recorded" && event.payload.category === "copilot_tool_evidence_v1"); @@ -1976,6 +1976,7 @@ describe("Codex ACPX harness driver", () => { const providerResponse = onElicitation( { mode: "form", + sessionId: "backend-1", message: "Choose deployment settings.", requestedSchema: { type: "object", @@ -2065,6 +2066,8 @@ describe("Codex ACPX harness driver", () => { onElicitation( { mode: "form", + sessionId: "backend-1", + message: "Provide a value.", requestedSchema: { type: "object", properties: { value: { type: "string" } }, @@ -2101,6 +2104,8 @@ describe("Codex ACPX harness driver", () => { const providerResponse = onElicitation( { mode: "form", + sessionId: "backend-1", + message: "Provide a value.", requestedSchema: { type: "object", properties: { value: { type: "string" } }, @@ -2148,6 +2153,8 @@ describe("Codex ACPX harness driver", () => { const providerResponse = onElicitation( { mode: "form", + sessionId: "backend-1", + message: "Provide a value.", requestedSchema: { type: "object", properties: { value: { type: "string" } }, @@ -2194,6 +2201,8 @@ describe("Codex ACPX harness driver", () => { const providerResponse = onElicitation( { mode: "form", + sessionId: "backend-1", + message: "Provide a value.", requestedSchema: { type: "object", properties: { value: { type: "string" } }, @@ -2262,6 +2271,8 @@ describe("Codex ACPX harness driver", () => { const providerResponse = onElicitation( { mode: "form", + sessionId: "backend-1", + message: "Provide a value.", requestedSchema: { type: "object", properties: { value: { type: "string" } }, @@ -3177,12 +3188,12 @@ function driverFixture( function recoveryWorkspaceLease( path = "/workspace", ): AcpxRecoveryWorkspaceLease & { - assertHeld: ReturnType; - close: ReturnType; + assertHeld: Mock<() => void>; + close: Mock<() => Promise>; } { return { path, - assertHeld: vi.fn(), + assertHeld: vi.fn<() => void>(), close: vi.fn(async () => undefined), }; } @@ -3207,6 +3218,7 @@ function fakeHost(createTurn: () => AcpxRuntimeTurn, onClose: () => void) { workspacePath: "/workspace", workspaceDigest: `sha256:${"b".repeat(64)}`, runtimeRoot: "/runtime/acpx/session-1", + commandDigest: `sha256:${"c".repeat(64)}`, profileDigest: `sha256:${"a".repeat(64)}`, requestedModel: "gpt-5.6-sol", effectiveModel: "gpt-5.6-sol", @@ -3225,7 +3237,7 @@ function fakeHost(createTurn: () => AcpxRuntimeTurn, onClose: () => void) { steerActiveTurn: vi.fn(async (_text: string, _requestId?: string) => undefined), queueFollowUp: vi.fn(async (_text: string, _requestId?: string) => undefined), interruptActiveTurn: vi.fn(async () => undefined), - close: vi.fn(async () => { + close: vi.fn(async (_input: { reason: string }) => { onClose(); }), }; diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts index 11c16adc8a5..467f2d11ee9 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts @@ -7,6 +7,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import type { + AcpPermissionRequest, AcpAgentRegistry, AcpRuntime, AcpRuntimeHandle, @@ -114,7 +115,7 @@ describe("Codex ACPX runtime adapter", () => { if (!restoring) spawnProvider(); return HANDLE; }); - vi.mocked(runtime.setConfigOption).mockImplementation(async input => { + vi.mocked(runtime.setConfigOption!).mockImplementation(async input => { if (restoring) spawnProvider(); const guard = created.protocolGuardFactory!(); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); @@ -546,7 +547,7 @@ describe("Codex ACPX runtime adapter", () => { const options = openOptions(fakeCommand()); options.profile = { ...options.profile, agent: "cursor" }; options.refreshConsumedCommand = vi.fn(async () => undefined); let created!: AcpRuntimeOptions; - vi.mocked(runtime.setConfigOption).mockImplementation(async () => { + vi.mocked(runtime.setConfigOption!).mockImplementation(async () => { const guard = created.protocolGuardFactory!(); const binding = cursorInstructionBinding(options.systemInstructions); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); @@ -574,7 +575,7 @@ describe("Codex ACPX runtime adapter", () => { let created!: AcpRuntimeOptions; const binding = cursorInstructionBinding(options.systemInstructions); const configs = (mode: string) => [{ id: "mode", currentValue: mode }]; - vi.mocked(runtime.setConfigOption).mockImplementation(async input => { + vi.mocked(runtime.setConfigOption!).mockImplementation(async input => { const guard = created.protocolGuardFactory!(); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); guard("inbound", { id: 0, result: { modes: { currentModeId: "agent" }, configOptions: configs("agent"), _meta: { paperclipCursorInstructions: { @@ -586,7 +587,7 @@ describe("Codex ACPX runtime adapter", () => { const port = await openCodexAcpxRuntime(options, { createRegistry: () => registry(), createStore: () => store(), createRuntime: value => { created = value; return runtime; }, }); - expect(vi.mocked(runtime.setConfigOption).mock.calls.map(([call]) => [call.key, call.value])).toEqual([["model", options.profile.reportedModelId], ["mode", selected]]); + expect(vi.mocked(runtime.setConfigOption!).mock.calls.map(([call]) => [call.key, call.value])).toEqual([["model", options.profile.reportedModelId], ["mode", selected]]); expect(options.refreshConsumedCommand).toHaveBeenCalledTimes(2); expect(await port.identity()).toMatchObject({ mode: selected }); expect(runtime.startTurn).not.toHaveBeenCalled(); @@ -652,13 +653,13 @@ describe("Codex ACPX runtime adapter", () => { it("requires the Pi capability handshake and acknowledgments, preserves follow-up order, and never starts a second prompt", async () => { const pending = pendingExtensionTurn("turn-1"); - const runtime = Object.assign(fakeRuntime(), { requestExtension: vi.fn(async () => ({ accepted: true })) }); + const runtime = Object.assign(fakeRuntime(), { requestExtension: vi.fn(async (_input: unknown) => ({ accepted: true })) }); vi.mocked(runtime.startTurn).mockReturnValue(pending.turn); let created!: AcpRuntimeOptions & { onAgentInitialize?: (result: unknown) => void }; const options = openOptions(fakeCommand()); options.profile = { ...options.profile, agent: "pi" }; options.piThinkingLevel = "low"; - vi.mocked(runtime.setConfigOption).mockImplementation(async input => { + vi.mocked(runtime.setConfigOption!).mockImplementation(async input => { const guard = created.protocolGuardFactory!(); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); guard("inbound", { id: 0, result: { modes: { currentModeId: "high" }, configOptions: [{ id: "thought_level", currentValue: "high" }] } }); @@ -734,9 +735,7 @@ describe("Codex ACPX runtime adapter", () => { expect(runtimeOptions!.permissionPolicy).toMatchObject({ defaultAction: "escalate" }); const turn = port.startTurn({ text: "Attempt a write.", requestId: "permission-test" }); try { - await runtimeOptions!.onPermissionRequest!({ - sessionId: "backend-1", inferredKind: "write", raw: {}, - }, { signal: new AbortController().signal }); + await runtimeOptions!.onPermissionRequest!(permissionRequest("backend-1", "edit"), { signal: new AbortController().signal }); expect(signal?.aborted).toBe(true); await expect(turn.result).rejects.toThrow("Approval required"); await expect((async () => { for await (const _event of turn.events) { /* drain */ } })()) @@ -2361,21 +2360,13 @@ describe("Codex ACPX runtime adapter", () => { ]); await expect( runtimeOptions?.onPermissionRequest?.( - { - sessionId: "session-1", - inferredKind: "execute", - raw: { _meta: { is_mcp_tool_approval: true } }, - }, + permissionRequest("session-1", "execute", { _meta: { is_mcp_tool_approval: true } }), { signal: new AbortController().signal }, ), ).resolves.toEqual({ outcome: "reject_once" }); await expect( runtimeOptions?.onPermissionRequest?.( - { - sessionId: "session-1", - inferredKind: "execute", - raw: {}, - }, + permissionRequest("session-1", "execute"), { signal: new AbortController().signal }, ), ).resolves.toEqual({ outcome: "reject_once" }); @@ -2399,11 +2390,7 @@ describe("Codex ACPX runtime adapter", () => { await expect( runtimeOptions?.onPermissionRequest?.( - { - sessionId: "session-1", - inferredKind: "write", - raw: {}, - }, + permissionRequest("session-1", "edit"), { signal: new AbortController().signal }, ), ).resolves.toEqual({ outcome: "reject_once" }); @@ -2438,11 +2425,7 @@ describe("Codex ACPX runtime adapter", () => { expect(runtimeOptions?.nonInteractivePermissions).toBe("fail"); await expect( runtimeOptions?.onPermissionRequest?.( - { - sessionId: "backend-1", - inferredKind: "write", - raw: {}, - }, + permissionRequest("backend-1", "edit"), { signal: new AbortController().signal }, ), ).resolves.toEqual(expected); @@ -3618,8 +3601,18 @@ function fakeCommand(): VerifiedAcpxCommandLease { return { spawn: vi.fn(), close: vi.fn() }; } -function fakeChild(): ChildProcess { - const child = new EventEmitter() as ChildProcess; +type FakeChildProcess = ChildProcess & { exitCode: number | null; signalCode: NodeJS.Signals | null }; + +function permissionRequest( + sessionId: string, + inferredKind: AcpPermissionRequest["inferredKind"], + raw: Record = {}, +): AcpPermissionRequest { + return { sessionId, inferredKind, raw: raw as unknown as AcpPermissionRequest["raw"] }; +} + +function fakeChild(): FakeChildProcess { + const child = new EventEmitter() as FakeChildProcess; Object.defineProperties(child, { exitCode: { value: null, writable: true }, signalCode: { value: null, writable: true }, @@ -3633,10 +3626,10 @@ function fakeChild(): ChildProcess { } function failingSignalChild(): { - child: ChildProcess; + child: FakeChildProcess; errors: [Error, Error]; } { - const child = new EventEmitter() as ChildProcess; + const child = new EventEmitter() as FakeChildProcess; const errors: [Error, Error] = [ new Error("SIGTERM delivery failed"), new Error("SIGKILL delivery failed"), @@ -3654,8 +3647,8 @@ function failingSignalChild(): { return { child, errors }; } -function childThatExitsAfterSignalCalls(exitAfter: number): ChildProcess { - const child = new EventEmitter() as ChildProcess; +function childThatExitsAfterSignalCalls(exitAfter: number): FakeChildProcess { + const child = new EventEmitter() as FakeChildProcess; Object.defineProperties(child, { exitCode: { value: null, writable: true }, signalCode: { value: null, writable: true }, @@ -3676,8 +3669,8 @@ function childThatExitsAfterSignalCalls(exitAfter: number): ChildProcess { return child; } -function stubbornChild(): ChildProcess { - const child = new EventEmitter() as ChildProcess; +function stubbornChild(): FakeChildProcess { + const child = new EventEmitter() as FakeChildProcess; Object.defineProperties(child, { pid: { value: 71_001 }, exitCode: { value: null, writable: true }, @@ -3691,8 +3684,8 @@ function stubbornChild(): ChildProcess { return child; } -function childThatExitsOnKill(): ChildProcess { - const child = new EventEmitter() as ChildProcess; +function childThatExitsOnKill(): FakeChildProcess { + const child = new EventEmitter() as FakeChildProcess; Object.defineProperties(child, { pid: { value: 71_002 }, exitCode: { value: null, writable: true }, diff --git a/packages/paperclip-runner/src/drivers/acpx/copilot-runtime-host.test.ts b/packages/paperclip-runner/src/drivers/acpx/copilot-runtime-host.test.ts index 536220811fa..cb2cf018190 100644 --- a/packages/paperclip-runner/src/drivers/acpx/copilot-runtime-host.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/copilot-runtime-host.test.ts @@ -20,7 +20,7 @@ it("reports missing Copilot authentication before spawn and releases admission o agentServerPackageJsonPath: join(root, "provider/package.json"), agentRuntimePackageJsonPath: null, openCommand }), openRuntime, reportRetainedCleanupFailure: () => {}, }; - const options = { agent: "copilot" as const, model: "explicit-model", normalizedSessionId: "copilot-auth-test", + const options = { agent: "copilot" as const, model: "explicit-model", permissionMode: "approve-reads" as const, normalizedSessionId: "copilot-auth-test", runtimeDirectory, workingDirectory, providerPolicy: { readOnly: false } }; try { await expect(AcpxRuntimeHost.open({ ...options, environment: { GH_TOKEN: "ambient-forbidden", GITHUB_TOKEN: "ambient-forbidden" } }, dependencies)) diff --git a/packages/paperclip-runner/src/drivers/acpx/cursor-extensions.test.ts b/packages/paperclip-runner/src/drivers/acpx/cursor-extensions.test.ts index 1e77f46018f..a921c5cc396 100644 --- a/packages/paperclip-runner/src/drivers/acpx/cursor-extensions.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/cursor-extensions.test.ts @@ -1,15 +1,16 @@ +/// import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { validateAcpxRichEvent } from "./profile-extensions.js"; -import { PAPERCLIP_QUESTION_RESPONSE_SCHEMA } from "../../contracts/question-set.js"; +import { PAPERCLIP_QUESTION_RESPONSE_SCHEMA, type PaperclipQuestionAnswer } from "../../contracts/question-set.js"; import { createCursorNotificationNormalizer, createCursorProfileExtensionAdapter, createCursorSubagentNormalizer, cursorWorkspaceArtifactReference, normalizeCursorPlanRequest, normalizeCursorQuestionRequest, normalizeCursorSubagentUpdate, } from "./cursor-extensions.js"; -const response = (answers: Record) => ({ schema: PAPERCLIP_QUESTION_RESPONSE_SCHEMA, answers }); +const response = (answers: Record) => ({ schema: PAPERCLIP_QUESTION_RESPONSE_SCHEMA, answers }); const roots: string[] = []; afterEach(async () => { await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true }))); }); async function workspace() { const root = await mkdtemp(join(tmpdir(), "cursor-extension-")); roots.push(root); return root; } diff --git a/packages/paperclip-runner/src/drivers/acpx/cursor-permission-identity.test.ts b/packages/paperclip-runner/src/drivers/acpx/cursor-permission-identity.test.ts index 57462493056..4c95ad21263 100644 --- a/packages/paperclip-runner/src/drivers/acpx/cursor-permission-identity.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/cursor-permission-identity.test.ts @@ -1,3 +1,4 @@ +/// import { spawn, type ChildProcess } from "node:child_process"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; @@ -43,7 +44,7 @@ it.each(["tool-first", "permission-first"])("real ACPX streams Cursor tool origi const evidence = createCursorToolEvidence({ sessionId: "native", turnId: "turn", workingDirectory: cwd, active: () => true, emit: event => { validateAcpxRichEvent(event); fields.push(Object.fromEntries((event.payload.details as Array<{ name: string; value: string }>).map(field => [field.name, field.value]))); }, }); - const runtime = createAcpRuntime({ cwd, agentRegistry: createAgentRegistry({ overrides: { cursor: "fixture" } }), sessionStore: createRuntimeStore({ stateDir: join(cwd, "state") }), permissionMode: "approve-reads", authPolicy: "skip", + const runtime = createAcpRuntime({ cwd, agentRegistry: createAgentRegistry({ overrides: { cursor: "fixture" } }), sessionStore: createRuntimeStore({ stateDir: join(cwd, "state") }), permissionMode: "approve-reads", spawnAgent: () => { const child = spawn(process.execPath, ["-e", peer], { cwd, env: { ORDER: order }, stdio: ["pipe", "pipe", "pipe"] }); children.push(child); return child; }, onAcpMessage: (direction, value) => { const message = value as { id?: number; result?: { outcome?: unknown } }; diff --git a/packages/paperclip-runner/src/drivers/acpx/cursor-sdk-interactions.test.ts b/packages/paperclip-runner/src/drivers/acpx/cursor-sdk-interactions.test.ts index 1566e01cd7d..8bf956f9af7 100644 --- a/packages/paperclip-runner/src/drivers/acpx/cursor-sdk-interactions.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/cursor-sdk-interactions.test.ts @@ -1,3 +1,4 @@ +/// import { spawn } from "node:child_process"; import { createRequire } from "node:module"; import { mkdtemp, rm } from "node:fs/promises"; diff --git a/packages/paperclip-runner/src/drivers/acpx/cursor-tool-evidence.test.ts b/packages/paperclip-runner/src/drivers/acpx/cursor-tool-evidence.test.ts index 0c208001bc6..5410fced811 100644 --- a/packages/paperclip-runner/src/drivers/acpx/cursor-tool-evidence.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/cursor-tool-evidence.test.ts @@ -34,7 +34,7 @@ it.each(["tool-first", "permission-first"])("correlates opaque C0/C1/DEL native expect(expectedIds.at(-1)).toBe("safe-tool-1"); for (const [index, toolCallId] of rawIds.entries()) { const native = { sessionId: "session", inferredKind: "execute" as const, raw: { - sessionId: "session", toolCall: { toolCallId, kind: "execute" }, + sessionId: "session", toolCall: { toolCallId, kind: "execute" as const }, options: [{ kind: "reject_once" as const, optionId: `native-denial-${index}`, name: "Deny" }], } }; const before = structuredClone(native); @@ -99,7 +99,7 @@ it("ignores inactive turns and never lets observation errors undo a delivered de }); it("bounds retained tool origins", () => { const s = setup(); for (let i = 0; i < 257; i++) s.p.tool({ ...initial, toolCallId: `tool-${i}` }); - expect(s.fields().at(-1).stage).toBe("evidence_incomplete"); expect(s.events).toHaveLength(257); + expect(s.fields().at(-1)!.stage).toBe("evidence_incomplete"); expect(s.events).toHaveLength(257); }); it("rejects commands missing from their original frame and bounded oversized input", () => { @@ -159,5 +159,5 @@ it("preserves execute denial evidence after valid edit and read permission input it.each(["edit", "read", "unknown-native-kind"])("still rejects a contradictory permission kind %s", kind => { const s = setup(); s.p.tool(initial); s.p.permission({ raw: { sessionId: "session", toolCall: { toolCallId: "tool", kind, rawInput: { path: "/fixture/file" } } } }, "request", ["decline"]); - expect(s.fields().at(-1).stage).toBe("evidence_incomplete"); expect(s.unavailable()).toBe(true); + expect(s.fields().at(-1)!.stage).toBe("evidence_incomplete"); expect(s.unavailable()).toBe(true); }); diff --git a/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts b/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts index 2b2e8798eec..317feaf02cd 100644 --- a/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/grok-native-session-options.test.ts @@ -7,6 +7,11 @@ import { expect, it } from "vitest"; import { createGrokEffortAdmission } from "./grok-effort.js"; import { acpxSidecarErrorCode } from "../../cli/acpx-sidecar-input.js"; +// The patched acpx runtime implements this method without declaring it. +interface ExtensionRuntime { + requestExtension(input: { handle: AcpRuntimeHandle; method: string; params: Record; sessionMode?: "persistent" | "oneshot" }): Promise>; +} + it("retains a native Grok acknowledgement failure through the real ACPX connection", async () => { const root = await mkdtemp(join(tmpdir(), "grok-rejected-admission-")); const fixture = join(root, "grok.cjs"); @@ -94,7 +99,7 @@ it.each(["low", "high"] as const)("transports and restores native Grok %s effort handle = await runtime.ensureSession(input); expect(handle.backendSessionId).toBe(originalSession); expect(admission.isReady()).toBe(false); - await runtime.requestExtension({ handle, method: "_x.ai/session/info", params: { sessionId: handle.backendSessionId }, sessionMode: "persistent" }); + await (runtime as typeof runtime & ExtensionRuntime).requestExtension({ handle, method: "_x.ai/session/info", params: { sessionId: handle.backendSessionId }, sessionMode: "persistent" }); admission.assertReady(); const turn = runtime.startTurn({ handle, text: "Verify the restored fixture", mode: "prompt", requestId: "grok-cold-turn", timeoutMs: 5_000 }); const output: string[] = []; diff --git a/packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.test.ts b/packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.test.ts index 397cd56a238..61cd382955b 100644 --- a/packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.test.ts @@ -686,7 +686,7 @@ describe("native ACPX execution closure", () => { const fences = await Promise.all([listen(), listen()]); const fds = fences.map(server => (server as Server & { _handle?: { fd?: number } })._handle!.fd!); const installation = await verifyNativeAcpxInstallation(declaration); - const owner = createAcpxCommandLeaseOwner(await installation.openCommand(), installation.openCommand); + const owner = createAcpxCommandLeaseOwner(await installation.openCommand(), signal => installation.openCommand({ signal })); const pidFile = join(declaration.distributionRoot, "native-pid"); let port: Awaited> | undefined; let deadline: ReturnType | undefined; diff --git a/packages/paperclip-runner/src/drivers/acpx/pi-runtime-extension.test.ts b/packages/paperclip-runner/src/drivers/acpx/pi-runtime-extension.test.ts index c11999afac8..d5e4ccca82c 100644 --- a/packages/paperclip-runner/src/drivers/acpx/pi-runtime-extension.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/pi-runtime-extension.test.ts @@ -4,7 +4,7 @@ import { mkdtemp, mkdir, realpath, rename, rm, symlink, writeFile } from "node:f import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; -import Ajv from "ajv"; +import { Ajv } from "ajv"; import { checkPiNativeTool, installPiRuntimeExtension, piMcpRequest, readPiRuntimeConfiguration, PI_NATIVE_QUESTION_TOOL, PI_PERMISSION_TITLE_PREFIX, type PiExtensionApi, type PiRuntimeConfiguration, type PiToolDefinition, @@ -12,7 +12,7 @@ import { const directories: string[] = []; afterEach(async () => { for (const path of directories.splice(0)) await rm(path, { recursive: true, force: true }); }); -async function workspace() { +async function workspace(): Promise<{ root: string; config: PiRuntimeConfiguration }> { const root = await mkdtemp(join(tmpdir(), "paperclip-pi-extension-")); directories.push(root); await mkdir(join(root, "workspace")); await mkdir(join(root, "skills")); await mkdir(join(root, "private")); return { root, config: { @@ -20,7 +20,7 @@ async function workspace() { workspace: join(root, "workspace"), readOnly: false, readRoots: [join(root, "skills")], protectedRoots: [join(root, "private")], instructions: "Use the assigned Paperclip tools", servers: [], - } satisfies PiRuntimeConfiguration }; + } }; } function harness() { const handlers = new Map any>(); @@ -241,7 +241,7 @@ describe("owned Pi runtime extension", () => { it("revalidates paths after a human wait and never treats cancellation as approval", async () => { const { root, config } = await workspace(); const h = harness(); await installPiRuntimeExtension(h.api, config); h.handlers.get("turn_start")!(); - const context = { cwd: config.workspace, ui: { select: vi.fn(async () => { + const context = { cwd: config.workspace, ui: { select: vi.fn(async (_title: string) => { await symlink(join(root, "private"), join(config.workspace, "target")); return "Allow once"; }) } }; diff --git a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.test.ts b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.test.ts index b2edd4c6367..cb38eec8b12 100644 --- a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.test.ts @@ -68,7 +68,7 @@ describe("historical ACPX profile decoding", () => { ["copilot", "copilot-profile-v85-cursor-cloud-qualification-historical-identity.json", "977d68b327f3964e8337ef8ba00230eb1b1697017db21f8010f664fee3d3c70a", "private Cloud qualification"], ["grok", "grok-profile-v14-identity.json", "08294ec6bdcbdbe46c8c8b711d8b368558a05f0d84d25c9f5fd0c3b67b79cd17", "published ed8"], ["grok", "grok-profile-v14-cursor-cloud-qualification-historical-identity.json", "fa34ba395ff51affed7e7195dc44f4e33a3c6718ada1c80e8e36852cf7af40ac", "private Cloud qualification"], - ] as const)("keeps %s identity %s immutable and decode-only", (agent, filename, checksum) => { + ] as const)("keeps %s identity %s immutable and decode-only", (agent, filename, checksum, _source) => { const bytes = readFileSync(new URL(`../../../test/fixtures/${filename}`, import.meta.url)); expect(createHash("sha256").update(bytes).digest("hex")).toBe(checksum); const identity = JSON.parse(bytes.toString("utf8")); diff --git a/packages/paperclip-runner/src/drivers/acpx/response-delivery.test.ts b/packages/paperclip-runner/src/drivers/acpx/response-delivery.test.ts index 8e2073ec064..06f721541a7 100644 --- a/packages/paperclip-runner/src/drivers/acpx/response-delivery.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/response-delivery.test.ts @@ -1,3 +1,4 @@ +/// import { describe, expect, it, vi } from "vitest"; import { deliverAcpxResponse, requireAcpxResponseDelivery } from "./response-delivery.js"; diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts index 3c663b40447..793aadce056 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts @@ -272,7 +272,7 @@ describe("ACPX runtime host", () => { it("rejects preconfigured Codex before admission resources or runtime launch", async () => { const fixture = await hostFixture(); const openRuntime = vi.fn(), prepareSandbox = vi.fn(); - await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "codex", permissionMode: "approve-all", + await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "codex", model: "gpt-5.6-sol", permissionMode: "approve-all", environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" }, }, fixture.dependencies({ openRuntime, prepareSandbox }))).rejects.toThrow("cannot protect Runner state"); expect(prepareSandbox).not.toHaveBeenCalled(); @@ -282,7 +282,7 @@ describe("ACPX runtime host", () => { it.each(["claude", "codex"] as const)("rejects preconfigured read-only %s before provider admission", async agent => { const fixture = await hostFixture(); const openRuntime = vi.fn(); - await expect(AcpxRuntimeHost.open({ ...fixture.options, agent, permissionMode: "approve-all", + await expect(AcpxRuntimeHost.open({ ...fixture.options, agent, model: "explicit-test-model", permissionMode: "approve-all", providerPolicy: { readOnly: true }, environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" }, }, fixture.dependencies({ openRuntime }))).rejects.toThrow("preconfigured ACPX read-only execution is not supported"); expect(openRuntime).not.toHaveBeenCalled(); @@ -291,7 +291,7 @@ describe("ACPX runtime host", () => { it.each(["approve-reads", "approve-paperclip", "deny-all"] as const)("rejects unqualified preconfigured permission mode %s before admission", async permissionMode => { const fixture = await hostFixture(); const openRuntime = vi.fn(); - await expect(AcpxRuntimeHost.open({ ...fixture.options, permissionMode, + await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", permissionMode, environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" }, }, fixture.dependencies({ openRuntime }))).rejects.toThrow("requires ACPX approve-all"); expect(openRuntime).not.toHaveBeenCalled(); @@ -379,7 +379,7 @@ describe("ACPX runtime host", () => { ])("rejects invalid assigned connection bindings before runtime launch", async (environment) => { const fixture = await hostFixture(); const openRuntime = vi.fn(); - await expect(AcpxRuntimeHost.open({ ...fixture.options, environment }, fixture.dependencies({ openRuntime }))).rejects.toThrow(/assigned native MCP/); + await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", permissionMode: "approve-reads", environment }, fixture.dependencies({ openRuntime }))).rejects.toThrow(/assigned native MCP/); expect(openRuntime).not.toHaveBeenCalled(); }); @@ -491,7 +491,8 @@ describe("ACPX runtime host", () => { const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", - environment: { ...fixture.options.environment, + permissionMode: "approve-reads", + environment: { PAPERCLIP_NATIVE_MCP_NAME: "paperclip-assigned", PAPERCLIP_NATIVE_MCP_URL: "http://127.0.0.1:3211/mcp/gateway", PAPERCLIP_NATIVE_MCP_TOKEN: "fixture-gateway-token-".repeat(3), @@ -518,7 +519,7 @@ describe("ACPX runtime host", () => { let servers: AcpxRuntimePortOpenOptions["mcpServers"] = []; const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", permissionMode: "approve-reads", providerPolicy: { readOnly }, - environment: { ...fixture.options.environment, PAPERCLIP_NATIVE_MCP_STDIO: JSON.stringify([binding]), PAPERCLIP_NATIVE_MCP_PROXY: "/verified/runnerd" }, + environment: { PAPERCLIP_NATIVE_MCP_STDIO: JSON.stringify([binding]), PAPERCLIP_NATIVE_MCP_PROXY: "/verified/runnerd" }, }, fixture.dependencies({ openRuntime: async options => { servers = options.mcpServers; return runtimePort({ getStatus: async () => ({ models: { currentModelId: "claude-sonnet-5" } }) }); @@ -1042,12 +1043,12 @@ describe("ACPX runtime host", () => { const events: CanonicalProviderEvent[] = []; const evidence = createCopilotToolEvidence({ sessionId: "backend-1", turnId: "turn-1", workingDirectory: fixture.options.workingDirectory, active: () => true, emit: event => { validateAcpxRichEvent(event); events.push(JSON.parse(JSON.stringify(event))); } }); - let bridge: AcpxRuntimePortOpenOptions["mcpServers"][number] | undefined; + let bridge: Extract | undefined; const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "copilot", model: "gpt-5.6-sol", permissionMode: "deny-all", environment: { COPILOT_GITHUB_TOKEN: "fixture-not-a-real-credential" }, semanticTools: { tools: [{ name: "get_task_context", inputSchema: { type: "object", properties: {}, additionalProperties: false } }], handler: async () => ({ accepted: false }), captureSemanticReceipt: () => evidence.captureSemanticReceipt() }, - }, fixture.dependencies({ openRuntime: async options => { bridge = options.mcpServers[0]; return runtimePort(); } })); + }, fixture.dependencies({ openRuntime: async options => { bridge = urlBridge(options.mcpServers[0]); return runtimePort(); } })); try { const response = await fetch(bridge!.url, { method: "POST", headers: { Authorization: `Bearer ${bridge!.bearerToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "get_task_context", arguments: {} } }) }); @@ -1092,7 +1093,7 @@ describe("ACPX runtime host", () => { }, fixture.dependencies({ openRuntime: async (options) => { - bridge = options.mcpServers[0]; + bridge = urlBridge(options.mcpServers[0]); return runtimePort(); }, }), @@ -1990,10 +1991,10 @@ describe("ACPX runtime host", () => { it("retains a settled turn when shutdown cleanup must be retried", async () => { const fixture = await hostFixture(); const credential = credentialLeaseFixture(fixture.root); - let settleTurn!: (value: { stopReason: string }) => void; + let settleTurn!: (value: Awaited) => void; const turn = { ...runtimeTurn(), - result: new Promise<{ stopReason: string }>((resolve) => { + result: new Promise>((resolve) => { settleTurn = resolve; }), }; @@ -2019,7 +2020,7 @@ describe("ACPX runtime host", () => { host.startTurn({ text: "Complete the task.", requestId: "turn-1" }); const firstClose = host.close({ reason: "first shutdown" }); - settleTurn({ stopReason: "end_turn" }); + settleTurn({ status: "completed", stopReason: "end_turn" }); await expect(firstClose).rejects.toThrow(/cleanup failed/); expect(turn.cancel).toHaveBeenCalledOnce(); expect(credential.close).not.toHaveBeenCalled(); @@ -2332,6 +2333,11 @@ function credentialLeaseFixture(root: string) { }; } +function urlBridge(server: AcpxRuntimePortOpenOptions["mcpServers"][number] | undefined) { + if (!server || !("url" in server)) throw new Error("missing tool bridge"); + return server; +} + function runtimePort( input: { identity?: AcpxRuntimePort["identity"]; @@ -2402,7 +2408,7 @@ async function hostFixture() { dependencies( input: Pick & Partial< - Pick + Pick > & { // Opt-in only. When true, `prepareSandbox` runs the real // preparation once, then returns that same sandbox for every @@ -2413,19 +2419,19 @@ async function hostFixture() { ): AcpxRuntimeHostDependencies { let reusedSandbox: AcpxRuntimeSandbox | null = null; return { - verifyInstallation: async (profile) => + verifyInstallation: input.verifyInstallation ?? (async (profile) => ({ commandDigest: profile.commandDigest, agentServerPackageJsonPath: join(root, "package.json"), agentRuntimePackageJsonPath: null, openCommand: async () => command, - }) satisfies VerifiedAcpxInstallation, + }) satisfies VerifiedAcpxInstallation), openRuntime: input.openRuntime, // Record the skills home the instant the sandbox exists, ahead of // the materialize call that seals it. A test that overrides // `prepareSandbox` for a non-Claude agent replaces this wrapper, but // those agents never materialize skills, so nothing is lost. - prepareSandbox: async (sandboxInput) => { + prepareSandbox: input.prepareSandbox ?? (async (sandboxInput) => { if (input.reuseSandbox && reusedSandbox) return reusedSandbox; const sandbox = await prepareAcpxRuntimeSandbox(sandboxInput); if (sandboxInput.agent === "claude") { @@ -2433,7 +2439,7 @@ async function hostFixture() { } if (input.reuseSandbox) reusedSandbox = sandbox; return sandbox; - }, + }), retainAdmissionCleanup: trackAdmissionCleanup, reportRetainedCleanupFailure: input.reportRetainedCleanupFailure ?? vi.fn(), diff --git a/packages/paperclip-runner/src/drivers/codex/app-server-transport.test.ts b/packages/paperclip-runner/src/drivers/codex/app-server-transport.test.ts index ddeef5fc806..b42c13323d0 100644 --- a/packages/paperclip-runner/src/drivers/codex/app-server-transport.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/app-server-transport.test.ts @@ -128,10 +128,11 @@ describe("Codex app-server transport limits", () => { }); it("installs cleanup handlers before synchronously reporting process ownership", async () => { - const originalSpawn = ChildProcess.prototype.spawn; + const childProcessPrototype = ChildProcess.prototype as ChildProcess & { spawn(...args: unknown[]): unknown }; + const originalSpawn = childProcessPrototype.spawn; let spawnedProcess: ChildProcess | undefined; const spawnSpy = vi - .spyOn(ChildProcess.prototype, "spawn") + .spyOn(childProcessPrototype, "spawn") .mockImplementation(function (this: ChildProcess, ...args) { spawnedProcess = this; return originalSpawn.apply(this, args); diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.context-control.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.context-control.test.ts index 719c7035661..13e83500aba 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.context-control.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.context-control.test.ts @@ -660,7 +660,7 @@ describe("Codex app-server Codex driver", () => { ).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError); for (const action of ["set", "pause", "resume", "clear"] as const) { await expect( - unsupported.goal?.({ action, ...(action === "set" ? { objective: "Must not reach the provider" } : {}) }), + unsupported.goal?.(action === "set" ? { action, objective: "Must not reach the provider" } : { action }), ).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError); } expect(unsupportedTransport.calls.filter(({ method }) => diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts index 142955c787c..19e218768af 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.events.test.ts @@ -401,7 +401,7 @@ describe("Codex app-server Codex driver", () => { { runDelta: first, runDeltaComplete: true }, { runDelta: { ...first, ...second }, runDeltaComplete: false }, ]); - expect(await session.usage()).toMatchObject({ runDeltaComplete: false }); + expect(await session.usage!()).toMatchObject({ runDeltaComplete: false }); }); it("keeps usage reported between runs in the next run's delta so totals stay exact", async () => { @@ -478,7 +478,7 @@ describe("Codex app-server Codex driver", () => { expect(events.some((event) => event.eventType === "session.failed")).toBe( false, ); - expect(await session.usage()).toMatchObject({ + expect(await session.usage!()).toMatchObject({ total: { inputTokens: 10, outputTokens: 4 }, }); }); diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts index 4cb21eaa922..a0baee374a6 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts @@ -785,6 +785,7 @@ describe("Codex app-server Codex driver", () => { runId: "run-1", environmentLeaseId: "lease-1", runnerInstanceId: "runner-codex", + driverSessionId: "thread-1", normalizedSessionId: "normalized-1", }, capabilities, @@ -819,7 +820,7 @@ it("sends explicit skill input on every requested turn", async () => { await collectUntilTerminal(session.events()); transport.turnStartResponse = Promise.resolve({ turn: { id: "turn-2", status: "inProgress", items: [] } }); } - await session.close(); + await session.close({ reason: "fixture complete" }); }); @@ -830,7 +831,7 @@ it("reapplies explicit skills after recovery without persisting them into ordina runId: "initial", normalizedSessionId: "skill-recovery", workingDirectory: WORKSPACE, }); const snapshot = await original.snapshot(); - await original.close(); + await original.close({ reason: "fixture complete" }); for (const selected of [skillInputs, []]) { const transport = new FakeCodexTransport(); const recovery = await makeDriver([transport], { skillInputs: selected }).recoverSession(snapshot); @@ -840,6 +841,6 @@ it("reapplies explicit skills after recovery without persisting them into ordina await session.startTurn({ message: { role: "user", text: "Continue" } }); const items = transport.calls.find((call) => call.method === "turn/start")?.params.input as Array<{type:string}>; expect(items.filter((item) => item.type === "skill")).toEqual(selected); - } finally { await session.close(); } + } finally { await session.close({ reason: "fixture complete" }); } } }); diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.runtime-requests.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.runtime-requests.test.ts index ada36dcf876..c3b718401ea 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.runtime-requests.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.runtime-requests.test.ts @@ -76,7 +76,7 @@ it("preserves runnerd permission expiry before a failed provider terminal and re expect(resolve).not.toHaveBeenCalled(); transport.push("turn/completed", { threadId: "thread-1", turn: { id: turnId, status: "failed", error: { code: "ACPX_PROVIDER_PROCESS_LOST", message: "Provider died" } } }); await vi.waitFor(() => expect(events.some(event => event.eventType === "turn.failed")).toBe(true)); - await session.close(); await consume; + await session.close({ reason: "fixture complete" }); await consume; const settlements = events.filter(event => ["runtime_request.expired", "runtime_request.cancelled", "runtime_request.resolved"].includes(event.eventType)); expect(settlements).toHaveLength(1); expect(settlements[0]).toMatchObject({ eventType: "runtime_request.expired", turnId, itemId: "native-edit", payload: { ...payload, turnId, itemId: "native-edit" } }); @@ -105,7 +105,7 @@ it("preserves canonical question expiry after provider loss and refuses late ans await expect(session.resolveRuntimeRequest?.({ requestId: "native-question", turnId, resolution: { action: "accept" } })).rejects.toThrow(); transport.push("turn/completed", { threadId: "thread-1", turn: { id: turnId, status: "failed", error: { code: "ACPX_PROVIDER_PROCESS_LOST", message: "Provider died" } } }); await vi.waitFor(() => expect(events.some(event => event.eventType === "turn.failed" || event.eventType === "session.failed")).toBe(true)); - await session.close(); await consume; + await session.close({ reason: "fixture complete" }); await consume; expect(events.filter(event => event.eventType === "runtime_request.expired")).toHaveLength(1); expect(events.find(event => event.eventType === "runtime_request.expired")).toMatchObject({ turnId, itemId: "question-item", payload }); expect(events.some(event => event.eventType === "session.failed" && event.payload.code === "turn_binding_mismatch")).toBe(false); @@ -127,7 +127,7 @@ it.each(["turn", "kind", "type", "replay"])("refuses canonical permission expiry requestType: kind === "type" ? "input" : "permission", replayAllowed: kind === "replay", reason: "provider_process_lost" }; const notification = runnerdRuntimeRequestExpiryNotification("runtime_request.expired", payload, "thread-1", kind === "turn" ? "foreign-turn" : turnId)!; transport.push(notification.method, notification.params); - await pending; await consume; await session.close(); + await pending; await consume; await session.close({ reason: "fixture complete" }); expect(events.some(e => e.eventType === "runtime_request.expired")).toBe(false); expect(events.some(e => e.eventType === "session.failed" && e.payload.code === "turn_binding_mismatch")).toBe(true); }); @@ -159,7 +159,7 @@ describe("Codex app-server Codex driver", () => { }); expect(await response).toEqual({ action: "accept", content: null, _meta: null }); expect(session.pendingRuntimeRequests?.()).toHaveLength(0); - await session.close(); + await session.close({ reason: "fixture complete" }); }); it("validates runtime request resolutions against the kind of request they answer", async () => { diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.semantic-results.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.semantic-results.test.ts index 8982c48fd5e..7c470f79972 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.semantic-results.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.semantic-results.test.ts @@ -68,7 +68,7 @@ describe("Codex app-server Codex driver", () => { threadId: "thread-1", turnId: "turn-1", arguments: {} }); expect(feedback).toHaveBeenCalledExactlyOnceWith({ ...report, continuation }); expect((await session.snapshot()).semanticResult?.result).toEqual({ ...report, continuation }); - } finally { await session.close(); } + } finally { await session.close({ reason: "fixture complete" }); } }); it.each([ @@ -92,7 +92,7 @@ describe("Codex app-server Codex driver", () => { expect(response).toMatchObject({ success: false }); expect(feedback).not.toHaveBeenCalled(); expect((await session.snapshot()).semanticResult).toBeNull(); - } finally { await session.close(); } + } finally { await session.close({ reason: "fixture complete" }); } }); it("returns current approval feedback and permits correcting a rejected completion report", async () => { @@ -111,7 +111,7 @@ describe("Codex app-server Codex driver", () => { expect(await call("corrected")).toMatchObject({ success: true, contentItems: [{ type: "inputText", text: expect.stringContaining("/approvals/approval-1") }] }); expect((await session.snapshot()).semanticResult?.result).toEqual(result); - await session.close(); + await session.close({ reason: "fixture complete" }); }); it("accepts an explicit response-wake yield through paperclip_finish", async () => { @@ -194,7 +194,7 @@ describe("Codex app-server Codex driver", () => { }, }, })).toMatchObject({ success: false }); - await otherSession.close(); + await otherSession.close({ reason: "fixture complete" }); }); it("makes duplicate semantic completion idempotent and rejects changed payloads", async () => { diff --git a/packages/paperclip-runner/src/drivers/codex/codex-protocol-integrity.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-protocol-integrity.test.ts index 20dbb749a2a..080b2717bbd 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-protocol-integrity.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-protocol-integrity.test.ts @@ -16,8 +16,8 @@ import { NativeSessionProtocolIntegrityError } from "../../contracts/native-sess import { DurablePrpControlPlane, durableRecoveryInternals, - type DurableRecoveryIdentity, } from "../../control-plane/durable-prp-control-plane.js"; +import type { DurableRecoveryIdentity } from "../../control-plane/prp-transport-types.js"; import { createCapabilityRunnerdCodexTransport } from "../../live/runnerd-codex-transport.js"; import { executeNativeSession } from "../../native-session-runtime.js"; import { CodexAppServerDriver } from "./codex-app-server-driver.js"; @@ -44,7 +44,7 @@ async function authenticatedRunner( runnerBinary: string, ) { const framed = (domain: string, parts: Buffer[]) => { - const values = [Buffer.from(domain), Buffer.from([0])]; + const values: Buffer[] = [Buffer.from(domain), Buffer.from([0])]; for (const part of parts) { const length = Buffer.alloc(8); length.writeBigUInt64BE(BigInt(part.length)); @@ -476,6 +476,7 @@ describe("Codex protocol integrity propagation", () => { normalizedSessionId: identity.normalizedSessionId, driverKind: "codex_app_server", protocolVersion: 1, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, }, provider: { kind: "codex", model: null }, completionContract: { diff --git a/packages/paperclip-runner/src/drivers/codex/codex-question-adapter.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-question-adapter.test.ts index 9297dbd2c0f..c38675e3186 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-question-adapter.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-question-adapter.test.ts @@ -335,7 +335,7 @@ describe("Codex structured question adapter", () => { questions: [{ id: "form", question: "Choose one" }], }); expect(safeForm?.description).toContain("Bearer [REDACTED]"); - expect(safeForm?.description.endsWith(preservedTail)).toBe(true); + expect(safeForm?.description?.endsWith(preservedTail)).toBe(true); expect(() => normalizeCodexQuestionSet("mcpServer/elicitation/request", { requestedSchema: { diff --git a/packages/paperclip-runner/src/drivers/codex/codex-resume-usage.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-resume-usage.test.ts index 7e986c46baf..54d325146fb 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-resume-usage.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-resume-usage.test.ts @@ -35,7 +35,7 @@ describe("Codex resume accounting through the production driver", () => { turnId: string, ) => { const saved = JSON.parse(JSON.stringify(await session.snapshot!())); - await session.close(); + await session.close({ reason: "test complete" }); transport.readResponse = { thread: { id: "thread-1", sessionId: "provider-session-1", cwd: WORKSPACE, turns: [] }, }; @@ -60,7 +60,7 @@ describe("Codex resume accounting through the production driver", () => { session = await coldRestart(session, transports[2]!, "third", "turn-3"); await expect(runTurn(session, transports[2]!, "turn-3", { inputTokens: 170, outputTokens: 20 })) .resolves.toEqual({ inputTokens: 30, outputTokens: 4 }); - await session.close(); + await session.close({ reason: "test complete" }); }); it.each(["thread", "snapshot"] as const)( @@ -137,7 +137,7 @@ describe("Codex resume accounting through the production driver", () => { expect(await session.usage!()).toMatchObject({ runDelta: { inputTokens: 40, outputTokens: 6 }, }); - await session.close(); + await session.close({ reason: "fixture complete" }); second.readResponse = readResponse(140, 16); const recovery = await driver.recoverSession(persisted); expect(recovery.recovered).toBe(true); @@ -187,7 +187,7 @@ describe("Codex resume accounting through the production driver", () => { inputTokens: 140, outputTokens: 16, }); - await recoveredSession.close(); + await recoveredSession.close({ reason: "fixture complete" }); third.readResponse = readResponse(150, 19); const secondRecovery = await driver.recoverSession(advanced); expect(secondRecovery.recovered).toBe(true); @@ -205,8 +205,8 @@ describe("Codex resume accounting through the production driver", () => { ), ).toEqual([]); } finally { - await recoveredSession?.close(); - await session.close(); + await recoveredSession?.close({ reason: "fixture complete" }); + await session.close({ reason: "fixture complete" }); } }, ); @@ -245,7 +245,7 @@ describe("Codex resume accounting through the production driver", () => { baseline: { inputTokens: 100 }, latest: { inputTokens: 140 }, }; - await session.close(); + await session.close({ reason: "fixture complete" }); const tokenUsage = { total: { inputTokens: 150 }, last: { inputTokens: 10 }, @@ -270,8 +270,8 @@ describe("Codex resume accounting through the production driver", () => { second.calls.filter((call) => call.method === "turn/start"), ).toEqual([]); } finally { - await recoveredSession?.close(); - await session.close(); + await recoveredSession?.close({ reason: "fixture complete" }); + await session.close({ reason: "fixture complete" }); } }, ); @@ -300,7 +300,7 @@ describe("Codex resume accounting through the production driver", () => { }); await collectUntilTerminal(session.events()); const persisted = JSON.parse(JSON.stringify(await session.snapshot!())); - await session.close(); + await session.close({ reason: "fixture complete" }); second.readResponse = { thread: { id: "thread-1", @@ -384,7 +384,7 @@ describe("Codex resume accounting through the production driver", () => { .filter((call) => call.method === "thread/read") .every((call) => call.params.includeTurns === false), ).toBe(true); - await session.close(); + await session.close({ reason: "fixture complete" }); }); it("keeps usage reported between runs in the next run's delta after a restore", async () => { @@ -401,7 +401,7 @@ describe("Codex resume accounting through the production driver", () => { await new Promise(resolve => setImmediate(resolve)); const persisted = JSON.parse(JSON.stringify(await session.snapshot!())); expect(persisted.codexUsageBaseline).toMatchObject({ latest: { inputTokens: 130 }, accounted: { inputTokens: 100 } }); - await session.close(); + await session.close({ reason: "test complete" }); second.readResponse = { thread: { id: "thread-1", sessionId: "provider-session-1", cwd: WORKSPACE, turns: [{ id: "turn-1", status: "completed", items: [] }], tokenUsage: { total: { inputTokens: 130, outputTokens: 10 } } } }; const recovery = await driver.recoverSession(persisted); @@ -413,7 +413,7 @@ describe("Codex resume accounting through the production driver", () => { second.push("thread/tokenUsage/updated", { threadId: "thread-1", turnId: "turn-2", tokenUsage: { total: { inputTokens: 150, outputTokens: 16 } } }); for await (const event of recovered.events()) if (event.payload.kind === "usage") break; expect(await recovered.usage!()).toMatchObject({ runDelta: { inputTokens: 50, outputTokens: 6 } }); - await recovered.close(); + await recovered.close({ reason: "test complete" }); }); it("keeps the startup external sandbox profile on subsequent turns", async () => { @@ -439,6 +439,6 @@ describe("Codex resume accounting through the production driver", () => { transport.calls.find((call) => call.method === "turn/start")?.params .permissions, ).toBe(start.params.permissions); - await session.close(); + await session.close({ reason: "fixture complete" }); }); }); diff --git a/packages/paperclip-runner/src/drivers/codex/codex-turn-diff.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-turn-diff.test.ts index c7cc22e1663..79849db1dba 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-turn-diff.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-turn-diff.test.ts @@ -1,17 +1,19 @@ import { describe, expect, it } from "vitest"; -import { parseCodexTurnDiff, summarizeCodexTurnDiff } from "./codex-turn-diff.js"; +import { parseCodexTurnDiff, summarizeCodexTurnDiff, type ParsedCodexTurnDiffFile } from "./codex-turn-diff.js"; describe("Codex turn diff parser", () => { it("summarizes parsed files for work-product metadata", () => { - expect(summarizeCodexTurnDiff([ + const textFiles: ParsedCodexTurnDiffFile[] = [ { path: "a.ts", operation: "modify", previousPath: null, additions: 4, deletions: 2, binary: false, diff: "patch" }, { path: "b.ts", operation: "create", previousPath: null, additions: 3, deletions: 0, binary: false, diff: "patch" }, - ])).toEqual({ files: 2, additions: 7, deletions: 2 }); + ]; + expect(summarizeCodexTurnDiff(textFiles)).toEqual({ files: 2, additions: 7, deletions: 2 }); - expect(summarizeCodexTurnDiff([ + const binaryFiles: ParsedCodexTurnDiffFile[] = [ { path: "logo.png", operation: "modify", previousPath: null, additions: null, deletions: null, binary: true, diff: null }, - ])).toEqual({ files: 1, additions: null, deletions: null }); + ]; + expect(summarizeCodexTurnDiff(binaryFiles)).toEqual({ files: 1, additions: null, deletions: null }); }); it("parses a complete snapshot with bounded file statistics", () => { diff --git a/packages/paperclip-runner/src/drivers/codex/prepared-context-transport.test.ts b/packages/paperclip-runner/src/drivers/codex/prepared-context-transport.test.ts index dadddbad2b6..5a4a522dc0e 100644 --- a/packages/paperclip-runner/src/drivers/codex/prepared-context-transport.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/prepared-context-transport.test.ts @@ -91,7 +91,7 @@ describe("prepared native context transport", () => { text: expect.stringContaining('"task"'), }), ]); - expect((input?.[0] as { text: string }).text).toContain( + expect((input as Array<{ text: string }>)[0]!.text).toContain( '"message":"ordinary standalone task"', ); }); diff --git a/packages/paperclip-runner/src/drivers/cursor-cloud/app-server-proxy.test.ts b/packages/paperclip-runner/src/drivers/cursor-cloud/app-server-proxy.test.ts index 72fe00b3ec6..2cffbfa7c91 100644 --- a/packages/paperclip-runner/src/drivers/cursor-cloud/app-server-proxy.test.ts +++ b/packages/paperclip-runner/src/drivers/cursor-cloud/app-server-proxy.test.ts @@ -87,7 +87,7 @@ describe("Cursor Cloud bounded stdio proxy", () => { const terminalGate = new Promise(resolve => { releaseTerminalWrites = resolve; }); let pausedTerminalWrites = 0; const write = CursorCloudCheckpointStore.prototype.write; - vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(value) { + vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(this: CursorCloudCheckpointStore, value) { if (value.phase === "terminal" && value.pendingTerminalDelivery) { pausedTerminalWrites += 1; await terminalGate; @@ -151,7 +151,7 @@ describe("Cursor Cloud bounded stdio proxy", () => { }); let openedSession!: CursorCloudSessionHandle; const openSession = CursorCloudDriver.prototype.openSession; - vi.spyOn(CursorCloudDriver.prototype, "openSession").mockImplementation(async function(input) { + vi.spyOn(CursorCloudDriver.prototype, "openSession").mockImplementation(async function(this: CursorCloudDriver, input) { openedSession = await openSession.call(this, input); return openedSession; }); diff --git a/packages/paperclip-runner/src/drivers/cursor-cloud/cloud-client.test.ts b/packages/paperclip-runner/src/drivers/cursor-cloud/cloud-client.test.ts index 404986ecec0..7476a828047 100644 --- a/packages/paperclip-runner/src/drivers/cursor-cloud/cloud-client.test.ts +++ b/packages/paperclip-runner/src/drivers/cursor-cloud/cloud-client.test.ts @@ -139,7 +139,7 @@ describe("Cursor Cloud v1 HTTP client", () => { if (request.url?.includes("/artifacts/download?")) return json(response, { url: `https://${host}/proof?private-signature`, expiresAt: TIME }); response.end(bytes); }); - const externalHeaders: HeadersInit[] = []; + const externalHeaders: Array> = []; let unexpectedTargets = 0; const transport: typeof fetch = (input, init) => { const url = String(input); diff --git a/packages/paperclip-runner/src/drivers/cursor-cloud/cursor-cloud-driver.test.ts b/packages/paperclip-runner/src/drivers/cursor-cloud/cursor-cloud-driver.test.ts index 219f45cd95d..f871999474b 100644 --- a/packages/paperclip-runner/src/drivers/cursor-cloud/cursor-cloud-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/cursor-cloud/cursor-cloud-driver.test.ts @@ -71,7 +71,7 @@ function maintainedMutations(peer: Peer, maxResponseBytes?: number) { async function fixture(peer = new Peer(), extra: Partial = {}) { const root = await mkdtemp("/tmp/cc-"); roots.push(root); - const handler = vi.fn(async () => ({ success: true, contentItems: [{ type: "inputText", text: "accepted" }] })); + const handler = vi.fn(async (_call: Parameters[0]) => ({ success: true, contentItems: [{ type: "inputText", text: "accepted" }] })); const options: CursorCloudDriverOptions = { model: "explicit-model", config: { schema: "paperclip.cursor-cloud.config.v1", repos: [{ url: "https://github.com/example/repo", startingRef: "main" }] }, binding: { companyId: "company", agentId: "agent", issueId: "issue", normalizedSessionId: "session" }, runtimeDirectory: root, relay: { url: "https://relay.example.test/mcp", token: "private-relay-token", socketPath: join(root, "mcp.sock"), expiresAt: Date.now() + 600_000 }, instructions: "Follow these exact instructions.", @@ -111,7 +111,11 @@ async function nextAuthority(f: Awaited>) { return { next, checkpoint: await savedCheckpoint(f.root, f.session), eventCount: f.events.length }; } -function relayHeaders(peer: Peer): Record { return structuredClone(peer.request!.mcpServers[0]!.headers!); } +function relayHeaders(peer: Peer): Record { + const server = peer.request!.mcpServers![0]!; + if (!("headers" in server)) throw new Error("Expected an HTTP relay MCP server"); + return structuredClone(server.headers!); +} async function rpc(path: string, body: unknown, token: string | Record = "private-relay-token", session?: string): Promise<{ status: number; value: any; session?: string }> { return new Promise((accept, reject) => { @@ -206,7 +210,7 @@ describe("Cursor Cloud native driver", () => { await f.session.startTurn({ message: { role: "user", text: "Stop an observation in flight" } }); await reading.promise; try { - await f.session.interrupt!(); + await f.session.interrupt!({}); await vi.waitFor(() => expect(readAborted).toBe(true), { timeout: 100 }); expect((await f.session.read()).phase).toBe("terminal"); } finally { blocked.resolve(await original()); } @@ -257,7 +261,7 @@ describe("Cursor Cloud native driver", () => { await f.session.startTurn({ message: { role: "user", text: "Stop during a provider retry delay" } }); await waitFor(() => f.events.some(event => (event.payload as { code?: string }).code === "cursor_cloud_observation_retry")); const signal = ((peer.getRun.mock.calls[0] as unknown[])[2] as { signal: AbortSignal }).signal; - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(signal.aborted).toBe(true); expect((await f.session.read()).phase).toBe("terminal"); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled(); @@ -354,7 +358,7 @@ describe("Cursor Cloud native driver", () => { } } await reading.promise; - await session.interrupt!(); + await session.interrupt!({}); await acknowledgeTerminal(session); await session.attachRun!({ runId: "paperclip-followup" }); const next = await session.startTurn({ message: { role: "user", text: "Second task" } }); @@ -370,7 +374,7 @@ describe("Cursor Cloud native driver", () => { expect(JSON.parse(await readFile(checkpointPath, "utf8"))).toEqual(before); expect(await session.read()).toMatchObject({ agentId: previous.agentId, runId: followupRunId, turnId: next.turnId, phase: "running", pendingTerminalDelivery: null }); expect(events.slice(eventCount)).toEqual([]); - await session.interrupt!(); + await session.interrupt!({}); expect(await session.read()).toMatchObject({ runId: followupRunId, phase: "terminal", pendingTerminalDelivery: { providerRunId: followupRunId, turnId: next.turnId, status: "CANCELLED" } }); expect(events.filter(event => event.turnId === next.turnId && event.eventType === "turn.interrupted")).toHaveLength(1); }); @@ -393,14 +397,14 @@ describe("Cursor Cloud native driver", () => { }; const f = await fixture(peer); await f.session.startTurn({ message: { role: "user", text: "First task" } }); await waiting.promise; - await f.session.interrupt!(); + await f.session.interrupt!({}); const next = await nextAuthority(f); release.resolve(); await new Promise(accept => setImmediate(accept)); expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint); expect(f.events.slice(next.eventCount)).toEqual([]); expect(streams.map(stream => stream.runId)).toEqual([runId, peer.native!.id]); expect(streams[1]!.signal!.aborted).toBe(false); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect((await f.session.read()).pendingTerminalDelivery).toMatchObject({ providerRunId: peer.native!.id, turnId: next.next.turnId }); }); @@ -421,7 +425,7 @@ describe("Cursor Cloud native driver", () => { released.resolve(); await new Promise(accept => setImmediate(accept)); expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint); expect(f.events.slice(next.eventCount)).toEqual([]); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: peer.native!.id, turnId: next.next.turnId } }); }); @@ -441,7 +445,7 @@ describe("Cursor Cloud native driver", () => { released.resolve(oldAgent); await oldReconcile; expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint); expect(f.events.slice(next.eventCount)).toEqual([]); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: peer.native!.id, turnId: next.next.turnId } }); }); @@ -453,19 +457,19 @@ describe("Cursor Cloud native driver", () => { const previous = { ...peer.native! }; const oldRead = deferred(), reading = deferred(); peer.getRun.mockImplementationOnce(async () => { reading.resolve(); return oldRead.promise; }); - const oldStop = f.session.interrupt!(); await reading.promise; + const oldStop = f.session.interrupt!({}); await reading.promise; peer.getRun.mockResolvedValueOnce({ ...previous, status: "FINISHED" }); await f.session.reconcile!(); const next = await nextAuthority(f); const currentRead = deferred(), currentReading = deferred(); peer.getRun.mockImplementationOnce(async () => { currentReading.resolve(); return currentRead.promise; }); - const currentStop = f.session.interrupt!(); await currentReading.promise; + const currentStop = f.session.interrupt!({}); await currentReading.promise; const before = await savedCheckpoint(f.root, f.session); oldRead.resolve(previous); await oldStop; expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(peer.cancelRun).not.toHaveBeenCalled(); const readCount = peer.getRun.mock.calls.length; - const duplicateStop = f.session.interrupt!(); + const duplicateStop = f.session.interrupt!({}); await new Promise(accept => setImmediate(accept)); expect(peer.getRun).toHaveBeenCalledTimes(readCount); currentRead.resolve({ ...peer.native! }); await Promise.all([currentStop, duplicateStop]); @@ -480,7 +484,7 @@ describe("Cursor Cloud native driver", () => { if (phase === "follow-up") { await f.session.startTurn({ message: { role: "user", text: "First task" } }); await waitFor(() => f.events.some(event => event.eventType === "item.delta")); - await f.session.interrupt!(); await acknowledgeTerminal(f.session); + await f.session.interrupt!({}); await acknowledgeTerminal(f.session); await f.session.attachRun!({ runId: "paperclip-followup" }); } const before = await savedCheckpoint(f.root, f.session); @@ -498,7 +502,7 @@ describe("Cursor Cloud native driver", () => { expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(f.events.slice(eventCount)).toEqual([]); const reconciliations = peer.getAgent.mock.calls.length, cancellations = peer.cancelRun.mock.calls.length; - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(peer.getAgent).toHaveBeenCalledTimes(reconciliations); expect(peer.cancelRun).toHaveBeenCalledTimes(cancellations); const snapshot = await f.session.snapshot(); await f.session.close({ reason: "storage_recovered" }); await f.pump; expect(peer.getAgent).toHaveBeenCalledTimes(reconciliations); expect(peer.cancelRun).toHaveBeenCalledTimes(cancellations); @@ -507,7 +511,7 @@ describe("Cursor Cloud native driver", () => { expect(await savedCheckpoint(f.root, recovered)).toEqual({ ...before, relayTurnToken: before.relayTurnToken ?? null }); await recovered.startTurn({ message: { role: "user", text: "Retry after storage recovery" } }); await waitFor(() => peer.createAgent.mock.calls.length === 1 && peer.createRun.mock.calls.length === (phase === "fresh" ? 0 : 1)); - await recovered.interrupt!(); + await recovered.interrupt!({}); }); it("retains accepted provider work when its confirmed-identity checkpoint write fails", async () => { @@ -531,7 +535,7 @@ describe("Cursor Cloud native driver", () => { await rm(checkpointPath, { recursive: true }); await rename(savedPath, checkpointPath); } expect(await savedCheckpoint(f.root, f.session)).toMatchObject({ agentId: peer.native!.agentId, runId, phase: "running", pendingOperation: null }); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.cancelRun).toHaveBeenCalledTimes(1); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { status: "CANCELLED", providerRunId: runId } }); }); @@ -543,7 +547,7 @@ describe("Cursor Cloud native driver", () => { const failure = new Error("controlled post-publication persistence failure"); const originalWrite = CursorCloudCheckpointStore.prototype.write; let intercepted = false; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { const persisted = originalWrite.call(this, value); if (value.phase === "creating" && !intercepted) { intercepted = true; @@ -556,13 +560,13 @@ describe("Cursor Cloud native driver", () => { const started = f.session.startTurn({ message: { role: "user", text: "Stop before unsent launch" } }); const rejected = expect(started).rejects.toBe(failure); await held.promise; - const stopped = f.session.interrupt!(); await stoppingWritten.promise; + const stopped = f.session.interrupt!({}); await stoppingWritten.promise; expect(f.peer.createAgent).not.toHaveBeenCalled(); release.resolve(); await rejected; await stopped; expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(f.peer.createAgent).not.toHaveBeenCalled(); expect(f.peer.getAgent).not.toHaveBeenCalled(); expect(f.peer.cancelRun).not.toHaveBeenCalled(); await f.session.startTurn({ message: { role: "user", text: "Next admitted task" } }); await f.peer.created.promise; - await f.session.interrupt!(); + await f.session.interrupt!({}); } finally { release.resolve(); write.mockRestore(); } }); @@ -571,10 +575,10 @@ describe("Cursor Cloud native driver", () => { const f = await fixture(peer); await f.session.startTurn({ message: { role: "user", text: "First task" } }); await peer.created.promise; await waitFor(() => f.events.some(event => event.eventType === "item.delta")); - await f.session.interrupt!(); await acknowledgeTerminal(f.session); + await f.session.interrupt!({}); await acknowledgeTerminal(f.session); const held = deferred(), release = deferred(), stoppingWritten = deferred(); const originalWrite = CursorCloudCheckpointStore.prototype.write; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { const persisted = originalWrite.call(this, value); if (value.phase === "creating") return persisted.then(async () => { held.resolve(); await release.promise; }); if (value.phase === "stopping") void persisted.then(() => stoppingWritten.resolve()); @@ -582,7 +586,7 @@ describe("Cursor Cloud native driver", () => { }); try { const started = f.session.startTurn({ message: { role: "user", text: "Second task" } }); await held.promise; - const stopped = f.session.interrupt!(); await stoppingWritten.promise; + const stopped = f.session.interrupt!({}); await stoppingWritten.promise; expect(peer.createRun).not.toHaveBeenCalled(); release.resolve(); const next = await started; await stopped; @@ -605,7 +609,7 @@ describe("Cursor Cloud native driver", () => { const after = await savedCheckpoint(f.root, f.session); expect(after).toEqual({ ...before, sourceSequence: before.sourceSequence + 1 }); expect(f.events.some(event => event.eventType === "turn.failed")).toBe(false); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: runId } }); }); @@ -617,7 +621,7 @@ describe("Cursor Cloud native driver", () => { f.peer.getRun.mockResolvedValueOnce({ ...f.peer.native!, status: "FINISHED", ...(identity === "agent" ? { agentId: "bc-different" } : { id: "run-22222222-2222-4222-8222-222222222222" }) }); await expect(f.session.reconcile!()).rejects.toThrow("confirmed Agent/Run identity"); expect(await savedCheckpoint(f.root, f.session)).toEqual(before); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: runId } }); }); @@ -660,7 +664,7 @@ describe("Cursor Cloud native driver", () => { const activeSnapshot = await restored.snapshot(); await restored.detachControllerForRestart!(); await expect(new CursorCloudDriver(f.options).recoverSession(activeSnapshot)).rejects.toThrow("instructions changed"); const final = (await new CursorCloudDriver(revised).recoverSession(activeSnapshot)).session!; sessions.push(final); - await final.interrupt!(); + await final.interrupt!({}); await acknowledgeTerminal(final); await final.startTurn({ message: { role: "user", text: "One more task" } }); await waitFor(() => f.peer.createRun.mock.calls.length === 2); @@ -702,7 +706,7 @@ describe("Cursor Cloud native driver", () => { expect(f.handler).toHaveBeenCalledWith(expect.objectContaining({ tool: "write_note", arguments: { note: "first" }, turnId: expect.stringMatching(/^turn-/) })); await rpc(path, { id: "missing", method: "tools/call", params: { name: "not_admitted", arguments: {} } }, relayHeaders(f.peer), initialized.session); expect(f.handler).toHaveBeenCalledTimes(1); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(f.peer.cancelRun).toHaveBeenCalledTimes(1); expect(f.peer.getRun.mock.calls.length).toBeGreaterThanOrEqual(2); const late = await rpc(path, { id: "late", method: "tools/call", params: { name: "write_note", arguments: { note: "late" } } }, relayHeaders(f.peer), initialized.session); @@ -849,7 +853,7 @@ describe("Cursor Cloud native driver", () => { await waitFor(() => peer.getAgent.mock.calls.length > 0); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect((await restored.read?.())?.effectiveModelAcknowledged).toBe(false); - await restored.interrupt!(); + await restored.interrupt!({}); const restoredSnapshot = await restored.snapshot(); await restored.close({ reason: "done" }); await expect(new CursorCloudDriver({ ...f.options, model: "different-model" }).recoverSession(restoredSnapshot)).rejects.toThrow("changed"); }); @@ -873,7 +877,7 @@ describe("Cursor Cloud native driver", () => { expect(after.requestedAgentId).toMatch(/^bc-/); await expect(restored.startTurn({ message: { role: "user", text: "Must not replace uncertain work" } })).rejects.toThrow("unresolved remote work"); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled(); - peer.getAgent.mockImplementation(getAgent); await restored.interrupt!(); + peer.getAgent.mockImplementation(getAgent); await restored.interrupt!({}); }); it("allows a definitive direct mutation rejection to settle without pretending work was accepted", async () => { @@ -893,7 +897,7 @@ describe("Cursor Cloud native driver", () => { if (phase === "follow-up") { await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await peer.created.promise; - await f.session.interrupt!(); await acknowledgeTerminal(f.session); + await f.session.interrupt!({}); await acknowledgeTerminal(f.session); } const before = { fetch: fetch.mock.calls.length, getAgent: peer.getAgent.mock.calls.length, getRun: peer.getRun.mock.calls.length, cancel: peer.cancelRun.mock.calls.length }; const text = "\u0001".repeat(400_000); @@ -905,11 +909,11 @@ describe("Cursor Cloud native driver", () => { expect(fetch).toHaveBeenCalledTimes(before.fetch); expect(await f.session.read()).toMatchObject({ phase: "idle", providerStatus: "REJECTED" }); expect(f.events.some(event => event.turnId === turn.turnId && event.eventType === "turn.failed" && event.payload.remoteWorkAccepted === false)).toBe(true); - await f.session.interrupt!(); + await f.session.interrupt!({}); expect(peer.getAgent).toHaveBeenCalledTimes(before.getAgent); expect(peer.getRun).toHaveBeenCalledTimes(before.getRun); expect(peer.cancelRun).toHaveBeenCalledTimes(before.cancel); - const snapshot = await f.session.snapshot(); await f.session.detachControllerForRestart(); await f.pump; + const snapshot = await f.session.snapshot(); await f.session.detachControllerForRestart!(); await f.pump; const recovered = await new CursorCloudDriver(f.options).recoverSession(snapshot); expect(recovered.recovered).toBe(true); const restored = recovered.session!; sessions.push(restored); @@ -919,7 +923,7 @@ describe("Cursor Cloud native driver", () => { const next = await restored.startTurn({ message: { role: "user", text: "A smaller admitted task" } }); await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId })); expect(fetch).toHaveBeenCalledTimes(before.fetch + 1); - await restored.interrupt!(); + await restored.interrupt!({}); }); it.each(["fresh", "follow-up"] as const)("keeps a %s oversized response fenced after the mutation was sent", async phase => { @@ -928,7 +932,7 @@ describe("Cursor Cloud native driver", () => { const f = await fixture(peer, { client: mutations.client }); if (phase === "follow-up") { await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); - await peer.created.promise; await f.session.interrupt!(); await acknowledgeTerminal(f.session); + await peer.created.promise; await f.session.interrupt!({}); await acknowledgeTerminal(f.session); } const before = mutations.fetch.mock.calls.length; mutations.fetch.mockImplementationOnce(async (url, init) => { @@ -954,7 +958,7 @@ describe("Cursor Cloud native driver", () => { if (phase === "follow-up") { await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await peer.created.promise; - await f.session.interrupt!(); + await f.session.interrupt!({}); await acknowledgeTerminal(f.session); } const entered = deferred(), rejectCreate = deferred(), stopHeld = deferred(), releaseStop = deferred(); @@ -975,7 +979,7 @@ describe("Cursor Cloud native driver", () => { const before = { getAgent: peer.getAgent.mock.calls.length, getRun: peer.getRun.mock.calls.length, cancel: peer.cancelRun.mock.calls.length }; const originalWrite = CursorCloudCheckpointStore.prototype.write; let pausedStop = false, pausedRejection = false; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { if (value.phase === "idle" && value.terminalStatus === "REJECTED" && !pausedRejection) { pausedRejection = true; rejectionHeld.resolve(); return releaseRejection.promise.then(() => originalWrite.call(this, value)); @@ -989,7 +993,7 @@ describe("Cursor Cloud native driver", () => { }); let stopDone = false; let stopped: Promise<{ error: unknown | null }> | undefined; - const stop = () => stopped = f.session.interrupt!().then(() => ({ error: null }), error => ({ error })) + const stop = () => stopped = f.session.interrupt!({}).then(() => ({ error: null }), error => ({ error })) .finally(() => { stopDone = true; }); try { const turn = await f.session.startTurn({ message: { role: "user", text: rejection === "unsent_payload" ? "\u0001".repeat(400_000) : "Rejected task" } }); @@ -1029,7 +1033,7 @@ describe("Cursor Cloud native driver", () => { expect(peer.cancelRun).toHaveBeenCalledTimes(before.cancel); if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(fetchesBefore!); const snapshot = await f.session.snapshot(); - await f.session.detachControllerForRestart(); await f.pump; + await f.session.detachControllerForRestart!(); await f.pump; const recovered = await new CursorCloudDriver(f.options).recoverSession(snapshot); expect(recovered.recovered).toBe(true); const restored = recovered.session!; sessions.push(restored); @@ -1037,7 +1041,7 @@ describe("Cursor Cloud native driver", () => { distinctFollowup(peer); peer.release = deferred(); const next = await restored.startTurn({ message: { role: "user", text: "Next admitted task" } }); await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId })); - await restored.interrupt!(); + await restored.interrupt!({}); expect(await restored.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { turnId: next.turnId } }); } finally { rejectCreate.resolve(); releaseStop.resolve(); releaseRejection.resolve(); @@ -1051,7 +1055,7 @@ describe("Cursor Cloud native driver", () => { const f = await fixture(peer), held = deferred(), release = deferred(); const originalWrite = CursorCloudCheckpointStore.prototype.write; let paused = false; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { if (value.phase === "idle" && value.terminalStatus === "REJECTED" && !paused) { paused = true; held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); } @@ -1082,7 +1086,7 @@ describe("Cursor Cloud native driver", () => { await f.session.reconcile!(); await acknowledgeTerminal(f.session); await f.session.attachRun!({ runId: "current-owner" }); peer.createRun.mockRejectedValue(new CursorCloudClientError("http_error", { status: 422 })); const held = deferred(), release = deferred(), originalWrite = CursorCloudCheckpointStore.prototype.write; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { if (value.phase === "idle" && value.terminalStatus === "REJECTED") { held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); } return originalWrite.call(this, value); }); @@ -1105,7 +1109,7 @@ describe("Cursor Cloud native driver", () => { const f = await fixture(peer, mutations ? { client: mutations.client } : {}); if (phase === "follow-up") { await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await peer.created.promise; - await f.session.interrupt!(); await acknowledgeTerminal(f.session); + await f.session.interrupt!({}); await acknowledgeTerminal(f.session); } const entered = deferred(), rejected = deferred(); if (mutations) { @@ -1119,14 +1123,14 @@ describe("Cursor Cloud native driver", () => { }); peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 })); const originalWrite = CursorCloudCheckpointStore.prototype.write; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { return value.phase === "idle" && value.terminalStatus === "REJECTED" ? Promise.reject(new Error("injected rejection checkpoint failure")) : originalWrite.call(this, value); }); try { const before = mutations?.fetch.mock.calls.length; const turn = await f.session.startTurn({ message: { role: "user", text: mutations ? "\u0001".repeat(400_000) : "Rejected task" } }); await entered.promise; - const stopped = f.session.interrupt!().then(() => null, error => error); + const stopped = f.session.interrupt!({}).then(() => null, error => error); rejected.resolve(); expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") }); if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(before!); @@ -1147,7 +1151,7 @@ describe("Cursor Cloud native driver", () => { peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 })); const f = await fixture(peer), turn = await f.session.startTurn({ message: { role: "user", text: "Unknown create" } }); await entered.promise; - const stopped = f.session.interrupt!().then(() => null, error => error); released.resolve(); + const stopped = f.session.interrupt!({}).then(() => null, error => error); released.resolve(); expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") }); expect(await savedCheckpoint(f.root, f.session)).toMatchObject({ phase: "stopping", pendingOperation: "create_agent", terminalStatus: null }); expect(f.events.some(event => event.turnId === turn.turnId && event.payload.remoteWorkAccepted === false)).toBe(false); @@ -1159,7 +1163,7 @@ describe("Cursor Cloud native driver", () => { const f = await fixture(peer, { cancelTimeoutMs: 120 }); await f.session.startTurn({ message: { role: "user", text: "Keep running" } }); await peer.created.promise; await waitFor(() => f.events.some(event => event.eventType === "item.delta")); - await expect(f.session.interrupt!()).rejects.toThrow("did not confirm"); + await expect(f.session.interrupt!({})).rejects.toThrow("did not confirm"); expect(f.events.some(event => event.eventType === "turn.interrupted" || event.eventType === "turn.completed")).toBe(false); expect((await f.session.read?.())?.phase).toBe("stopping"); await expect(f.session.startTurn({ message: { role: "user", text: "New work" } })).rejects.toThrow("unresolved"); @@ -1204,7 +1208,7 @@ describe("Cursor Cloud native driver", () => { const originalWrite = CursorCloudCheckpointStore.prototype.write; const attempts: Array> = []; let injected = false; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(this: CursorCloudCheckpointStore, value) { if (this.path !== checkpointPath || value.phase !== "terminal") return originalWrite.call(this, value); attempts.push(structuredClone(value.pendingTerminalDelivery)); if (injected) { await releaseRetry.promise; return originalWrite.call(this, value); } @@ -1248,7 +1252,7 @@ describe("Cursor Cloud native driver", () => { const held = deferred(), release = deferred(); const originalWrite = CursorCloudCheckpointStore.prototype.write; let paused = false; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { if (this.path === checkpointPath && value.phase === "terminal" && !paused) { paused = true; held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); } @@ -1261,7 +1265,7 @@ describe("Cursor Cloud native driver", () => { const late = await rpc(f.options.relay.socketPath, { id: "late", method: "tools/call", params: { name: "write_note", arguments: { note: "No authority after provider termination" } } }, relayHeaders(f.peer), initialized.session); expect(late.status).toBe(403); expect(f.handler).not.toHaveBeenCalled(); let stopped = false, reconciled = false, snapshotted = false; - const stop = f.session.interrupt!().finally(() => { stopped = true; }); + const stop = f.session.interrupt!({}).finally(() => { stopped = true; }); const reconcile = f.session.reconcile!().finally(() => { reconciled = true; }); const snapshot = f.session.snapshot().finally(() => { snapshotted = true; }); await new Promise(accept => setImmediate(accept)); @@ -1290,7 +1294,7 @@ describe("Cursor Cloud native driver", () => { const originalWrite = CursorCloudCheckpointStore.prototype.write; const deliveries: unknown[] = []; let unavailable = true; - const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { + const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) { if (this.path === checkpointPath && value.phase === "terminal") { deliveries.push(structuredClone(value.pendingTerminalDelivery)); if (unavailable) return Promise.reject(Object.assign(new Error("controlled unavailable checkpoint storage"), { code: "ENOSPC" })); @@ -1363,7 +1367,7 @@ describe("Cursor Cloud native driver", () => { await waitFor(() => stream.mock.calls.length > 0); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled(); expect(JSON.parse(await readFile(checkpointPath, "utf8")).deadlineAt).toBe(deadlineAt); - await restored.interrupt!(); + await restored.interrupt!({}); }); it("enforces the saved deadline while the first recovery read remains pending", async () => { diff --git a/packages/paperclip-runner/src/drivers/dot/dot-harness-driver.test.ts b/packages/paperclip-runner/src/drivers/dot/dot-harness-driver.test.ts index 15fd39f32a2..05f174cc754 100644 --- a/packages/paperclip-runner/src/drivers/dot/dot-harness-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/dot/dot-harness-driver.test.ts @@ -11,7 +11,7 @@ const result = { schema: "paperclip.run_result.v1", reportedWorkDisposition: "do async function fixture(overrides: Partial = {}) { const identity = { companyId: randomUUID(), agentId: randomUUID(), issueId: randomUUID(), runId: randomUUID(), sessionId: randomUUID() }; const principal = { companyId: identity.companyId, grantId: randomUUID() }; - const publish = vi.fn(async () => {}); + const publish = vi.fn(async () => {}); const executeTool = vi.fn(async () => ({ saved: true })); const assertAuthority = vi.fn(async () => {}); const driver = new DotHarnessDriver({ identity, principal, expiresAt: Date.now() + 60_000, diff --git a/packages/paperclip-runner/src/drivers/dot/runnerd-dot-driver.test.ts b/packages/paperclip-runner/src/drivers/dot/runnerd-dot-driver.test.ts index 9e14a1bdd2b..6758f8769b6 100644 --- a/packages/paperclip-runner/src/drivers/dot/runnerd-dot-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/dot/runnerd-dot-driver.test.ts @@ -96,7 +96,7 @@ it.each([ return handle; }); const getCommand = controlPlane.DurablePrpControlPlane.prototype.getCommand; - const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "getCommand").mockImplementation(function (id) { + const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "getCommand").mockImplementation(function (this: controlPlane.DurablePrpControlPlane, id) { const command = getCommand.call(this, id); // Hold the SDK poll until the real process has exited. The authenticated // shutdown receipt and its ACK remain committed in the transport journal. @@ -112,7 +112,7 @@ it.each([ }); let session: Awaited> | undefined; try { - session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); + session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root }); if (kind === "shutdown") { await expect(session.close({ reason: "Test complete" })).resolves.toBeUndefined(); expect(exited).toBe(true); @@ -152,7 +152,7 @@ it.each(["local", "local pending finish", "custom local directory", "external la identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), - onSpawn: process => { spawned = process; }, + onSpawn: async process => { spawned = process; }, controlPlaneRegistration: async authority => { if (emptyController) { authority.setCheckpointPersistence(async saved => { checkpoint = controlPlane.decodeDurablePrpCheckpoint(new Map(saved.records)); }); @@ -190,7 +190,7 @@ it.each(["local", "local pending finish", "custom local directory", "external la }; } const driver = new RunnerdDotDriver(options); - let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); + let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root }); const operation = (action: ExternalProviderOperation["action"], args: Record): ExternalProviderOperation => ({ requestId: randomUUID(), bindingId: input.provider.binding.bindingId, bindingGeneration: 1, runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, turnId: options.identity.turnId, assignmentRevision: 1, diff --git a/packages/paperclip-runner/src/drivers/muse/runnerd-muse-driver.test.ts b/packages/paperclip-runner/src/drivers/muse/runnerd-muse-driver.test.ts index d0b4e6f6527..9e8da75fd51 100644 --- a/packages/paperclip-runner/src/drivers/muse/runnerd-muse-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/muse/runnerd-muse-driver.test.ts @@ -82,7 +82,7 @@ it("uses the native request, exact question command ID and durable answer receip execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, - onSpawn: process => { spawned = process; }, + onSpawn: async process => { spawned = process; }, controlPlaneRegistration: async authority => { await authority.start(controllerPort); controllerPort = Number(new URL(authority.connectUrl).port); return { connectUrl: authority.connectUrl, release: () => authority.stop() }; }, port: { dispatch: async () => {}, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome); }, @@ -95,7 +95,7 @@ it("uses the native request, exact question command ID and durable answer receip }); const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand"); let driver = new RunnerdMuseDriver(options); - let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); + let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root }); try { const prepared = commandSpy.mock.calls.find(call => call[0] === "run.prepare"); expect(JSON.stringify(prepared?.[1])).not.toContain("connections_search"); @@ -166,7 +166,7 @@ it("bounds default Muse question answer command IDs at the native queue boundary execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, - onSpawn: process => { spawned = process; }, + onSpawn: async process => { spawned = process; }, controlPlaneRegistration: async authority => { await authority.start(controllerPort); controllerPort = Number(new URL(authority.connectUrl).port); return { connectUrl: authority.connectUrl, release: () => authority.stop() }; }, port: { dispatch: async () => {}, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome); }, @@ -182,7 +182,7 @@ it("bounds default Muse question answer command IDs at the native queue boundary const response = { schema: "paperclip.question_response.v1" as const, answers: { environment: { selectedOptionIds: ["test"] } } }; const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand"); let driver = new RunnerdMuseDriver(options); - let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); + let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root }); try { await session.startTurn({ message: { role: "user", text: "Answer boundary questions." } }); await send!(operation("accept", {})); @@ -253,7 +253,7 @@ it("rejects bridge-unsupported questions at native admission without a stored re execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, - onSpawn: process => { spawned = process; }, + onSpawn: async process => { spawned = process; }, port: { dispatch: async () => {}, settle: async () => {}, inputAvailable: async () => {}, attach: async callback => { send = callback; return async () => { send = undefined; }; } }, }; @@ -263,7 +263,7 @@ it("rejects bridge-unsupported questions at native admission without a stored re action: "request_user_input", input: args, digest: externalOperationDigest("request_user_input", args), }); const driver = new RunnerdMuseDriver(options); - const session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); + const session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root }); const events: PrpEvent[] = []; let eventReadFailure: unknown; const reading = (async () => { for await (const event of session.events()) events.push(event); })() @@ -315,8 +315,8 @@ it("keeps the exact Muse question and assignment alive past the native live wind await writeFile(join(root, "AGENTS.md"), "Use only first-party tools."); const input = execution(root); const events: PrpEvent[] = []; - const dispatches: PrpEvent[] = []; - const deliveries: PrpEvent[] = []; + const dispatches: Array<{ sourceEventId: string; payload: Record }> = []; + const deliveries: Array<{ sourceEventId: string; payload: Record }> = []; const settlements = new Map>(); let send: ((operation: ExternalProviderOperation) => Promise) | undefined; let spawned: { pid: number; processGroupId: number | null; startedAt: string } | undefined; @@ -327,7 +327,7 @@ it("keeps the exact Muse question and assignment alive past the native live wind normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }; const driver = new RunnerdMuseDriver({ execution: input, stateDirectory: join(root, "state"), identity, - runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), onSpawn: process => { spawned = process; }, + runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), onSpawn: async process => { spawned = process; }, port: { dispatch: async event => { dispatches.push(event); }, inputAvailable: async event => { deliveries.push(event); }, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome as Record); }, attach: async callback => { send = callback; return async () => { send = undefined; }; } }, diff --git a/packages/paperclip-runner/src/drivers/opencode/mcp-bridge-diagnostics.test.ts b/packages/paperclip-runner/src/drivers/opencode/mcp-bridge-diagnostics.test.ts index 4c3b253a83c..59844a49dc7 100644 --- a/packages/paperclip-runner/src/drivers/opencode/mcp-bridge-diagnostics.test.ts +++ b/packages/paperclip-runner/src/drivers/opencode/mcp-bridge-diagnostics.test.ts @@ -12,10 +12,14 @@ const report = { evidence: [], verification: [], }; +type RpcResponse = { + result: { isError?: boolean; tools: Array<{ name: string; inputSchema: unknown }> }; +}; + async function rpc(bridge: OpenCodeMcpBridge, id: number, method: string, params?: unknown) { return (await fetch(bridge.url, { method: "POST", headers: { Authorization: `Bearer ${bridge.secret}`, "Content-Type": "application/json", - }, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }) })).json(); + }, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }) })).json() as Promise; } it("binds closed rejection shapes to the actual offered finish schema without recording values", async () => { @@ -23,7 +27,7 @@ it("binds closed rejection shapes to the actual offered finish schema without re const bridge = await startOpenCodeMcpBridge({ handler, onDiagnostic: message => messages.push(message) }); bridges.push(bridge); const listed = await rpc(bridge, 1, "tools/list"); - const schema = listed.result.tools.find((tool: { name: string }) => tool.name === "paperclip_finish").inputSchema; + const schema = listed.result.tools.find((tool: { name: string }) => tool.name === "paperclip_finish")!.inputSchema; const digest = createHash("sha256").update(JSON.stringify(schema)).digest("hex"); for (const [index, input] of [report, { ...report, continuation: null }].entries()) { const result = await rpc(bridge, index + 2, "tools/call", { name: "paperclip_finish", arguments: input }); diff --git a/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts b/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts index 5a2c95e2ad0..0613569216a 100644 --- a/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts @@ -241,7 +241,7 @@ describe("OpenCodeServerDriver", () => { }); const session = await driver.openSession({ runId: "empty", normalizedSessionId: "empty", workingDirectory: root }); try { - expect(await session.read()).toMatchObject({ messages: [] }); + expect(await session.read!()).toMatchObject({ messages: [] }); } finally { await session.close({ reason: "test" }); } @@ -338,7 +338,7 @@ describe("OpenCodeServerDriver", () => { }); const descriptor = await driver.descriptor(); expect( - descriptor.capabilities.typedEventFamilies.find( + descriptor.capabilities.typedEventFamilies?.find( (family) => family.family === "plan", ), ).toMatchObject({ availability: "unsupported" }); @@ -751,7 +751,7 @@ describe("OpenCodeServerDriver", () => { // run.terminal is owned by the native-session layer above this driver. // Keep both absences explicit instead of synthesizing Codex-shaped input. expect( - (await driver.descriptor()).capabilities.typedEventFamilies.find( + (await driver.descriptor()).capabilities.typedEventFamilies?.find( (family) => family.family === "plan", ), ).toMatchObject({ availability: "unsupported" }); @@ -1105,7 +1105,7 @@ describe("OpenCodeServerDriver", () => { expect(JSON.stringify(events)).not.toContain( "submitted prompt must not become assistant text", ); - expect(await session.usage()).toMatchObject({ + expect(await session.usage!()).toMatchObject({ input: 3, output: 2, costUsd: 0.001, @@ -1772,7 +1772,7 @@ describe("OpenCodeServerDriver", () => { // A prompt tools map replaces OpenCode's session permissions. Question is // enabled in config without overwriting the allow/ask/deny or path policy. expect(submittedPrompt).not.toHaveProperty("tools"); - expect(JSON.stringify(submittedPrompt?.parts ?? null)).not.toContain( + expect(JSON.stringify((submittedPrompt as Record | null)?.parts ?? null)).not.toContain( PAPERCLIP_EXECUTION_PROMPT, ); const sessionRoot = join(root, "context"); @@ -2871,13 +2871,13 @@ describe("OpenCodeServerDriver", () => { await session.startTurn({ message: { role: "user", text: "finish" } }); await started; let stopped = false; - const stop = session[operation]({ reason: "fixture stop" }).then(() => { stopped = true; }); + const stop = session[operation]!({ reason: "fixture stop" }).then(() => { stopped = true; }); await new Promise(resolve => setImmediate(resolve)); expect(stopped).toBe(false); releaseFeedback(); await stop; expect((await session.snapshot()).semanticResult).not.toBeNull(); - const transcript = await session.transcript(); + const transcript = await session.transcript!(); expect(transcript.events.filter(event => event.eventType === "run.result.proposed")).toHaveLength(1); expect(transcript.events.some(event => event.eventType === "item.completed" && (event.payload.item as { is_error?: boolean })?.is_error)).toBe(false); } finally { diff --git a/packages/paperclip-runner/src/drivers/opencode/reasoning-mode.test.ts b/packages/paperclip-runner/src/drivers/opencode/reasoning-mode.test.ts index d6d9263046f..db3a875e4d2 100644 --- a/packages/paperclip-runner/src/drivers/opencode/reasoning-mode.test.ts +++ b/packages/paperclip-runner/src/drivers/opencode/reasoning-mode.test.ts @@ -57,7 +57,7 @@ describe("per-turn OpenCode reasoning", () => { }); expect(config.provider.openrouter.models["deepseek/deepseek-v4-flash-0731"].options).toBeUndefined(); } finally { - try { await session?.close(); } + try { await session?.close({ reason: "fixture complete" }); } finally { await rm(root, { recursive: true, force: true }); } } }); diff --git a/packages/paperclip-runner/src/drivers/runner-tool-bridge.test.ts b/packages/paperclip-runner/src/drivers/runner-tool-bridge.test.ts index c9818c65076..208a738d697 100644 --- a/packages/paperclip-runner/src/drivers/runner-tool-bridge.test.ts +++ b/packages/paperclip-runner/src/drivers/runner-tool-bridge.test.ts @@ -23,6 +23,8 @@ afterEach(async () => { await Promise.all(bridges.splice(0).map((bridge) => bridge.close())); }); +type ToolCallBody = { result: { content: unknown } }; + async function rpc( bridge: RunnerToolBridge, body: Record, @@ -563,7 +565,7 @@ describe("Copilot semantic receipt opt-in", () => { } }); bridges.push(bridge); const request = { id: 3, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } }; - const body = await (await rpc(bridge, request)).json(); + const body = await (await rpc(bridge, request)).json() as ToolCallBody; expect(receipts).toHaveLength(1); expect(readNativeSemanticReceipt({ contents: body.result.content })).toEqual(receipts[0]); expect(receipts[0]).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(rawFinish), @@ -587,11 +589,11 @@ describe("Copilot semantic receipt opt-in", () => { const normalizedFirst = validatePrpStructuredRunResult(rawFinish); const normalizedSecond = validatePrpStructuredRunResult(secondInput); if (!normalizedFirst.ok || !normalizedSecond.ok) throw new Error("Invalid fixture"); pending.get("second")!.capture(normalizedSecond.result)(); pending.get("second")!.settle(); - const secondReceipt = readNativeSemanticReceipt({ contents: (await (await second).json()).result.content }); + const secondReceipt = readNativeSemanticReceipt({ contents: (await (await second).json() as ToolCallBody).result.content }); expect(secondReceipt).toMatchObject({ inputSha256: semanticInputSha256(secondInput), normalizedInputSha256: semanticInputSha256(normalizedSecond.result) }); pending.get("second")!.capture(normalizedFirst.result)(); pending.get("first")!.capture(normalizedFirst.result)(); pending.get("first")!.settle(); - const firstReceipt = readNativeSemanticReceipt({ contents: (await (await first).json()).result.content }); + const firstReceipt = readNativeSemanticReceipt({ contents: (await (await first).json() as ToolCallBody).result.content }); expect(firstReceipt).toMatchObject({ inputSha256: semanticInputSha256(rawFinish), normalizedInputSha256: semanticInputSha256(normalizedFirst.result) }); expect(receipts).toEqual([secondReceipt, firstReceipt]); }); @@ -600,10 +602,10 @@ describe("Copilot semantic receipt opt-in", () => { const withEvidence = await startRunnerToolBridge({ tools: [tool("documents.read")], captureSemanticReceipt: () => () => {}, handler: async call => { expect(call.captureNormalizedInput).toBeUndefined(); return modelMetadata; } }); bridges.push(withEvidence); - const body = await (await rpc(withEvidence, { id: 1, method: "tools/call", params: { name: "documents.read", arguments: modelMetadata } })).json(); + const body = await (await rpc(withEvidence, { id: 1, method: "tools/call", params: { name: "documents.read", arguments: modelMetadata } })).json() as ToolCallBody; expect(readNativeSemanticReceipt({ contents: body.result.content })).toHaveProperty("normalizedInputSha256", null); const plain = await startRunnerToolBridge({ handler: async call => { expect(call.captureNormalizedInput).toBeUndefined(); return { accepted: true }; } }); bridges.push(plain); - const plainBody = await (await rpc(plain, { id: 2, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } })).json(); + const plainBody = await (await rpc(plain, { id: 2, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } })).json() as ToolCallBody; expect(readNativeSemanticReceipt({ contents: plainBody.result.content })).toBeNull(); }); it.each(["small", "chunked", "tagged", "error"])("preserves %s result encoding and captures scope before dispatch", async encoding => { diff --git a/packages/paperclip-runner/src/eval/workflow-evals.test.ts b/packages/paperclip-runner/src/eval/workflow-evals.test.ts index b4df89c143a..5c7a399fe1f 100644 --- a/packages/paperclip-runner/src/eval/workflow-evals.test.ts +++ b/packages/paperclip-runner/src/eval/workflow-evals.test.ts @@ -1,7 +1,7 @@ import { readFile, stat } from "node:fs/promises"; import { resolve } from "node:path"; -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { diff --git a/packages/paperclip-runner/src/evals/native-execution.test.ts b/packages/paperclip-runner/src/evals/native-execution.test.ts index 9700ae81b00..f241b35883b 100644 --- a/packages/paperclip-runner/src/evals/native-execution.test.ts +++ b/packages/paperclip-runner/src/evals/native-execution.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { prpSchemaBundle } from "../protocol/generated/schema-bundle.js"; diff --git a/packages/paperclip-runner/src/live/acpx-request-recovery.test.ts b/packages/paperclip-runner/src/live/acpx-request-recovery.test.ts index 4eb952ed63d..c7ed7f36d3b 100644 --- a/packages/paperclip-runner/src/live/acpx-request-recovery.test.ts +++ b/packages/paperclip-runner/src/live/acpx-request-recovery.test.ts @@ -76,7 +76,7 @@ describe("live ACP request recovery", () => { recovered = await original.snapshot(); expect(recovered.pendingRuntimeRequests).toHaveLength(2); } finally { - await original.close(); + await original.close({ reason: "test complete" }); } class AdoptedTransport extends FakeCodexTransport { takeRestoredRuntimeRequests(): CodexRpcServerRequest[] { return requests; } @@ -99,7 +99,7 @@ describe("live ACP request recovery", () => { await expect(session.resolveRuntimeRequest?.({ requestId, turnId: "turn-1", resolution })).rejects.toThrow("no longer pending"); expect(deliver).toHaveBeenCalledTimes(1); } finally { - await session.close(); await drained; + await session.close({ reason: "test complete" }); await drained; expect(recoveredEvents.map(event => event.eventType)).not.toContain("runtime_request.created"); expect(recoveredEvents.filter(event => event.eventType === "runtime_request.expired").map(event => event.payload.requestId)).not.toContain(requestId); } diff --git a/packages/paperclip-runner/src/live/antigravity-candidate-startup.test.ts b/packages/paperclip-runner/src/live/antigravity-candidate-startup.test.ts index 80389f03b96..3776ad229ef 100644 --- a/packages/paperclip-runner/src/live/antigravity-candidate-startup.test.ts +++ b/packages/paperclip-runner/src/live/antigravity-candidate-startup.test.ts @@ -63,7 +63,9 @@ describe("Antigravity candidate trusted startup", () => { const root = await mkdtemp(join(tmpdir(), "antigravity-candidate-continuation-")); const admission = candidate(); const retained = { driverSessionId: "driver-retained", providerSessionId: "native-retained", providerIdentity: { - kind: "acpx", normalizedSessionId: identity.normalizedSessionId, + kind: "acpx" as const, normalizedSessionId: identity.normalizedSessionId, + acpxRecordId: "acpx-retained", backendSessionId: "backend-retained", agentSessionId: "native-retained", + workspaceDigest: `sha256:${"e".repeat(64)}`, providerLifetimeFenceCandidates: [1, 2, 3] as const, // The recovery binding digest is distinct from the release command digest. profileDigest: `sha256:${"a".repeat(64)}`, requestedModel: admission.model, effectiveModel: admission.model, } }; diff --git a/packages/paperclip-runner/src/live/clean-room-server.test.ts b/packages/paperclip-runner/src/live/clean-room-server.test.ts index 65b0872adab..f9a5df8b05c 100644 --- a/packages/paperclip-runner/src/live/clean-room-server.test.ts +++ b/packages/paperclip-runner/src/live/clean-room-server.test.ts @@ -15,6 +15,7 @@ import type { } from "../drivers/codex/app-server-transport.js"; import type { CapabilityIssueThreadSnapshot, CapabilityThreadItem } from "../issue-thread/types.js"; import type { CapabilityLiveTransportFactory } from "./live-session.js"; +import type { CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js"; import { createCapabilityTurnStreamDecoder, readCapabilityTurnStream, @@ -343,10 +344,25 @@ class FakeCleanRoomTransport implements CodexAppServerTransport { function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory { return (options) => { - const evidence = { + const evidence: CapabilityRunnerdProcessEvidence = { runnerPid: 7100, runnerProcessGroupId: 7100, + providerPid: null, + providerProcessStartedAt: null, codexPid: 7200, + codexProcessStartedAt: null, + sidecarPid: null, + sidecarProcessStartedAt: null, + agentPid: null, + agentProcessStartedAt: null, + providerDriver: null, + providerVersion: null, + acpxAgent: null, + agentServerVersion: null, + agentRuntimeVersion: null, + acpProtocolVersion: null, + providerExecutionKind: null, + providerService: null, runnerExited: false, runnerExitCode: null, runnerSignal: null, @@ -359,7 +375,7 @@ function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransport options.onEvidence?.(evidence); }); options.onEvidence?.(evidence); - return { transport, evidence: () => ({ ...evidence }) }; + return { transport, evidence: () => ({ ...evidence }), detachControllerForRestart: async () => {} }; }; } @@ -369,6 +385,8 @@ interface CleanRoomPayload { identity: { token: string; companyId: string; actorId: string; taskId: string; identifier: string }; limits: { maxTurns: number; maxMessageBytes: number }; view: CapabilityIssueThreadSnapshot; + toolResult?: unknown; + toolTurnId?: string | null; configuration: { provider: "codex" | "opencode"; model: string | null; @@ -801,9 +819,9 @@ describe("Capability clean-room chat server", () => { expect(after.toolTurnId).toMatch(/^turn-/); const turn = after.view.turns.find((candidate: { id: string }) => candidate.id === after.toolTurnId); expect(turn).toBeDefined(); - expect(turn.items.some((item: CapabilityThreadItem) => item.kind === "user_message")).toBe(true); - expect(turn.items.some((item: CapabilityThreadItem) => item.kind === "tool_activity" && item.operationId === "report_progress")).toBe(true); - expect(turn.items.some((item: CapabilityThreadItem) => item.kind === "durable_comment" && item.body === "Progress entered manually from DevTools.")).toBe(true); + expect(turn!.items.some((item: CapabilityThreadItem) => item.kind === "user_message")).toBe(true); + expect(turn!.items.some((item: CapabilityThreadItem) => item.kind === "tool_activity" && item.operationId === "report_progress")).toBe(true); + expect(turn!.items.some((item: CapabilityThreadItem) => item.kind === "durable_comment" && item.body === "Progress entered manually from DevTools.")).toBe(true); expect(after.view.evidence.state.at(-1)?.toRevision).toBeGreaterThan(0); }); diff --git a/packages/paperclip-runner/src/live/live-session.test.ts b/packages/paperclip-runner/src/live/live-session.test.ts index c3d4eb9abac..ec35ad7c5e8 100644 --- a/packages/paperclip-runner/src/live/live-session.test.ts +++ b/packages/paperclip-runner/src/live/live-session.test.ts @@ -24,7 +24,7 @@ import { type CapabilityLiveTurnEvent, } from "./live-session.js"; import { DurableCapabilityLiveSessionStore } from "./durable-live-session-store.js"; -import { defaultCapabilityRunnerdBinary } from "./runnerd-codex-transport.js"; +import { defaultCapabilityRunnerdBinary, type CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js"; import { persistedCursorUsageNotice } from "../drivers/acpx/usage-accounting.js"; import { captureTurnRejection } from "../../test/capture-turn-rejection.js"; import * as workspaceDiff from "./workspace-diff.js"; @@ -45,7 +45,7 @@ it("admits Pi live sessions without candidate opt-in while preserving the exact const session = await service.create({ provider: "acpx", acpxAgent: "pi", piThinkingLevel: "low", requestedModel: "openrouter/deepseek/deepseek-v4-flash-0731" }); try { expect(session.snapshot().config.acpxProfile).toMatchObject({ - agent: "pi", ...resolveQualifiedAcpxProfile("pi", "openrouter/deepseek/deepseek-v4-flash-0731"), + ...resolveQualifiedAcpxProfile("pi", "openrouter/deepseek/deepseek-v4-flash-0731"), agent: "pi", }); const custom = await service.create({ provider: "acpx", acpxAgent: "pi", piThinkingLevel: "low", requestedModel: "another-model" }); expect(custom.snapshot().config.acpxProfile).toMatchObject({ qualificationModel: "another-model", reportedModelId: "another-model" }); @@ -322,10 +322,25 @@ class FakeCapabilityCodexTransport implements CodexAppServerTransport { function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory { return (options) => { - const evidence = { + const evidence: CapabilityRunnerdProcessEvidence = { runnerPid: 4100 + state.transports.length, runnerProcessGroupId: 4100 + state.transports.length, + providerPid: null, + providerProcessStartedAt: null, codexPid: 5100 + state.transports.length, + codexProcessStartedAt: null, + sidecarPid: null, + sidecarProcessStartedAt: null, + agentPid: null, + agentProcessStartedAt: null, + providerDriver: null, + providerVersion: null, + acpxAgent: null, + agentServerVersion: null, + agentRuntimeVersion: null, + acpProtocolVersion: null, + providerExecutionKind: null, + providerService: null, runnerExited: false, runnerExitCode: null, runnerSignal: null, @@ -337,7 +352,7 @@ function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransport options.onEvidence?.(evidence); }); options.onEvidence?.(evidence); - return { transport, evidence: () => ({ ...evidence, runnerExited: transport.processInfo().exited }) }; + return { transport, evidence: () => ({ ...evidence, runnerExited: transport.processInfo().exited }), detachControllerForRestart: async () => {} }; }; } @@ -394,7 +409,7 @@ class TransientFailureLiveSessionStore implements CapabilityLiveSessionStore { throw new Error("transient live-session store failure"); } if ( - snapshot.terminalTurns.length > 0 && + snapshot.terminalTurns!.length > 0 && (this.#rejectTerminalSaves || this.#terminalSaveFailuresRemaining > 0) ) { if (this.#terminalSaveFailuresRemaining > 0) { @@ -647,7 +662,7 @@ describe("Capability live runnerd and Codex session", () => { outputTokens: 4, })).resolves.toBe("committed"); - expect((await store.load(session.id))?.usageLedger.map((receipt) => receipt.receiptId)).toEqual([ + expect((await store.load(session.id))?.usageLedger!.map((receipt) => receipt.receiptId)).toEqual([ "usage-before-recovery", "usage-after-recovery", ]); @@ -1159,7 +1174,7 @@ describe("Capability live runnerd and Codex session", () => { expect(first.snapshot.providerThreadId).toBe(second.snapshot.providerThreadId); expect(state.attachments).toHaveLength(1); expect(second.snapshot.providerRunBinding).toEqual(state.attachments[0]); - expect(second.snapshot.usageLedger.slice(-2)).toMatchObject([ + expect(second.snapshot.usageLedger!.slice(-2)).toMatchObject([ { providerRequests: 1, inputTokens: 100, outputTokens: 10 }, { providerRequests: 1, inputTokens: 100, outputTokens: 10 }, ]); @@ -1497,7 +1512,7 @@ describe("Capability live runnerd and Codex session", () => { const result = await session.sendMessage("report overlapping usage"); expect(result.snapshot.usageLedger).toHaveLength(1); - expect(result.snapshot.usageLedger[0]).toMatchObject({ + expect(result.snapshot.usageLedger![0]).toMatchObject({ providerRequests: 1, inputTokens: 100, outputTokens: 10, @@ -1596,7 +1611,7 @@ describe("Capability live runnerd and Codex session", () => { const result = await session.sendMessage("report late usage"); expect(result.snapshot.usageLedger).toHaveLength(1); - expect(result.snapshot.usageLedger[0]).toMatchObject({ + expect(result.snapshot.usageLedger![0]).toMatchObject({ providerRequests: 2, inputTokens: 100, outputTokens: 10, diff --git a/packages/paperclip-runner/src/live/runner-prepared-launch-workspace.test.ts b/packages/paperclip-runner/src/live/runner-prepared-launch-workspace.test.ts index 943871742f0..6112d7f87bb 100644 --- a/packages/paperclip-runner/src/live/runner-prepared-launch-workspace.test.ts +++ b/packages/paperclip-runner/src/live/runner-prepared-launch-workspace.test.ts @@ -106,10 +106,10 @@ it.each([ const recovered = await restarted.backend.recoverSession!(snapshot, { signal: new AbortController().signal }); expect(recovered).toMatchObject({ recovered: true }); if (!recovered.recovered) throw new Error(recovered.reason); - expect((await recovered.session.snapshot()).workingDirectory).toBe(cwd); + expect((await recovered.session!.snapshot()).workingDirectory).toBe(cwd); // Recovery reuses the saved preparation instead of resolving a new directory. expect(restarted.prepareLaunch.mock.calls[0]![1]).toMatchObject({ configurationId: "plugin-run" }); - await recovered.session.close({ reason: "test complete" }); + await recovered.session!.close({ reason: "test complete" }); const moved = await pluginBackend(root, join(root, "unused"), "runner_prepared_launch"); await expect(moved.backend.recoverSession!({ ...snapshot, workingDirectory: join(root, "elsewhere") }, diff --git a/packages/paperclip-runner/src/live/runnerd-acpx-questions.test.ts b/packages/paperclip-runner/src/live/runnerd-acpx-questions.test.ts index 4d208fc1652..e5fe9de7647 100644 --- a/packages/paperclip-runner/src/live/runnerd-acpx-questions.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-acpx-questions.test.ts @@ -24,7 +24,7 @@ describe("ACPX questions through the shared runner transport", () => { [timing!.id]: { selectedOptionIds: [timing!.options![1]!.id] }, } }; expect(runtimeRequestResponse({ requestId: "question-1", requestKind: "elicitation", method: origin.method, - turnId: "turn", itemId: "item", status: "pending", prompt: "Interview", input: shown }, { action: "submit", response }, context)).toEqual({ action: "submit", response }); + turnId: "turn", itemId: "item", status: "pending", prompt: "Interview", details: {}, input: shown }, { action: "submit", response }, context)).toEqual({ action: "submit", response }); expect(form.accept(response)).toEqual({ action: "accept", content: { organization: "Garden club", goal: "Welcome note", timing: "later" } }); }); it("does not admit an invalid canonical question set", () => { diff --git a/packages/paperclip-runner/src/live/runnerd-acpx-source-reconciliation.test.ts b/packages/paperclip-runner/src/live/runnerd-acpx-source-reconciliation.test.ts index bb26e9428b5..34263fd42a7 100644 --- a/packages/paperclip-runner/src/live/runnerd-acpx-source-reconciliation.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-acpx-source-reconciliation.test.ts @@ -47,7 +47,7 @@ async function fixture(agent: "codex" | "claude" = "codex", nativeSource = false binding: { companyId: nativeIdentity.companyId, agentId: nativeIdentity.agentId, issueId: nativeIdentity.issueId, runId: identity.runId, executionWorkspaceId: "workspace" }, task: { identifier: "TASK-1", title: "Complete fake work", description: null, prompt: "Complete fake work", workMode: "standard" }, workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null }, - session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "acpx_runtime", protocolVersion: 1 }, + session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "acpx_runtime", protocolVersion: 1, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } }, provider: { kind: "acpx", agent, model: "explicit-source-model", permissionPolicy: "interactive", profile: resolveQualifiedAcpxProfile(agent, "explicit-source-model") }, completionContract: { id: "contract", sha256: sha("contract"), schemaVersion: "paperclip.completion-contract.v1", contract: { revision: "1", objective: "Complete fake work", criteria: [{ id: "objective", requirement: "Complete fake work" }] } }, interactionResponses: [], credentialBindings: [] }; @@ -147,7 +147,7 @@ it.skipIf(process.platform === "win32")("recovers and adopts ACPX through the re environment: { PAPERCLIP_WORKSPACE_CWD: f.root }, dynamicTools: [] }); let recovered = await backend().recoverSession!(f.nativeSnapshot!, { signal: new AbortController().signal }); expect(recovered.recovered).toBe(true); - const pid = source.evidence().runnerPid, startedAt = source.transport.processInfo().startedAt; + const pid = source.evidence().runnerPid, startedAt = source.transport.processInfo!().startedAt; expect(await f.count("turn.start")).toBe(1); expect(await f.count("processStart")).toBe(2); await recovered.session!.detachControllerForRestart!(); const launch = vi.fn(() => { throw new Error("duplicate source launch"); }); @@ -197,7 +197,7 @@ it.skipIf(process.platform === "win32").each(["codex", "claude", "adopt", "pause await expect(source.transport.request("turn/start", {})).rejects.toThrow("source_attachment_pending"); const pid = source.evidence().runnerPid; if (mode === "adopt") { - const startedAt = source.transport.processInfo().startedAt; + const startedAt = source.transport.processInfo!().startedAt; await source.detachControllerForRestart(); const launch = vi.fn(() => { throw new Error("duplicate source launch"); }); source = f.recover({ runnerProcessLauncher: launch, authorizeRunnerAdoption: async () => { throw new Error("old source configuration"); }, diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport-adoption.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport-adoption.test.ts index 424db66444b..664f7db4a44 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport-adoption.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport-adoption.test.ts @@ -38,7 +38,7 @@ async function verifyLiveRunnerAdoption( evidence: [], verification: [], attentionRequests: [], artifacts: [], })); const server = createServer(); - let authority: DurablePrpControlPlane | null = null; + let authority = null as DurablePrpControlPlane | null; const checkpoint = vi.fn(async () => undefined); server.on("upgrade", (request, socket, head) => { if (!authority) { @@ -136,7 +136,7 @@ async function verifyLiveRunnerAdoption( ); } runnerPid = first.evidence().runnerPid; - runnerStartedAt = first.transport.processInfo().startedAt; + runnerStartedAt = first.transport.processInfo!().startedAt; expect(runnerPid).toEqual(expect.any(Number)); if (goalMidTurn) { @@ -404,7 +404,7 @@ async function verifyLiveRunnerAdoption( } else expect(redispatchedTool).not.toHaveBeenCalled(); } expect(adopted.evidence().runnerPid).toBe(runnerPid); - expect(adopted.transport.processInfo().startedAt).toBe(runnerStartedAt); + expect(adopted.transport.processInfo!().startedAt).toBe(runnerStartedAt); if (startWithoutCheckpoint) { const retained = JSON.parse(await readFile(controlPlaneStatePath, "utf8")); for (const type of ["run.prepare", "session.open"]) { @@ -448,7 +448,7 @@ async function verifyLiveRunnerAdoption( expect(await Promise.race([notificationsClosed, new Promise(resolve => { queueDeadline = setTimeout(() => resolve(false), 250); })])).toBe(true); } finally { clearTimeout(queueDeadline); } await expect(adopted.transport.close()).rejects.toBe(probeError); - expect(adopted.transport.processInfo()).toMatchObject({ exited: false }); + expect(adopted.transport.processInfo!()).toMatchObject({ exited: false }); expect(() => process.kill(runnerPid!, 0)).not.toThrow(); expect(signal).not.toHaveBeenCalled(); expect(checkpoint).not.toHaveBeenCalled(); diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport-attachment-recovery.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport-attachment-recovery.test.ts index d672d9bb2e3..e81f64320c7 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport-attachment-recovery.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport-attachment-recovery.test.ts @@ -14,9 +14,18 @@ import { expect, it, vi } from "vitest"; import { DurablePrpControlPlane } from "../control-plane/durable-prp-control-plane.js"; import * as durableControlPlane from "../control-plane/durable-prp-control-plane.js"; import { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js"; -import { defaultCapabilityRunnerdBinary, fakeCodex, fakeCodexArgs } from "../../test/support/runnerd-transport.js"; +import { committableStore, defaultCapabilityRunnerdBinary, fakeCodex, fakeCodexArgs } from "../../test/support/runnerd-transport.js"; -it.each([ +type WarmAttachRecoveryCase = { + lossPoint: string; + routed: boolean; + recoveryFault: string; + executionPrepared?: boolean; + externalArtifact?: boolean; + ordinaryFollowup?: boolean | string; +}; + +it.each([ ...[ "before-result", "after-result", @@ -478,14 +487,15 @@ it.each([ ).toBe(false); return; } - const commit = core.store.commit.bind(core.store); + const store = committableStore(core); + const commit = store.commit.bind(store); let lossObserved = false; let signalLoss!: () => void; const loss = new Promise((resolveLoss) => { signalLoss = resolveLoss; }); commitSpy = vi - .spyOn(core.store, "commit") + .spyOn(store, "commit") .mockImplementation((candidate) => { // The selected process-crash boundary stays unavailable until the // exact owned runner is joined. A later replay must not silently diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport-deadlines.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport-deadlines.test.ts index 1f0fbaef19f..06cd0ae3735 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport-deadlines.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport-deadlines.test.ts @@ -67,7 +67,7 @@ it("keeps a quiet active Codex turn in the same process across connection lease expect(calls).not.toContain("turn/interrupt"); expect(calls).not.toContain("thread/resume"); const state = JSON.parse(await readFile(join(stateDirectory, "fake-codex-state.json"), "utf8")); - expect(state.threadId).toBe(opened.thread.id); + expect(state.threadId).toBe((opened.thread as { id: string }).id); expect(state.activeTurnId).toBe("provider-turn-1"); } finally { await bundle.transport.close(); diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport-settlement.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport-settlement.test.ts index 9c97cd48540..f06b0a3e8bf 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport-settlement.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport-settlement.test.ts @@ -286,7 +286,7 @@ it.each([ // output plus another full provider-owned prefix behind the old suspend. const runnerFile = join(original, "runner/runner-state.json"); const runnerBefore = JSON.parse(await readFile(runnerFile, "utf8")); - const template = builder.store.state.committedEvents[0]!.envelope.payload; + const template = builder.store.state.committedEvents[0]!.envelope.payload as Record; for (let index = 0; index < 90; index++) { const sourceSeq = runnerBefore.nextSourceSeq++; const event = { @@ -388,7 +388,7 @@ it.each([ sessionId: identity.normalizedSessionId, }, backend: { - kind: "codex", + kind: "runner" as const, name: maintenanceFixtureBackendName(fixtureId), }, identity, @@ -406,7 +406,7 @@ it.each([ recordEpoch, signal: maintenanceAbort.signal, }; - const close = vi.fn(async () => { + const close = vi.fn(async (): Promise => { throw new NativeSessionCloseUnrecoverableError(); }); const start = vi.fn(async () => { @@ -424,9 +424,10 @@ it.each([ capabilities: async () => capabilities, events: async function* () {}, startTurn: start, + result: async () => { throw new Error("fixture never produces a result"); }, close, snapshot: async () => ({ - backendKind: "codex", + backendKind: "runner", sessionId: input.binding.sessionId, identity: input.binding, providerSessionId: thread.thread.id, @@ -471,6 +472,7 @@ it.each([ normalizedSessionId: input.binding.sessionId, driverKind: "codex_app_server", protocolVersion: 1, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, }, completionContract: { id: "contract-maintenance", diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport-suspension.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport-suspension.test.ts index dba8ec470b3..004453760bd 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport-suspension.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport-suspension.test.ts @@ -211,6 +211,7 @@ it("does not retry a real memoized transport close whose suspension proof is una normalizedSessionId: identity.sessionId, driverKind: "codex_app_server", protocolVersion: 1, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, }, provider: { kind: "codex", model: null }, completionContract: { @@ -499,7 +500,7 @@ it.each([ message: { role: "user", text: "Keep working until interrupted." }, }); if (label === "interrupted") { - await session.interrupt({ + await session.interrupt!({ turnId: turn.turnId, reason: "Stopped by the user", }); @@ -525,7 +526,7 @@ it.each([ }, }); } finally { - await session.close().catch(() => undefined); + await session.close({ reason: "test complete" }).catch(() => undefined); await rm(stateDirectory, { recursive: true, force: true, diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport-warm-attachment.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport-warm-attachment.test.ts index 7739d4e2851..083290d5fae 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport-warm-attachment.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport-warm-attachment.test.ts @@ -13,6 +13,7 @@ import { codexSemanticToolSpecs } from "../drivers/codex/codex-app-server-driver import { releaseMaterializedNativeRuntimeSkills } from "../drivers/runtime-context-materializer.js"; import { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js"; import { + committableStore, defaultCapabilityRunnerdBinary, fakeCodex, fakeCodexArgs, @@ -163,8 +164,9 @@ it.each([ providerPid = bundle.evidence().codexPid; const rotations: (typeof core.store.state)[] = []; const preparedStates: (typeof core.store.state)[] = []; - const commit = core.store.commit.bind(core.store); - vi.spyOn(core.store, "commit").mockImplementation((candidate) => { + const store = committableStore(core); + const commit = store.commit.bind(store); + vi.spyOn(store, "commit").mockImplementation((candidate) => { commit(candidate); // Capture the durable old epoch at publication. The authenticated // successor may activate before attachRun observes the completed command. @@ -716,7 +718,7 @@ it("recovers an active second warm business run after deleting its controller co await writeFile(join(skillRoot, "SKILL.md"), "# Pinned skill\n"); await writeFile(join(instructionRoot, "AGENTS.md"), "Pinned instructions\n"); const server = createServer(); - let authority: DurablePrpControlPlane | null = null, generation = 0; + let authority = null as DurablePrpControlPlane | null, generation = 0; let checkpoint: durableControlPlane.DurablePrpCheckpoint | undefined; const registered = new WeakSet(); server.on("upgrade", (request, socket, head) => authority @@ -760,7 +762,7 @@ it("recovers an active second warm business run after deleting its controller co providerPid = first.evidence().codexPid; expect(providerPid).toEqual(expect.any(Number)); const providerStart = execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim(); - const startedAt = first.transport.processInfo().startedAt; + const startedAt = first.transport.processInfo!().startedAt; const firstCompleted = waitComplete(first.transport); await within("first turn/start", first.transport.request("turn/start", { input: [{ type: "text", text: "first healthy business run" }] })); await within("healthy first turn completion", firstCompleted); @@ -805,7 +807,7 @@ it("recovers an active second warm business run after deleting its controller co expect(execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim()).toBe(providerStart); expect(calls).toBe(2); expect(authority!.store.state.commands.filter(command => command.type === "semantic_tool.result")).toHaveLength(0); - expect(recovered.transport.processInfo().startedAt).toBe(startedAt); + expect(recovered.transport.processInfo!().startedAt).toBe(startedAt); expect(authority!.store.state.identity).toEqual(savedIdentity); expect(authority!.store.state.commands.filter(command => command.type === "turn.start")).toHaveLength(1); expect(noSpawn).not.toHaveBeenCalled(); diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts index 1c2b35e6f7c..87253a57f48 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts @@ -283,7 +283,7 @@ it.each(["resolved", "rejected"] as const)( expect(error).toMatchObject({ message: expect.stringContaining("runner_process_termination_unconfirmed") }); if (outcome === "rejected") expect(error).toHaveProperty("cause", probeError); expect(children).toHaveLength(1); - expect(bundle.transport.processInfo()).toMatchObject({ exited: false }); + expect(bundle.transport.processInfo!()).toMatchObject({ exited: false }); expect(() => process.kill(children[0]!.pid!, 0)).not.toThrow(); } finally { const providerPid = bundle.evidence().codexPid; @@ -358,7 +358,7 @@ it("preserves the cause when a replacement runner's health cannot be verified", if (child) return { child: { pid: 42, exitCode: null, kill: () => false }, completion: Promise.reject(probeError), - isExitConfirmed: () => false, + isExitConfirmed: (): boolean => false, }; child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" }); const ownedChild = child; @@ -390,7 +390,7 @@ it("preserves the cause when a replacement runner's health cannot be verified", }); expect(error).toHaveProperty("message", expect.stringContaining("probe_timeout")); expect(launch).toHaveBeenCalledTimes(2); - expect(bundle.transport.processInfo()).toMatchObject({ exited: false }); + expect(bundle.transport.processInfo!()).toMatchObject({ exited: false }); } finally { const providerPid = bundle.evidence().codexPid; if (providerPid) { @@ -415,7 +415,7 @@ it("retries a replacement whose staging failed before any process launched", asy if (launch.mock.calls.length === 2) return { child: { pid: undefined, exitCode: null, kill: () => false }, completion: Promise.reject(stagingError), - isExitConfirmed: () => true, + isExitConfirmed: (): boolean => true, }; const child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" }); children.push(child); @@ -2659,7 +2659,7 @@ it.each(["reuse", "large-instructions", "rotated-recovery", "removed-context", " resolved.skills[0]!.bundle.rootPath = "/guest/.agents/skills/assigned"; const materialize = vi.spyOn(runtimeMaterializer, "prepareIsolatedCodexHome").mockRejectedValue(new Error("unexpected Core materialization")); const discover = vi.spyOn(codexCommandRuntime, "resolveCodexCommand").mockImplementation(() => { throw new Error("unexpected Core discovery"); }); - let configuration: Record | undefined; + let configuration: Record | null | undefined; let recovered = false; let requestedRun = ""; let requestedInstructions = ""; @@ -3574,7 +3574,7 @@ it.each(["stuck", "late-release", "late-release-error", "stuck-release", "cooper let template: Record | null = null; let registrationStarted!: () => void; const started = new Promise(resolve => { registrationStarted = resolve; }); - let returnRegistration!: (registration: { connection: { mode: "listen"; listenAddress: string; listenPort: number; listenPath: string }; release: () => Promise }) => void; + let returnRegistration!: (registration: { connection: { mode: "listen"; listenAddress: "0.0.0.0"; listenPort: number; listenPath: string }; release: () => Promise }) => void; const registration = new Promise[0]>(resolve => { returnRegistration = resolve; }); let finishRelease!: () => void; const releasing = new Promise(resolve => { finishRelease = resolve; }); @@ -3585,7 +3585,7 @@ it.each(["stuck", "late-release", "late-release-error", "stuck-release", "cooper if (releaseFails) throw releaseError; }); const registered = { - connection: { mode: "listen" as const, listenAddress: "127.0.0.1", listenPort: 43127, listenPath: "/setup-probe" }, + connection: { mode: "listen" as const, listenAddress: "0.0.0.0" as const, listenPort: 43127, listenPath: "/setup-probe" }, release, }; let registrationSignal: AbortSignal | undefined; @@ -4037,7 +4037,7 @@ it("runs the lab provider boundary through authenticated durable PRP", async () } expect(methods).toContain("turn/completed"); expect(terminalParams).toMatchObject({ - threadId: opened.thread.id, + threadId: (opened.thread as { id: string }).id, turnId: "provider-turn-1", }); expect(bundle.evidence().diagnostics).toContain( @@ -4081,7 +4081,7 @@ it("controls a Codex session goal end to end through durable PRP v2", async () = }, ], }); - const threadId = opened.thread.id; + const threadId = (opened.thread as { id: string }).id; await expect( bundle.transport.request("thread/goal/set", { @@ -4236,7 +4236,7 @@ it.each([false, true])("binds goal turns through the full Codex harness (autonom }); expect(observed.some((event) => event.eventType === "session.failed")).toBe(false); } finally { - await session?.close(); + await session?.close({ reason: "test complete" }); await bundle.transport.close(); await rm(stateDirectory, { recursive: true, force: true }); } @@ -4882,7 +4882,7 @@ it("binds an immediately failed durable turn before exposing its terminal", asyn ], }); } finally { - await session.close(); + await session.close({ reason: "test complete" }); await rm(stateDirectory, { recursive: true, force: true, @@ -5089,9 +5089,10 @@ it("captures exact provider frames and correlates Rust and TypeScript interpreta if (event.eventType === "turn.completed") break; } } finally { - await session.close(); + await session.close({ reason: "test complete" }); } const snapshot = await session.snapshot(); + if (snapshot.lastSourceSequence === undefined) throw new Error("Expected a source sequence in the session snapshot"); for ( let sourceSeq = 1; sourceSeq <= snapshot.lastSourceSequence; @@ -5791,7 +5792,7 @@ it.each([ expect(outcomes[1].result).toMatchObject({ content: [{ text: expect.stringContaining(feedback) }] }); if (adoptAfterTurn) { const snapshot = await session.snapshot(); - const runnerStartedAt = bundle.transport.processInfo().startedAt; + const runnerStartedAt = bundle.transport.processInfo!().startedAt; survivingPid = bundle.evidence().runnerPid; expect(survivingPid).toEqual(expect.any(Number)); const artifact = readRunnerdArtifactBinding(defaultCapabilityRunnerdBinary()); @@ -5833,7 +5834,7 @@ it.each([ expect(recovery.session).toBeDefined(); session = recovery.session!; expect(adopted.evidence().runnerPid).toBe(survivingPid); - expect(adopted.transport.processInfo().startedAt).toBe(runnerStartedAt); + expect(adopted.transport.processInfo!().startedAt).toBe(runnerStartedAt); expect((await session.snapshot()).providerSessionId).toBe(snapshot.providerSessionId); expect(duplicateLauncher).not.toHaveBeenCalled(); } @@ -5843,7 +5844,7 @@ it.each([ expect(allRequests.map(request => request.variant)).toEqual(supportsReasoning ? ["paperclip-no-reasoning", "paperclip-default"] : [undefined, undefined]); }, - closeSession: async () => { await session?.close(); }, + closeSession: async () => { await session?.close({ reason: "test complete" }); }, closeTransport: async () => { try { await adopted?.transport.close(); } finally { diff --git a/packages/paperclip-runner/src/live/runnerd-detach-durability.test.ts b/packages/paperclip-runner/src/live/runnerd-detach-durability.test.ts index d49672cf964..add7ff63fa8 100644 --- a/packages/paperclip-runner/src/live/runnerd-detach-durability.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-detach-durability.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { expect, it, vi } from "vitest"; -import { DurablePrpControlPlane } from "../control-plane/durable-prp-control-plane.js"; +import { DurablePrpControlPlane, type RunnerProcessResult } from "../control-plane/durable-prp-control-plane.js"; import { createCapabilityRunnerdCodexTransport, defaultCapabilityRunnerdBinary, readRunnerdArtifactBinding, runnerdRecoveryInternals } from "./runnerd-codex-transport.js"; it.each(["resolved", "rejected"] as const)("keeps the checkpoint behind controller retirement (%s)", async outcome => { @@ -39,7 +39,7 @@ it.each(["drain", "timeout", "external_close"] as const)("joins an output flush const artifact = readRunnerdArtifactBinding(binary); let core!: DurablePrpControlPlane; const start = DurablePrpControlPlane.prototype.start; - const startSpy = vi.spyOn(DurablePrpControlPlane.prototype, "start").mockImplementation(function (port) { + const startSpy = vi.spyOn(DurablePrpControlPlane.prototype, "start").mockImplementation(function (this: DurablePrpControlPlane, port) { core = this; return start.call(this, port); }); @@ -81,7 +81,7 @@ it.each(["drain", "timeout", "external_close"] as const)("joins an output flush let stderr = ""; owned.stderr?.on("data", chunk => { stderr += String(chunk); }); exit = new Promise(resolveExit => owned.once("exit", () => resolveExit())); - const completion = new Promise<{ code: number | null; signal: string | null; stdout: string; stderr: string }>((resolveExit, reject) => { + const completion = new Promise((resolveExit, reject) => { owned.once("error", reject); owned.once("exit", (code, signal) => resolveExit({ code, signal, stdout: "", stderr })); }); diff --git a/packages/paperclip-runner/src/live/runnerd-opencode-streaming-tools.test.ts b/packages/paperclip-runner/src/live/runnerd-opencode-streaming-tools.test.ts index 604cc561a98..1809b0cfc53 100644 --- a/packages/paperclip-runner/src/live/runnerd-opencode-streaming-tools.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-opencode-streaming-tools.test.ts @@ -76,14 +76,14 @@ it.skipIf(!compilerAvailable || !existsSync(runner)).each(["discovery", "complet expect(seen).toEqual(tool !== "completion" ? ["paperclip_search_assigned_tools", "paperclip_finish"] : ["paperclip_finish"]); expect(text.join("")).toBe("Streaming progress. ".repeat(800)); if (tool === "durable") { - const runnerPid = transport.processInfo().pid; + const runnerPid = transport.processInfo!().pid; await transport.attachRun!({ runId: "next-settled-run", turnId: "next-turn", itemId: "next-item" }); completed = (async () => { for await (const event of transport.notifications()) if (event.method === "turn/completed") return; })(); await transport.request("turn/start", { input: [{ type: "text", text: "stream-backlog-discovery" }] }); await completed; - expect(transport.processInfo().pid).toBe(runnerPid); + expect(transport.processInfo!().pid).toBe(runnerPid); expect(seen).toEqual(["paperclip_search_assigned_tools", "paperclip_finish", "paperclip_search_assigned_tools", "paperclip_finish"]); const provider = JSON.parse(await readFile(join(root, "state", "runner", "codex-provider-state.json"), "utf8")); expect(provider.toolBridge.pending).toEqual({}); diff --git a/packages/paperclip-runner/src/live/runnerd-restart-continuation.test.ts b/packages/paperclip-runner/src/live/runnerd-restart-continuation.test.ts index e1e10951657..efc2e95833b 100644 --- a/packages/paperclip-runner/src/live/runnerd-restart-continuation.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-restart-continuation.test.ts @@ -15,7 +15,7 @@ it.each(["none", "before-start", "after-start"] as const)("completes after the r const execution: NativeExecutionInputV1 = { schema: "paperclip.native-execution-input.v1", binding: { runId: identity.runId, companyId: identity.companyId, issueId: identity.issueId, agentId: identity.agentId, executionWorkspaceId: "workspace-restart" }, - session: { normalizedSessionId: identity.sessionId, driverKind: "codex_app_server", protocolVersion: 1 }, + session: { normalizedSessionId: identity.sessionId, driverKind: "codex_app_server", protocolVersion: 1, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } }, task: { identifier: "TEST-1", title: "Finish checks", description: null, prompt: "Watch the already running checks and finish the request.", workMode: "standard" }, workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null }, provider: { kind: "codex", model: null }, @@ -88,7 +88,7 @@ it.each(["none", "before-start", "after-start"] as const)("completes after the r throw interruptedController; } }, - async appendEvent(event) { events.push(event); durableEvents.push(event); return { cursor: durableEvents.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event) { if (!("sourceSeq" in event)) throw new Error("Expected a durable PRP event"); events.push(event); durableEvents.push(event); return { cursor: durableEvents.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents(request) { const source = durableEvents.filter(event => event.sourceInstanceId === request.sourceInstanceId); return { events: source.filter(event => event.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: source.at(-1)?.sourceSeq ?? 0 }; diff --git a/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts b/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts index 96bec323f05..6e2e3c46e14 100644 --- a/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-runtime-activity.test.ts @@ -63,7 +63,7 @@ it("delivers authenticated runtime activity through the real Runner bridge acros const firstContext = (await helper(["notification-context"])).stdout.trim(); for (const runId of ["activity-first", "activity-next"]) { if (runId === "activity-next") { - attaching = session.attachRun!({ runId }); + attaching = Promise.resolve(session.attachRun!({ runId })); await releasing; await vi.waitFor(async () => { expect((await helper(["notification-context"])).stdout.trim()).not.toBe(firstContext); @@ -93,7 +93,7 @@ it("delivers authenticated runtime activity through the real Runner bridge acros } finally { releasePrevious(); await attaching?.catch(() => undefined); - await session?.close(); + await session?.close({ reason: "test complete" }); await bundle.transport.close(); await rm(root, { recursive: true, force: true }); } diff --git a/packages/paperclip-runner/src/live/runnerd-saved-continuation.test.ts b/packages/paperclip-runner/src/live/runnerd-saved-continuation.test.ts index 8f750ea11e7..72fcb3c100c 100644 --- a/packages/paperclip-runner/src/live/runnerd-saved-continuation.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-saved-continuation.test.ts @@ -49,7 +49,7 @@ it.each(["omitted", "null", "resolved"] as const)("retains a server-owned worksp expect(terminals).toHaveLength(1); expect(terminals[0].envelope.payload).toMatchObject({ runId, eventType: "run.terminal", payload: { runTerminalState: "succeeded", reportedWorkDisposition: "yielded" } }); } finally { - await session?.close(); + await session?.close({ reason: "test complete" }); await bundle.transport.close(); await rm(root, { recursive: true, force: true }); } diff --git a/packages/paperclip-runner/src/live/runnerd-source-reconciliation.test.ts b/packages/paperclip-runner/src/live/runnerd-source-reconciliation.test.ts index 0728c1b8832..6ef8adc0f4b 100644 --- a/packages/paperclip-runner/src/live/runnerd-source-reconciliation.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-source-reconciliation.test.ts @@ -61,7 +61,7 @@ async function sourceFixture(registration?: CapabilityRunnerdCodexTransportOptio runId: identity.runId, executionWorkspaceId: "workspace" }, task: { identifier: "TASK-1", title: "Complete fake work", description: null, prompt: "Complete fake work", workMode: "standard" }, workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null }, - session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "codex_app_server", protocolVersion: 1 }, + session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "codex_app_server", protocolVersion: 1, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } }, provider: { kind: "codex", model: null, approvalPolicy: "never" }, completionContract: { id: "contract", sha256: `sha256:${"a".repeat(64)}`, schemaVersion: "paperclip.completion-contract.v1", contract: { revision: "1", objective: "Complete fake work", criteria: [{ id: "objective", requirement: "Complete fake work" }] } }, interactionResponses: [], credentialBindings: [] }; @@ -231,7 +231,7 @@ it.each([["launch", "default"], ["adopt", "default"], ["adopt-denied", "default" expect((await read(fixture.coreFile)).preparedLaunchConfigurations).toEqual(before.preparedLaunchConfigurations); await expect(readFile(join(fixture.root, "launch-preparation.json"))).rejects.toHaveProperty("code", "ENOENT"); if (mode !== "launch") { - const evidence = resumed.evidence(), startedAt = resumed.transport.processInfo().startedAt; + const evidence = resumed.evidence(), startedAt = resumed.transport.processInfo!().startedAt; await resumed.detachControllerForRestart(); prepare.mockClear(); if (mode === "adopt-denied") prepare.mockRejectedValueOnce(new Error("source preparation authorization revoked")); diff --git a/packages/paperclip-runner/src/live/runnerd-staged-handoff.test.ts b/packages/paperclip-runner/src/live/runnerd-staged-handoff.test.ts index 4e16fd7a3b7..bdad642b3d5 100644 --- a/packages/paperclip-runner/src/live/runnerd-staged-handoff.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-staged-handoff.test.ts @@ -205,7 +205,7 @@ it.each([["connect", "owned"], ["listen", "owned"], ["external", "owned"], ["con try { const opened = await within(first.transport.request("thread/start", { cwd: root, dynamicTools: [] }), "source open"); runnerPid = first.evidence().runnerPid; providerPid = first.evidence().providerPid; - const startedAt = first.transport.processInfo().startedAt; + const startedAt = first.transport.processInfo!().startedAt; const providerStart = execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim(); expect(runnerPid).toBeGreaterThan(0); expect(providerPid).toBeGreaterThan(0); const attach = first.transport.attachRun!({ runId: target.runId, turnId: target.turnId, itemId: target.itemId }); diff --git a/packages/paperclip-runner/src/live/runnerd-trace-frame-index.test.ts b/packages/paperclip-runner/src/live/runnerd-trace-frame-index.test.ts index 2c1731e7ee2..63cb83f5fcb 100644 --- a/packages/paperclip-runner/src/live/runnerd-trace-frame-index.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-trace-frame-index.test.ts @@ -9,6 +9,9 @@ vi.mock("node:fs", async (importOriginal) => { return { ...fs, readSync: vi.fn(fs.readSync) }; }); +type PositionalRead = [fd: number, buffer: NodeJS.ArrayBufferView, offset: number, length: number, position: number | null]; +const readPosition = (call: number) => (vi.mocked(readSync).mock.calls[call] as unknown as PositionalRead)[4]; + const interpretation = (frameId: number, ...emittedEventIds: string[]) => `${JSON.stringify({ kind: "interpretation", frameId, emittedEventIds })}\n`; const settled = `${JSON.stringify({ kind: "trace_status", debugChannel: "rust_native", status: "complete" })}\n`; @@ -41,7 +44,7 @@ describe("runnerd trace frame index", () => { expect(index.locate(path, "pending-1")).toEqual({ frameId: 2, nativeChannelSettled: true }); expect(index.locate(path, "missing")).toEqual({ frameId: null, nativeChannelSettled: true }); expect(readSync).toHaveBeenCalledTimes(2); - expect(vi.mocked(readSync).mock.calls[1]![4]).toBe(Buffer.byteLength(first)); + expect(readPosition(1)).toBe(Buffer.byteLength(first)); }); it("retains partial lines and split UTF-8 until the append is complete", () => { @@ -87,7 +90,7 @@ describe("runnerd trace frame index", () => { expect(index.locate(path, "last").frameId).toBeNull(); expect(index.locate(path, "last").frameId).toBeNull(); expect(index.locate(path, "last").frameId).toBe(4); - for (const call of vi.mocked(readSync).mock.calls) expect(call[3]).toBeLessThanOrEqual(1024 * 1024); + for (const call of vi.mocked(readSync).mock.calls as unknown as PositionalRead[]) expect(call[3]).toBeLessThanOrEqual(1024 * 1024); const calls = vi.mocked(readSync).mock.calls.length; for (let i = 0; i < 1000; i++) index.locate(path, "missing"); expect(readSync).toHaveBeenCalledTimes(calls); @@ -124,8 +127,8 @@ describe("runnerd trace frame index", () => { } expect(readSync).toHaveBeenCalledTimes(72); for (let i = 0; i < 24; i++) { - expect(vi.mocked(readSync).mock.calls[24 + i]![4]).toBe(1024 * 1024); - expect(vi.mocked(readSync).mock.calls[48 + i]![4]).toBe(2 * 1024 * 1024); + expect(readPosition(24 + i)).toBe(1024 * 1024); + expect(readPosition(48 + i)).toBe(2 * 1024 * 1024); } }); diff --git a/packages/paperclip-runner/src/live/runnerd-welcome.integration.test.ts b/packages/paperclip-runner/src/live/runnerd-welcome.integration.test.ts index 49d44895434..933dc68564f 100644 --- a/packages/paperclip-runner/src/live/runnerd-welcome.integration.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-welcome.integration.test.ts @@ -39,7 +39,7 @@ it("starts one provider after delayed bootstrap admission and retains it for a s await bundle.transport.request("turn/start", { input: [{ type: "text", text }] }); for await (const event of bundle.transport.notifications()) { if (event.method === "turn/completed") { - expect(event.params).toMatchObject({ threadId: opened.thread.id }); + expect(event.params).toMatchObject({ threadId: (opened.thread as { id: string }).id }); terminalTurns.push(event.params); break; } diff --git a/packages/paperclip-runner/src/live/scenario-chat-demo-server.test.ts b/packages/paperclip-runner/src/live/scenario-chat-demo-server.test.ts index 59c88dfdd32..9b22f8f769d 100644 --- a/packages/paperclip-runner/src/live/scenario-chat-demo-server.test.ts +++ b/packages/paperclip-runner/src/live/scenario-chat-demo-server.test.ts @@ -136,7 +136,7 @@ describe("Scenario chat deployment boundary", () => { it("requires exact Origin and Fetch Metadata on every mutation", async () => { await withHttpRuntime(async ({ request }) => { const body = JSON.stringify({ scenarioId: SCENARIO_ID }); - const invalidHeaders = [ + const invalidHeaders: Record[] = [ { origin: `${PUBLIC_ORIGIN}/` }, { "sec-fetch-site": "none" }, { "sec-fetch-site": "cross-site" }, diff --git a/packages/paperclip-runner/src/live/turn-stream.test.ts b/packages/paperclip-runner/src/live/turn-stream.test.ts index e91f6e83527..f3547736500 100644 --- a/packages/paperclip-runner/src/live/turn-stream.test.ts +++ b/packages/paperclip-runner/src/live/turn-stream.test.ts @@ -14,6 +14,7 @@ import { type CapabilityLiveTransportFactory, type CapabilityLiveTurnEvent, } from "./live-session.js"; +import type { CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js"; import { createCapabilityTurnStreamDecoder, encodeCapabilityTurnStreamFrame, @@ -161,10 +162,25 @@ class GatedTransport implements CodexAppServerTransport { function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory { return (options) => { - const evidence = { + const evidence: CapabilityRunnerdProcessEvidence = { runnerPid: 7100, runnerProcessGroupId: 7100, + providerPid: null, + providerProcessStartedAt: null, codexPid: 7200, + codexProcessStartedAt: null, + sidecarPid: null, + sidecarProcessStartedAt: null, + agentPid: null, + agentProcessStartedAt: null, + providerDriver: null, + providerVersion: null, + acpxAgent: null, + agentServerVersion: null, + agentRuntimeVersion: null, + acpProtocolVersion: null, + providerExecutionKind: null, + providerService: null, runnerExited: false, runnerExitCode: null, runnerSignal: null, @@ -172,7 +188,7 @@ function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory { diagnostics: [], }; options.onEvidence?.(evidence); - return { transport: new GatedTransport(gate), evidence: () => ({ ...evidence }) }; + return { transport: new GatedTransport(gate), evidence: () => ({ ...evidence }), detachControllerForRestart: async () => {} }; }; } diff --git a/packages/paperclip-runner/src/mock-core/capability-mock-control-plane-adapter.test.ts b/packages/paperclip-runner/src/mock-core/capability-mock-control-plane-adapter.test.ts index a21225b63c8..80e19a86926 100644 --- a/packages/paperclip-runner/src/mock-core/capability-mock-control-plane-adapter.test.ts +++ b/packages/paperclip-runner/src/mock-core/capability-mock-control-plane-adapter.test.ts @@ -584,6 +584,7 @@ describe("CapabilityMockControlPlaneAdapter", () => { commandKind: "record_budget_usage", effect: "lost_ack", remaining: 1, + code: "budget_ack_lost", }], }); await adapter.start(); diff --git a/packages/paperclip-runner/src/mock-core/live-console-demo-server.test.ts b/packages/paperclip-runner/src/mock-core/live-console-demo-server.test.ts index c370aaf6e1b..56cfb03309a 100644 --- a/packages/paperclip-runner/src/mock-core/live-console-demo-server.test.ts +++ b/packages/paperclip-runner/src/mock-core/live-console-demo-server.test.ts @@ -333,7 +333,7 @@ class StubDriver implements HarnessDriver { const servers: LiveConsoleDemoServer[] = []; -function browserHeaders(url: string, headers: HeadersInit = {}): Headers { +function browserHeaders(url: string, headers: ConstructorParameters[0] = {}): Headers { const result = new Headers(headers); result.set("origin", new URL(url).origin); result.set("sec-fetch-site", "same-origin"); diff --git a/packages/paperclip-runner/src/mock-core/live-console-scripted-driver.test.ts b/packages/paperclip-runner/src/mock-core/live-console-scripted-driver.test.ts index 42010232374..9fa24ed1a2c 100644 --- a/packages/paperclip-runner/src/mock-core/live-console-scripted-driver.test.ts +++ b/packages/paperclip-runner/src/mock-core/live-console-scripted-driver.test.ts @@ -82,6 +82,7 @@ function reduce(events: readonly PrpEvent[]): SessionSnapshot { environmentLeaseId: "package-local", runnerInstanceId: "live-console-demo-server", normalizedSessionId: events[0]?.normalizedSessionId ?? "session", + driverSessionId: "live-console-demo", }, capabilities: { schema: "paperclip.prp.capabilities.v1", diff --git a/packages/paperclip-runner/src/mock-core/local-runner.test.ts b/packages/paperclip-runner/src/mock-core/local-runner.test.ts index 3fe96dcdfe0..d60b3296f60 100644 --- a/packages/paperclip-runner/src/mock-core/local-runner.test.ts +++ b/packages/paperclip-runner/src/mock-core/local-runner.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; +import type { LocalRunnerRunMetadata, LocalRunnerRunTrace } from "../contracts/local-runner.js"; import { applyLocalRunnerLiveEvent, createLocalRunnerLiveSnapshot, @@ -13,6 +14,16 @@ import { startLocalRunnerScenario, } from "./local-runner.js"; +function traceMetadata(trace: LocalRunnerRunTrace): LocalRunnerRunMetadata { + return { + schema: "paperclip.runner.local-runner.metadata.v1", + scenario: trace.scenario, + fixtureName: trace.fixtureName, + identity: trace.identity, + capabilities: trace.capabilities, + }; +} + describe("Local runner and fake harness", () => { it("runs lifecycle, tool, file, structured-result, and process-exit events", async () => { const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 }); @@ -108,7 +119,7 @@ describe("Local runner and fake harness", () => { ), ).toBe(true); expect( - replayLocalRunnerEvents(trace, trace.events).timeline.find( + replayLocalRunnerEvents(traceMetadata(trace), trace.events).timeline.find( (event) => event.eventType === "harness.diagnostic", )?.summary, ).toBe( @@ -160,7 +171,7 @@ describe("Local runner and fake harness", () => { it("produces the same snapshot for validated live events and replay", async () => { const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 }); - let live = createLocalRunnerLiveSnapshot(trace); + let live = createLocalRunnerLiveSnapshot(traceMetadata(trace)); for (const event of trace.events) { const next = applyLocalRunnerLiveEvent(live, event); expect(next.ok).toBe(true); @@ -168,7 +179,7 @@ describe("Local runner and fake harness", () => { live = next.snapshot; } } - const replay = replayLocalRunnerEvents(trace, trace.events); + const replay = replayLocalRunnerEvents(traceMetadata(trace), trace.events); expect(localRunnerSnapshotsMatch(live, replay)).toBe(true); }); diff --git a/packages/paperclip-runner/src/native-session-runtime.test.ts b/packages/paperclip-runner/src/native-session-runtime.test.ts index 2f9fc42456c..de63070094e 100644 --- a/packages/paperclip-runner/src/native-session-runtime.test.ts +++ b/packages/paperclip-runner/src/native-session-runtime.test.ts @@ -33,6 +33,7 @@ import { PAPERCLIP_EXECUTION_PROMPT_REVISION, canonicalNativeRuntimeContextDigest, nativeRuntimePromptDigest, + type NativeRuntimeContextSnapshot, } from "./contracts/runtime-context.js"; import { executeNativeSession, @@ -129,6 +130,7 @@ const input: NativeExecutionInputV1 = { normalizedSessionId: identity.sessionId, driverKind: "codex_app_server", protocolVersion: 1, + lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, }, provider: { kind: "codex", model: null }, completionContract: { @@ -145,13 +147,15 @@ const input: NativeExecutionInputV1 = { credentialBindings: [], }; +type RuntimeContextFields = Omit; + function preparedInput(): NativeExecutionInputV5 { const digest = "0".repeat(64); const context = { prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() }, instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } }, skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null }, - } as const; + } satisfies RuntimeContextFields; const requirement = input.completionContract.contract.criteria[0]!.requirement; const prompt = `${input.task.prompt}\n\n${requirement}`; return { @@ -256,7 +260,7 @@ describe("executeNativeSession recovery", () => { const completeRun = vi.fn(async () => undefined); const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { committed.push(structuredClone(event)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { committed.push(structuredClone(event)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents(query) { const events = committed.filter(event => event.sourceInstanceId === query.sourceInstanceId); return { events: events.filter(event => event.sourceSeq > query.afterSourceSeq).slice(0, query.limit), highestContiguousSourceSeq: highestContiguous(events) }; }, completeRun, }; @@ -318,7 +322,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, async loadSessionCheckpoint() { return recovered ? structuredClone(snapshot) : null; }, - async appendEvent(event) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async completeRun() {}, }; @@ -390,7 +394,7 @@ describe("executeNativeSession recovery", () => { async openRun() {}, async loadSessionCheckpoint() { return structuredClone(checkpoint); }, async checkpointSession(value) { checkpoints.push(structuredClone(value)); }, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const existing = journal.find(value => value.sourceEventId === event.sourceEventId); if (existing) expect(event).toEqual(existing); else journal.push(structuredClone(event as PrpEvent)); @@ -479,7 +483,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession(snapshot) { checkpoints.push(structuredClone(snapshot)); }, - async appendEvent(event) { events.push(event); return { cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { events.push(event); return { cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async completeRun() {}, }; @@ -533,7 +537,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async completeRun() {}, }; @@ -555,7 +559,7 @@ describe("executeNativeSession recovery", () => { resume: true, typedEvents: true, steering: false, interruption: true, structuredResult: true, }; - const cancel = vi.fn(async () => { finish(); }); + const cancel = vi.fn(() => { finish(); return { cleanup: Promise.resolve() }; }); const close = vi.fn(async () => { finish(); }); const session: NativeSession = { identity: () => identity, @@ -574,7 +578,7 @@ describe("executeNativeSession recovery", () => { }, cancel, close, }; - const appendEvent = vi.fn(async event => ({ + const appendEvent = vi.fn(async (event: PrpEvent) => ({ cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed", })); @@ -728,7 +732,7 @@ describe("executeNativeSession recovery", () => { prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() }, instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } }, skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null }, - } as const; + } satisfies RuntimeContextFields; const completed = await executeNativeSession({ input: snapshotBeforeUpdate ? { ...input, schema: "paperclip.native-execution-input.v4", executionMode: "default", planningContext: null, @@ -748,7 +752,7 @@ describe("executeNativeSession recovery", () => { controlPlane: { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { appended.push(event); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, @@ -885,7 +889,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { appended.push(event); return { cursor: event.sourceSeq, @@ -1034,7 +1038,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { appended.push(event); return { cursor: event.sourceSeq, @@ -1185,7 +1189,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, @@ -1241,7 +1245,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents() { @@ -1574,7 +1578,7 @@ describe("executeNativeSession recovery", () => { }, controlPlane: { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { appended.push(event); return { + async appendEvent(event: PrpEvent) { appended.push(event); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed", }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, @@ -2568,12 +2572,12 @@ describe("executeNativeSession recovery", () => { }, skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null }, - } as const; + } satisfies RuntimeContextFields; const openSession = vi.fn(); const backend: NativeSessionBackend = { async descriptor() { return { - kind: "future-provider", + kind: "mock", name: "future-provider", version: "1", capabilities: { @@ -2741,6 +2745,7 @@ describe("executeNativeSession recovery", () => { typedEvents: true, steering: false, interruption: true, + structuredResult: true, }; }, async *events() {}, @@ -2854,7 +2859,7 @@ describe("executeNativeSession recovery", () => { const snapshot = vi.fn(async () => { throw snapshotFailure; }); const session: NativeSession = { identity: () => identity, - async capabilities() { return { resume: false, typedEvents: true, steering: false, interruption: true }; }, + async capabilities() { return { resume: false, typedEvents: true, steering: false, interruption: true, structuredResult: true }; }, async *events() {}, async startTurn() { throw new Error("unexpected turn"); }, async result() { return null; }, @@ -2886,6 +2891,7 @@ describe("executeNativeSession recovery", () => { typedEvents: true, steering: false, interruption: true, + structuredResult: true, }; }, async *events() {}, @@ -3509,7 +3515,7 @@ describe("executeNativeSession recovery", () => { } }, ); - const checkpointSession = vi.fn( + const checkpointSession = vi.fn>( async () => undefined, ); const port: ControlPlanePort = { @@ -3744,8 +3750,9 @@ describe("executeNativeSession recovery", () => { timeoutMs: 10, keepSessionOpen: true, onSession: (current) => retainedSessions.push(current), - onPostCompletionEnrichmentFailure: ({ stage }) => - enrichmentFailures.push(stage), + onPostCompletionEnrichmentFailure: ({ stage }) => { + enrichmentFailures.push(stage); + }, }); await enrichmentStalled; if (stalledSignal !== undefined) @@ -4650,12 +4657,12 @@ describe("executeNativeSession recovery", () => { async *events() { throw new Error("provider failed after editing instructions"); }, startTurn: async () => ({ turnId: "instruction-turn" }), result: async () => null, - snapshot: async () => ({ backendKind: "local", sessionId: "instruction-session", identity: scopedIdentity, + snapshot: async () => ({ backendKind: "runner", sessionId: "instruction-session", identity: scopedIdentity, providerSessionId: "instruction-provider", cursor: null, activeTurnId: null, pendingRuntimeRequests: [], lineage: [] }), close: async () => { order.push("closing"); await closed; order.push("stopped"); }, }; const backend: NativeSessionBackend = { - descriptor: async () => ({ kind: "local", name: "instruction-close-test", version: "1", capabilities }), + descriptor: async () => ({ kind: "runner", name: "instruction-close-test", version: "1", capabilities }), openSession: async () => session, }; const controlPlane: ControlPlanePort = { @@ -4747,7 +4754,7 @@ describe("executeNativeSession recovery", () => { close: vi.fn(async () => { throw failure; }), }; const backend: NativeSessionBackend = { - descriptor: async () => ({ kind: "local", name: "local-stop-test", version: "1", capabilities }), + descriptor: async () => ({ kind: "runner", name: "local-stop-test", version: "1", capabilities }), openSession: vi.fn(async () => session), }; const controlPlane: ControlPlanePort = { @@ -4982,7 +4989,7 @@ describe("executeNativeSession recovery", () => { runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 5_000, controlPlane: { async openRun() {}, async checkpointSession() {}, completeRun, - async appendEvent(event, options) { + async appendEvent(event: PrpEvent, options) { if (options?.deferPostCommitObservers) deferredEventTypes.push(event.eventType); events.push(structuredClone(event)); return { cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed", @@ -5080,7 +5087,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { events.push(structuredClone(event as PrpEvent)); return { cursor: events.length, @@ -5199,7 +5206,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { events.push(structuredClone(event as PrpEvent)); const sourceEvents = events.filter( (candidate) => candidate.sourceInstanceId === event.sourceInstanceId, @@ -5417,7 +5424,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { if (event.eventType === "item.completed") { persistingAnswer = true; await persistence; @@ -5925,7 +5932,7 @@ describe("executeNativeSession recovery", () => { const openReplacementSession = vi.fn(); const backend: NativeSessionBackend = { descriptor: async () => ({ kind: "mock", name: "incompatible", version: "1", - capabilities: { resume: true, typedEvents: true, steering: false, interruption: true } }), + capabilities: { resume: true, typedEvents: true, steering: false, interruption: true, structuredResult: true } }), openSession, openReplacementSession, recoverSession: async () => ({ recovered: false, reason: "saved launch profile changed", failureClass: PROVIDER_STATE_INCOMPATIBLE }), @@ -6093,6 +6100,7 @@ describe("executeNativeSession recovery", () => { typedEvents: true, steering: false, interruption: true, + structuredResult: true, }; }, async *events() {}, @@ -6136,6 +6144,7 @@ describe("executeNativeSession recovery", () => { typedEvents: true, steering: false, interruption: true, + structuredResult: true, }, }; }, @@ -6224,6 +6233,7 @@ describe("executeNativeSession recovery", () => { typedEvents: true, steering: false, interruption: true, + structuredResult: true, }; }, async *events() {}, @@ -6268,6 +6278,7 @@ describe("executeNativeSession recovery", () => { typedEvents: true, steering: false, interruption: true, + structuredResult: true, }, }; }, @@ -6375,7 +6386,7 @@ describe("executeNativeSession recovery", () => { }, async replayEvents(replay) { return { - events: replay.afterSourceSeq === 0 ? [runnerEvent(1)] : [], + events: replay.afterSourceSeq === 0 ? [runnerEvent(1, "turn.started")] : [], highestContiguousSourceSeq: 1, }; }, @@ -6479,7 +6490,7 @@ describe("executeNativeSession recovery", () => { }, async replayEvents(replay) { return { - events: replay.afterSourceSeq === 0 ? [runnerEvent(1)] : [], + events: replay.afterSourceSeq === 0 ? [runnerEvent(1, "turn.started")] : [], highestContiguousSourceSeq: 1, }; }, @@ -6530,7 +6541,7 @@ describe("executeNativeSession recovery", () => { sourceEventId: "provider-recovery:1", sourceSeq: 1, sourceInstanceId: "provider-recovery", - sourceKind: "provider", + sourceKind: "runner", runId: identity.runId, normalizedSessionId: identity.sessionId, turnId: "turn-recovery", @@ -6594,7 +6605,7 @@ describe("executeNativeSession recovery", () => { return structuredClone(checkpoint); }, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const list = bySource.get(event.sourceInstanceId) ?? []; list.push(structuredClone(event)); bySource.set(event.sourceInstanceId, list); @@ -6718,7 +6729,7 @@ describe("executeNativeSession recovery", () => { async checkpointSession(snapshot) { checkpoints.push(structuredClone(snapshot)); }, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const target = event.sourceInstanceId === "runner-recovery" ? runnerEvents @@ -6852,7 +6863,7 @@ describe("executeNativeSession recovery", () => { return structuredClone(checkpoint); }, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { events.push(structuredClone(event)); return { cursor: events.length, @@ -6989,8 +7000,8 @@ describe("executeNativeSession recovery", () => { instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } }, skills: [skill], mcp: { assignmentSetId: "none", digest, bindingId: null }, - } as const; - const legacyContext = { ...context, skills: [] as const }; + } satisfies RuntimeContextFields; + const legacyContext = { ...context, skills: [] } satisfies RuntimeContextFields; const prepared = preparedInput(); const executionInput = preparedMode ? { @@ -7049,7 +7060,7 @@ describe("executeNativeSession recovery", () => { terminal: null, terminalTurns: [], }; - const startTurn = vi.fn(async () => ({ turnId: "turn-replacement" })); + const startTurn = vi.fn(async () => ({ turnId: "turn-replacement" })); const replacementSession: NativeSession = { identity: () => identity, async capabilities() { @@ -7108,7 +7119,7 @@ describe("executeNativeSession recovery", () => { return structuredClone(checkpoint); }, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { events.push(structuredClone(event)); return { cursor: events.length, @@ -7194,7 +7205,7 @@ describe("executeNativeSession recovery", () => { sourceEventId: "provider-recovery:2", sourceSeq: 2, sourceInstanceId: "provider-recovery", - sourceKind: "provider", + sourceKind: "runner", runId: identity.runId, normalizedSessionId: identity.sessionId, turnId: "turn-continuation", @@ -7204,7 +7215,7 @@ describe("executeNativeSession recovery", () => { emittedAt: "2026-08-09T00:00:01.000Z", payload: {}, }; - const startTurn = vi.fn(async () => ({ turnId: "turn-continuation" })); + const startTurn = vi.fn(async () => ({ turnId: "turn-continuation" })); const session: NativeSession = { identity: () => identity, async capabilities() { @@ -7273,7 +7284,7 @@ describe("executeNativeSession recovery", () => { return structuredClone(checkpoint); }, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const list = bySource.get(event.sourceInstanceId) ?? []; list.push(structuredClone(event)); bySource.set(event.sourceInstanceId, list); @@ -7490,7 +7501,7 @@ describe("executeNativeSession recovery", () => { sourceEventId: "provider-recovery:2", sourceSeq: 2, sourceInstanceId: "provider-recovery", - sourceKind: "provider", + sourceKind: "runner", runId: identity.runId, normalizedSessionId: identity.sessionId, turnId: "turn-disposition", @@ -7571,7 +7582,7 @@ describe("executeNativeSession recovery", () => { prematureDispositionCheckpoint = true; durableCheckpoint = structuredClone(snapshot); }, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { if (event.eventType === terminalType && failNextAppend) { failNextAppend = false; throw appendFailure; @@ -7674,7 +7685,7 @@ describe("executeNativeSession recovery", () => { sourceEventId: "runner-recovery:run-native:4", sourceSeq: 4, sourceInstanceId: "runner-recovery", - sourceKind: "provider", + sourceKind: "runner", runId: identity.runId, normalizedSessionId: identity.sessionId, turnId: "turn-disposition", @@ -7784,7 +7795,7 @@ describe("executeNativeSession recovery", () => { return structuredClone(checkpoint); }, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const list = bySource.get(event.sourceInstanceId) ?? []; list.push(structuredClone(event)); bySource.set(event.sourceInstanceId, list); @@ -7935,7 +7946,7 @@ describe("executeNativeSession recovery", () => { return structuredClone(checkpoint); }, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const list = bySource.get(event.sourceInstanceId) ?? []; list.push(structuredClone(event)); bySource.set(event.sourceInstanceId, list); @@ -8085,7 +8096,7 @@ describe("executeNativeSession recovery", () => { pendingRuntimeRequests: [], lineage: [], }; - const events = [ + const events: PrpEvent[] = [ { ...controlEvent(1, "run.result.accepted", { result }), turnId: "turn-with-result", @@ -8223,7 +8234,7 @@ describe("executeNativeSession recovery", () => { backend: { async descriptor() { return { kind: "mock", name: "missing-current-completion", version: "1", capabilities }; }, async openSession() { return session; } }, controlPlane: { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { events.push(structuredClone(event as PrpEvent)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { events.push(structuredClone(event as PrpEvent)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, completeRun, }, runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 1000, resolveMissingResult: async () => null, @@ -8239,7 +8250,7 @@ describe("executeNativeSession recovery", () => { sourceEventId: "provider-recovery:1", sourceSeq: 1, sourceInstanceId: "provider-recovery", - sourceKind: "provider", + sourceKind: "runner", runId: identity.runId, normalizedSessionId: identity.sessionId, turnId: "turn-waiting", @@ -8420,7 +8431,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, completeRun: complete, async checkpointSession(value) { checkpoint = structuredClone(value); }, - async appendEvent(event) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents(request) { return { events: history.filter(row => row.sourceInstanceId === request.sourceInstanceId && row.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: 0 }; }, }; const outcome = expect(executeNativeSession({ input, backend, controlPlane: port, timeoutMs: 1_000, @@ -8475,7 +8486,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, completeRun: complete, async checkpointSession(value) { checkpoint = structuredClone(value); }, - async appendEvent(event) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, + async appendEvent(event: PrpEvent) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async replayEvents(request) { return { events: history.filter(row => row.sourceInstanceId === request.sourceInstanceId && row.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: 0 }; }, }; const execution = executeNativeSession({ input, backend, controlPlane: port, timeoutMs: operationMs ?? 120_000, @@ -8523,7 +8534,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, completeRun: complete, async checkpointSession(value) { checkpoint = structuredClone(value); }, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { const duplicate = history.some(row => row.sourceEventId === event.sourceEventId); if (!duplicate) history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: duplicate ? "duplicate" : "committed" }; @@ -8585,7 +8596,7 @@ describe("executeNativeSession recovery", () => { throw new Error("controller lost after governed checkpoint"); } }, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { if (!history.some(row => row.sourceEventId === event.sourceEventId)) history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, @@ -8673,14 +8684,14 @@ describe("executeNativeSession recovery", () => { }), sourceEventId: "provider-recovery:1", sourceInstanceId: "provider-recovery", - sourceKind: "provider", + sourceKind: "runner", turnId: "turn-waiting", }; const turnInterrupted: PrpEvent = { ...controlEvent(3, "turn.interrupted", { reason: "governed_wait" }), sourceEventId: "provider-recovery:3", sourceInstanceId: "provider-recovery", - sourceKind: "provider", + sourceKind: "runner", turnId: "turn-waiting", }; let releaseCancelled!: () => void; @@ -8945,7 +8956,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { events.push(structuredClone(event as PrpEvent)); if (event.eventType === "runtime_request.created") releaseCreated(); const sourceEvents = events.filter( @@ -9491,7 +9502,7 @@ describe("executeNativeSession recovery", () => { const port: ControlPlanePort = { async openRun() {}, async checkpointSession() {}, - async appendEvent(event) { + async appendEvent(event: PrpEvent) { if (event.eventType === "runtime_request.resolved") { markSettlementAppendStarted(); await settlementAppendReleased; @@ -9521,11 +9532,11 @@ describe("executeNativeSession recovery", () => { }; const originalSetTimeout = globalThis.setTimeout; - let queuedHandoffCallback: (() => void) | null = null; + let queuedHandoffCallback = null as (() => void) | null; const timeoutSpy = vi.spyOn(globalThis, "setTimeout").mockImplementation((( - callback, - delay, - ...args + callback: (...args: unknown[]) => void, + delay?: number, + ...args: unknown[] ) => { if (delay === 123_456) { queuedHandoffCallback = () => callback(...args); @@ -9576,7 +9587,7 @@ it("fails preconfigured recovery without replacing a missing provider session", const backend: NativeSessionBackend = { descriptor: async () => ({ kind: "mock", name: "preconfigured", version: "1", runtimeContextCapabilities: { instructions: "native", skills: "native", mcp: "native" }, - capabilities: { resume: true, typedEvents: true, steering: false, interruption: true } }), + capabilities: { resume: true, typedEvents: true, steering: false, interruption: true, structuredResult: true } }), openSession, openReplacementSession, recoverSession: async () => ({ recovered: false, reason: "provider history is missing" }), }; @@ -9586,7 +9597,7 @@ it("fails preconfigured recovery without replacing a missing provider session", }; await expect(executeNativeSession({ input: { ...preparedInput(), schema: "paperclip.native-execution-input.v7", homeMode: "preconfigured" }, - backend, controlPlane: port, runnerInstanceId: "runner-preconfigured", persistedSession: checkpoint, + backend, controlPlane: port, runnerInstanceId: "runner-preconfigured", controlPlaneInstanceId: "control-preconfigured", persistedSession: checkpoint, })).rejects.toThrow("native_session_recovery_failed: provider history is missing"); expect(openSession).not.toHaveBeenCalled(); expect(openReplacementSession).not.toHaveBeenCalled(); diff --git a/packages/paperclip-runner/src/protocol-actions/case-data.test.ts b/packages/paperclip-runner/src/protocol-actions/case-data.test.ts index ea569d9472d..d2459f92eac 100644 --- a/packages/paperclip-runner/src/protocol-actions/case-data.test.ts +++ b/packages/paperclip-runner/src/protocol-actions/case-data.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { paperclipSemanticAction } from "../catalog/semantic-action-catalog.js"; import { paperclipSemanticActionNamespace } from "../semantic-tools/paperclip-discovery.js"; diff --git a/packages/paperclip-runner/src/protocol-actions/collaboration-contracts.test.ts b/packages/paperclip-runner/src/protocol-actions/collaboration-contracts.test.ts index 0458475389d..03758fe075b 100644 --- a/packages/paperclip-runner/src/protocol-actions/collaboration-contracts.test.ts +++ b/packages/paperclip-runner/src/protocol-actions/collaboration-contracts.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { PAPERCLIP_COLLABORATION_PROTOCOL_ACTIONS } from "./collaboration.js"; diff --git a/packages/paperclip-runner/src/protocol-actions/core-contracts.test.ts b/packages/paperclip-runner/src/protocol-actions/core-contracts.test.ts index 8a76a6f2659..fe06c2f98d5 100644 --- a/packages/paperclip-runner/src/protocol-actions/core-contracts.test.ts +++ b/packages/paperclip-runner/src/protocol-actions/core-contracts.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { PAPERCLIP_CORE_PROTOCOL_ACTIONS } from "./core.js"; diff --git a/packages/paperclip-runner/src/protocol/permission-request.test.ts b/packages/paperclip-runner/src/protocol/permission-request.test.ts index 2f5a18d2fa3..f0ac44e6fa4 100644 --- a/packages/paperclip-runner/src/protocol/permission-request.test.ts +++ b/packages/paperclip-runner/src/protocol/permission-request.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { questionSetSchema, requestSchema } from "./generated/schema-bundle.js"; diff --git a/packages/paperclip-runner/src/protocol/provider-descriptor.test.ts b/packages/paperclip-runner/src/protocol/provider-descriptor.test.ts index 891fb68a5f5..d6d1f3d5b6e 100644 --- a/packages/paperclip-runner/src/protocol/provider-descriptor.test.ts +++ b/packages/paperclip-runner/src/protocol/provider-descriptor.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; import { providerDescriptorSchema } from "./generated/schema-bundle.js"; @@ -19,7 +19,7 @@ describe("provider runtime descriptor", () => { } const event: PrpEvent = { schema: "paperclip.prp.event.v2", schemaVersion: 2, sourceEventId: "controls", sourceSeq: 1, - sourceInstanceId: "runner", sourceKind: "runner", runId: "run", eventType: "session.capabilities.updated", + sourceInstanceId: "runner", sourceKind: "runner", runId: "run", normalizedSessionId: "session", eventType: "session.capabilities.updated", priority: 0, emittedAt: "2026-09-28T12:00:00.000Z", payload: { sessionGoals: { availability: "unsupported", reason: "provider has no native goals", actions: [], autonomousUpdates: false, persistentAcrossResume: false, maxObjectiveChars: 4000, diff --git a/packages/paperclip-runner/src/protocol/prp-v1-expressiveness-crosswalk.test.ts b/packages/paperclip-runner/src/protocol/prp-v1-expressiveness-crosswalk.test.ts index 2909598d388..e190ea1fa3e 100644 --- a/packages/paperclip-runner/src/protocol/prp-v1-expressiveness-crosswalk.test.ts +++ b/packages/paperclip-runner/src/protocol/prp-v1-expressiveness-crosswalk.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, resolve } from "node:path"; -import Ajv2020 from "ajv/dist/2020"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { describe, expect, it } from "vitest"; type Classification = diff --git a/packages/paperclip-runner/src/protocol/replay-contract.test.ts b/packages/paperclip-runner/src/protocol/replay-contract.test.ts index bad4d2536ee..2b98ffe4acb 100644 --- a/packages/paperclip-runner/src/protocol/replay-contract.test.ts +++ b/packages/paperclip-runner/src/protocol/replay-contract.test.ts @@ -1,4 +1,4 @@ -import Ajv2020 from "ajv/dist/2020.js"; +import { Ajv2020 } from "ajv/dist/2020.js"; import { usageSchema } from "./generated/schema-bundle.js"; import { readFile } from "node:fs/promises"; @@ -77,7 +77,7 @@ describe("PRP v1 JSON Schema contract", () => { expect(result.fixture.futureFixtureHint).toEqual({ producerVersion: "1.1-preview", }); - expect(result.fixture.events[0]?.futureEnvelopeField).toBe(42); + expect((result.fixture.events[0] as unknown as Record).futureEnvelopeField).toBe(42); } }); @@ -269,7 +269,7 @@ describe("PRP v1 JSON Schema contract", () => { expect(result.ok).toBe(true); if (!result.ok) return; expect( - result.fixture.events[0]?.payload.semantic_tool?.futureEnvelopeHint, + (result.fixture.events[0]?.payload.semantic_tool as { futureEnvelopeHint?: unknown } | undefined)?.futureEnvelopeHint, ).toEqual({ version: "1.1-preview" }); const withoutEnvelope = structuredClone(result.fixture); diff --git a/packages/paperclip-runner/src/protocol/workspace-diff-events.test.ts b/packages/paperclip-runner/src/protocol/workspace-diff-events.test.ts index 9a25aec4bb3..a6fcdd91d81 100644 --- a/packages/paperclip-runner/src/protocol/workspace-diff-events.test.ts +++ b/packages/paperclip-runner/src/protocol/workspace-diff-events.test.ts @@ -10,6 +10,7 @@ function workspaceEvent(eventType: "workspace.change.updated" | "workspace.diff. sourceInstanceId: "runner-1", sourceKind: "runner", runId: "run-1", + normalizedSessionId: "session-1", turnId: "turn-1", eventType, schemaVersion: 1, @@ -44,7 +45,7 @@ describe("PRP workspace change events", () => { it("rejects traversal paths and incomplete recorded diffs", () => { const traversal = workspaceEvent("workspace.diff.recorded"); - traversal.payload.files[0]!.path = "../secret"; + (traversal.payload.files as Array<{ path: string }>)[0]!.path = "../secret"; expect(validatePrpEvent(traversal).ok).toBe(false); const incomplete = workspaceEvent("workspace.diff.recorded"); incomplete.payload.complete = false; @@ -59,6 +60,7 @@ describe("PRP workspace change events", () => { sourceInstanceId: "runner-1", sourceKind: "runner", runId: "run-1", + normalizedSessionId: "session-1", turnId: "turn-1", eventType: "workspace.file.referenced", schemaVersion: 1, diff --git a/packages/paperclip-runner/src/react/public-contract.test.tsx b/packages/paperclip-runner/src/react/public-contract.test.tsx index cae0aa98f6a..3f60ddadd1c 100644 --- a/packages/paperclip-runner/src/react/public-contract.test.tsx +++ b/packages/paperclip-runner/src/react/public-contract.test.tsx @@ -3,7 +3,7 @@ import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vitest"; import type { SessionItemSnapshot } from "../reducer/session-reducer.js"; -import type { TranscriptEntry } from "../browser/transcript-model.js"; +import type { TranscriptEntry, TranscriptItemEntry } from "../browser/transcript-model.js"; import { Composer } from "./components/composer.js"; import { Conversation } from "./components/conversation.js"; import { parseStructuredResponse } from "./components/message.js"; @@ -17,7 +17,7 @@ const item: SessionItemSnapshot = { text: "Canonical answer", }; -const entries: TranscriptEntry[] = [ +const entries: TranscriptItemEntry[] = [ { kind: "item", key: "item:assistant-1", diff --git a/packages/paperclip-runner/src/tools/capability-semantic-tools.test.ts b/packages/paperclip-runner/src/tools/capability-semantic-tools.test.ts index 5a4685a670f..9d4b010b046 100644 --- a/packages/paperclip-runner/src/tools/capability-semantic-tools.test.ts +++ b/packages/paperclip-runner/src/tools/capability-semantic-tools.test.ts @@ -93,6 +93,11 @@ async function runtimeFor(options: { }; } +function pendingLeaseExpiry(snapshot: CapabilitySemanticToolRuntimeSnapshot | null): number | undefined { + const extension = snapshot?.extensions[0]; + return extension?.status === "pending" ? extension.leaseExpiresAtMs : undefined; +} + describe("Capability semantic tool catalog", () => { it("defines a unique versioned provider-neutral catalog and every optional group", () => { const operationIds = CAPABILITY_SEMANTIC_TOOL_CATALOG.map((tool) => tool.operationId); @@ -441,7 +446,7 @@ describe("Capability exposure and authorization", () => { }); it("retries the completion merge after a concurrent lease heartbeat", async () => { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let injectedHeartbeats = 0; let completionAttempts = 0; const durableStore: CapabilitySemanticToolRuntimeStore = { @@ -494,7 +499,7 @@ describe("Capability exposure and authorization", () => { it("bounds completion contention and retries the fulfilled execution without rerunning it", async () => { vi.useFakeTimers({ now: 0 }); try { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let rejectCompletion = true; let completionAttempts = 0; const durableStore: CapabilitySemanticToolRuntimeStore = { @@ -574,7 +579,7 @@ describe("Capability exposure and authorization", () => { vi.useFakeTimers({ now: 0 }); try { let now = 0; - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let rejectCompletion = true; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => durableSnapshot === null ? null : structuredClone(durableSnapshot), @@ -704,7 +709,7 @@ describe("Capability exposure and authorization", () => { it("retains a failed lease until cleanup contention clears", async () => { vi.useFakeTimers({ now: 0 }); try { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let failExecutionAllocation = true; let cleanupAttempts = 0; const durableStore: CapabilitySemanticToolRuntimeStore = { @@ -764,7 +769,7 @@ describe("Capability exposure and authorization", () => { }); it("allocates read result ids from the latest shared durable sequence", async () => { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => durableSnapshot === null ? null : structuredClone(durableSnapshot), save: (_runId, snapshot) => { @@ -800,7 +805,7 @@ describe("Capability exposure and authorization", () => { }); it("retries extension acquisition after an unrelated snapshot update", async () => { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let injectedContention = false; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => durableSnapshot === null ? null : structuredClone(durableSnapshot), @@ -903,7 +908,7 @@ describe("Capability exposure and authorization", () => { }); it("does not revive a serialized pending extension when the durable store is empty", async () => { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => durableSnapshot === null ? null : structuredClone(durableSnapshot), save: (_runId, snapshot) => { @@ -928,7 +933,7 @@ describe("Capability exposure and authorization", () => { const inFlight = first.runtime.invoke(invocation); const serializedWhilePending = first.adapter.serialize(); await expect(inFlight).resolves.toMatchObject({ ok: true }); - durableSnapshot = null; + durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; const restoredAdapter = CapabilityMockControlPlaneAdapter.restore( serializedWhilePending, @@ -952,7 +957,7 @@ describe("Capability exposure and authorization", () => { }); it("does not revive serialized completed extensions when the durable store is empty", async () => { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => durableSnapshot === null ? null : structuredClone(durableSnapshot), save: (_runId, snapshot) => { @@ -974,7 +979,7 @@ describe("Capability exposure and authorization", () => { idempotencyKey: "stale-completed", })).resolves.toMatchObject({ ok: true }); const serializedWithCompleted = first.adapter.serialize(); - durableSnapshot = null; + durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; const restoredAdapter = CapabilityMockControlPlaneAdapter.restore( serializedWithCompleted, @@ -1004,7 +1009,7 @@ describe("Capability exposure and authorization", () => { it("renews a live extension lease before another runtime can reclaim it", async () => { vi.useFakeTimers({ now: 0 }); try { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => durableSnapshot === null ? null : structuredClone(durableSnapshot), @@ -1066,7 +1071,7 @@ describe("Capability exposure and authorization", () => { it("retries a lease heartbeat after a transient durable-store failure", async () => { vi.useFakeTimers({ now: 0 }); try { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let failNextLoad = false; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => { @@ -1099,9 +1104,9 @@ describe("Capability exposure and authorization", () => { failNextLoad = true; expect(() => vi.advanceTimersByTime(10_000)).not.toThrow(); - expect(durableSnapshot?.extensions[0]?.leaseExpiresAtMs).toBe(30_000); + expect(pendingLeaseExpiry(durableSnapshot)).toBe(30_000); vi.advanceTimersByTime(10_000); - expect(durableSnapshot?.extensions[0]?.leaseExpiresAtMs).toBe(50_000); + expect(pendingLeaseExpiry(durableSnapshot)).toBe(50_000); await expect(inFlight).resolves.toMatchObject({ ok: true }); } finally { vi.useRealTimers(); @@ -1111,7 +1116,7 @@ describe("Capability exposure and authorization", () => { it("recovers a prepared extension after repeated heartbeat failures", async () => { vi.useFakeTimers({ now: 0 }); try { - let durableSnapshot: CapabilitySemanticToolRuntimeSnapshot | null = null; + let durableSnapshot = null as CapabilitySemanticToolRuntimeSnapshot | null; let failHeartbeatLoads = false; const durableStore: CapabilitySemanticToolRuntimeStore = { load: () => { diff --git a/packages/paperclip-runner/test/support/runnerd-transport.ts b/packages/paperclip-runner/test/support/runnerd-transport.ts index 171414f2415..4fb1359f567 100644 --- a/packages/paperclip-runner/test/support/runnerd-transport.ts +++ b/packages/paperclip-runner/test/support/runnerd-transport.ts @@ -8,6 +8,7 @@ import { nativeRuntimePromptDigest, type NativeRuntimeContextSnapshot, } from "../../src/contracts/runtime-context.js"; +import type { DurablePrpControlPlane, DurablePrpControlPlaneStore } from "../../src/control-plane/durable-prp-control-plane.js"; import { defaultCapabilityRunnerdBinary as qualifiedCapabilityRunnerdBinary } from "../../src/live/runnerd-codex-transport.js"; export const defaultCapabilityRunnerdBinary = () => @@ -86,3 +87,11 @@ export function assignedRuntimeContext( aggregateDigest: canonicalNativeRuntimeContextDigest(value), }; } + +export type CommittableStore = DurablePrpControlPlaneStore & { + commit(candidate: DurablePrpControlPlaneStore["state"], checkpointRequired?: boolean): void; +}; + +export function committableStore(authority: DurablePrpControlPlane): CommittableStore { + return authority.store as CommittableStore; +} diff --git a/packages/paperclip-runner/tsconfig.browser.json b/packages/paperclip-runner/tsconfig.browser.json index 624c92fc56c..f97c6806310 100644 --- a/packages/paperclip-runner/tsconfig.browser.json +++ b/packages/paperclip-runner/tsconfig.browser.json @@ -14,22 +14,21 @@ "vite/client", "node" ], - "baseUrl": ".", "paths": { "@paperclipai/paperclip-runner/browser": [ - "src/browser/index.ts" + "./src/browser/index.ts" ], "@paperclipai/paperclip-runner/react": [ - "src/react/index.ts" + "./src/react/index.ts" ], "@paperclipai/paperclip-runner/standalone": [ - "src/standalone/index.ts" + "./src/standalone/index.ts" ], "@paperclipai/paperclip-runner/styles.css": [ - "styles.css" + "./styles.css" ], "@paperclip-runner-local/capability": [ - "src/scenarios/index.ts" + "./src/scenarios/index.ts" ] } }, diff --git a/packages/paperclip-runner/tsconfig.browser.test.json b/packages/paperclip-runner/tsconfig.browser.test.json new file mode 100644 index 00000000000..4d74f669f27 --- /dev/null +++ b/packages/paperclip-runner/tsconfig.browser.test.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.browser.json", + "compilerOptions": { + "noEmit": true + }, + "include": [ + "devtools/browser/**/*.ts", + "devtools/browser/**/*.tsx", + "devtools/issue-thread/**/*.ts", + "devtools/issue-thread/**/*.tsx", + "examples/**/*.ts", + "examples/**/*.tsx" + ], + "exclude": ["**/dist/**", "**/vite.config.ts"] +} diff --git a/packages/paperclip-runner/tsconfig.surfaces.test.json b/packages/paperclip-runner/tsconfig.surfaces.test.json new file mode 100644 index 00000000000..b6b27ea0e3a --- /dev/null +++ b/packages/paperclip-runner/tsconfig.surfaces.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.surfaces.json", + "compilerOptions": { + "allowJs": true, + "noEmit": true, + "rootDir": "." + }, + "exclude": [] +} diff --git a/packages/paperclip-runner/tsconfig.test.json b/packages/paperclip-runner/tsconfig.test.json new file mode 100644 index 00000000000..1b05e128951 --- /dev/null +++ b/packages/paperclip-runner/tsconfig.test.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowJs": true, + "noEmit": true, + "rootDir": ".." + }, + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": [ + "src/browser/**", + "src/devtools/**", + "src/issue-thread/**", + "src/react/**", + "src/scenarios/**", + "src/standalone/**" + ] +} diff --git a/packages/plugins/examples/plugin-orchestration-smoke-example/tests/plugin.spec.ts b/packages/plugins/examples/plugin-orchestration-smoke-example/tests/plugin.spec.ts index dd879eeef07..a583fb4080a 100644 --- a/packages/plugins/examples/plugin-orchestration-smoke-example/tests/plugin.spec.ts +++ b/packages/plugins/examples/plugin-orchestration-smoke-example/tests/plugin.spec.ts @@ -20,8 +20,11 @@ function issue(input: Partial & Pick title, description: null, status: "todo", + workMode: "standard", priority: "medium", + reviewPolicy: null, assigneeAgentId: null, + responsibleUserId: null, assigneeUserId: null, checkoutRunId: null, executionRunId: null, diff --git a/packages/plugins/examples/plugin-orchestration-smoke-example/tsconfig.test.json b/packages/plugins/examples/plugin-orchestration-smoke-example/tsconfig.test.json new file mode 100644 index 00000000000..f20d34cbaee --- /dev/null +++ b/packages/plugins/examples/plugin-orchestration-smoke-example/tsconfig.test.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "paths": { + "@paperclipai/shared": ["../../../shared/dist/index.d.ts"] + }, + "types": ["node"], + "typeRoots": ["../../../../server/node_modules/@types"] + }, + "include": ["tests"] +} diff --git a/packages/plugins/sandbox-providers/boxd/tsconfig.test.json b/packages/plugins/sandbox-providers/boxd/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/boxd/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/cloudflare/bridge-template/tsconfig.test.json b/packages/plugins/sandbox-providers/cloudflare/bridge-template/tsconfig.test.json new file mode 100644 index 00000000000..a8d4317b49c --- /dev/null +++ b/packages/plugins/sandbox-providers/cloudflare/bridge-template/tsconfig.test.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/cloudflare/tsconfig.test.json b/packages/plugins/sandbox-providers/cloudflare/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/cloudflare/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/createos/src/file-sync.test.ts b/packages/plugins/sandbox-providers/createos/src/file-sync.test.ts index 4ac98c98907..259734a1f80 100644 --- a/packages/plugins/sandbox-providers/createos/src/file-sync.test.ts +++ b/packages/plugins/sandbox-providers/createos/src/file-sync.test.ts @@ -2,7 +2,7 @@ import path from "node:path"; import os from "node:os"; import { promises as fs } from "node:fs"; import * as tar from "tar"; -import { afterEach, beforeEach, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, expect, it, vi, type Mock } from "vitest"; import { CreateosClient } from "./client.js"; import { parseConfig } from "./config.js"; import { assertRemotePath, syncFiles, validateArchive } from "./file-sync.js"; @@ -16,7 +16,7 @@ const params = { driverKey: "createos", companyId: "c", environmentId: "e", conf let temp: string; let downloads: Buffer; let uploads: Buffer[]; -let fetchMock: ReturnType; +let fetchMock: Mock<(url: string, init?: RequestInit) => Promise>; beforeEach(async () => { temp = await fs.mkdtemp(path.join(os.tmpdir(), "createos-sync-test-")); diff --git a/packages/plugins/sandbox-providers/createos/tsconfig.test.json b/packages/plugins/sandbox-providers/createos/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/createos/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/daytona/tsconfig.test.json b/packages/plugins/sandbox-providers/daytona/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/daytona/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/e2b/src/plugin.test.ts b/packages/plugins/sandbox-providers/e2b/src/plugin.test.ts index 851807e98b1..e74c4b92beb 100644 --- a/packages/plugins/sandbox-providers/e2b/src/plugin.test.ts +++ b/packages/plugins/sandbox-providers/e2b/src/plugin.test.ts @@ -308,7 +308,7 @@ describe("E2B sandbox provider plugin", () => { expect(mockConnect).toHaveBeenCalledWith("sandbox-123", expect.objectContaining({ apiKey: "resolved-key" })); expect(sandbox.files.write).toHaveBeenCalledWith(expect.stringMatching(/^\/tmp\/paperclip-stdin-/), "input"); - const stdinCall = sandbox.commands.run.mock.calls.find(([cmd]: [string]) => cmd.includes("'printf'")); + const stdinCall = sandbox.commands.run.mock.calls.find(([cmd]) => cmd.includes("'printf'")); expect(stdinCall).toBeDefined(); if (!stdinCall) throw new Error("stdinCall not found"); expect(stdinCall[0]).toMatch(/\.profile/); @@ -365,7 +365,7 @@ describe("E2B sandbox provider plugin", () => { timeoutMs: 1000, }); - const fgCall = sandbox.commands.run.mock.calls.find(([cmd]: [string]) => cmd.includes("'printf'")); + const fgCall = sandbox.commands.run.mock.calls.find(([cmd]) => cmd.includes("'printf'")); expect(fgCall).toBeDefined(); if (!fgCall) throw new Error("fgCall not found"); expect(fgCall[0]).toMatch(/\.profile/); diff --git a/packages/plugins/sandbox-providers/e2b/tsconfig.test.json b/packages/plugins/sandbox-providers/e2b/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/e2b/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/exe-dev/tsconfig.test.json b/packages/plugins/sandbox-providers/exe-dev/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/exe-dev/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/kubernetes/package.json b/packages/plugins/sandbox-providers/kubernetes/package.json index 057f6685b8b..efb51c2a4f9 100644 --- a/packages/plugins/sandbox-providers/kubernetes/package.json +++ b/packages/plugins/sandbox-providers/kubernetes/package.json @@ -49,12 +49,12 @@ }, "dependencies": { "@kubernetes/client-node": "^1.0.0", - "zod": "^3.24.2" + "zod": "^4.6.6" }, "devDependencies": { "@types/node": "^24.0.0", "typescript": "^5.7.3", - "vitest": "^3.2.4" + "vitest": "^4.1.8" }, "engines": { "node": ">=24.11.0" diff --git a/packages/plugins/sandbox-providers/kubernetes/src/adapter-registry.ts b/packages/plugins/sandbox-providers/kubernetes/src/adapter-registry.ts index ce732b57db5..0e71c6035ce 100644 --- a/packages/plugins/sandbox-providers/kubernetes/src/adapter-registry.ts +++ b/packages/plugins/sandbox-providers/kubernetes/src/adapter-registry.ts @@ -19,7 +19,7 @@ export const adapterRegistryEntrySchema = z envKeys: z.array(z.string()).optional(), allowFqdns: z.array(z.string()).optional(), probeCommand: z.array(z.string()).optional(), - defaultEnv: z.record(z.string()).optional(), + defaultEnv: z.record(z.string(), z.string()).optional(), }) .strict(); diff --git a/packages/plugins/sandbox-providers/kubernetes/src/types.ts b/packages/plugins/sandbox-providers/kubernetes/src/types.ts index 5626daa9384..e39ccac78ed 100644 --- a/packages/plugins/sandbox-providers/kubernetes/src/types.ts +++ b/packages/plugins/sandbox-providers/kubernetes/src/types.ts @@ -28,7 +28,7 @@ export const kubernetesProviderConfigSchema = z .optional(), runtimeClassName: z.string().optional(), - serviceAccountAnnotations: z.record(z.string()).default({}), + serviceAccountAnnotations: z.record(z.string(), z.string()).default({}), jobTtlSecondsAfterFinished: z.number().int().nonnegative().default(900), podActivityDeadlineSec: z.number().int().positive().default(3600), diff --git a/packages/plugins/sandbox-providers/kubernetes/test/integration/end-to-end-run.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/integration/end-to-end-run.test.ts index 8fb64270bcf..44f5772894b 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/integration/end-to-end-run.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/integration/end-to-end-run.test.ts @@ -65,6 +65,7 @@ describe("plugin-kubernetes end-to-end", () => { }); expect(lease.providerLeaseId).toMatch(/^pc-/); + const providerLeaseId = lease.providerLeaseId!; // Verify the Job exists in the tenant namespace const jobs = kubectl(`get jobs -n ${NAMESPACE} -o name`); @@ -137,6 +138,7 @@ describe("plugin-kubernetes end-to-end", () => { }); expect(lease.providerLeaseId).toMatch(/^pc-/); + const providerLeaseId = lease.providerLeaseId!; // Verify the Sandbox CR exists in the tenant namespace const sandboxes = kubectl( @@ -156,7 +158,7 @@ describe("plugin-kubernetes end-to-end", () => { await sandboxCrOrchestrator.waitForCompletion( clients, NAMESPACE, - lease.providerLeaseId, + providerLeaseId, { timeoutMs: 90_000, pollMs: 3000 }, ); @@ -164,7 +166,7 @@ describe("plugin-kubernetes end-to-end", () => { const podName = await sandboxCrOrchestrator.findPod( clients, NAMESPACE, - lease.providerLeaseId, + providerLeaseId, ); expect(podName).toBeTruthy(); diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/adapter-defaults.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/adapter-defaults.test.ts index 513c2506809..f7f1b78037e 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/adapter-defaults.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/adapter-defaults.test.ts @@ -75,6 +75,7 @@ describe("getAdapterDefaults", () => { const registry: AdapterRegistryEntry[] = [ { adapterType: "opencode_local", + enabled: true, runtimeImage: "x", envKeys: [], allowFqdns: [], @@ -88,7 +89,7 @@ describe("getAdapterDefaults", () => { it("throws when a supplied registry entry is missing runtimeImage", () => { const registry: AdapterRegistryEntry[] = [ - { adapterType: "opencode_local", envKeys: [], allowFqdns: [], probeCommand: ["x"] }, + { adapterType: "opencode_local", enabled: true, envKeys: [], allowFqdns: [], probeCommand: ["x"] }, ]; expect(() => getAdapterDefaults("opencode_local", registry)).toThrow( /missing required runtime field: runtimeImage/, @@ -97,7 +98,7 @@ describe("getAdapterDefaults", () => { it("defaults the optional array fields to [] when the registry omits them", () => { const registry: AdapterRegistryEntry[] = [ - { adapterType: "opencode_local", runtimeImage: "img" }, + { adapterType: "opencode_local", enabled: true, runtimeImage: "img" }, ]; const d = getAdapterDefaults("opencode_local", registry); expect(d.envKeys).toEqual([]); diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/cilium-network-policy.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/cilium-network-policy.test.ts index 419f4b1b33c..4cb6faa8693 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/cilium-network-policy.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/cilium-network-policy.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from "vitest"; -import { buildCiliumNetworkPolicyManifest } from "../../src/cilium-network-policy.js"; +import { buildCiliumNetworkPolicyManifest as buildRawCiliumNetworkPolicyManifest, type BuildCiliumNetworkPolicyInput } from "../../src/cilium-network-policy.js"; +import type { CiliumNetworkPolicyView } from "./manifest-views.js"; + +const buildCiliumNetworkPolicyManifest = (input: BuildCiliumNetworkPolicyInput) => + buildRawCiliumNetworkPolicyManifest(input) as unknown as CiliumNetworkPolicyView; describe("buildCiliumNetworkPolicyManifest", () => { const baseInput = { @@ -27,7 +31,7 @@ describe("buildCiliumNetworkPolicyManifest", () => { }); const fqdnRule = cnp.spec.egress.find((e: { toFQDNs?: { matchName: string }[] }) => e.toFQDNs); expect(fqdnRule).toBeDefined(); - expect(fqdnRule.toFQDNs.map((f: { matchName: string }) => f.matchName).sort()).toEqual([ + expect(fqdnRule!.toFQDNs!.map((f: { matchName: string }) => f.matchName).sort()).toEqual([ "api.anthropic.com", "api.openai.com", ]); @@ -55,7 +59,7 @@ describe("buildCiliumNetworkPolicyManifest", () => { egressAllowCidrs: ["10.0.0.0/8"], }); const cidrRule = cnp.spec.egress.find((e: { toCIDRSet?: { cidr: string }[] }) => e.toCIDRSet); - expect(cidrRule.toCIDRSet[0].cidr).toBe("10.0.0.0/8"); + expect(cidrRule?.toCIDRSet?.[0]?.cidr).toBe("10.0.0.0/8"); }); it("targets only the granted run when building a scoped policy", () => { diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest-views.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest-views.ts new file mode 100644 index 00000000000..a0314c5096e --- /dev/null +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest-views.ts @@ -0,0 +1,98 @@ +// The manifest builders return Record because the manifests go +// straight to the Kubernetes API. These views name only the fields the tests read. + +export interface PodTemplateView { + metadata: { labels: Record }; + spec: { + automountServiceAccountToken: boolean; + restartPolicy: string; + runtimeClassName?: string; + imagePullSecrets?: { name: string }[]; + securityContext: { + runAsNonRoot: boolean; + runAsUser: number; + fsGroupChangePolicy: string; + seccompProfile: { type: string }; + }; + containers: { + command: string[]; + envFrom: { secretRef: { name: string } }[]; + securityContext: { + runAsNonRoot: boolean; + readOnlyRootFilesystem: boolean; + allowPrivilegeEscalation: boolean; + capabilities: { drop: string[] }; + }; + volumeMounts: { mountPath: string }[]; + }[]; + volumes: { emptyDir?: unknown }[]; + }; +} + +export interface JobManifestView { + apiVersion: string; + kind: string; + metadata: { labels: Record }; + spec: { + backoffLimit: number; + ttlSecondsAfterFinished: number; + activeDeadlineSeconds: number; + template: PodTemplateView; + }; +} + +export interface SandboxCrManifestView { + apiVersion: string; + kind: string; + metadata: { + name: string; + namespace: string; + labels: Record; + ownerReferences?: unknown[]; + }; + spec: { podTemplate: PodTemplateView }; +} + +export interface NetworkPolicyEgressRuleView { + to: { + namespaceSelector?: { matchLabels?: Record }; + podSelector?: { matchLabels?: Record }; + ipBlock?: { cidr: string; except?: string[] }; + }[]; + ports?: { protocol: string; port: number }[]; +} + +export interface DenyAllPolicyView { + metadata: { name: string }; + spec: { + podSelector: Record; + policyTypes: string[]; + ingress?: unknown[]; + egress?: unknown[]; + }; +} + +export interface EgressAllowPolicyView { + metadata: { name: string; ownerReferences?: unknown[] }; + spec: { + podSelector: { matchLabels: Record }; + egress: NetworkPolicyEgressRuleView[]; + }; +} + +export interface CiliumEgressRuleView { + toEndpoints?: { matchLabels: Record }[]; + toPorts?: { ports: { port: string }[] }[]; + toFQDNs?: { matchName: string }[]; + toCIDRSet?: { cidr: string }[]; +} + +export interface CiliumNetworkPolicyView { + apiVersion: string; + kind: string; + metadata: { name: string; ownerReferences?: unknown[] }; + spec: { + endpointSelector: { matchLabels: Record }; + egress: CiliumEgressRuleView[]; + }; +} diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/network-policy.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/network-policy.test.ts index 80338e70129..5562d8ed6e4 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/network-policy.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/network-policy.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from "vitest"; -import { buildNetworkPolicyManifests } from "../../src/network-policy.js"; +import { buildNetworkPolicyManifests as buildRawNetworkPolicyManifests, type BuildNetworkPolicyInput } from "../../src/network-policy.js"; +import type { DenyAllPolicyView, EgressAllowPolicyView } from "./manifest-views.js"; + +const buildNetworkPolicyManifests = (input: BuildNetworkPolicyInput) => + buildRawNetworkPolicyManifests(input) as unknown as [DenyAllPolicyView, EgressAllowPolicyView]; describe("buildNetworkPolicyManifests", () => { const baseInput = { @@ -50,7 +54,7 @@ describe("buildNetworkPolicyManifests", () => { r.to.some((t) => t.podSelector?.matchLabels?.app === "paperclip-server"), ); expect(callbackRule).toBeDefined(); - expect(callbackRule.ports[0].port).toBe(3100); + expect(callbackRule!.ports![0].port).toBe(3100); }); it("adds public-IPv4 fallback (with private/link-local excluded) when FQDNs are configured and no CIDRs are supplied", () => { @@ -62,10 +66,10 @@ describe("buildNetworkPolicyManifests", () => { r.to.some((t) => t.ipBlock?.cidr === "0.0.0.0/0"), ); expect(fallback).toBeDefined(); - expect(fallback.to[0].ipBlock.except).toEqual( + expect(fallback!.to[0].ipBlock!.except).toEqual( expect.arrayContaining(["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", "127.0.0.0/8"]), ); - expect(fallback.ports).toEqual( + expect(fallback!.ports).toEqual( expect.arrayContaining([ { protocol: "TCP", port: 443 }, { protocol: "TCP", port: 80 }, @@ -107,6 +111,6 @@ describe("buildNetworkPolicyManifests", () => { expect(egress.metadata.ownerReferences).toHaveLength(1); expect(egress.spec.podSelector.matchLabels).toEqual({ "paperclip.io/run-id": "run-123" }); expect(egress.spec.egress).toHaveLength(1); - expect(egress.spec.egress[0].to[0].ipBlock.cidr).toBe("0.0.0.0/0"); + expect(egress.spec.egress[0].to[0].ipBlock?.cidr).toBe("0.0.0.0/0"); }); }); diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/pod-spec-builder.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/pod-spec-builder.test.ts index 75d1da3cc92..593dca7df74 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/pod-spec-builder.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/pod-spec-builder.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from "vitest"; -import { buildJobManifest } from "../../src/pod-spec-builder.js"; +import { buildJobManifest as buildRawJobManifest, type BuildJobManifestInput } from "../../src/pod-spec-builder.js"; +import type { JobManifestView } from "./manifest-views.js"; + +const buildJobManifest = (input: BuildJobManifestInput) => + buildRawJobManifest(input) as unknown as JobManifestView; const baseInput = { namespace: "paperclip-acme", diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/sandbox-cr-builder.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/sandbox-cr-builder.test.ts index 97beb8e9a53..01e68d1f59e 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/sandbox-cr-builder.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/sandbox-cr-builder.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from "vitest"; -import { buildSandboxCrManifest } from "../../src/sandbox-cr-builder.js"; +import { buildSandboxCrManifest as buildRawSandboxCrManifest, type BuildSandboxCrManifestInput } from "../../src/sandbox-cr-builder.js"; +import type { SandboxCrManifestView } from "./manifest-views.js"; + +const buildSandboxCrManifest = (input: BuildSandboxCrManifestInput) => + buildRawSandboxCrManifest(input) as unknown as SandboxCrManifestView; const baseInput = { namespace: "paperclip-acme", diff --git a/packages/plugins/sandbox-providers/kubernetes/tsconfig.test.json b/packages/plugins/sandbox-providers/kubernetes/tsconfig.test.json new file mode 100644 index 00000000000..8305c630843 --- /dev/null +++ b/packages/plugins/sandbox-providers/kubernetes/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "." + }, + "include": ["src", "test"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/modal/tsconfig.test.json b/packages/plugins/sandbox-providers/modal/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/modal/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sandbox-providers/novita/src/plugin.test.ts b/packages/plugins/sandbox-providers/novita/src/plugin.test.ts index 29a3fe22514..6f6b9b7898f 100644 --- a/packages/plugins/sandbox-providers/novita/src/plugin.test.ts +++ b/packages/plugins/sandbox-providers/novita/src/plugin.test.ts @@ -120,10 +120,10 @@ describe("Novita sandbox provider plugin", () => { mockSandboxConnect.mockRejectedValueOnce(new Error("sandbox not found")); const result = await plugin.definition.onEnvironmentExecute?.({ + driverKey: "novita", companyId: "company-1", environmentId: "env-1", issueId: "issue-1", - runId: "run-1", config: { apiKey: "sk-test" }, lease: { providerLeaseId: "sb-expired", metadata: {} }, command: "true", diff --git a/packages/plugins/sandbox-providers/novita/tsconfig.test.json b/packages/plugins/sandbox-providers/novita/tsconfig.test.json new file mode 100644 index 00000000000..90040f25e53 --- /dev/null +++ b/packages/plugins/sandbox-providers/novita/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/packages/plugins/sdk/tests/testing-actions.test.ts b/packages/plugins/sdk/tests/testing-actions.test.ts index ff5eeaf9cef..203a9851d9d 100644 --- a/packages/plugins/sdk/tests/testing-actions.test.ts +++ b/packages/plugins/sdk/tests/testing-actions.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { createTestHarness } from "../src/testing.js"; -import type { PaperclipPluginManifestV1 } from "../src/types.js"; +import type { PaperclipPluginManifestV1, PluginIssuesClient } from "../src/types.js"; const manifest = { id: "paperclip.test-actions", @@ -12,7 +12,7 @@ const manifest = { author: "Paperclip", categories: ["automation"], capabilities: [], - entrypoints: {}, + entrypoints: { worker: "./worker.js" }, } satisfies PaperclipPluginManifestV1; describe("createTestHarness action context", () => { @@ -136,7 +136,8 @@ describe("createTestHarness issue interactions", () => { minSelected: 1, maxSelected: 2, }, - }, + // Omits the fields the schema defaults, so the assertions below cover those defaults. + } as Parameters[1], "company-1", { authorAgentId: "agent-1" }, ); diff --git a/packages/plugins/sdk/tests/testing-lifecycle.test.ts b/packages/plugins/sdk/tests/testing-lifecycle.test.ts index dc8963f6bfa..53b794b33e5 100644 --- a/packages/plugins/sdk/tests/testing-lifecycle.test.ts +++ b/packages/plugins/sdk/tests/testing-lifecycle.test.ts @@ -4,7 +4,7 @@ import type { PaperclipPluginManifestV1, ResourceLifecycleEvent } from "../src/t const manifest: PaperclipPluginManifestV1 = { id: "fixture.lifecycle", apiVersion: 1, version: "1.0.0", displayName: "Lifecycle", description: "Fixture", - author: "Tests", categories: ["automation"], capabilities: ["events.subscribe"], entrypoints: {}, + author: "Tests", categories: ["automation"], capabilities: ["events.subscribe"], entrypoints: { worker: "./worker.js" }, }; describe("Lifecycle inbox test harness", () => { diff --git a/packages/plugins/sdk/tests/worker-rpc-host.test.ts b/packages/plugins/sdk/tests/worker-rpc-host.test.ts index ae409e00ef6..9aafcf5a26b 100644 --- a/packages/plugins/sdk/tests/worker-rpc-host.test.ts +++ b/packages/plugins/sdk/tests/worker-rpc-host.test.ts @@ -874,8 +874,8 @@ describe("worker setup-token pseudo-terminal dispatch", () => { let nextRequestId = 1; // The fake session the opener returns. The test drives its output and exit. - let emitOutput: ((chunk: string) => void) | null = null; - let resolveWait: ((value: { exitCode: number | null }) => void) | null = null; + let emitOutput = null as ((chunk: string) => void) | null; + let resolveWait = null as ((value: { exitCode: number | null }) => void) | null; const inputs: string[] = []; let killed = 0; let closed = 0; @@ -1029,16 +1029,16 @@ describe("worker duplex channel dispatch", () => { let stopped = 0; let closed = 0; - // The plugin declares the four duplex channel handlers. The open returns a - // worker session id. The close keys on the host route id and returns a bound - // acknowledgement. + // The plugin declares the four duplex channel handlers. The open echoes the + // host route id and returns a worker session id. The close keys on the host + // route id and returns a bound acknowledgement. const controllablePlugin = definePlugin({ async setup() {}, async onDuplexChannelOpen(params) { expect(params.hostRouteId).toBe("route-1"); expect(params.command).toEqual(["paperclip-bridge"]); expect(params.providerLeaseId).toBe("lease-1"); - return { workerSessionId: "ws-1" }; + return { hostRouteId: params.hostRouteId, workerSessionId: "ws-1" }; }, async onDuplexChannelWrite(params) { writes.push(params.data); @@ -1117,7 +1117,7 @@ describe("worker duplex channel dispatch", () => { providerLeaseId: "lease-1", command: ["paperclip-bridge"], }), - ).resolves.toEqual({ workerSessionId: "ws-1" }); + ).resolves.toEqual({ hostRouteId: "route-1", workerSessionId: "ws-1" }); await callWorker("duplexChannelWrite", { workerSessionId: "ws-1", data: "payload" }); await callWorker("duplexChannelStop", { workerSessionId: "ws-1" }); @@ -1212,7 +1212,7 @@ describe("AI connection router RPC", () => { const reader = createInterface({ input: output }); const request = { companyId: "company", taskKey: "task", candidates: [], memberOrder: [] }; let received: unknown; - const plugin = definePlugin({ async setup() {}, onRouteAiConnection(params) { received = params; return { kind: "selected", memberId: "authorized-member" }; } }); + const plugin = definePlugin({ async setup() {}, async onRouteAiConnection(params) { received = params; return { kind: "selected", memberId: "authorized-member" }; } }); const worker = startWorkerRpcHost({ plugin, stdin: input, stdout: output }); let sequence = 0; const pending = new Map void>(); @@ -1235,9 +1235,10 @@ describe("Durable lifecycle inbox RPC", () => { const event = { id: "7", companyId: "company-a", resourceType: "agent", resourceId: "agent-a", action: "pause", createdAt: new Date(0).toISOString() }; const plugin = definePlugin({ async setup(ctx) { ctx.data.register("drain", async ({ companyId }) => { - const events = await ctx.events.listLifecycle(companyId!, 10, "6"); - await ctx.events.acknowledgeLifecycle(companyId!, events[0].id); - await ctx.agents.getRuntime("agent-a", companyId!, "environment-a", true); + if (typeof companyId !== "string") throw new Error("companyId is required"); + const events = await ctx.events.listLifecycle(companyId, 10, "6"); + await ctx.events.acknowledgeLifecycle(companyId, events[0].id); + await ctx.agents.getRuntime("agent-a", companyId, "environment-a", true); return events; }); } }); diff --git a/packages/plugins/sdk/tsconfig.test.json b/packages/plugins/sdk/tsconfig.test.json new file mode 100644 index 00000000000..508201b3b8e --- /dev/null +++ b/packages/plugins/sdk/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "." + }, + "include": ["src", "tests"] +} diff --git a/scripts/typecheck-sandbox-providers/package.json b/scripts/typecheck-sandbox-providers/package.json new file mode 100644 index 00000000000..c26c721beb8 --- /dev/null +++ b/scripts/typecheck-sandbox-providers/package.json @@ -0,0 +1,8 @@ +{ + "name": "@paperclipai/typecheck-sandbox-providers", + "private": true, + "description": "Installs sandbox provider dependencies for scripts/typecheck-test-programs.mjs. See doc/DEVELOPING.md.", + "engines": { + "node": ">=24.11.0" + } +} diff --git a/scripts/typecheck-sandbox-providers/pnpm-lock.yaml b/scripts/typecheck-sandbox-providers/pnpm-lock.yaml new file mode 100644 index 00000000000..b3c4b347403 --- /dev/null +++ b/scripts/typecheck-sandbox-providers/pnpm-lock.yaml @@ -0,0 +1,4206 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + ../../packages/plugins/sandbox-providers/boxd: + dependencies: + '@boxd-sh/sdk': + specifier: ^0.2.20 + version: 0.2.22 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/cloudflare: + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/cloudflare/bridge-template: + dependencies: + '@cloudflare/sandbox': + specifier: ^0.7.0 + version: 0.7.21 + devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20260501.0 + version: 4.20260702.1 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + wrangler: + specifier: ^4.15.0 + version: 4.156.2(@cloudflare/workers-types@4.20260702.1)(@types/node@24.19.2) + + ../../packages/plugins/sandbox-providers/createos: + dependencies: + tar: + specifier: ^7.5.22 + version: 7.5.22 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/daytona: + dependencies: + '@daytonaio/sdk': + specifier: 0.203.0 + version: 0.203.0 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/e2b: + dependencies: + e2b: + specifier: ^2.19.0 + version: 2.62.1 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/exe-dev: + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/kubernetes: + dependencies: + '@kubernetes/client-node': + specifier: ^1.0.0 + version: 1.4.0 + zod: + specifier: ^4.6.6 + version: 4.6.7 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/modal: + dependencies: + modal: + specifier: ^0.7.4 + version: 0.7.6 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + + ../../packages/plugins/sandbox-providers/novita: + dependencies: + novita-sandbox: + specifier: ^1.0.3-b2 + version: 1.0.3-b2 + devDependencies: + '@types/node': + specifier: ^24.0.0 + version: 24.19.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^4.1.8 + version: 4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + +packages: + + '@aws-sdk/checksums@3.1002.1': + resolution: {integrity: sha512-CXNESwkQC9jdrUG6cKXBfudVt1frGcYQykcCYbNclVrsewuGMbo3PlFjRteMe4OftNAqhyc3xC2jTz7SUqLasQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1161.0': + resolution: {integrity: sha512-iRZGMsJnYRrI98O+U6o5fHeLPQTP+Dc/w0mWLcBLQcEQ10DA06+XQj0WmdRiCM7vIso7QEtN1fsj9Vzo5/R3LQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.979.1': + resolution: {integrity: sha512-ayOlV1VhLNMiq8PW9ZkVobG4gkldnUoHjG69yy2b8j+VYFlhz/uAL7ZdYQAoQ+eShtGUdNDnnG5rxDg6yUJrOg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.80': + resolution: {integrity: sha512-uO5TiSvb6j1w9RTe5z28OaOmsrOxVQUPaC5+wZl50RfOXcrYD/KgnS8I9lfyGXc7sGbkCopK6VnhNIAoLqfewQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.82': + resolution: {integrity: sha512-vQzlDHlC24Yz42p+rT4NBoP/5VdetkBoujbOLq9h6QYWC8THuZSPYnXIR9OP3XcOrdMgPBBipOrdssfxnIiWUg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.973.25': + resolution: {integrity: sha512-b5xl71uCRdWsu7P1VeGpiGuCf6zfYETsSNOv8AmYVTrsgbvTJRXdmmOPVfxkvGGVNsiKkGctClQW3jAn0SZLug==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.87': + resolution: {integrity: sha512-C4ZFmcwM+Zj14Z8huVUk54Gv4uzT1ZdzG9cY3Gdh8FePakq7Bss1OFsgZjAhWFOkFOGpBvFBeu0OWjWf8HNKRQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.92': + resolution: {integrity: sha512-jC3zmOo7Qxlk+oBivvSsS1Ph7sFGm0QfuhqDGbqgRXi4cxUYMG+dWtBwS2XYYXgc8TIVXJiwgQ65sbNpbYupTA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.80': + resolution: {integrity: sha512-xqfixO5aclje7tbQJDias+iNND5wp/3QZixCdZcVQpgWPTZ0V5DfTW9A804A5Z7ezu7sH3WRO+vzUjtiJBA8yg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.973.24': + resolution: {integrity: sha512-nlg3BKG03Cgzvv/dMnhxvDJPhAhQSpaN0RUdwgU7ziwkasoev7iI8el8Xy4Rd5sqzgaDUbV269fOXck5SargqA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.86': + resolution: {integrity: sha512-zKTyLJBEyfQBVlYqT6Jt+mYV4r8kGbWM7dn6v4Y4MRARizhYafjDXP/iMu76UMRhToSPNmIKX1XAb24gtpBFIA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.1161.0': + resolution: {integrity: sha512-ROSytGylVRtkUsp64mpzryv1Puvgo6jAm4RWTW2m3kC4SSsjrvhqZa5uPKfz116a0U1kk/hBz9S1eBItkyd++w==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': ^3.1161.0 + + '@aws-sdk/middleware-sdk-s3@3.972.85': + resolution: {integrity: sha512-Kyl2G6zhIS9qFoeLdlxvXFrWyc6g9D9ZCIRJnGkkfX6JZhxCZB5ebXBc7GKEeetrnhXcail6ALZT0lKbojcaNA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.997.54': + resolution: {integrity: sha512-/OczlavsS7U5sOodRO10Ug4R/AKFVZiXTstKipWe68EfjPkmdDFNXNl9FCJyXWkg1DG6RBm3qAuVB8FNW9O8KQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.51': + resolution: {integrity: sha512-+aHvUugVBuMPXxgkPfWnTMyGdE/yWS8GHi5+qQ37HowS0YqzrbuaDLznj+Dtmtu5jgCdqz36gXmujOToUhNNWw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1161.0': + resolution: {integrity: sha512-ypbXSofNcSHwf/KA40bFCXJb0MLaAKrfT7sot4dFaA+2lsuXNah4zLRJIAU7Qmvs9PFJ6rnY0bZFEC+mE7attA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.975.0': + resolution: {integrity: sha512-WQlhs6juByzWOzj5fwxOx0mPJaEqMcG/sDpl3Id61B3XhKE63xY+0QWcnG+sWR/sCfprmreyUo1lM7IKZmHMzw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.42': + resolution: {integrity: sha512-smX8/gNP8Kjq+CWTBMZQLQ912vOQjiWExzwzvMO+eujPB5RY8IbtF0AGdUUXHKN7KoDWK9dIcuW0WG269kiP6w==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + + '@boxd-sh/sdk@0.2.22': + resolution: {integrity: sha512-NvvYy5nblUPUM+F3EUcjgIH7uzu1ebMiEuL7tf0+4D/2CaszleSAd93S3Pn7py5ywrVLXfB5Rzj/GuABj67Z5g==} + engines: {node: '>=20'} + + '@bufbuild/protobuf@2.16.0': + resolution: {integrity: sha512-FWa0sPlqYGJgpTs6OxBRcL/AW4JT0OIO+W1ajerluoTVh8CV0B7XM1qsNCRgnkSCkRCgOICKHRQlY17jrsm0+Q==} + + '@cbor-extract/cbor-extract-darwin-arm64@2.2.3': + resolution: {integrity: sha512-qpUy9Yoisi6a7QHOVa+AlnqfN7eVstSRhpLwiLnwi6i3SNLkM5D2Z7MXl4LC5nUGA77mZEt479cPH3u8GD163Q==} + cpu: [arm64] + os: [darwin] + + '@cbor-extract/cbor-extract-darwin-x64@2.2.3': + resolution: {integrity: sha512-6bZbK7dyKn8BUNmG3UfzSnM4L/OzJ8xGAtBHYycprl+bgvtZmZoQC7CucWXuF/oHJP5skzqkqFr6BLSSGIkoWw==} + cpu: [x64] + os: [darwin] + + '@cbor-extract/cbor-extract-linux-arm64@2.2.3': + resolution: {integrity: sha512-QAuFuQkOw0cm4MNc+QwxH9aiMxmrczESnVEu33B8m2Du9fBuH/1AvJfmO8YajpeWLoHqt2iUG3d1/gIWAKY8IQ==} + cpu: [arm64] + os: [linux] + + '@cbor-extract/cbor-extract-linux-arm@2.2.3': + resolution: {integrity: sha512-joCovTc7uEYCL9XxdAJJpGIH7Vvc31Fi7vh3HnlY5zgkLSU/64WxYl5iUi/puE21qT3bmxMfOZEZUbnqVKeJTQ==} + cpu: [arm] + os: [linux] + + '@cbor-extract/cbor-extract-linux-x64@2.2.3': + resolution: {integrity: sha512-IQMPVLulOvFv9Had7J1idJyfQzsFamDVdjH37qW7suHI7mgO4w1Oo3MisL7AjnWARxo0/ZmEEQ3i81WA7y34Ug==} + cpu: [x64] + os: [linux] + + '@cbor-extract/cbor-extract-win32-x64@2.2.3': + resolution: {integrity: sha512-fGFfq6F3l0GCoKOZeyjjm3q+NHO7jo8ILtQ93U3rYJOgG2BGLS/QmBdoFLNEsoWPjyxAS8svWhTyo73MTDawog==} + cpu: [x64] + os: [win32] + + '@cloudflare/containers@0.1.1': + resolution: {integrity: sha512-YTdobRTnTlUOUPMFemufH367A9Z8pDfZ+UboYMLbGpO0VlvEXZDiioSmXPQMHld2vRtkL31mcRii3bcbQU6fdw==} + + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} + + '@cloudflare/sandbox@0.7.21': + resolution: {integrity: sha512-BlfbhIWF83NkmixxGVpE9zrcR0Ix5fVgV+zzfJByxyRnXUS+g2aUeIXK2opBcnX8WWlk6v5+RYTKkSz1XKQ4eg==} + peerDependencies: + '@openai/agents': ^0.3.3 + '@opencode-ai/sdk': ^1.1.40 + '@xterm/xterm': '>=5.0.0' + peerDependenciesMeta: + '@openai/agents': + optional: true + '@opencode-ai/sdk': + optional: true + '@xterm/xterm': + optional: true + + '@cloudflare/unenv-preset@2.16.2': + resolution: {integrity: sha512-JBP1+Z7ZSNG/d4mRP+y8VC5dka3tZVMLEZRvS+rzQ4DGV1EoxRFQckcJTTkXbHSQiTj0DtNI01Zwb/V2fX0mvQ==} + peerDependencies: + unenv: 2.0.0-rc.24 + workerd: '>1.20260305.0 <2.0.0-0' + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/workerd-darwin-64@1.20261102.1': + resolution: {integrity: sha512-5vxE27qqftDMv9H0mCZt0B1cr+EvszT/lhpoFmdKqtS2Uu+Jo2PMIUYGepLe6yxbdV1hPKkgLBD2scnW3lXe/Q==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20261102.1': + resolution: {integrity: sha512-VanKNl3pLP2k8j9nEvETp/l3+dvfGvdWKI6bNJByKVz5szVVIMnGvDvrr+JmotreUZHWTDqfLh2RzPGRq1cZzg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20261102.1': + resolution: {integrity: sha512-hQdSdaHLS4UFGZRoue9TFVOO9UPnOAGWw3ZTZnUtXoUGWf4THwatq0l55F7IbOQYcOJfeKpHoFcMoGEH/88GCA==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20261102.1': + resolution: {integrity: sha512-cqMnRpvDT6v6K80YClFs7XnZMq2RC5H6aHawlrVsIHrmVdSEXUm7OEVMQ9lY5nx3EI4JftiTeFpkLHFZqTSNVg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20261102.1': + resolution: {integrity: sha512-s9iKBbAdNIdVjpMUHh48zbCfCHw5wbLl8I2aIW74H3OtxpWZP/cHeEwZZMq3xAz5kt4MmvwwO4hDbNYoAD4yhw==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workers-types@4.20260702.1': + resolution: {integrity: sha512-mOhf5TUEB1m2vPrxtqoIGfz0fUC9xyxRDx5gWHy5s+OCo6dcV+g7wI1R7gYCMFohhqF/2y2xeKVwMwCJjfn/WA==} + + '@connectrpc/connect-web@2.0.0-rc.3': + resolution: {integrity: sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw==} + peerDependencies: + '@bufbuild/protobuf': ^2.2.0 + '@connectrpc/connect': 2.0.0-rc.3 + + '@connectrpc/connect-web@2.3.0': + resolution: {integrity: sha512-JPXOiWaVsj+ZwaXOQjwvhc0ORnkl0a1w36Qyl3j1WTuak6/VYyuWGl1Aq8I60gQfQRjsjLLFGr64tO0sjwstcg==} + peerDependencies: + '@bufbuild/protobuf': ^2.7.0 + '@connectrpc/connect': 2.3.0 + + '@connectrpc/connect@2.0.0-rc.3': + resolution: {integrity: sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ==} + peerDependencies: + '@bufbuild/protobuf': ^2.2.0 + + '@connectrpc/connect@2.3.0': + resolution: {integrity: sha512-rjAn8L9KbAZrkwiB8m1qzDsLs74hkYmwwM2iSWuxTPVOTRm/EZw31smKbQqPU50AXqJC5eKKpEnA6Uf/swQOgQ==} + peerDependencies: + '@bufbuild/protobuf': ^2.7.0 + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@daytona/analytics-api-client@0.203.0': + resolution: {integrity: sha512-sUELDOmSJW73kG8xsEOUVAE+SGAhPIaLThIBJc6Zbp+AnEMObeUNwXFh9r9ATCdYULoDkTVVA8mMqzP46io9LQ==} + + '@daytona/api-client@0.203.0': + resolution: {integrity: sha512-d72iv8+FECQo0h4G9fOIAr7iVuJaa/c1KJXfG2LeeI3uG+uCWh+E0d8pfgW9nAMQS4wD1ZoTuuzPPUedSn7g7Q==} + + '@daytona/toolbox-api-client@0.203.0': + resolution: {integrity: sha512-J3X72PLZOrzR4KUQj6EHdNU4ALZnN8FBPt49h7Q7UqGWdeq21ZmgQ69acOcGZyDq/rc6xPlHKgfQ5W5YLYHdpg==} + + '@daytonaio/sdk@0.203.0': + resolution: {integrity: sha512-wrCfeZFm36l/oVVyOVJsbPwmcaxNAkzH/z00UKBaEjoda5PYz9gqTqJF9e8vgSpkDT28ZMz3CaYnyl6AO/5Ucw==} + deprecated: 'Moved to @daytona/sdk, same API, no breaking changes. Please update: npm uninstall @daytonaio/sdk && npm i @daytona/sdk' + + '@e2b/dockerfile-utils@0.1.1': + resolution: {integrity: sha512-eDmlWRMrJNzSUbBOf6Y/qTChd1Ys/o3YY8PK6tRQmwkV2u7z3ZS2cak/YJB07odHMT/Z6HJKoUElR/b0N6bHUw==} + engines: {node: '>=22'} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@grpc/grpc-js@1.14.7': + resolution: {integrity: sha512-W4W/fcBMGRVtG6dW2c7cw2mnpS3hvbBd+oQXjh2iYFTZDk1JJmcNqkM5KSaPubQpamlUUurrlb7G1s9NJeERmQ==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} + engines: {node: '>=6'} + hasBin: true + + '@iarna/toml@2.2.5': + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@isaacs/nonrecursive-glob-match@1.0.3': + resolution: {integrity: sha512-SI7kyKHq4XXDRozw+r9GlRZkia1ZkFrpLW98r2IC2Bpr8YkFGE3fvjzmxCG0x78A++69HmDNrDvSMYBZmHGpKA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@jsep-plugin/assignment@1.4.0': + resolution: {integrity: sha512-nw2Px23Eirx0PFTVoPrb3FEq9Wkmg6vxcDtt38MtBCf1G0tjLCB9cK+3seOXyiw+6+aalgYhvJabQbWwMVKlEQ==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/regex@1.1.0': + resolution: {integrity: sha512-2Vkbda+XRjK6VTI6Lrh0sNBuMT8mZ51EhqMvO+V2rSdmqjs7NAGssxPZfkL23EFzYfSrvxyd1ZjOdqPQMM50og==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@kubernetes/client-node@1.4.0': + resolution: {integrity: sha512-Zge3YvF7DJi264dU1b3wb/GmzR99JhUpqTvp+VGHfwZT+g7EOOYNScDJNZwXy9cszyIGPIs0VHr+kk8e95qqrA==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@opentelemetry/api-logs@0.220.0': + resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.10.0': + resolution: {integrity: sha512-lMkyes8iHrG+mk6WH7X70MdCLI2RS0FwNBY1PyQ8UemaijE2m5mv5A0Niy3HtbYTFrgkUEQjFMXmSAk2hQDuPQ==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/configuration@0.220.0': + resolution: {integrity: sha512-glfIVKnZevRin8fY/9uES/mhRtMT1lGINLHc9MIo5fTQZXswEEHamJtgjv4MTtzgnhHGC92mIS/0lzAUZMyE0w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + + '@opentelemetry/context-async-hooks@2.9.0': + resolution: {integrity: sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.12.0': + resolution: {integrity: sha512-1HsSAuvT4/my0QrXWsyXFdWjKaPeCHsismKZgEEIb8NK13LP6DrMRlsUbvD/0F9mDWK3xDom9AMv3IscxgOnOQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.9.0': + resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-logs-otlp-grpc@0.220.0': + resolution: {integrity: sha512-s0sRPCSlXYqlgObOpCftomJllp3LfUL9FobQ5csg2172ydVhSEnu1ptpsVBJadazs5nUNp7vDuLE03FAFWTLOQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-http@0.220.0': + resolution: {integrity: sha512-8186thl+pTw64iz/qEEen5oJZoZ/gO73XruChdaGlYdWOdBIQ42r+vHLf6a7vIDqTD4b8ZOoMlyxptanECaI9A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-proto@0.220.0': + resolution: {integrity: sha512-8LZAxdJ0ENDAFwr4j0oY35mHBltiSzvlhdQAPGiC7p9VnxtuSq4SW1gfBAdW6t6hiQG6OwUl8w7KHaOdJPKHWg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-grpc@0.220.0': + resolution: {integrity: sha512-U128izvJfX/dW9jRGP0gIfadR1Hg7ft3UEGIeRxLFK70m2BWw6AtNCOnsUygpw2zCgR/ygdWbGpcL6TmhW0ZGw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-http@0.220.0': + resolution: {integrity: sha512-Yqt3RBw/bRVncaE9qIIhk4WfjbAQqXuP9FgAaU+IKPndnLEp/cUqZlSC324+bpmduRz7DoTjig8Ub0PeILWXUA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-proto@0.220.0': + resolution: {integrity: sha512-lyO+IQBdSvqHN/ZOW/OzrSWemtfD+HgWngn+HBNLhjy0YrCQQTz0OE/kSekH2Pl340dn9DWzhqHdz5Eftr+HLA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.220.0': + resolution: {integrity: sha512-JZD5DL/NBpVd2BHefvYosm3G40UZ/KzExLv5tc0eZe0CtrsHHtcOk3YPUxR2EINmUeBf8+w5UReTV8fFPn95lA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.220.0': + resolution: {integrity: sha512-bv1xmNhmNwIM6MdUBw4yYuJeVcEViVLk3uD69vOQMwueHBnfyl/u0HnBlB1FNY/Te0UOzJzvcbyR8wN6b+iGbA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-http@0.220.0': + resolution: {integrity: sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.220.0': + resolution: {integrity: sha512-voTAD8XgJxlK7zLkXh8EzMB09zrQr3tyY/BsnDTlDiQU/UdK58MZ63A3mUjdEDrxMjCVmBHU3WQJhRmQe+Dvzg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-zipkin@2.9.0': + resolution: {integrity: sha512-RwINoce2BH8T4obT5pMcAla2sWma1YZvYuaktWmTluQ0PkQdvv5D060rWI1+kawX+J2qBRcMbwrZJJNcMJUauQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-http@0.220.0': + resolution: {integrity: sha512-Szt4dO2Boz2CDr38DaSw/lnqwhwKl+IAdgNGEGgSm2Anb+fwPtIAGmIwkhsLLN69QQZQE96JxjMKYY4rlRkYKw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.220.0': + resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.220.0': + resolution: {integrity: sha512-CXYo8UD5Mn9YbgebO2EL4wejtA+gxLmLiu6HCk2KH2BR7XhFN6/6p1UlCb23DYCjeYkndevLHuejCCN1yx4+OQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.220.0': + resolution: {integrity: sha512-/eIkBPMBTIvM3x/0mDX4aJeSkYifYClnBPr68PL1h5LV4VQv4+SV6CGrpiZ4fIWDnobVmhTWCm1J/QRdAWUfvA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.220.0': + resolution: {integrity: sha512-lXGrv7KXZ0gNH9SVNUaa6vv6phVYGvJxfXAlMbzbakiXru75f5MZl8Z7oqiMMQD77riVHJCFlQvbZs/VVN2/4A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/propagator-b3@2.9.0': + resolution: {integrity: sha512-WrOT1WsOUG+B7hstD2RYoMPIOK76G8E9AQHhMjUvrQaGx/oA7rPWQvvr1Rqv7+yy4R0ZMVwWLC4vW2xnkgWPAQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@2.9.0': + resolution: {integrity: sha512-4mYGty27rYvSM0jtp1ZUOqd3LfVRCYg9H5G9OFzSx5HViYToU21MFhWfco7x1HwXr7ER8yGOiCIHZUwjPksc0Q==} + engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Package no longer supported. See https://github.com/open-telemetry/opentelemetry-js/pull/7077 + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/resources@2.12.0': + resolution: {integrity: sha512-+rSRoOdln6NhXTVcBdHSlZxJDNzwxadeweK2ltZO3tVtfp6aklABc06xclDk6ovRR+EiGgIcSbh/6NgjFhnb3A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/resources@2.9.0': + resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.220.0': + resolution: {integrity: sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.9.0': + resolution: {integrity: sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-node@0.220.0': + resolution: {integrity: sha512-wHtGyHhSKHNH3fym33xRu4Ef/HXTFvX8eQ42xdQdEO9LYx9Y2qNyBDJytyqVlvmo6abWZlNYTUthuAGUMYqYnQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.12.0': + resolution: {integrity: sha512-JOERgZCxRM06FEJ5kEWxlSRChah6MmXDx3KwCruqEv/iEyLkTokCSemf2y1w8oIHtfrr8EVTpzebq/31vn2Cpg==} + engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.9.0': + resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} + engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@2.9.0': + resolution: {integrity: sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==} + engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace@2.12.0': + resolution: {integrity: sha512-ImVuRQ6faOsTi1Wna88s2avgrrf/iDgXPigIeynkUhjstMuoQUDOJXgkez2j1mlU4btjXcXBGa+q7qKFYR3GsA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace@2.9.0': + resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.44.0': + resolution: {integrity: sha512-E3JkOlMBi3N8OGGkbl7tFyqk2MAS6/lySRZBK3Mu7AzjF1fT8oIChH9ChE48SUWzDn9Kdwta+G/p5LJ4xW4CLQ==} + engines: {node: '>=14'} + + '@poppinss/colors@4.1.6': + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + + '@poppinss/dumper@0.6.5': + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + + '@poppinss/exception@1.2.3': + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + + '@rollup/rollup-android-arm-eabi@4.67.4': + resolution: {integrity: sha512-/v/pUAPOcwp+r1Hjjdg0cBV1TE89CzGKrKh2FgkIQw2QBcIMmVPOCSfuOZStCn4Xh/98HXfo/ht6ztcssctPNg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.67.4': + resolution: {integrity: sha512-6tmmB/P52Vt167FXXEw5auiEUgTtZz7aqUgWHqYXToIUE0HFDY6fTdAyeekjjsNk6O/8dtpSKtClxSj1mMneKQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-android-x64@4.67.4': + resolution: {integrity: sha512-8Z9Q4RmotVmuL4llOvqX814JNiOK1oqoBv+glG5KbpRfUiypqzz2/fxIzK4Pq129l4dT5a8Gszmpc0/XPYoFuQ==} + cpu: [x64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.67.4': + resolution: {integrity: sha512-pnq6hssxTtwuRAl6Kdi/+IDY+b1IhkUetjhUkdLAAJCssjfqtL2Oqy/FXJVJFtAWOu1oRlArIvf0Yk1O92x2HQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.67.4': + resolution: {integrity: sha512-BeIrGZvHMVdJg/N+ohZfFywG/gF41684YhDj2wBvouj7m9ziP1tZDUNwzXRiDY+3WIKngaC4hiUBqVtBPGP5og==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.67.4': + resolution: {integrity: sha512-fRFX/oqNg/g780DKoS8RIfYcZD59Ru5O/bSeK1N7gQljBFrTxJY/q5EX4eMs5SrJf32VgVBVVYCdfPpzAwPPnQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.67.4': + resolution: {integrity: sha512-bSrBRopG1q+0RuOsW0NtYTtIODlvNA3T3ebx5WnQVdSxWIoXFmj+rZdHUHdSLgFdZ5M6VGmekYE7r4RMGQXjfg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.67.4': + resolution: {integrity: sha512-L8IvdrJVtl65g1ozwDSyjGPG+FmibhCdFUwmqTItJLw0S3+Oj4QzFs7rfgTUiK7rsPjkCLdG1/GApFxoynXFJQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.67.4': + resolution: {integrity: sha512-h7PcoQOzC31jshNpQ48J0akB9tsghjkSjSl5AZ6XsVGTxVsln7t7C4SQyw62tNVSaz2SeHgk4VT2ELbL17BSHQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.67.4': + resolution: {integrity: sha512-KSPuOBNHoOl7AsVjeY1KZci8+0up0U4TFAHU5IzBYN8P+Z6cB9DZq28+JQycZW7f6WLLa/tSzhV7Rdy95bVi+g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.67.4': + resolution: {integrity: sha512-0E/mKmVk1PlQeQYygIwQm0jlwjANTzGSiNBMPrFx+rdFGFj/2Axh4p6XwcrystQIqXZVudTaGsdbDpzYgJ4wSA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.67.4': + resolution: {integrity: sha512-Fvw7dOSc2Sa9UOipRZVI0zOgdLCzsjVyD/6JuHXzUHTXFu7mMgmeX/0/bCVBIut1dz+nWz3pV4WpS/Qg0XVnxw==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.67.4': + resolution: {integrity: sha512-jzxbVHaE7V3F+P0wEK7UydQtvv7Twfdtx58jI5/3VRx1jJxyrn+qFwfXmqjVFxPhBl+zjaRs+ZJMFJw7vdHaQA==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.67.4': + resolution: {integrity: sha512-fhtrzp9p4GjEJVaigaap9g6X9nP6xgJtKiLLXSDgpE18HXufA5aqfltGn+KAqMtVZReJZfOrXm//y5YSA9BWbA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.67.4': + resolution: {integrity: sha512-uynaF7IUHwAckV0S+yC1f5esy9gB+aqCBsPH5BOck32TYdCuAn2lVG5tvUcFnEsrviGEmRZxE1NHSOieUPYJEg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.67.4': + resolution: {integrity: sha512-uaqRFdfb8eIa2bVHN/FE3ndCvR0zV2xv0AwjCfxrqSu6l6fp/54VhjfhslKlf2RIik9L1jwlt4E56PeVX6tQtQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.67.4': + resolution: {integrity: sha512-JZRow7kmxUDEASnxHuVG5itTm+nTXi9BQUABcVUnUEngC+OPwMrpx1uJ9hIA/v+IcAEBL52ktCSPXk5OJn9Jqg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.67.4': + resolution: {integrity: sha512-rdrh4e+Lq9fCNKoDPaCqeHXbQCe7yl77Ukl45tlSClwYdZ1gpFGZz1XLJdYtY++FRlfL5aHvb6/0n3GDW/Nxsw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.67.4': + resolution: {integrity: sha512-wL7MS4r0fYcxluF3ne2plofHZiB/LsqNCnyaUlyiKpAJLb6agPSFz7DEFCfkonWAZXMrLwCmC08b2ZEnEdJkog==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.67.4': + resolution: {integrity: sha512-4H0XHfd2VufGjX7NjUyeue78sD0bRIoKSGjGqO+F5nSvL6jN48NAU7esdmtFEYxZXwObLtt3MJ/kHS0jASMW3g==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.67.4': + resolution: {integrity: sha512-osRHQbE55pZpCLetpMsFmhkapyZnAUH73qRKMh+npi3boUom6u6hdaTTPSxlInXxwJHsn9r2uDhdz0V7Yoxx2A==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.67.4': + resolution: {integrity: sha512-95+exYEyOwrZePsE0czi4q7X5lxKPa21+lmyAZlwbYENITQ0Unk4HAQAFp/p+KhXBHBx9mUABXRYjOC5A1wY3Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.67.4': + resolution: {integrity: sha512-FOXOcKhNQpO4Po+Ynan6IbtM67SrXZbgpRJWVYwz53CT5veI7sVzDwVBmWS65N5Imv+wK3SVhIPAl+C5DXXNCg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.67.4': + resolution: {integrity: sha512-cklKh0gJOkAZQcWziQa3hrjxZKN+PCHZ7SPTyGJDAiBVr05ZXlEjGFCG7zFJMaVibxsmzAe1FimN4u/HBns9+Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.67.4': + resolution: {integrity: sha512-AegwxJPc9bvUp8fQtlsUHkFMOMkGylWU7glyL+YD4vwG+QaYoGXYLSsQoeTD7kvoLZrVlMWNVhTg1dedY5C1VQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.67.4': + resolution: {integrity: sha512-RJZnrwTWj7Iq2ooBwoGbDdC65tDwYawneGcAfFMJqVUrRZMcskka6nTkBpXR/47G7kzn3EW2qupExnaBy11vwg==} + cpu: [x64] + os: [win32] + + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + + '@smithy/core@3.38.1': + resolution: {integrity: sha512-U/mB2XiwTLaf+NSgLSWwJfptsMT/H8aBi0aTAA/E5e2/TagFeGCqAyhKGG0w0A9IWTkm4qYEmLoD/HB6aYfnRA==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.5.2': + resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.8.0': + resolution: {integrity: sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.14.1': + resolution: {integrity: sha512-/T6E1jzCL0fErS9uq8JqES/5yYQQ4ppVmmYHwXw2nlov8KFzLb+3QqHR5iAun1BsuAB50IPxZliV8YMgghLwhA==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.7.5': + resolution: {integrity: sha512-WPjdr5oWOk1LArpI4pavu+fSo+/evi0Re1olxGvqwuZopd4gYR1UGCi+Soyn/IjmafqbWs+ZC+3AKxJS3ZpwUg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.19.0': + resolution: {integrity: sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==} + engines: {node: '>=18.0.0'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@speed-highlight/core@1.2.24': + resolution: {integrity: sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.10': + resolution: {integrity: sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node@24.19.2': + resolution: {integrity: sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==} + + '@types/stream-buffers@3.0.8': + resolution: {integrity: sha512-J+7VaHKNvlNPJPEJXX/fKa9DZtR/xPMwuIbe+yNOwp1YB+ApUOBv2aUpEoBJEi8nJgbgs1x8e73ttg0r1rSUdw==} + + '@vitest/expect@4.1.12': + resolution: {integrity: sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==} + + '@vitest/mocker@4.1.12': + resolution: {integrity: sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.12': + resolution: {integrity: sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==} + + '@vitest/runner@4.1.12': + resolution: {integrity: sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==} + + '@vitest/snapshot@4.1.12': + resolution: {integrity: sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==} + + '@vitest/spy@4.1.12': + resolution: {integrity: sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==} + + '@vitest/utils@4.1.12': + resolution: {integrity: sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==} + + abort-controller-x@0.5.0: + resolution: {integrity: sha512-yTt9CI0x+nRfX6BFMenEGP8ooPvErGH6AbFz20C2IeOLIlDsrw/VHpgne3GsCEuTA410IiFiaLVFKmgM4bKEPQ==} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + aws4fetch@1.0.20: + resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} + + axios@1.21.0: + resolution: {integrity: sha512-YCzXMB53Q9PZBsh7yN7wtapit4WepwcHS2fP8QhGsBxSz5YtgYobV7GGlWlAJi+RxfU77GQTQi2DSLUma42EYA==} + + b4a@1.9.0: + resolution: {integrity: sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + bare-events@2.9.2: + resolution: {integrity: sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.8.4: + resolution: {integrity: sha512-MPC2eyrpbk3A873sK4y6rk+Iv4o2enxTjHZ8zH3HO0mjrpm5XjnguY2XmfidIsDV6vnjpbMBKUAJpV0ISD2SsQ==} + engines: {bare: '>=1.28.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-path@3.1.2: + resolution: {integrity: sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==} + + bare-stream@2.13.4: + resolution: {integrity: sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==} + peerDependencies: + bare-abort-controller: '*' + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.5.5: + resolution: {integrity: sha512-iM9qJCruvpt2rPaLsrumVDru9pogI7PHziHIa9kTNRFt8C8HuSa+kI9kFezK+NvHEFnnz71rKGZ1EAilq/3V/Q==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + + bowser@2.15.0: + resolution: {integrity: sha512-xic6KSZglg/I4ymp45fdM4GWfYpBK79Fxd/TOvJktlaPgQqqDxvmGwOPZl2V0dyeS3Lj5c180bLhXF43iyAjZQ==} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + cbor-extract@2.2.3: + resolution: {integrity: sha512-zTOCEYwiQDChj5DV/sUqccv3jhdZqbhymztwcrW2LncIG2fPPkb1ceK5rmr0bdtxzJCXoWzZ95shA+uaSH8cKw==} + hasBin: true + + cbor-x@1.6.9: + resolution: {integrity: sha512-TCT9XHLb4iPGQ99fpQjcTRncbus6cXHvSD/RY+bPrDACBCJKiQLmAM6r7/BgQcI5i+8h4gwp4vjfb6S6T8bPGw==} + + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} + engines: {node: '>=18'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + cjs-module-lexer@2.3.0: + resolution: {integrity: sha512-lmNyBzi6iYiqyrExTctfmjd8tInx+dHu9PBomTfH5RXS53c27JMFJgzNv5TyAnm3PgGf+MLy0+Ws41/HlVxdKQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + e2b@2.62.1: + resolution: {integrity: sha512-8TxPojt1659LmX9SX8zSet6fdefWxeSZoh+obad8DuC5wdRQdMbzN6ahEKNGzjrCNJFR26YeRZWOboXsy9UDYA==} + engines: {node: '>=22.19.0'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-client@6.6.7: + resolution: {integrity: sha512-GyAvmPA7gnRCpDQbYACAy79Lg4ewevrmaPzySZz0OVvgyZc3gEfLLqPTYk8wVBScjOEpM9ZO4KryyFpVItjGEA==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-module-lexer@3.0.4: + resolution: {integrity: sha512-BldNY5mwkVu+cQ5Ymn3e6hB9Sx8XHx4ZevWbiUyZbfKxJxYO27Hf0wfzkxIaD5CAJMaGl3YspKELj0ff/7nCMA==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + follow-redirects@1.16.2: + resolution: {integrity: sha512-/kVP2U8z596Ia0gwFrwyIeo74ta5aVmSeU1FfAUMAmcEP5EP7zydmag3SLiN64NaRKef3lxsq7i/g7ltJQDGow==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@14.0.3: + resolution: {integrity: sha512-A1vjIwC3qpdCso45Iw127gnp7sPhqgY8lUQhDrArIs+GbmfaaVPFuoyy7WyLT9EPZCRZ7j7aLdwUhQmHWesEow==} + engines: {node: 18 || 20 || >=22} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + + hpagent@1.2.0: + resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} + engines: {node: '>=14'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + import-in-the-middle@3.5.3: + resolution: {integrity: sha512-usm/IiY63VtOyx/dm7uEoZV+EsanCDmwRUroQZIkFLGu1NVXZ8mZqIOhaN4QIJ7EsbMTuQbJXwCySCx+ozMA8g==} + engines: {node: '>=18'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ip-address@10.7.5: + resolution: {integrity: sha512-t2v0rkILYv9WXmSL7TpAaU3HXgaFYOPUcH3QHgwQ++gpJuH3bqyMy4Fvt/DJCTKwzH3Cf+bAp5W/mFL39KPLzA==} + engines: {node: '>= 12'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + jose@6.2.15: + resolution: {integrity: sha512-qdF6ewcZARtef63fJ9AKKZPgxw+t7USH6HLXYcD4CrdmSvDp79rpJSPFEKc90QBeGS63u1KQtN2MZQHC0yDIzA==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsep@1.6.0: + resolution: {integrity: sha512-Y8Tr54CX7B5YGAwo7/n8eumidEX3Opx98RyyQVnlHqUaZcPA8uUzL2v1rshr9ypW9Q6hNjfJtg3sL+8kaNE7TA==} + engines: {node: '>= 20.16.0'} + + jsonpath-plus@10.4.0: + resolution: {integrity: sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==} + engines: {node: '>=18.0.0'} + hasBin: true + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + miniflare@5.20261102.0-beta: + resolution: {integrity: sha512-FGqoi2BF7Vq6v8aSraq9Tmhg8ORpUBC5RkNGhfa11J597cWlLJ5QP9vJwDMOzwJx+W4U+IuHWSidVceBK6VRZQ==} + engines: {node: '>=22.0.0'} + + minimatch@11.0.4: + resolution: {integrity: sha512-/UJ1dEoVQTjPNDh6SGlUo+ueLMmoHTas4q+8yaAR/5t6nLAIiBDJtoYUh20IYleix7ehiQkgczb84PE3uzHkxg==} + engines: {node: 20 || >=22} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + modal@0.7.6: + resolution: {integrity: sha512-AOFRO/eGl4fcNKxHkNLx55+tL318IeAiTDJCMh/Q1ZXhoaZFnpmlirVV2J5BhO32XLA7AiH6KYGA7gRBGA09lQ==} + + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nice-grpc-common@2.0.4: + resolution: {integrity: sha512-gOEXlD6ShXZMZ8k+49wm/bA2j1+3IKbEFV9WYREJcvZV4kM5L1KkILYTX9VYBzZF2OuYCe1wp8c82bQkvR0fHw==} + + nice-grpc@2.1.18: + resolution: {integrity: sha512-m1if7HuVIhhVo/obvLgoSco2tj46zfeIEKkavlAnAsjklBzmFthvYX/uoKZmMKIwpVJllYpj+HVFRb+qGxibrA==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build-optional-packages@5.1.1: + resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} + hasBin: true + + novita-sandbox@1.0.3-b2: + resolution: {integrity: sha512-W0TbVfacreckxdbw1Tbh9dPYVZKjr4e0qRnMj/oxNj+gw01CRKA5Nf8uwSPaMI5daRazyo+ZVnV7WZchKhHbNQ==} + engines: {node: '>=16.0.0'} + + oauth4webapi@3.8.8: + resolution: {integrity: sha512-8N28E+a/oxfXWBgOMt+ZP/JUf/XR+IFbvkAEPP3gznXOMv9BpAAwiIj0TFNz3tGTPc0ZQ8zmWBNgN1nAys0gng==} + + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openapi-fetch@0.17.0: + resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==} + + openapi-fetch@0.9.8: + resolution: {integrity: sha512-zM6elH0EZStD/gSiNlcPrzXcVQ/pZo3BDvC6CDwRDUt1dDzxlshpmQnpD6cZaJ39THaSmwVCxxRrPKNM1hHrDg==} + + openapi-typescript-helpers@0.0.8: + resolution: {integrity: sha512-1eNjQtbfNi5Z/kFhagDIaIRj6qqDzhjNJKz8cmMW0CVdGwT6e1GLbAfgI0d28VTJa1A8jz82jm/4dG8qNoNS8g==} + + openapi-typescript-helpers@0.1.0: + resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} + + openid-client@6.8.9: + resolution: {integrity: sha512-6mXMRNJxDOf6BflEWLXul1UwzLFz++9Ofz1PYwPwwsJsyXBsbfkO+Jo255SW1qnFNLYz4EJWHz42X4OJJrya/g==} + + parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + + path-scurry@3.0.0: + resolution: {integrity: sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + platform@1.3.6: + resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + + postcss@8.5.32: + resolution: {integrity: sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==} + engines: {node: ^10 || ^12 || >=14} + + protobufjs@7.6.6: + resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} + engines: {node: '>=12.0.0'} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@8.0.1: + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfc4648@1.5.4: + resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} + + rollup@4.67.4: + resolution: {integrity: sha512-Lsv3oKcz5jgOQJej/p7nfwbVEdAbDT+z6OPf/SCdI+nF+q2ENp0pTKT35ZwXAmH8g45mfrIiJeEyAqKYSYoL9Q==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@7.8.6: + resolution: {integrity: sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + shell-quote@1.12.0: + resolution: {integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + smol-toml@1.9.1: + resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==} + engines: {node: '>= 18'} + + socket.io-client@4.8.4: + resolution: {integrity: sha512-7wjKAvVXYOJsbmUgkWbnhPD4bFpkqrUR0++XOjpGFmqDqsqV1CI93VTraeExLxpjxEqrWgFoSRZu1ihjLWfkAA==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.7: + resolution: {integrity: sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==} + engines: {node: '>=10.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.10: + resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-buffers@3.0.3: + resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==} + engines: {node: '>= 0.10.0'} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + streamx@2.28.1: + resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + tar-fs@3.1.3: + resolution: {integrity: sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==} + + tar-stream@3.2.2: + resolution: {integrity: sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==} + + tar@7.5.22: + resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} + engines: {node: '>=18'} + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-error@1.0.6: + resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tslib@2.9.1: + resolution: {integrity: sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + undici@7.30.1: + resolution: {integrity: sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==} + engines: {node: '>=20.18.1'} + + undici@8.12.3: + resolution: {integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==} + engines: {node: '>=22.19.0'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} + hasBin: true + + vite@7.3.7: + resolution: {integrity: sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.12: + resolution: {integrity: sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.12 + '@vitest/browser-preview': 4.1.12 + '@vitest/browser-webdriverio': ^4.1.11 + '@vitest/coverage-istanbul': 4.1.12 + '@vitest/coverage-v8': 4.1.12 + '@vitest/ui': 4.1.12 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + workerd@1.20261102.1: + resolution: {integrity: sha512-5A+/vm0M5oc98pB3aR+DZ5cIOvRMhVWVS9tg+CrjUap7PIo8tqAnvNdvuiHBIE47/wkxtNM3jToAaU38dBsZTw==} + engines: {node: '>=16'} + hasBin: true + + wrangler@4.156.2: + resolution: {integrity: sha512-3RKI10rRqDHlqLFeCgCOmuPzoXw8npyGZbixYB5Vj5RW9jWPoSrupo8CzxCSkw1nVdvpzlFaRrHFjh4+AMt8Cw==} + engines: {node: '>=22.0.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^5.20261102.1 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.22.1: + resolution: {integrity: sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.0-beta.10: + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} + +snapshots: + + '@aws-sdk/checksums@3.1002.1': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1161.0': + dependencies: + '@aws-sdk/checksums': 3.1002.1 + '@aws-sdk/core': 3.979.1 + '@aws-sdk/credential-provider-node': 3.972.92 + '@aws-sdk/middleware-sdk-s3': 3.972.85 + '@aws-sdk/signature-v4-multi-region': 3.996.51 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.14.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/core@3.979.1': + dependencies: + '@aws-sdk/types': 3.975.0 + '@aws-sdk/xml-builder': 3.972.42 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.38.1 + '@smithy/signature-v4': 5.7.5 + '@smithy/types': 4.19.0 + bowser: 2.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.80': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.82': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.14.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.973.25': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/credential-provider-env': 3.972.80 + '@aws-sdk/credential-provider-http': 3.972.82 + '@aws-sdk/credential-provider-login': 3.972.87 + '@aws-sdk/credential-provider-process': 3.972.80 + '@aws-sdk/credential-provider-sso': 3.973.24 + '@aws-sdk/credential-provider-web-identity': 3.972.86 + '@aws-sdk/nested-clients': 3.997.54 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.87': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/nested-clients': 3.997.54 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.92': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.80 + '@aws-sdk/credential-provider-http': 3.972.82 + '@aws-sdk/credential-provider-ini': 3.973.25 + '@aws-sdk/credential-provider-process': 3.972.80 + '@aws-sdk/credential-provider-sso': 3.973.24 + '@aws-sdk/credential-provider-web-identity': 3.972.86 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.80': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.973.24': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/nested-clients': 3.997.54 + '@aws-sdk/token-providers': 3.1161.0 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.86': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/nested-clients': 3.997.54 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/lib-storage@3.1161.0(@aws-sdk/client-s3@3.1161.0)': + dependencies: + '@aws-sdk/client-s3': 3.1161.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.85': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/signature-v4-multi-region': 3.996.51 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.54': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/signature-v4-multi-region': 3.996.51 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.14.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.51': + dependencies: + '@aws-sdk/types': 3.975.0 + '@smithy/signature-v4': 5.7.5 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1161.0': + dependencies: + '@aws-sdk/core': 3.979.1 + '@aws-sdk/nested-clients': 3.997.54 + '@aws-sdk/types': 3.975.0 + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.975.0': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.42': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.3.0': {} + + '@boxd-sh/sdk@0.2.22': + dependencies: + '@bufbuild/protobuf': 2.16.0 + '@grpc/grpc-js': 1.14.7 + + '@bufbuild/protobuf@2.16.0': {} + + '@cbor-extract/cbor-extract-darwin-arm64@2.2.3': + optional: true + + '@cbor-extract/cbor-extract-darwin-x64@2.2.3': + optional: true + + '@cbor-extract/cbor-extract-linux-arm64@2.2.3': + optional: true + + '@cbor-extract/cbor-extract-linux-arm@2.2.3': + optional: true + + '@cbor-extract/cbor-extract-linux-x64@2.2.3': + optional: true + + '@cbor-extract/cbor-extract-win32-x64@2.2.3': + optional: true + + '@cloudflare/containers@0.1.1': {} + + '@cloudflare/kv-asset-handler@0.5.0': {} + + '@cloudflare/sandbox@0.7.21': + dependencies: + '@cloudflare/containers': 0.1.1 + aws4fetch: 1.0.20 + + '@cloudflare/unenv-preset@2.16.2(unenv@2.0.0-rc.24)(workerd@1.20261102.1)': + dependencies: + unenv: 2.0.0-rc.24 + optionalDependencies: + workerd: 1.20261102.1 + + '@cloudflare/workerd-darwin-64@1.20261102.1': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20261102.1': + optional: true + + '@cloudflare/workerd-linux-64@1.20261102.1': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20261102.1': + optional: true + + '@cloudflare/workerd-windows-64@1.20261102.1': + optional: true + + '@cloudflare/workers-types@4.20260702.1': {} + + '@connectrpc/connect-web@2.0.0-rc.3(@bufbuild/protobuf@2.16.0)(@connectrpc/connect@2.0.0-rc.3(@bufbuild/protobuf@2.16.0))': + dependencies: + '@bufbuild/protobuf': 2.16.0 + '@connectrpc/connect': 2.0.0-rc.3(@bufbuild/protobuf@2.16.0) + + '@connectrpc/connect-web@2.3.0(@bufbuild/protobuf@2.16.0)(@connectrpc/connect@2.3.0(@bufbuild/protobuf@2.16.0))': + dependencies: + '@bufbuild/protobuf': 2.16.0 + '@connectrpc/connect': 2.3.0(@bufbuild/protobuf@2.16.0) + + '@connectrpc/connect@2.0.0-rc.3(@bufbuild/protobuf@2.16.0)': + dependencies: + '@bufbuild/protobuf': 2.16.0 + + '@connectrpc/connect@2.3.0(@bufbuild/protobuf@2.16.0)': + dependencies: + '@bufbuild/protobuf': 2.16.0 + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@daytona/analytics-api-client@0.203.0': + dependencies: + axios: 1.21.0 + transitivePeerDependencies: + - debug + - supports-color + + '@daytona/api-client@0.203.0': + dependencies: + axios: 1.21.0 + transitivePeerDependencies: + - debug + - supports-color + + '@daytona/toolbox-api-client@0.203.0': + dependencies: + axios: 1.21.0 + transitivePeerDependencies: + - debug + - supports-color + + '@daytonaio/sdk@0.203.0': + dependencies: + '@aws-sdk/client-s3': 3.1161.0 + '@aws-sdk/lib-storage': 3.1161.0(@aws-sdk/client-s3@3.1161.0) + '@daytona/analytics-api-client': 0.203.0 + '@daytona/api-client': 0.203.0 + '@daytona/toolbox-api-client': 0.203.0 + '@iarna/toml': 2.2.5 + '@opentelemetry/api': 1.10.0 + '@opentelemetry/exporter-trace-otlp-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/instrumentation-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-node': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + axios: 1.21.0 + busboy: 1.6.0 + dotenv: 17.4.2 + expand-tilde: 2.0.2 + fast-glob: 3.3.3 + form-data: 4.0.6 + isomorphic-ws: 5.0.0(ws@8.22.1) + pathe: 2.0.3 + shell-quote: 1.12.0 + socket.io-client: 4.8.4 + tar: 7.5.22 + tslib: 2.8.1 + ws: 8.22.1 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@e2b/dockerfile-utils@0.1.1': {} + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.9.1 + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@grpc/grpc-js@1.14.7': + dependencies: + '@grpc/proto-loader': 0.8.1 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.8.1': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.6 + yargs: 17.7.3 + + '@iarna/toml@2.2.5': {} + + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.4 + optional: true + + '@img/sharp-darwin-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.4 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + optional: true + + '@img/sharp-linux-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.4 + optional: true + + '@img/sharp-linux-arm@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.4 + optional: true + + '@img/sharp-linux-ppc64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.4 + optional: true + + '@img/sharp-linux-riscv64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.4 + optional: true + + '@img/sharp-linux-s390x@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.4 + optional: true + + '@img/sharp-linux-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + optional: true + + '@img/sharp-wasm32@0.35.5': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-win32-arm64@0.35.5': + optional: true + + '@img/sharp-win32-ia32@0.35.5': + optional: true + + '@img/sharp-win32-x64@0.35.5': + optional: true + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@isaacs/nonrecursive-glob-match@1.0.3': {} + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@jsep-plugin/assignment@1.4.0(jsep@1.6.0)': + dependencies: + jsep: 1.6.0 + + '@jsep-plugin/regex@1.1.0(jsep@1.6.0)': + dependencies: + jsep: 1.6.0 + + '@kubernetes/client-node@1.4.0': + dependencies: + '@types/js-yaml': 4.0.9 + '@types/node': 24.19.2 + '@types/node-fetch': 2.6.13 + '@types/stream-buffers': 3.0.8 + form-data: 4.0.6 + hpagent: 1.2.0 + isomorphic-ws: 5.0.0(ws@8.22.1) + js-yaml: 4.3.2 + jsonpath-plus: 10.4.0 + node-fetch: 2.7.0 + openid-client: 6.8.9 + rfc4648: 1.5.4 + socks-proxy-agent: 8.0.5 + stream-buffers: 3.0.3 + tar-fs: 3.1.3 + ws: 8.22.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - bufferutil + - encoding + - react-native-b4a + - supports-color + - utf-8-validate + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.3 + + '@opentelemetry/api-logs@0.220.0': + dependencies: + '@opentelemetry/api': 1.10.0 + + '@opentelemetry/api@1.10.0': {} + + '@opentelemetry/configuration@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + yaml: 2.9.1 + + '@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + + '@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/core@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/exporter-logs-otlp-grpc@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@grpc/grpc-js': 1.14.7 + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-logs-otlp-http@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-logs-otlp-proto@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-metrics-otlp-grpc@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@grpc/grpc-js': 1.14.7 + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-metrics-otlp-http@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-metrics-otlp-proto@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-prometheus@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@grpc/grpc-js': 1.14.7 + '@opentelemetry/api': 1.10.0 + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/exporter-zipkin@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/instrumentation-http@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + forwarded-parse: 2.1.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/api-logs': 0.220.0 + import-in-the-middle: 3.5.3 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/otlp-exporter-base@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/otlp-grpc-exporter-base@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@grpc/grpc-js': 1.14.7 + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/otlp-transformer@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/propagator-b3@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/propagator-jaeger@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/resources@2.12.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/resources@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/sdk-logs@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/sdk-node@0.220.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/configuration': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-logs-otlp-grpc': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-logs-otlp-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-logs-otlp-proto': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-prometheus': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-trace-otlp-http': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-trace-otlp-proto': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/exporter-zipkin': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/propagator-b3': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/propagator-jaeger': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace-node': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.10.0) + + '@opentelemetry/sdk-trace@2.12.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.10.0)': + dependencies: + '@opentelemetry/api': 1.10.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.10.0) + '@opentelemetry/semantic-conventions': 1.44.0 + + '@opentelemetry/semantic-conventions@1.44.0': {} + + '@poppinss/colors@4.1.6': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.5': + dependencies: + '@poppinss/colors': 4.1.6 + '@sindresorhus/is': 7.2.0 + supports-color: 10.2.2 + + '@poppinss/exception@1.2.3': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.1': {} + + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.2': {} + + '@rollup/rollup-android-arm-eabi@4.67.4': + optional: true + + '@rollup/rollup-android-arm64@4.67.4': + optional: true + + '@rollup/rollup-android-x64@4.67.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.67.4': + optional: true + + '@rollup/rollup-darwin-x64@4.67.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.67.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.67.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.67.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.67.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.67.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.67.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.67.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.67.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.67.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.67.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.67.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.67.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.67.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.67.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.67.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.67.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.67.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.67.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.67.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.67.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.67.4': + optional: true + + '@sindresorhus/is@7.2.0': {} + + '@smithy/core@3.38.1': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.5.2': + dependencies: + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.8.0': + dependencies: + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.14.1': + dependencies: + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.7.5': + dependencies: + '@smithy/core': 3.38.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/types@4.19.0': + dependencies: + tslib: 2.8.1 + + '@socket.io/component-emitter@3.1.2': {} + + '@speed-highlight/core@1.2.24': {} + + '@standard-schema/spec@1.1.0': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.10': {} + + '@types/js-yaml@4.0.9': {} + + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 24.19.2 + form-data: 4.0.6 + + '@types/node@24.19.2': + dependencies: + undici-types: 7.24.6 + + '@types/stream-buffers@3.0.8': + dependencies: + '@types/node': 24.19.2 + + '@vitest/expect@4.1.12': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.12 + '@vitest/utils': 4.1.12 + chai: 6.3.0 + tinyrainbow: 3.2.0 + + '@vitest/mocker@4.1.12(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1))': + dependencies: + '@vitest/spy': 4.1.12 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.7(@types/node@24.19.2)(yaml@2.9.1) + + '@vitest/pretty-format@4.1.12': + dependencies: + tinyrainbow: 3.2.0 + + '@vitest/runner@4.1.12': + dependencies: + '@vitest/utils': 4.1.12 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.12': + dependencies: + '@vitest/pretty-format': 4.1.12 + '@vitest/utils': 4.1.12 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.12': {} + + '@vitest/utils@4.1.12': + dependencies: + '@vitest/pretty-format': 4.1.12 + convert-source-map: 2.0.0 + tinyrainbow: 3.2.0 + + abort-controller-x@0.5.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + asynckit@0.4.0: {} + + aws4fetch@1.0.20: {} + + axios@1.21.0: + dependencies: + follow-redirects: 1.16.2 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + b4a@1.9.0: {} + + balanced-match@4.0.4: {} + + bare-events@2.9.2: {} + + bare-fs@4.8.4: + dependencies: + bare-events: 2.9.2 + bare-path: 3.1.2 + bare-stream: 2.13.4(bare-events@2.9.2) + bare-url: 2.5.5 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + bare-path@3.1.2: {} + + bare-stream@2.13.4(bare-events@2.9.2): + dependencies: + b4a: 1.9.0 + streamx: 2.28.1 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.9.2 + transitivePeerDependencies: + - react-native-b4a + + bare-url@2.5.5: + dependencies: + bare-path: 3.1.2 + + base64-js@1.5.1: {} + + blake3-wasm@2.1.5: {} + + bowser@2.15.0: {} + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + cbor-extract@2.2.3: + dependencies: + node-gyp-build-optional-packages: 5.1.1 + optionalDependencies: + '@cbor-extract/cbor-extract-darwin-arm64': 2.2.3 + '@cbor-extract/cbor-extract-darwin-x64': 2.2.3 + '@cbor-extract/cbor-extract-linux-arm': 2.2.3 + '@cbor-extract/cbor-extract-linux-arm64': 2.2.3 + '@cbor-extract/cbor-extract-linux-x64': 2.2.3 + '@cbor-extract/cbor-extract-win32-x64': 2.2.3 + optional: true + + cbor-x@1.6.9: + optionalDependencies: + cbor-extract: 2.2.3 + + chai@6.3.0: {} + + chalk@5.6.2: {} + + chownr@3.0.0: {} + + cjs-module-lexer@2.3.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + compare-versions@6.1.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + dotenv@17.4.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + e2b@2.62.1: + dependencies: + '@bufbuild/protobuf': 2.16.0 + '@connectrpc/connect': 2.3.0(@bufbuild/protobuf@2.16.0) + '@connectrpc/connect-web': 2.3.0(@bufbuild/protobuf@2.16.0)(@connectrpc/connect@2.3.0(@bufbuild/protobuf@2.16.0)) + '@e2b/dockerfile-utils': 0.1.1 + chalk: 5.6.2 + compare-versions: 6.1.1 + glob: 14.0.3 + openapi-fetch: 0.17.0 + platform: 1.3.6 + tar: 7.5.22 + undici: 8.12.3 + + emoji-regex@8.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + engine.io-client@6.6.7: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.21.3 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + error-stack-parser-es@1.0.5: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-module-lexer@3.0.4: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.10 + + events-universal@1.0.1: + dependencies: + bare-events: 2.9.2 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + + expand-tilde@2.0.2: + dependencies: + homedir-polyfill: 1.0.3 + + expect-type@1.4.0: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.20.3: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + follow-redirects@1.16.2: {} + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + forwarded-parse@2.1.2: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@14.0.3: + dependencies: + minimatch: 11.0.4 + minipass: 7.1.3 + path-scurry: 3.0.0 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + + hpagent@1.2.0: {} + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + ieee754@1.2.1: {} + + import-in-the-middle@3.5.3: + dependencies: + cjs-module-lexer: 2.3.0 + es-module-lexer: 3.0.4 + module-details-from-path: 1.0.4 + + inherits@2.0.4: {} + + ip-address@10.7.5: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isomorphic-ws@5.0.0(ws@8.22.1): + dependencies: + ws: 8.22.1 + + jose@6.2.15: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsep@1.6.0: {} + + jsonpath-plus@10.4.0: + dependencies: + '@jsep-plugin/assignment': 1.4.0(jsep@1.6.0) + '@jsep-plugin/regex': 1.1.0(jsep@1.6.0) + jsep: 1.6.0 + + kleur@4.1.5: {} + + lodash.camelcase@4.3.0: {} + + long@5.3.2: {} + + lru-cache@11.5.3: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + miniflare@5.20261102.0-beta(@types/node@24.19.2): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + sharp: 0.35.5(@types/node@24.19.2) + undici: 7.30.1 + workerd: 1.20261102.1 + ws: 8.21.0 + youch: 4.1.0-beta.10 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - utf-8-validate + + minimatch@11.0.4: + dependencies: + '@isaacs/nonrecursive-glob-match': 1.0.3 + brace-expansion: 5.0.12 + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + modal@0.7.6: + dependencies: + cbor-x: 1.6.9 + long: 5.3.2 + nice-grpc: 2.1.18 + protobufjs: 7.6.6 + smol-toml: 1.9.1 + uuid: 11.1.1 + + module-details-from-path@1.0.4: {} + + ms@2.1.3: {} + + nanoid@3.3.20: {} + + nice-grpc-common@2.0.4: + dependencies: + ts-error: 1.0.6 + + nice-grpc@2.1.18: + dependencies: + '@grpc/grpc-js': 1.14.7 + abort-controller-x: 0.5.0 + nice-grpc-common: 2.0.4 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build-optional-packages@5.1.1: + dependencies: + detect-libc: 2.1.2 + optional: true + + novita-sandbox@1.0.3-b2: + dependencies: + '@bufbuild/protobuf': 2.16.0 + '@connectrpc/connect': 2.0.0-rc.3(@bufbuild/protobuf@2.16.0) + '@connectrpc/connect-web': 2.0.0-rc.3(@bufbuild/protobuf@2.16.0)(@connectrpc/connect@2.0.0-rc.3(@bufbuild/protobuf@2.16.0)) + compare-versions: 6.1.1 + openapi-fetch: 0.9.8 + platform: 1.3.6 + + oauth4webapi@3.8.8: {} + + obug@2.2.1: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + openapi-fetch@0.17.0: + dependencies: + openapi-typescript-helpers: 0.1.0 + + openapi-fetch@0.9.8: + dependencies: + openapi-typescript-helpers: 0.0.8 + + openapi-typescript-helpers@0.0.8: {} + + openapi-typescript-helpers@0.1.0: {} + + openid-client@6.8.9: + dependencies: + jose: 6.2.15 + oauth4webapi: 3.8.8 + + parse-passwd@1.0.0: {} + + path-scurry@3.0.0: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.7: {} + + platform@1.3.6: {} + + postcss@8.5.32: + dependencies: + nanoid: 3.3.20 + picocolors: 1.1.1 + source-map-js: 1.2.2 + + protobufjs@7.6.6: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 24.19.2 + long: 5.3.2 + + proxy-from-env@2.1.0: {} + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + queue-microtask@1.2.3: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + require-directory@2.1.1: {} + + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + + reusify@1.1.0: {} + + rfc4648@1.5.4: {} + + rollup@4.67.4: + dependencies: + '@types/estree': 1.0.10 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.67.4 + '@rollup/rollup-android-arm64': 4.67.4 + '@rollup/rollup-android-x64': 4.67.4 + '@rollup/rollup-darwin-arm64': 4.67.4 + '@rollup/rollup-darwin-x64': 4.67.4 + '@rollup/rollup-freebsd-arm64': 4.67.4 + '@rollup/rollup-freebsd-x64': 4.67.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.67.4 + '@rollup/rollup-linux-arm-musleabihf': 4.67.4 + '@rollup/rollup-linux-arm64-gnu': 4.67.4 + '@rollup/rollup-linux-arm64-musl': 4.67.4 + '@rollup/rollup-linux-loong64-gnu': 4.67.4 + '@rollup/rollup-linux-loong64-musl': 4.67.4 + '@rollup/rollup-linux-ppc64-gnu': 4.67.4 + '@rollup/rollup-linux-ppc64-musl': 4.67.4 + '@rollup/rollup-linux-riscv64-gnu': 4.67.4 + '@rollup/rollup-linux-riscv64-musl': 4.67.4 + '@rollup/rollup-linux-s390x-gnu': 4.67.4 + '@rollup/rollup-linux-x64-gnu': 4.67.4 + '@rollup/rollup-linux-x64-musl': 4.67.4 + '@rollup/rollup-openbsd-x64': 4.67.4 + '@rollup/rollup-openharmony-arm64': 4.67.4 + '@rollup/rollup-win32-arm64-msvc': 4.67.4 + '@rollup/rollup-win32-ia32-msvc': 4.67.4 + '@rollup/rollup-win32-x64-gnu': 4.67.4 + '@rollup/rollup-win32-x64-msvc': 4.67.4 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.2.1: {} + + semver@7.8.6: {} + + sharp@0.35.5(@types/node@24.19.2): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.6 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 24.19.2 + + shell-quote@1.12.0: {} + + siginfo@2.0.0: {} + + smart-buffer@4.2.0: {} + + smol-toml@1.9.1: {} + + socket.io-client@4.8.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-client: 6.6.7 + socket.io-parser: 4.2.7 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.7: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.10 + transitivePeerDependencies: + - supports-color + + socks@2.8.10: + dependencies: + ip-address: 10.7.5 + smart-buffer: 4.2.0 + + source-map-js@1.2.2: {} + + stackback@0.0.2: {} + + std-env@4.3.0: {} + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-buffers@3.0.3: {} + + streamsearch@1.1.0: {} + + streamx@2.28.1: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@10.2.2: {} + + tar-fs@3.1.3: + dependencies: + pump: 3.0.4 + tar-stream: 3.2.2 + optionalDependencies: + bare-fs: 4.8.4 + bare-path: 3.1.2 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tar-stream@3.2.2: + dependencies: + b4a: 1.9.0 + bare-fs: 4.8.4 + fast-fifo: 1.3.2 + streamx: 2.28.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tar@7.5.22: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + teex@1.0.1: + dependencies: + streamx: 2.28.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + text-decoder@1.2.7: + dependencies: + b4a: 1.9.0 + transitivePeerDependencies: + - react-native-b4a + + tinybench@2.9.0: {} + + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tinyrainbow@3.2.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@0.0.3: {} + + ts-error@1.0.6: {} + + tslib@2.8.1: {} + + tslib@2.9.1: + optional: true + + typescript@5.9.3: {} + + undici-types@7.24.6: {} + + undici@7.30.1: {} + + undici@8.12.3: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + util-deprecate@1.0.2: {} + + uuid@11.1.1: {} + + vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1): + dependencies: + esbuild: 0.28.2 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + postcss: 8.5.32 + rollup: 4.67.4 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.19.2 + fsevents: 2.3.3 + yaml: 2.9.1 + + vitest@4.1.12(@opentelemetry/api@1.10.0)(@types/node@24.19.2)(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)): + dependencies: + '@vitest/expect': 4.1.12 + '@vitest/mocker': 4.1.12(vite@7.3.7(@types/node@24.19.2)(yaml@2.9.1)) + '@vitest/pretty-format': 4.1.12 + '@vitest/runner': 4.1.12 + '@vitest/snapshot': 4.1.12 + '@vitest/spy': 4.1.12 + '@vitest/utils': 4.1.12 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.2.1 + pathe: 2.0.3 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 2.9.0 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tinyrainbow: 3.2.0 + vite: 7.3.7(@types/node@24.19.2)(yaml@2.9.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.10.0 + '@types/node': 24.19.2 + transitivePeerDependencies: + - msw + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + workerd@1.20261102.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20261102.1 + '@cloudflare/workerd-darwin-arm64': 1.20261102.1 + '@cloudflare/workerd-linux-64': 1.20261102.1 + '@cloudflare/workerd-linux-arm64': 1.20261102.1 + '@cloudflare/workerd-windows-64': 1.20261102.1 + + wrangler@4.156.2(@cloudflare/workers-types@4.20260702.1)(@types/node@24.19.2): + dependencies: + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.2(unenv@2.0.0-rc.24)(workerd@1.20261102.1) + '@cspotcode/source-map-support': 0.8.1 + blake3-wasm: 2.1.5 + esbuild: 0.28.2 + miniflare: 5.20261102.0-beta(@types/node@24.19.2) + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.24 + workerd: 1.20261102.1 + optionalDependencies: + '@cloudflare/workers-types': 4.20260702.1 + fsevents: 2.3.3 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - utf-8-validate + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@8.21.0: {} + + ws@8.21.3: {} + + ws@8.22.1: {} + + xmlhttprequest-ssl@2.1.2: {} + + y18n@5.0.8: {} + + yallist@5.0.0: {} + + yaml@2.9.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.3 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.10: + dependencies: + '@poppinss/colors': 4.1.6 + '@poppinss/dumper': 0.6.5 + '@speed-highlight/core': 1.2.24 + cookie: 1.1.1 + youch-core: 0.3.3 + + zod@4.6.7: {} diff --git a/scripts/typecheck-sandbox-providers/pnpm-workspace.yaml b/scripts/typecheck-sandbox-providers/pnpm-workspace.yaml new file mode 100644 index 00000000000..8a7199ba45e --- /dev/null +++ b/scripts/typecheck-sandbox-providers/pnpm-workspace.yaml @@ -0,0 +1,7 @@ +# Sandbox providers stay out of the root workspace, and most of them must not +# commit a lockfile: scripts/build-standalone-public-packages.mjs uses a frozen +# install when one exists. This workspace pins their dependencies in one lockfile +# for scripts/typecheck-test-programs.mjs, without changing how they are released. +packages: + - ../../packages/plugins/sandbox-providers/* + - ../../packages/plugins/sandbox-providers/cloudflare/bridge-template diff --git a/scripts/typecheck-test-programs.mjs b/scripts/typecheck-test-programs.mjs new file mode 100644 index 00000000000..562efef0939 --- /dev/null +++ b/scripts/typecheck-test-programs.mjs @@ -0,0 +1,220 @@ +#!/usr/bin/env node +// Typechecks the test files that no build or package typecheck reads. +// +// Most package tsconfigs exclude tests so that `tsc` does not emit them, and +// Vitest strips types, so a type error in those tests fails nothing. Each program +// in PROGRAMS reads them without emitting. Sandbox providers are outside the +// workspace, so the root install has none of their dependencies. They install from +// typecheck-sandbox-providers/pnpm-lock.yaml here, because their SDK types are part +// of what a test checks. +// +// The script then fails if a tracked test file is in no program. tsc decides that, +// not a glob: `include` silently drops foo.test.tsx when foo.test.ts exists. +// +// Usage: node scripts/typecheck-test-programs.mjs [--low-memory] [tsconfig ...] +// Naming a tsconfig runs only that program and skips the coverage check. + +import { spawnSync } from "node:child_process"; +import { existsSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { linkExcludedPlugins } from "./link-plugin-dev-sdk.mjs"; +import { LOW_MEMORY_VARIABLE, runTsc } from "./run-tsc.mjs"; +import { PROJECTS } from "./typecheck-test-projects.mjs"; + +export const PROVIDER_WORKSPACE = "scripts/typecheck-sandbox-providers"; + +export const SANDBOX_PROVIDERS = [ + "packages/plugins/sandbox-providers/boxd", + "packages/plugins/sandbox-providers/cloudflare", + "packages/plugins/sandbox-providers/cloudflare/bridge-template", + "packages/plugins/sandbox-providers/createos", + "packages/plugins/sandbox-providers/daytona", + "packages/plugins/sandbox-providers/e2b", + "packages/plugins/sandbox-providers/exe-dev", + "packages/plugins/sandbox-providers/kubernetes", + "packages/plugins/sandbox-providers/modal", + "packages/plugins/sandbox-providers/novita", +]; + +export const PROGRAMS = [ + "packages/db/tsconfig.test.json", + "packages/kv-demo-mcp-server/tsconfig.test.json", + "packages/paperclip-runner/tsconfig.browser.test.json", + "packages/paperclip-runner/tsconfig.surfaces.test.json", + "packages/paperclip-runner/tsconfig.test.json", + "packages/plugins/examples/plugin-orchestration-smoke-example/tsconfig.test.json", + "packages/plugins/plugin-workspace-diff/tsconfig.test.json", + "packages/plugins/sdk/tsconfig.test.json", + ...SANDBOX_PROVIDERS.map((directory) => `${directory}/tsconfig.test.json`), + "tests/runner-acceptance/tsconfig.json", + "tests/tsconfig.playwright.json", +]; + +// server/scripts/typecheck-layers.mjs reads this tree. Everything else must be in a program. +export const CHECKED_ELSEWHERE = ["server/src/"]; + +const PREPARE_STEPS = [ + ["--filter", "@paperclipai/plugin-sdk", "ensure-build-deps"], + ["--filter", "@paperclipai/paperclip-runner", "run", "ensure:eval-build-deps"], +]; +// Replacing a provider's standalone node_modules would otherwise ask for confirmation. +const INSTALL_ARGS = ["install", "--frozen-lockfile", "--ignore-scripts", "--prefer-offline", "--config.confirmModulesPurge=false"]; + +export function findUncheckedTests({ trackedFiles, checkedFiles, checkedElsewhere = CHECKED_ELSEWHERE }) { + const checked = new Set(checkedFiles); + return trackedFiles.filter( + (file) => + /\.(test|spec)\.tsx?$/.test(file) && + !checked.has(file) && + !checkedElsewhere.some((prefix) => file.startsWith(prefix)), + ); +} + +function runPnpm(args, { cwd, env }) { + const result = spawnSync("pnpm", args, { + cwd, + env, + stdio: "inherit", + // A bare `pnpm` on Windows is pnpm.cmd, which needs the shell to resolve. + shell: process.platform === "win32", + }); + if (result.error) throw result.error; + return result.status ?? 1; +} + +function listProgramFiles(config, { cwd, env }) { + const tsc = path.join(cwd, "node_modules", "typescript", "bin", "tsc"); + const result = spawnSync(process.execPath, [tsc, "-p", config, "--listFilesOnly"], { + cwd, + env, + encoding: "utf8", + maxBuffer: 1 << 28, + }); + if (result.error) throw result.error; + return { + status: result.status ?? 1, + output: result.stdout, + files: result.stdout + .split("\n") + .filter(Boolean) + .map((file) => path.relative(cwd, file).split(path.sep).join("/")), + }; +} + +function listWorkspaceConfigs(root) { + const result = spawnSync("pnpm", ["ls", "-r", "--depth", "-1", "--json"], { + cwd: root, + encoding: "utf8", + maxBuffer: 1 << 26, + shell: process.platform === "win32", + }); + if (result.error) throw result.error; + if (result.status !== 0) throw new Error("Unable to list pnpm workspace packages."); + return JSON.parse(result.stdout) + .map((workspace) => path.relative(root, path.join(workspace.path, "tsconfig.json")).split(path.sep).join("/")) + .filter((config) => config !== "server/tsconfig.json" && existsSync(path.join(root, config))); +} + +function listTrackedFiles(root) { + const result = spawnSync("git", ["ls-files", "-z"], { cwd: root, encoding: "utf8", maxBuffer: 1 << 28 }); + if (result.error) throw result.error; + if (result.status !== 0) throw new Error("Unable to list tracked files."); + return result.stdout.split("\0").filter(Boolean); +} + +/** Returns the test files in no program, or null if a program could not be listed. */ +export function checkCoverage({ + root, + env = process.env, + programs = PROGRAMS, + workspaceConfigs = listWorkspaceConfigs, + trackedFiles = listTrackedFiles, + listFiles = listProgramFiles, + logError = console.error, +}) { + const checkedFiles = []; + // `pnpm build` and `typecheck:build-gaps` check each workspace's tsconfig.json. + // scripts/typecheck-test-projects.mjs checks these same files through tsconfig.layers.json. + const projectConfigs = PROJECTS.map((project) => `${project}/tsconfig.json`); + for (const config of [...workspaceConfigs(root), ...projectConfigs, ...programs]) { + const listing = listFiles(config, { cwd: root, env }); + if (listing.status !== 0) { + logError(`typecheck:test-programs: could not list the files of ${config}\n${listing.output ?? ""}`); + return null; + } + checkedFiles.push(...listing.files); + } + return findUncheckedTests({ trackedFiles: trackedFiles(root), checkedFiles }); +} + +export function typecheckTestPrograms({ + root, + env = process.env, + programs = PROGRAMS, + coverage = true, + pnpm = runPnpm, + linkPlugins = linkExcludedPlugins, + run = runTsc, + checkFiles = checkCoverage, + log = console.log, + logError = console.error, +}) { + for (const args of PREPARE_STEPS) { + const status = pnpm(args, { cwd: root, env }); + if (status !== 0) return status; + } + const installed = pnpm(INSTALL_ARGS, { cwd: path.join(root, PROVIDER_WORKSPACE), env }); + if (installed !== 0) { + logError( + `typecheck:test-programs: if a provider's package.json changed, run ` + + `\`pnpm install --ignore-scripts --lockfile-only\` in ${PROVIDER_WORKSPACE} and commit its pnpm-lock.yaml`, + ); + return installed; + } + // The install removes the SDK link from each provider's node_modules. + linkPlugins(); + + let exitCode = 0; + for (const config of programs) { + log(`typecheck:test-programs: ${config}`); + const { status, signal } = run(["-p", config], { cwd: root, env }); + if (signal) return status || 1; + if (status !== 0) exitCode = status; + } + if (!coverage) return exitCode; + + const unchecked = checkFiles({ root, env, logError }); + if (unchecked === null) return exitCode || 1; + if (unchecked.length > 0) { + logError( + `typecheck:test-programs: ${unchecked.length} test files are in no typechecked program. ` + + `Add them to a program in scripts/typecheck-test-programs.mjs. A foo.test.tsx beside a foo.test.ts needs a new name.\n` + + unchecked.map((file) => ` ${file}`).join("\n"), + ); + return exitCode || 1; + } + return exitCode; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + const args = process.argv.slice(2); + const flags = args.filter((arg) => arg.startsWith("--")); + const programs = args.filter((arg) => !arg.startsWith("--")); + const unknownFlags = flags.filter((flag) => flag !== "--low-memory"); + const unknown = programs.filter((program) => !PROGRAMS.includes(program)); + if (unknownFlags.length > 0) { + console.error(`Unknown option: ${unknownFlags.join(", ")}`); + process.exitCode = 2; + } else if (unknown.length > 0) { + console.error(`Unknown program: ${unknown.join(", ")}. Known programs:\n ${PROGRAMS.join("\n ")}`); + process.exitCode = 2; + } else { + process.exitCode = typecheckTestPrograms({ + root: path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."), + env: flags.includes("--low-memory") ? { ...process.env, [LOW_MEMORY_VARIABLE]: "1" } : process.env, + programs: programs.length > 0 ? programs : PROGRAMS, + coverage: programs.length === 0, + }); + } +} diff --git a/scripts/typecheck-test-programs.test.mjs b/scripts/typecheck-test-programs.test.mjs new file mode 100644 index 00000000000..52816e4fbdb --- /dev/null +++ b/scripts/typecheck-test-programs.test.mjs @@ -0,0 +1,170 @@ +import assert from "node:assert/strict"; +import { execFileSync, spawnSync } from "node:child_process"; +import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { test } from "node:test"; +import { fileURLToPath } from "node:url"; +import { + checkCoverage, + findUncheckedTests, + PROGRAMS, + PROVIDER_WORKSPACE, + SANDBOX_PROVIDERS, + typecheckTestPrograms, +} from "./typecheck-test-programs.mjs"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const tsc = path.join(root, "node_modules", "typescript", "bin", "tsc"); + +const harness = (overrides = {}) => { + const calls = { pnpm: [], tsc: [], linked: 0, errors: [] }; + return { + calls, + options: { + root: "/repo", + programs: ["a/tsconfig.test.json", "b/tsconfig.test.json"], + pnpm: (args, { cwd }) => { + calls.pnpm.push({ args, cwd }); + return 0; + }, + linkPlugins: () => { + calls.linked += 1; + }, + run: (args) => { + calls.tsc.push(args); + return { status: 0 }; + }, + checkFiles: () => [], + log: () => {}, + logError: (message) => calls.errors.push(message), + ...overrides, + }, + }; +}; + +test("findUncheckedTests reports only test files outside programs and other checks", () => { + assert.deepEqual( + findUncheckedTests({ + trackedFiles: ["a/x.test.ts", "a/y.spec.tsx", "a/z.test.tsx", "a/helper.ts", "a/n.test.mjs", "server/src/s.test.ts"], + checkedFiles: ["a/x.test.ts", "a/y.spec.tsx"], + checkedElsewhere: ["server/src/"], + }), + ["a/z.test.tsx"], + ); +}); + +test("every program exists, and the provider list matches the tracked provider packages", () => { + for (const program of PROGRAMS) assert.ok(existsSync(path.join(root, program)), program); + assert.ok(existsSync(path.join(root, PROVIDER_WORKSPACE, "pnpm-lock.yaml"))); + const tracked = execFileSync("git", ["ls-files", "packages/plugins/sandbox-providers"], { cwd: root, encoding: "utf8" }); + const manifests = tracked.split("\n").filter((file) => file.endsWith("/package.json")); + assert.deepEqual(manifests.map((manifest) => path.dirname(manifest)).sort(), [...SANDBOX_PROVIDERS].sort()); +}); + +test("no provider commits a lockfile, except the one Daytona already has", () => { + const tracked = execFileSync("git", ["ls-files", "packages/plugins/sandbox-providers"], { cwd: root, encoding: "utf8" }); + assert.deepEqual( + tracked.split("\n").filter((file) => file.endsWith("/pnpm-lock.yaml")), + ["packages/plugins/sandbox-providers/daytona/pnpm-lock.yaml"], + ); +}); + +test("a type error in one program still checks the rest and fails the run", () => { + const { options, calls } = harness({ + run: (args) => { + calls.tsc.push(args); + return { status: args[1].startsWith("a/") ? 2 : 0 }; + }, + }); + assert.equal(typecheckTestPrograms(options), 2); + assert.deepEqual(calls.tsc, [ + ["-p", "a/tsconfig.test.json"], + ["-p", "b/tsconfig.test.json"], + ]); +}); + +test("a killed compiler stops the run", () => { + const { options, calls } = harness({ + run: (args) => { + calls.tsc.push(args); + return { status: null, signal: "SIGKILL" }; + }, + }); + assert.equal(typecheckTestPrograms(options), 1); + assert.equal(calls.tsc.length, 1); +}); + +test("providers install frozen without scripts, then relink the SDK", () => { + const { options, calls } = harness(); + assert.equal(typecheckTestPrograms(options), 0); + const install = calls.pnpm.find(({ cwd }) => cwd === path.join("/repo", PROVIDER_WORKSPACE)); + assert.ok(install.args.includes("--frozen-lockfile")); + assert.ok(install.args.includes("--ignore-scripts")); + assert.equal(calls.linked, 1); +}); + +test("a stale provider lockfile fails before any program runs and names the fix", () => { + const { options, calls } = harness({ + pnpm: (args, { cwd }) => (cwd === path.join("/repo", PROVIDER_WORKSPACE) ? 1 : 0), + }); + assert.equal(typecheckTestPrograms(options), 1); + assert.equal(calls.tsc.length, 0); + assert.match(calls.errors[0], /pnpm-lock\.yaml/); +}); + +test("an uncovered test file fails the run and is named", () => { + const { options, calls } = harness({ checkFiles: () => ["a/z.test.tsx"] }); + assert.equal(typecheckTestPrograms(options), 1); + assert.match(calls.errors[0], /a\/z\.test\.tsx/); +}); + +test("the coverage check fails closed when a program cannot be listed", () => { + const { options } = harness({ checkFiles: () => null }); + assert.equal(typecheckTestPrograms(options), 1); +}); + +test("naming programs skips the coverage check", () => { + let checked = false; + const { options } = harness({ + coverage: false, + checkFiles: () => { + checked = true; + return []; + }, + }); + assert.equal(typecheckTestPrograms(options), 0); + assert.equal(checked, false); +}); + +test("checkCoverage uses the compiler's file list, so a dropped sibling is caught", () => { + const directory = mkdtempSync(path.join(root, "node_modules", ".typecheck-test-programs-")); + try { + const relative = path.relative(root, directory).split(path.sep).join("/"); + writeFileSync(path.join(directory, "foo.test.ts"), "export {};\n"); + writeFileSync(path.join(directory, "foo.test.tsx"), "export {};\n"); + writeFileSync(path.join(directory, "tsconfig.json"), JSON.stringify({ compilerOptions: { noEmit: true, jsx: "preserve" }, include: ["*"] })); + const unchecked = checkCoverage({ + root, + programs: [`${relative}/tsconfig.json`], + workspaceConfigs: () => [], + trackedFiles: () => [`${relative}/foo.test.ts`, `${relative}/foo.test.tsx`], + }); + assert.deepEqual(unchecked, [`${relative}/foo.test.tsx`]); + } finally { + rmSync(directory, { recursive: true, force: true }); + } +}); + +test("a real type error in a test-style program makes tsc exit non-zero", () => { + const directory = mkdtempSync(path.join(tmpdir(), "typecheck-test-programs-")); + try { + writeFileSync(path.join(directory, "bad.test.ts"), "export const value: number = 'text';\n"); + writeFileSync(path.join(directory, "tsconfig.json"), JSON.stringify({ compilerOptions: { noEmit: true, types: [] }, include: ["*.ts"] })); + const { status, stdout } = spawnSync(process.execPath, [tsc, "-p", directory, "--pretty", "false"], { encoding: "utf8" }); + assert.notEqual(status, 0); + assert.match(stdout, /bad\.test\.ts.*TS2322/); + } finally { + rmSync(directory, { recursive: true, force: true }); + } +}); diff --git a/scripts/typecheck-test-projects.mjs b/scripts/typecheck-test-projects.mjs index 8fd8cef014f..0c6d537b216 100644 --- a/scripts/typecheck-test-projects.mjs +++ b/scripts/typecheck-test-projects.mjs @@ -22,6 +22,7 @@ import { isTestFile, typecheckLayers } from "../server/scripts/typecheck-layers. export const PROJECTS = [ "server/src/__tests__/helpers/maintenance", + "tests/e2e", "tests/lifecycle-baseline", "tests/runner-e2e", "tests/speko-native", diff --git a/tests/ai-connections-app/playwright.config.ts b/tests/ai-connections-app/playwright.config.ts index 4d2c4748141..3901f6a5cb6 100644 --- a/tests/ai-connections-app/playwright.config.ts +++ b/tests/ai-connections-app/playwright.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ use: { baseURL: process.env.AI_CONNECTIONS_TEST_URL ?? "http://127.0.0.1:3100", browserName: "chromium", - reducedMotion: "reduce", + contextOptions: { reducedMotion: "reduce" }, screenshot: "only-on-failure", }, }); diff --git a/tests/ai-connections-review/playwright.config.ts b/tests/ai-connections-review/playwright.config.ts index 75b89da5f85..48896eaddad 100644 --- a/tests/ai-connections-review/playwright.config.ts +++ b/tests/ai-connections-review/playwright.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ use: { baseURL: "http://localhost:6116", browserName: "chromium", - reducedMotion: "reduce", + contextOptions: { reducedMotion: "reduce" }, }, webServer: { command: "node ../../scripts/serve-storybook-static.mjs --port 6116", diff --git a/tests/e2e/acp-stop-continuation.spec.ts b/tests/e2e/acp-stop-continuation.spec.ts index de927420a86..9b9037d027c 100644 --- a/tests/e2e/acp-stop-continuation.spec.ts +++ b/tests/e2e/acp-stop-continuation.spec.ts @@ -47,7 +47,7 @@ for (const { unfinishedWrite, stopResponse, sendSavedQueue = false } of [ .toContain("List my recent Drive files."); // Both actions stop the response; composer Stop must not create a task hold. - let stopped; + let stopped!: { status: string; resultJson?: { executionCancellation?: { state?: string } } | null }; if (stopResponse) { await page.getByRole("button", { name: "Stop", exact: true }).click(); } else { @@ -58,7 +58,7 @@ for (const { unfinishedWrite, stopResponse, sendSavedQueue = false } of [ return stopped.resultJson?.executionCancellation?.state; }, { timeout: 30_000 }).toBe("acknowledged"); expect(stopped.status).toBe("cancelled"); - expect(stopped.resultJson.executionCancellation.state).toBe("acknowledged"); + expect(stopped.resultJson?.executionCancellation?.state).toBe("acknowledged"); const writesAtStop = unfinishedWrite ? await readFile(path.join(root, "writes"), "utf8") : null; await page.reload(); if (stopResponse) { diff --git a/tests/e2e/agent-configuration.spec.ts b/tests/e2e/agent-configuration.spec.ts index 227e67ba551..653e0f6c5eb 100644 --- a/tests/e2e/agent-configuration.spec.ts +++ b/tests/e2e/agent-configuration.spec.ts @@ -51,7 +51,7 @@ async function document(page: Page): Promise { await editor(page).press("ControlOrMeta+a"); const selectedEditor = await editor(page).evaluate(element => { const selection = window.getSelection(); - return document.activeElement === element && !!selection?.anchorNode && !!selection.focusNode + return window.document.activeElement === element && !!selection?.anchorNode && !!selection.focusNode && element.contains(selection.anchorNode) && element.contains(selection.focusNode); }); if (!selectedEditor) return false; @@ -135,7 +135,7 @@ test("compact YAML stays small as the company grows and preserves hidden configu await json(await request.put(`/api/agents/${agent.id}/instructions-bundle/file`, { data: { path: "AGENTS.md", content: instructions, baseHash: entry.contentHash, baseRevisionId: entry.revision?.id ?? null, } })); - const profiles = []; + const profiles: Array<{ id: string }> = []; for (let index = 0; index < 8; index++) profiles.push(await json(await request.post(`/api/companies/${company.id}/tools/profiles`, { data: { profileKey: `profile-${index}`, name: `Profile ${index}`, defaultAction: "deny", entries: Array.from({ length: 30 }, (_, tool) => ({ selectorType: "tool_name", effect: "include", toolName: `fixture_tool_${tool}` })), @@ -238,7 +238,7 @@ test("Advanced YAML persists settings and commands through their real resource A expect((await json(await request.get(`/api/agents/${agent.id}/instructions-bundle`))).files.map((file: { path: string }) => file.path)).not.toContain("workflow.md"); expect(cleared.spec.skills).toBeUndefined(); expect(cleared.spec.tools).toBeUndefined(); - expect(cleared.spec.channels![0].settings.allowUnlinkedPeople).toBe(true); + expect(cleared.spec.channels![0].settings!.allowUnlinkedPeople).toBe(true); expect(cleared.spec.channels![0].settings).not.toHaveProperty("communicationInstructions"); expect((await json(await request.get(`/api/agents/${agent.id}/keys`))).find((row: { id: string }) => row.id === key.id).revokedAt).not.toBeNull(); await page.screenshot({ path: info.outputPath("saved-agent-yaml.png") }); @@ -305,7 +305,7 @@ test("a lost creation response requires reload and cannot blindly replay command // Reproduce the observed CI failure once: a copy contains surrounding page // text as well as the YAML header. The read helper must reacquire a clean // editor copy instead of accepting the header and parsing the whole page. - await page.evaluate(() => document.addEventListener("copy", event => { + await page.evaluate(() => window.document.addEventListener("copy", event => { event.clipboardData?.setData("text/plain", "Skip to Main Content\napiVersion: paperclip/v1\nCopied surrounding page text"); event.preventDefault(); }, { capture: true, once: true })); diff --git a/tests/e2e/board-attachment-receipts.spec.ts b/tests/e2e/board-attachment-receipts.spec.ts index 4b89c54f1a3..044e1d03772 100644 --- a/tests/e2e/board-attachment-receipts.spec.ts +++ b/tests/e2e/board-attachment-receipts.spec.ts @@ -3,6 +3,7 @@ import { expect, test, type APIRequestContext, + type APIResponse, type Locator, type Page, } from "@playwright/test"; @@ -18,7 +19,7 @@ type Attachment = { type Comment = { id: string; body: string; clientRequestId?: string | null }; async function body( - response: Awaited>, + response: Pick, ): Promise { expect(response.ok(), `${response.status()}: ${await response.text()}`).toBe( true, @@ -612,7 +613,7 @@ test("announcement dismissal handler tolerates detachment after its element snap const dismiss = page.getByRole("complementary", { name: "Paperclip announcements" }).getByRole("button", { name: "Dismiss announcement" }); const snapshot = await dismiss.elementHandles(); expect(snapshot).toHaveLength(1); - await snapshot[0]!.evaluate(element => element.remove()); + await snapshot[0]!.evaluate(element => (element as ChildNode).remove()); dismiss.elementHandles = async () => snapshot; await dismissAnnouncement(page, dismiss); await expect(dismiss).toHaveCount(0); diff --git a/tests/e2e/chat-adapters-ui-providers.spec.ts b/tests/e2e/chat-adapters-ui-providers.spec.ts index 6ebe799976c..bd3f627f2bc 100644 --- a/tests/e2e/chat-adapters-ui-providers.spec.ts +++ b/tests/e2e/chat-adapters-ui-providers.spec.ts @@ -614,13 +614,6 @@ test.describe.serial("native chat adapter UI", () => { await expectMinimumProviderSetup(page, provider); await fillProviderSetup(page, provider); - if (provider.provider === "github") { - // A completed click does not mean the async configure request reached the mock. - await expect.poll(() => mock.setupAttempts).toBe(2); - expect(mock.githubPrivateKeyMatchedFile).toBe(true); - expect(mock.githubPrivateKeyMatchedPaste).toBe(true); - } - if (provider.provider === "telegram") { const submittedToken = "123456:e2e-redacted"; // The next step intentionally has its own identity-readiness alert. @@ -762,25 +755,19 @@ test.describe.serial("native chat adapter UI", () => { { id: `resource-${provider.provider}`, enabled: true }, ]); - if (provider.provider === "github") { + const directMessages = page.getByRole("switch", { + name: "Allow direct messages", + }); + await expect(directMessages).toBeVisible(); + if (provider.provider === "discord") { await expect( - page.getByRole("heading", { name: "Private conversations" }), - ).toHaveCount(0); - } else { - const directMessages = page.getByRole("switch", { - name: "Allow direct messages", - }); - await expect(directMessages).toBeVisible(); - if (provider.provider === "discord") { - await expect( - page.getByText( - "People must also enable Direct Messages in their shared Discord server’s Privacy Settings.", - ), - ).toBeVisible(); - } - await directMessages.click(); - await expect.poll(() => mock.allowDirectMessages).toBe(true); + page.getByText( + "People must also enable Direct Messages in their shared Discord server’s Privacy Settings.", + ), + ).toBeVisible(); } + await directMessages.click(); + await expect.poll(() => mock.allowDirectMessages).toBe(true); if (provider.provider === "microsoft-teams") { const groupChats = page.getByRole("switch", { name: "Allow group chats", @@ -978,93 +965,13 @@ test.describe.serial("native chat adapter UI", () => { expect(new URL(page.url()).searchParams.get("reconnect")).toBe("1"); await expect( page.getByRole("heading", { - name: - provider.provider === "github" - ? "Reconnect GitHub App" - : provider.provider === "slack" ? "Add Slack credentials" : provider.setupHeading, + name: provider.provider === "slack" ? "Add Slack credentials" : provider.setupHeading, }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Choose an active agent" }), ).toHaveCount(0); - if (provider.provider === "github") { - const connectButton = page.getByRole("button", { - name: provider.setupButton, - }); - await expect( - page.getByText( - /Under the target user or organization, create a new GitHub App/, - ), - ).toHaveCount(0); - await expect( - page.getByText( - /Leave App ID and private key blank to reuse saved credentials/, - ), - ).toBeVisible(); - await page.getByLabel("GitHub App ID").fill("123456"); - await page - .getByLabel("Private key (PEM)") - .fill("reconnect-private-key"); - await expect(connectButton).toBeEnabled(); - await page.evaluate((buttonName) => { - const state = window as typeof window & { - __githubConnectEnabledAfterRotation?: boolean; - }; - state.__githubConnectEnabledAfterRotation = false; - new MutationObserver(() => { - const button = [...document.querySelectorAll("button")].find( - (candidate) => candidate.textContent?.trim() === buttonName, - ); - if (button instanceof HTMLButtonElement && !button.disabled) { - state.__githubConnectEnabledAfterRotation = true; - } - }).observe(document.body, { - attributes: true, - childList: true, - subtree: true, - }); - }, provider.setupButton); - await page - .getByRole("button", { name: "Regenerate webhook secret" }) - .click(); - await expect(connectButton).toBeDisabled(); - await page.waitForTimeout(100); - expect( - await page.evaluate( - () => - ( - window as typeof window & { - __githubConnectEnabledAfterRotation?: boolean; - } - ).__githubConnectEnabledAfterRotation, - ), - ).toBe(false); - - await page.goBack(); - await page.getByText("Connection health and controls", { exact: true }).click(); - await expect( - page.getByRole("heading", { name: "Connection activity" }), - ).toBeVisible(); - await page - .getByRole("button", { name: "Reconnect", exact: true }) - .click(); - await expect( - page.getByRole("heading", { name: "Reconnect GitHub App" }), - ).toBeVisible(); - await page.getByLabel("GitHub App ID").fill("123456"); - await page - .getByLabel("Private key (PEM)") - .fill("reconnect-private-key"); - await expect( - page.getByRole("button", { name: provider.setupButton }), - ).toBeDisabled(); - mock.setWebhookVerified(); - await expect( - page.getByRole("button", { name: provider.setupButton }), - ).toBeEnabled(); - } - mock.setStatus("active"); await page.goto( `/${seed.prefix}/apps/chat/endpoint-${provider.provider}/activity`, diff --git a/tests/e2e/chat-adapters-ui.shared.ts b/tests/e2e/chat-adapters-ui.shared.ts index afa8a9686b9..5022b3afd17 100644 --- a/tests/e2e/chat-adapters-ui.shared.ts +++ b/tests/e2e/chat-adapters-ui.shared.ts @@ -284,6 +284,9 @@ export function endpointFixture(provider: ProviderCase, seed: Seed) { slackSetupMethod: undefined as "managed" | "automatic" | "manual" | "existing" | undefined, slackRegistration: undefined as SlackRegistrationState | undefined, slackApp: undefined as SlackAppConfiguration | undefined, + slackAccount: undefined as Record | undefined, + slackAvatar: undefined as Record | undefined, + github: undefined as Record | undefined, slackOAuthCallbackUri: "https://paperclip.example.test/api/chat-slack/oauth/callback", step: "provider_setup", authorizationUrl: @@ -301,7 +304,7 @@ export function endpointFixture(provider: ProviderCase, seed: Seed) { messagingEndpoint: `https://paperclip.example.test/api/chat-webhooks/public-${provider.provider}/microsoft-teams`, command: provider.provider === "slack" ? "/maya-public" : undefined, testStartedAt: null as string | null, - webhookVerifiedAt: null, + webhookVerifiedAt: null as string | null, webhookSecretConfigured: false, }, healthMessage: null, diff --git a/tests/e2e/company-context-refresh.spec.ts b/tests/e2e/company-context-refresh.spec.ts index 0a90d8a6fec..04f0698fb07 100644 --- a/tests/e2e/company-context-refresh.spec.ts +++ b/tests/e2e/company-context-refresh.spec.ts @@ -18,8 +18,8 @@ test.beforeAll(async () => { server: { host: "127.0.0.1", port: 0 }, plugins: [{ name: "company-context-regression", - resolveId(id) { if (id === "/src/__company_context_probe.tsx") return probePath; }, - load(id) { + resolveId(id: string) { if (id === "/src/__company_context_probe.tsx") return probePath; }, + load(id: string) { if (id !== probePath) return; return ` import React from "react"; @@ -39,7 +39,7 @@ test.beforeAll(async () => { }; `; }, - configureServer(vite) { + configureServer(vite: ViteDevServer) { vite.middlewares.use((req, res, next) => { if (req.url !== "/") return next(); res.setHeader("Content-Type", "text/html"); diff --git a/tests/e2e/connection-intents.spec.ts b/tests/e2e/connection-intents.spec.ts index 34ceaf43909..a2b37a9c4d8 100644 --- a/tests/e2e/connection-intents.spec.ts +++ b/tests/e2e/connection-intents.spec.ts @@ -250,7 +250,7 @@ async function waitForAgentRun( companyId: string, agentId: string, ) { - let terminalRun: { id: string; status: string } | null = null; + let terminalRun = null as { id: string; status: string } | null; await expect .poll( async () => { diff --git a/tests/e2e/execution-recovery/recovery.spec.ts b/tests/e2e/execution-recovery/recovery.spec.ts index 0ef482333e7..18ca0b85faf 100644 --- a/tests/e2e/execution-recovery/recovery.spec.ts +++ b/tests/e2e/execution-recovery/recovery.spec.ts @@ -109,7 +109,7 @@ for (const journey of [ }); try { const port = await listenOnFetchAllowedPort(fixture); - const env = { + const env: NodeJS.ProcessEnv = { ...process.env, PAPERCLIP_RECOVERY_CEO_LINEAGE: journey === "ceo_lineage" ? "1" : "0", IN_FEED_FIXTURE_KEY: "not-a-real-model-key", diff --git a/tests/e2e/in-feed-native/completion-stream.spec.ts b/tests/e2e/in-feed-native/completion-stream.spec.ts index 3095ca5b01a..e63c95bc6b4 100644 --- a/tests/e2e/in-feed-native/completion-stream.spec.ts +++ b/tests/e2e/in-feed-native/completion-stream.spec.ts @@ -34,7 +34,7 @@ test('a completion tool does not cut off a delayed final answer', async ({ page let child: ChildProcess | undefined; let logs = ''; try { - const env = { ...process.env, STREAM_FIXTURE_KEY: 'not-a-real-model-key', NODE_ENV: 'test', + const env: NodeJS.ProcessEnv = { ...process.env, STREAM_FIXTURE_KEY: 'not-a-real-model-key', NODE_ENV: 'test', PATH: `${root}/tests/e2e/fixtures/completion-stream-bin:${process.env.PATH}` }; delete env.DATABASE_URL; delete env.DATABASE_MIGRATION_URL; diff --git a/tests/e2e/in-feed-native/connections.spec.ts b/tests/e2e/in-feed-native/connections.spec.ts index c242a14ea6a..5b3f124cfc8 100644 --- a/tests/e2e/in-feed-native/connections.spec.ts +++ b/tests/e2e/in-feed-native/connections.spec.ts @@ -25,7 +25,7 @@ for (const journey of ['connect', 'decline', 'restart'] as const) test(`fresh na }); try { const port = await listenOnFetchAllowedPort(fixture); - const env = { ...process.env, IN_FEED_FIXTURE_KEY: 'not-a-real-model-key', NODE_ENV: 'test', PAPERCLIP_TEST_CONNECTION_DELIVERY_HOLD: journey === 'restart' ? '1' : '0', PATH: `${root}/tests/e2e/fixtures/in-feed-bin:${process.env.PATH}` }; + const env: NodeJS.ProcessEnv = { ...process.env, IN_FEED_FIXTURE_KEY: 'not-a-real-model-key', NODE_ENV: 'test', PAPERCLIP_TEST_CONNECTION_DELIVERY_HOLD: journey === 'restart' ? '1' : '0', PATH: `${root}/tests/e2e/fixtures/in-feed-bin:${process.env.PATH}` }; delete env.DATABASE_URL; delete env.DATABASE_MIGRATION_URL; processHandle = spawn(process.execPath, ['cli/node_modules/tsx/dist/cli.mjs', 'cli/src/index.ts', 'test-drive', '--harness', 'codex', '--api-key-env', 'IN_FEED_FIXTURE_KEY', '--company-name', 'In-feed native fixture', '--no-browser'], { cwd: root, env, detached: true, stdio: ['ignore', 'pipe', 'pipe'] }); processHandle.stdout!.on('data', (chunk) => { logs += chunk.toString(); }); diff --git a/tests/e2e/playwright-composer-stop.config.ts b/tests/e2e/playwright-composer-stop.config.ts index a58f8507034..f98508ae384 100644 --- a/tests/e2e/playwright-composer-stop.config.ts +++ b/tests/e2e/playwright-composer-stop.config.ts @@ -27,7 +27,7 @@ if (fixture) { { mode: 0o755 }, ); } -const server = base.webServer as Exclude; +const server = base.webServer as Exclude; export default defineConfig({ ...base, testMatch: "composer-stop.spec.ts", diff --git a/tests/e2e/playwright-transcript-delivery.config.ts b/tests/e2e/playwright-transcript-delivery.config.ts index f2e35aa2d67..c1d6eea22d3 100644 --- a/tests/e2e/playwright-transcript-delivery.config.ts +++ b/tests/e2e/playwright-transcript-delivery.config.ts @@ -5,7 +5,7 @@ import { defineConfig } from "@playwright/test"; import base from "./playwright.config"; const providerHome = fs.mkdtempSync(path.join(os.tmpdir(), "transcript-provider-")); -const server = base.webServer as Exclude; +const server = base.webServer as Exclude; export default defineConfig({ ...base, testMatch: "transcript-delivery.journey.ts", diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json new file mode 100644 index 00000000000..ad82b65f0a9 --- /dev/null +++ b/tests/e2e/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "allowImportingTsExtensions": true, + "allowJs": true, + "jsx": "react-jsx", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "paths": { + "@/*": ["../../ui/src/*"], + "@paperclipai/shared": ["../../packages/shared/src/index.ts"] + }, + "rootDir": "../..", + "types": ["node"], + "typeRoots": ["../../server/node_modules/@types"] + }, + "include": ["./**/*.ts", "./**/*.tsx", "../../ui/src/vite-env.d.ts", "../../server/src/types/express.d.ts"] +} diff --git a/tests/e2e/tsconfig.layers.json b/tests/e2e/tsconfig.layers.json new file mode 100644 index 00000000000..f1c68632282 --- /dev/null +++ b/tests/e2e/tsconfig.layers.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": false, + "declarationMap": false + }, + "references": [{ "path": "../../server/node_modules/.cache/paperclip-typecheck/tsconfig.declarations.json" }] +} diff --git a/tests/perf/issue-detail/issue-detail.perf.spec.ts b/tests/perf/issue-detail/issue-detail.perf.spec.ts index 2dc5cca727d..e469e442533 100644 --- a/tests/perf/issue-detail/issue-detail.perf.spec.ts +++ b/tests/perf/issue-detail/issue-detail.perf.spec.ts @@ -10,13 +10,15 @@ const PAGE_READY_TIMEOUT_MS = 90_000; const HEADER_MEASURE = "issue-detail:navigate→header-paint"; const CONTENT_MEASURE = "issue-detail:navigate→content-paint"; -type Profile = { - name: "unthrottled" | "fast-4g-4x-cpu"; - latencyMs?: number; - downloadBytesPerSecond?: number; - uploadBytesPerSecond?: number; - cpuSlowdownRate?: number; -}; +type Profile = + | { name: "unthrottled"; latencyMs?: undefined } + | { + name: "fast-4g-4x-cpu"; + latencyMs: number; + downloadBytesPerSecond: number; + uploadBytesPerSecond: number; + cpuSlowdownRate: number; + }; type NetworkRecord = { requestId: string; diff --git a/tests/perf/issue-detail/rich-loading-fixture.ts b/tests/perf/issue-detail/rich-loading-fixture.ts index 7b32a59f6cb..a9fc3a0e539 100644 --- a/tests/perf/issue-detail/rich-loading-fixture.ts +++ b/tests/perf/issue-detail/rich-loading-fixture.ts @@ -83,7 +83,7 @@ export async function richLoadingFixture(request: APIRequestContext): Promise ({ - id: randomUUID(), companyId: company.id, issueId: issue.id, authorType: "agent", authorAgentId: agent.id, + id: randomUUID(), companyId: company.id, issueId: issue.id, authorType: "agent" as const, authorAgentId: agent.id, body: `## ${index === 199 ? "Latest investigation findings" : `Investigation update ${index}`}\n\n` + "Verified the saved workspace, inspected the reported behavior, and checked that the evidence still matches the current revision. ".repeat(index === 199 ? 12 : 5) + (index === 199 ? "\n\n[Read the saved analysis](#document-analysis) and [verification results](#document-verification).\n\n| Check | Result |\n|---|---|\n| Saved evidence | Complete |\n| Review | Waiting for your decision |" : ""), diff --git a/tests/perf/issue-detail/window-globals.d.ts b/tests/perf/issue-detail/window-globals.d.ts new file mode 100644 index 00000000000..4abf722fa69 --- /dev/null +++ b/tests/perf/issue-detail/window-globals.d.ts @@ -0,0 +1,9 @@ +export {}; + +// Mirrors the declarations in ui/src/lib/issue-detail-performance.ts, which these specs set from init scripts. +declare global { + interface Window { + __PAPERCLIP_ISSUE_DETAIL_NAVIGATE_START__?: number; + __PAPERCLIP_ISSUE_DETAIL_PROFILE__?: boolean; + } +} diff --git a/tests/storybook-visual/agent-personas.config.ts b/tests/storybook-visual/agent-personas.config.ts index bfda1740b67..bbdd8fbf8c6 100644 --- a/tests/storybook-visual/agent-personas.config.ts +++ b/tests/storybook-visual/agent-personas.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ snapshotPathTemplate: "{testDir}/.snapshots/agent-personas/{arg}{ext}", use: { browserName: "chromium", baseURL: process.env.PAPERCLIP_PERSONA_STORYBOOK_URL ?? "http://localhost:6106", - viewport: { width: 1200, height: 900 }, deviceScaleFactor: 1, reducedMotion: "reduce", + viewport: { width: 1200, height: 900 }, deviceScaleFactor: 1, contextOptions: { reducedMotion: "reduce" }, launchOptions: { args: ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"] }, }, }); diff --git a/tests/storybook-visual/agent-personas.spec.ts b/tests/storybook-visual/agent-personas.spec.ts index 555a8e653ec..d735eac2331 100644 --- a/tests/storybook-visual/agent-personas.spec.ts +++ b/tests/storybook-visual/agent-personas.spec.ts @@ -24,7 +24,7 @@ test("500 avatars load images without WebGL, live modules or avatar frame loops" await page.addInitScript(() => { (window as any).__personaProbe = { webgl: 0, frames: 0 }; const getContext = HTMLCanvasElement.prototype.getContext; - HTMLCanvasElement.prototype.getContext = function (kind: string, ...args: any[]) { + HTMLCanvasElement.prototype.getContext = function (this: HTMLCanvasElement, kind: string, ...args: any[]) { if (kind.includes("webgl")) (window as any).__personaProbe.webgl++; return (getContext as any).call(this, kind, ...args); } as any; diff --git a/tests/storybook-visual/app-shell-design.spec.ts b/tests/storybook-visual/app-shell-design.spec.ts index 1f55ff87cfd..17992c32723 100644 --- a/tests/storybook-visual/app-shell-design.spec.ts +++ b/tests/storybook-visual/app-shell-design.spec.ts @@ -140,6 +140,7 @@ test("right divider matches the left highlight, resizes in both directions, pers }); test("both pane dividers animate a double-click reset while manual sizing stays immediate", async ({ page }) => { + await page.emulateMedia({ reducedMotion: "no-preference" }); await openShell(page, "three-panes"); const left = page.getByRole("separator", { name: "Resize sidebar", exact: true }); const leftShell = page.locator(".design-page-navigation"); diff --git a/tests/storybook-visual/feature-gap-stories.config.ts b/tests/storybook-visual/feature-gap-stories.config.ts index d774c97fda9..c5a41bb9fa7 100644 --- a/tests/storybook-visual/feature-gap-stories.config.ts +++ b/tests/storybook-visual/feature-gap-stories.config.ts @@ -12,6 +12,6 @@ export default defineConfig({ use: { baseURL: process.env.PAPERCLIP_GAP_STORYBOOK_URL ?? "http://localhost:6136", viewport: { width: 1280, height: 900 }, - reducedMotion: "reduce", + contextOptions: { reducedMotion: "reduce" }, }, }); diff --git a/tests/storybook-visual/fireflies-pr.config.ts b/tests/storybook-visual/fireflies-pr.config.ts index 13bcabdcd24..3ea471f78f0 100644 --- a/tests/storybook-visual/fireflies-pr.config.ts +++ b/tests/storybook-visual/fireflies-pr.config.ts @@ -8,6 +8,6 @@ export default defineConfig({ retries: 0, outputDir: "./test-results/fireflies-pr", reporter: [["list"]], - use: { baseURL: "http://127.0.0.1:6149", viewport: { width: 1440, height: 1000 }, reducedMotion: "reduce", trace: "retain-on-failure" }, + use: { baseURL: "http://127.0.0.1:6149", viewport: { width: 1440, height: 1000 }, contextOptions: { reducedMotion: "reduce" }, trace: "retain-on-failure" }, webServer: { command: "node ../../scripts/serve-storybook-static.mjs --port 6149", url: "http://127.0.0.1:6149/index.json", reuseExistingServer: false }, }); diff --git a/tests/storybook-visual/imessage-photon.config.ts b/tests/storybook-visual/imessage-photon.config.ts index aaac3e55cef..6dc8bc0d5f9 100644 --- a/tests/storybook-visual/imessage-photon.config.ts +++ b/tests/storybook-visual/imessage-photon.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ testDir: ".", testMatch: "imessage-photon.spec.ts", workers: 2, timeout: 30_000, retries: 0, outputDir: "./test-results/imessage-photon", reporter: [["list"]], - use: { browserName: "chromium", baseURL: "http://127.0.0.1:6128", reducedMotion: "reduce" }, + use: { browserName: "chromium", baseURL: "http://127.0.0.1:6128", contextOptions: { reducedMotion: "reduce" } }, webServer: { command: "node ../../scripts/serve-storybook-static.mjs --port 6128", url: "http://127.0.0.1:6128/index.json", reuseExistingServer: false, diff --git a/tests/storybook-visual/in-feed-connections.config.ts b/tests/storybook-visual/in-feed-connections.config.ts index 32aa673a073..2325532f13d 100644 --- a/tests/storybook-visual/in-feed-connections.config.ts +++ b/tests/storybook-visual/in-feed-connections.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from '@playwright/test'; export default defineConfig({ testDir: '.', testMatch: 'in-feed-connections.spec.ts', workers: 4, timeout: 30_000, retries: 0, outputDir: './test-results/in-feed', reporter: [['list']], - use: { browserName: 'chromium', baseURL: 'http://127.0.0.1:6126', reducedMotion: 'reduce' }, + use: { browserName: 'chromium', baseURL: 'http://127.0.0.1:6126', contextOptions: { reducedMotion: 'reduce' } }, webServer: { command: 'node ../../scripts/serve-storybook-static.mjs --port 6126', url: 'http://127.0.0.1:6126/index.json', reuseExistingServer: false } }); diff --git a/tests/storybook-visual/playwright.config.ts b/tests/storybook-visual/playwright.config.ts index 0e7608f1859..2e099a3d53e 100644 --- a/tests/storybook-visual/playwright.config.ts +++ b/tests/storybook-visual/playwright.config.ts @@ -49,7 +49,7 @@ export default defineConfig({ deviceScaleFactor: 1, // JS-driven tickers/timers key off prefers-reduced-motion for // deterministic captures (CSS animations are already disabled). - reducedMotion: "reduce", + contextOptions: { reducedMotion: "reduce" }, baseURL: process.env.STORYBOOK_TEST_BASE_URL ?? `http://localhost:${storybookPort}`, }, webServer: process.env.STORYBOOK_TEST_BASE_URL ? undefined : { diff --git a/tests/storybook-visual/remote-mcp-connections.config.ts b/tests/storybook-visual/remote-mcp-connections.config.ts index 041b689c720..da791479e95 100644 --- a/tests/storybook-visual/remote-mcp-connections.config.ts +++ b/tests/storybook-visual/remote-mcp-connections.config.ts @@ -3,6 +3,6 @@ import { defineConfig } from "@playwright/test"; export default defineConfig({ testDir: ".", testMatch: "remote-mcp-connections.spec.ts", workers: 1, fullyParallel: false, timeout: 180_000, retries: 0, outputDir: "./test-results/remote-mcp", reporter: [["list"]], - use: { browserName: "chromium", baseURL: "http://127.0.0.1:6138", reducedMotion: "reduce", actionTimeout: 10_000, trace: "retain-on-failure" }, + use: { browserName: "chromium", baseURL: "http://127.0.0.1:6138", contextOptions: { reducedMotion: "reduce" }, actionTimeout: 10_000, trace: "retain-on-failure" }, webServer: { command: "node ../../scripts/serve-storybook-static.mjs --port 6138", url: "http://127.0.0.1:6138/index.json", reuseExistingServer: false }, }); diff --git a/tests/storybook-visual/repository-skill-packages.config.ts b/tests/storybook-visual/repository-skill-packages.config.ts index b969734a280..764afd07f02 100644 --- a/tests/storybook-visual/repository-skill-packages.config.ts +++ b/tests/storybook-visual/repository-skill-packages.config.ts @@ -11,6 +11,6 @@ export default defineConfig({ use: { baseURL: process.env.PAPERCLIP_REPOSITORY_STORYBOOK_URL ?? 'http://localhost:6106', viewport: { width: 1280, height: 900 }, - reducedMotion: 'reduce', + contextOptions: { reducedMotion: 'reduce' }, }, }); diff --git a/tests/storybook-visual/task-list-reproduction-trial.config.ts b/tests/storybook-visual/task-list-reproduction-trial.config.ts index e130e5f2ae4..17c0e04c082 100644 --- a/tests/storybook-visual/task-list-reproduction-trial.config.ts +++ b/tests/storybook-visual/task-list-reproduction-trial.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1, colorScheme: "dark", - reducedMotion: "reduce", + contextOptions: { reducedMotion: "reduce" }, locale: "en-US", timezoneId: "America/Los_Angeles", launchOptions: { executablePath: process.env.TRIAL_CHROMIUM_EXECUTABLE }, diff --git a/tests/storybook-visual/task-sidebar-loading.spec.ts b/tests/storybook-visual/task-sidebar-loading.spec.ts index 77bced22d48..c31526ee666 100644 --- a/tests/storybook-visual/task-sidebar-loading.spec.ts +++ b/tests/storybook-visual/task-sidebar-loading.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "@playwright/test"; -test.use({ viewport: { width: 1440, height: 960 }, reducedMotion: "no-preference" }); +test.use({ viewport: { width: 1440, height: 960 }, contextOptions: { reducedMotion: "no-preference" } }); async function sampleLayout(page: import("@playwright/test").Page, duration: number) { return page.evaluate(async (duration) => { diff --git a/tests/storybook-visual/trial-2/task-list-reproduction-trial.spec.ts b/tests/storybook-visual/trial-2/task-list-reproduction-trial.spec.ts index 395d3ba7c5e..46844829e97 100644 --- a/tests/storybook-visual/trial-2/task-list-reproduction-trial.spec.ts +++ b/tests/storybook-visual/trial-2/task-list-reproduction-trial.spec.ts @@ -10,7 +10,7 @@ const clock = "2026-10-15T00:00:00.000Z"; test.use({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1, - colorScheme: "dark", reducedMotion: "reduce", locale: "en-US", + colorScheme: "dark", contextOptions: { reducedMotion: "reduce" }, locale: "en-US", timezoneId: "America/Los_Angeles", launchOptions: { executablePath: process.env.TRIAL_CHROMIUM_EXECUTABLE }, }); diff --git a/tests/storybook-visual/typography-foundations.spec.ts b/tests/storybook-visual/typography-foundations.spec.ts index 6ebbadad34a..3e39dff26bf 100644 --- a/tests/storybook-visual/typography-foundations.spec.ts +++ b/tests/storybook-visual/typography-foundations.spec.ts @@ -390,7 +390,7 @@ test("mixed-font tool text shares one uninterrupted shimmer loop", async ({ page const animations = element.getAnimations({ subtree: true }); const animation = animations.find(candidate => (candidate as CSSAnimation).animationName === "shimmer-text-slide")!; const timing = animation.effect!.getTiming(); - const keyframes = animation.effect!.getKeyframes(); + const keyframes = (animation.effect as KeyframeEffect).getKeyframes(); // Sample the old dead zone: the background must keep moving after 60%. animation.pause(); animation.currentTime = Number(timing.duration) * 0.65; diff --git a/tests/tsconfig.playwright.json b/tests/tsconfig.playwright.json new file mode 100644 index 00000000000..615325aa972 --- /dev/null +++ b/tests/tsconfig.playwright.json @@ -0,0 +1,28 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "paths": { + "@/*": ["../ui/src/*"], + "@paperclipai/shared": ["../packages/shared/src/index.ts"] + }, + "rootDir": "..", + "types": ["node"], + "typeRoots": ["../server/node_modules/@types"] + }, + "include": [ + "ai-connections-app/**/*.ts", + "ai-connections-review/**/*.ts", + "canary-onboarding/**/*.ts", + "hiring-ai-connections/**/*.ts", + "html-preview/**/*.ts", + "perf/**/*.ts", + "release-smoke/**/*.ts", + "storybook-visual/**/*.ts", + "../ui/src/vite-env.d.ts" + ] +} diff --git a/ui/src/components/AttentionInteractionResolver.test.ts b/ui/src/components/AttentionInteractionResolver.replace.test.ts similarity index 100% rename from ui/src/components/AttentionInteractionResolver.test.ts rename to ui/src/components/AttentionInteractionResolver.replace.test.ts