feat(ai): add HarnessAgent sidecar foundation (#560)

* feat(ai): add HarnessAgent sidecar foundation

* feat(ai): integrate Pi as a Harness provider

* fix(ai): harden Harness provider integration

* fix(ai): address Harness integration review

* fix(ai): align Harness code with project conventions

* fix(ai): preserve Harness sessions on cancellation

* test(ai): cover Harness JSONL framing

* refactor(ai): generalize Harness sidecar adapters

* fix(ci): build Harness sidecar for native checks
This commit is contained in:
Danila Poyarkov 2026-08-19 17:41:10 +03:00 committed by GitHub
parent 44d9ba477a
commit 83a5ea1b42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 2359 additions and 86 deletions

View file

@ -53,6 +53,10 @@ jobs:
- uses: ./.github/actions/setup-bun
- name: Build Harness sidecar
shell: bash
run: bun run build:harness-sidecar -- ${{ matrix.target }}
- name: Extract release notes
if: startsWith(github.ref, 'refs/tags/v')
id: changelog

View file

@ -135,6 +135,9 @@ jobs:
- uses: ./.github/actions/setup-bun
- name: Build Harness sidecar
run: bun run build:harness-sidecar -- x86_64-unknown-linux-gnu
- name: Typecheck native interaction tests
run: bun run check:native-test

View file

@ -17,6 +17,7 @@ Bun workspace packages:
- `packages/vue``@open-pencil/vue`: headless Vue 3 SDK (Reka UI-style) for building custom OpenPencil-powered editor shells and embedded editing surfaces. Renderless components and composables. The app is one consumer of the SDK.
- `packages/cli``@open-pencil/cli`: headless CLI for .fig inspection, export, linting. Uses `citty` + `agentfmt`.
- `packages/mcp``@open-pencil/mcp`: MCP server for AI coding tools. Stdio + HTTP (Hono). Reuses core tools.
- `packages/harness``@open-pencil/harness`: private Node sidecar runtime for backend-neutral coding-agent sessions. Owns HarnessAgent adapters, opaque resume-state persistence, and the JSONL host protocol; app/editor integration remains outside the package.
- `packages/docs``@open-pencil/docs`: published VitePress documentation site. Use `bun run docs:dev` for authoring, `bun run docs:build` for the default fast local render check without minification or generated LLM files, and `bun run docs:build:production` for the complete deployment output.
The root app (`src/`) is the Tauri/Vite desktop editor. App-specific editor, document, AI, collaboration, shell, tabs, demo, and automation code lives under `src/app/*`. The app consumes scene graph primitives from `@open-pencil/scene-graph`, editor/rendering services through targeted `@open-pencil/core` subpath exports, and `@open-pencil/vue` through the public Vue SDK entrypoint.
@ -152,6 +153,7 @@ Release commits are the exception: keep using `Release v0.x.y`.
Keep this section light; implementation details move often.
- Harness-based coding agents live in the private `packages/harness` Node sidecar package rather than the browser app. Keep its session service backend-neutral, persist only opaque non-secret resume state, and expose host integration through its bounded JSONL protocol. Pi may use local `just-bash` for development, but that in-memory sandbox does not provide process-restart recovery.
- ACP UI/transport lives under `src/app/ai/acp/**`; provider definitions live in `packages/core/src/constants.ts`; app prompts live under `src/app/ai/**`. Direct model configuration lives under `src/app/ai/models/**`: reusable profiles reference provider connections, roles resolve to profiles, and runtime creation resolves credentials lazily. Keep model profiles, provider connections, and role assignments separate rather than returning to singleton provider/model settings. Public docs: `packages/docs/programmable/ai-chat.md` and `packages/docs/programmable/mcp-server.md`.
- ACP transport uses Tauri shell permissions, so check `desktop/capabilities/**` when changing agent launch behavior.
- Collaboration lives under `src/app/collab/**` and is documented in `packages/docs/programmable/collaboration.md`. It uses Trystero + Yjs + awareness; preserve crypto-safe room IDs and peer cleanup semantics when changing it.

View file

@ -9,6 +9,7 @@
### Added
- Snap vector points, moved layers, and resized edges to nearby geometry, sibling layer bounds, canvas and frame layout guides, and whole-pixel coordinates with visible alignment guides, fractional-coordinate preservation when pixel snapping is off, and persistent geometry, object, and pixel-grid controls in General settings and the Preferences menu.
- Run Pi through AI SDK HarnessAgent as a configurable desktop provider with multiple saved model profiles, secure credentials, existing MCP design tools, and per-profile thinking and permission settings.
- Open multiple selected design files in separate tabs.
- Import, render, edit, resize, select, and export Figma text-on-path layers while preserving their curved glyph layout.
- Show Figma-style temporary distance measurements between selected and Option/Alt-hovered layers. (#491)

405
bun.lock
View file

@ -29,6 +29,7 @@
"@open-pencil/core": "workspace:*",
"@open-pencil/dom-css": "workspace:*",
"@open-pencil/fig": "workspace:*",
"@open-pencil/harness": "workspace:*",
"@open-pencil/kiwi": "workspace:*",
"@open-pencil/pen": "workspace:*",
"@open-pencil/scene-graph": "workspace:*",
@ -254,6 +255,22 @@
"@open-pencil/scene-graph": "workspace:*",
},
},
"packages/harness": {
"name": "@open-pencil/harness",
"version": "0.0.0",
"bin": {
"openpencil-harness": "./dist/stdio.mjs",
},
"dependencies": {
"@ai-sdk/harness": "1.0.74",
"@ai-sdk/harness-pi": "1.0.76",
"@ai-sdk/sandbox-just-bash": "1.0.74",
},
"devDependencies": {
"@types/bun": "^1.3.14",
"typescript": "~5.8.3",
},
},
"packages/kiwi": {
"name": "@open-pencil/kiwi",
"version": "0.14.0",
@ -366,19 +383,25 @@
"@ai-sdk/deepseek": ["@ai-sdk/deepseek@3.0.28", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ySiafZL/1KQq8NF49eGWmVRYP4ezBjxAwAlMK+hhTV+9ee37LcCxlSIbURe4C4KFq8kYaTFKsTGI2poLFLOu0w=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.53", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4ZgwJjv/lGA7ywlD/3oYg6XMkIy1VvVKiSRYB83PyLDApwMGXSF+oT7D5lPYJFhNE5Vr3W2H+SwCFuTsbfgn9w=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.54", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-x4fAXDqCtYzB/M5vsIQLYcyrzpJuaRgcIwDSw+lpTMMbgH19fU3ds75GSlHNLzfx6Z5yL4Z9+EMr0GJcqVy9QA=="],
"@ai-sdk/google": ["@ai-sdk/google@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-1oLHhc2twSQzm7O8j/HaRWLfBh+tPjQP55d8yXmrZvvopoH6/P7WaDznVJqjouGSof429lR+tYdINkjvUl+8Qg=="],
"@ai-sdk/openai": ["@ai-sdk/openai@4.0.42", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZxDca6jJalYuXrIGVrw6dnkpz1Io9AWy+/b/wVWIbjigHCbd+zWLpPi8NnK0OFU+U3YCpP+KWfUvEnG5pFhltA=="],
"@ai-sdk/harness": ["@ai-sdk/harness@1.0.74", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27", "ai": "7.0.67" }, "peerDependencies": { "ws": "^8.21.0", "zod": "^3.25.76 || ^4.1.8" }, "optionalPeers": ["ws"] }, "sha512-FdTOH0UhDjdFzcWFWiKK0M0+9PEUEFibx3kEhfRPO8eSGeQvel2AtTUXmpi3OCPcr3y1ExVjh4HmvjNrqUFoxQ=="],
"@ai-sdk/harness-pi": ["@ai-sdk/harness-pi@1.0.76", "", { "dependencies": { "@ai-sdk/harness": "1.0.74", "@ai-sdk/provider-utils": "5.0.27", "@earendil-works/pi-ai": "0.74.2", "@earendil-works/pi-coding-agent": "^0.80.10", "pi-mcp-adapter": "2.12.1", "typebox": "^1.1.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/rb2w3w+9OKxl4V8bg0z4YietjUlnqvenQIDdgMxVqnZh85d3GYJLI5pkWp7OCC4x3Oneqe1qweMqWgGxgVlSg=="],
"@ai-sdk/openai": ["@ai-sdk/openai@4.0.43", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-O5t21AmcBwsEg/l8ro91CHzdBjEkxtJRFh6DH9KDjRrWDeTMuqJt7XkaFHIqL8H0nvjFv8H7kck+JwirUWPS2g=="],
"@ai-sdk/provider": ["@ai-sdk/provider@4.0.7", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6or44XprPzKbr8zkmzosowSE0pxkvJcoojBL+mCZvPUt3kvXp3XSNqeVun9golb1acEfSo6yaEBRT18h2VU+1Q=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@5.0.27", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8", "undici": "^7.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-EzAn4pdgG5g0xXtH6lE2zyNmfjDQIDjATkfqzuidEI35g++hh4+07vnjzkT/RmGmIClPZiRj/Q2GMPV2V7mkHw=="],
"@ai-sdk/sandbox-just-bash": ["@ai-sdk/sandbox-just-bash@1.0.74", "", { "dependencies": { "@ai-sdk/harness": "1.0.74", "@ai-sdk/provider-utils": "5.0.27", "just-bash": "^2.14.5" } }, "sha512-m0Zl63bRgebl/hm7qz+9IN1FFtd1ret9+/bxkuUomo+zG6IVKwFK5l9yxrR9zBlhnHMOXP+5cM4zUWgjR0x2dA=="],
"@ai-sdk/valibot": ["@ai-sdk/valibot@3.0.27", "", { "dependencies": { "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "@valibot/to-json-schema": "^1.3.0", "valibot": "^1.1.0" } }, "sha512-c77tkUqvaivemWbJjjVDCCbSfeRGd/Eg0t0GHQdlsS5GDBog9D7D9Ig/BiTvx0jDnKdGp0qQFs4k4NCP/JILUg=="],
"@ai-sdk/vue": ["@ai-sdk/vue@4.0.67", "", { "dependencies": { "@ai-sdk/provider-utils": "5.0.27", "ai": "7.0.67", "swrv": "^1.2.0" }, "peerDependencies": { "vue": "^3.3.4" } }, "sha512-81vFZPlH8zxeloo8rOJsh/JI4hxT+4P9VyXzJLO5C3vezbH5suniW+4Ch09gTtblN6Zz2Sqe8PRAc9r2IQEOEA=="],
"@ai-sdk/vue": ["@ai-sdk/vue@4.0.68", "", { "dependencies": { "@ai-sdk/provider-utils": "5.0.27", "ai": "7.0.68", "swrv": "^1.2.0" }, "peerDependencies": { "vue": "^3.3.4" } }, "sha512-L72N4+rJKSEG0jX9TT0qkP13rFp6/q9M4G76XuBH1BAZgmtsnvyOhuwwnxxJOiEpkMp7IyogIw7PEcJNA7+LBw=="],
"@andrewbranch/untar.js": ["@andrewbranch/untar.js@1.0.4", "", {}, "sha512-pVXSwPsLuw8IGLo2Di0EaOfsk+ntVvpkk942J/sHYIkwvtKUakEcPh7HBgZ6tuimgzKSEHgCvO4XgQ05DEbwDw=="],
@ -386,6 +409,8 @@
"@antfu/utils": ["@antfu/utils@9.3.0", "", {}, "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA=="],
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.91.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw=="],
"@apideck/better-ajv-errors": ["@apideck/better-ajv-errors@0.3.7", "", { "dependencies": { "jsonpointer": "^5.0.1", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw=="],
"@arethetypeswrong/cli": ["@arethetypeswrong/cli@0.18.4", "", { "dependencies": { "@arethetypeswrong/core": "0.18.4", "chalk": "^4.1.2", "cli-table3": "^0.6.3", "commander": "^10.0.1", "marked": "^9.1.2", "marked-terminal": "^7.1.0", "semver": "^7.5.4" }, "bin": { "attw": "./dist/index.js" } }, "sha512-kNWo6LTzGAuLYPpJ7Sgo63whSUeeSuKMlYx6IBgzs4ONEG807gW4hSSENvpeCHzO2H2wIzG5EFl0OKBbqGBAyA=="],
@ -396,6 +421,54 @@
"@atlaskit/pragmatic-drag-and-drop-hitbox": ["@atlaskit/pragmatic-drag-and-drop-hitbox@1.2.0", "", { "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.8.0", "@babel/runtime": "^7.0.0" } }, "sha512-eWJvvuHZOC4Yk+Li7QpS+JM2F/I50/3PhMvEcyvcHbXI0FP0kCDD1MiF8Hv7uSOxpk5JNqKoOmK8e1ncOzTgqA=="],
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
"@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="],
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1113.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/credential-provider-node": "^3.972.80", "@aws-sdk/eventstream-handler-node": "^3.972.33", "@aws-sdk/middleware-eventstream": "^3.972.28", "@aws-sdk/middleware-websocket": "^3.972.51", "@aws-sdk/token-providers": "3.1113.0", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/node-http-handler": "^4.9.13", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-yksECclekh+RPC4t5oS4RnLBGArt8g9if5b/2QQJSr8InX4r3LutS3VPHeEFlEKkwJ+qckKkPZCF2Cmv6hT1wA=="],
"@aws-sdk/core": ["@aws-sdk/core@3.977.8", "", { "dependencies": { "@aws-sdk/types": "^3.974.4", "@aws-sdk/xml-builder": "^3.972.39", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.31.1", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.16.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-7+Kcrkvrk9lM/m7jRhHpT4jCdvzGHsuaSRbF8TdzzkY1mRzp/Ogwf9c7H29k4gGhey0BBWhCWr16+t0J61gwmg=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.69", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-AreCFzcB4kH2HF9031Ot0jSJr3KXvRg6e8uDeub20JEVdZU3Bv0sTq1plc7VsT3KiqutlzH7l0j50UcCWHUioA=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.71", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/node-http-handler": "^4.9.13", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-A8ObcqVmDMnk4F9NozZ7JwmUu9Q4xyBJkmyq1C5U+wNM9ht9J7+EuuyabsLWXZnOoTqFaJuYBYTKf5CTipkEjA=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.14", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/credential-provider-env": "^3.972.69", "@aws-sdk/credential-provider-http": "^3.972.71", "@aws-sdk/credential-provider-login": "^3.972.76", "@aws-sdk/credential-provider-process": "^3.972.69", "@aws-sdk/credential-provider-sso": "^3.973.13", "@aws-sdk/credential-provider-web-identity": "^3.972.75", "@aws-sdk/nested-clients": "^3.997.43", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-7c+Wti2LsERNWMfm7ySz3/6RPopFW3Nmn7s63Xpcq6R/tRuY5hpvkHA2xVgi5ukJbvok9l0IDtVEvqTtg+X7dw=="],
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.76", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/nested-clients": "^3.997.43", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-LVixwOnEJfrrfKHeZjBA8pIMTZjNDq8ak8VpcoWUuCJDrSnBNU8POJksULMgvN089P0MXtQYH2Zs627/MK1K0g=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.80", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.69", "@aws-sdk/credential-provider-http": "^3.972.71", "@aws-sdk/credential-provider-ini": "^3.973.14", "@aws-sdk/credential-provider-process": "^3.972.69", "@aws-sdk/credential-provider-sso": "^3.973.13", "@aws-sdk/credential-provider-web-identity": "^3.972.75", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-bE2qh8ww4iClO1jHsBXdOE8FUgzDbdxbyorNjSCoPSkQd51k3jODItuPZfuwcLHZqDXsH+bI4AMHhqtuyR7mSg=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.69", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-9kpTNdZTrcqXTfhxM7fgl9Z68ek3Fu5oe3Yf+A/pJGibEqpgZxz2tSY7SinmyCIU2PJ+ygY4FPoBBnLpocMtrQ=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.13", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/nested-clients": "^3.997.43", "@aws-sdk/token-providers": "3.1111.0", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-Oc81qauMPzUoTnAS2YKpNwY6sY/LUyQTEeaf6yP197WMxkEBQfcKLR1MFpD7+pNTubXnfkH6gwpji+Gc7iyD2Q=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.75", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/nested-clients": "^3.997.43", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-YPN6uoGDgjjjeVFZrcOeCJqmB6zpXoeeNgIjqe+DexJaWqdjVfCCe+VAZwli9Z2h8KhFW8oxkO39emQ1tyz/Mw=="],
"@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.33", "", { "dependencies": { "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-1Dd5WyEE2Kb3HvY44u7Ob16ST2W6iutOqsQ8Y2hUmsL2mAH/STlGS1dS9h3IOE6L7Ld3AR2HzKJ6XeCMOw8Peg=="],
"@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.972.28", "", { "dependencies": { "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-Z1EDXnS01P7H5jVrUx+/dBqV0m7dta7bSxLclkOuDuS93pNNQm0IcT4YLUbuvWKPYNxbI8aTG0p5Br30GSKDgA=="],
"@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.51", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-jdgP3jR5Q96j1jjZ98GGwpGg1CBNFIO2YE+vXg8cg8PvNY4NvgQNYJsqDaRX2PYv5gSUX/+C0D58Fhspj9ELMQ=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.43", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/signature-v4-multi-region": "^3.996.45", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/node-http-handler": "^4.9.13", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-bit+VpqWNyi3wHxFoTsTliNXimCSL2r2OeDTm7ZrG+YsTZ2D7ofDJ6r/t9PVBn80i6/v0X2h9Tgw6QP2MAKfPw=="],
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.45", "", { "dependencies": { "@aws-sdk/types": "^3.974.4", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-bBuyztukzXq6plzFGHAWiQt0QXo+HL8b8lX5cFTzkez/74PtS1c0qPFCIVuHkyoT+miH2qOjAcm1/yoro2ESPA=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1113.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/nested-clients": "^3.997.43", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-E34rqSrdHq0AWi7B8R85GE9UvyzTyBWQLpGbIw5B3BBmJEDbitumoArDCqoeecp1z81EFLoH7KETFOD6CkMedg=="],
"@aws-sdk/types": ["@aws-sdk/types@3.974.4", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-dSFDNG00MEz0/xl5gxL62giLd1iYyJsTxZ1I1DOj6lC+bbgLB4TRsYClJg3b62dhXT1uATzsTNXPnC+33EJV3A=="],
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.10", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g=="],
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.39", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-FTti8DS5MMWXNUWiRwXAJeYS+0GHHiMy0+7XOhcwk63ILHmfS2UFy2z/HNpZCSOJJ3P3dnWY6hfYNW3DF0nXUA=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
"@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
"@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="],
@ -578,6 +651,8 @@
"@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="],
"@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="],
"@braidai/lang": ["@braidai/lang@1.1.2", "", {}, "sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA=="],
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
@ -632,6 +707,14 @@
"@docsearch/sidepanel-js": ["@docsearch/sidepanel-js@4.7.0", "", {}, "sha512-A8r34jCU8kcIk2viECEn2msA28ojUF1BLi/3v5OWWc5G2N3jOuuumBXoeYjfr8dA0UxgFSy5R2bt12dnFJQSyA=="],
"@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.80.10", "", { "dependencies": { "@earendil-works/pi-ai": "^0.80.10", "ignore": "7.0.5", "typebox": "1.1.38", "yaml": "2.9.0" } }, "sha512-nwnOR3SuLYGRFfyQm8ri4Nj5VGVAvAM9GuqQd3u7BUQj0d6hmD2F8w7OHAAjThE3CuySIdM+v8E22QJG6/RfCg=="],
"@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.74.2", "", { "dependencies": { "@anthropic-ai/sdk": "^0.91.1", "@aws-sdk/client-bedrock-runtime": "^3.1030.0", "@google/genai": "^1.40.0", "@mistralai/mistralai": "^2.2.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "openai": "6.26.0", "partial-json": "^0.1.7", "typebox": "^1.1.24" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-ukQBHGDm20k9ZUS2cGjNN9vDJp/48r35xmvgSx3paCaC06r2N/PLuRZoJmwQ1ZM7f8T3072odv9YPWn+77w0LA=="],
"@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.80.10", "", { "dependencies": { "@earendil-works/pi-agent-core": "^0.80.10", "@earendil-works/pi-ai": "^0.80.10", "@earendil-works/pi-tui": "^0.80.10", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", "diff": "8.0.4", "glob": "13.0.6", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.5", "jiti": "2.7.0", "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", "typebox": "1.1.38", "undici": "8.5.0", "yaml": "2.9.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "pi": "dist/cli.js" } }, "sha512-aL4apbupCHiVLSXASXvRzH4Q2vmtfrDa+0s909CJuVu/GgGylbDzr7oyF1mPmip5E+VxYYxKWmph4hV04wUcQg=="],
"@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.80.10", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.5" } }, "sha512-c2JO29PbhKPEQ6fgHQKAl0WhwuFqzWfzspMmP+8B5tpDuP+0mvarRbKKg8gq4b+pQx/QX+6aVS4ko7deoyjQjg=="],
"@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
"@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
@ -798,6 +881,8 @@
"@floating-ui/vue": ["@floating-ui/vue@1.1.11", "", { "dependencies": { "@floating-ui/dom": "^1.7.6", "@floating-ui/utils": "^0.2.11", "vue-demi": ">=0.13.0" } }, "sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw=="],
"@google/genai": ["@google/genai@1.52.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q=="],
"@grpc/grpc-js": ["@grpc/grpc-js@1.9.16", "", { "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ=="],
"@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="],
@ -864,6 +949,16 @@
"@jest/types": ["@jest/types@30.4.1", "", { "dependencies": { "@jest/pattern": "30.4.0", "@jest/schemas": "30.4.1", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ=="],
"@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="],
"@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw=="],
"@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q=="],
"@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q=="],
"@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
@ -934,6 +1029,28 @@
"@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.3", "", {}, "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA=="],
"@mariozechner/clipboard": ["@mariozechner/clipboard@0.3.9", "", { "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.9", "@mariozechner/clipboard-darwin-universal": "0.3.9", "@mariozechner/clipboard-darwin-x64": "0.3.9", "@mariozechner/clipboard-linux-arm64-gnu": "0.3.9", "@mariozechner/clipboard-linux-arm64-musl": "0.3.9", "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-musl": "0.3.9", "@mariozechner/clipboard-win32-arm64-msvc": "0.3.9", "@mariozechner/clipboard-win32-x64-msvc": "0.3.9" } }, "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA=="],
"@mariozechner/clipboard-darwin-arm64": ["@mariozechner/clipboard-darwin-arm64@0.3.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ=="],
"@mariozechner/clipboard-darwin-universal": ["@mariozechner/clipboard-darwin-universal@0.3.9", "", { "os": "darwin" }, "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ=="],
"@mariozechner/clipboard-darwin-x64": ["@mariozechner/clipboard-darwin-x64@0.3.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg=="],
"@mariozechner/clipboard-linux-arm64-gnu": ["@mariozechner/clipboard-linux-arm64-gnu@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw=="],
"@mariozechner/clipboard-linux-arm64-musl": ["@mariozechner/clipboard-linux-arm64-musl@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ=="],
"@mariozechner/clipboard-linux-riscv64-gnu": ["@mariozechner/clipboard-linux-riscv64-gnu@0.3.9", "", { "os": "linux", "cpu": "none" }, "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw=="],
"@mariozechner/clipboard-linux-x64-gnu": ["@mariozechner/clipboard-linux-x64-gnu@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw=="],
"@mariozechner/clipboard-linux-x64-musl": ["@mariozechner/clipboard-linux-x64-musl@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ=="],
"@mariozechner/clipboard-win32-arm64-msvc": ["@mariozechner/clipboard-win32-arm64-msvc@0.3.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ=="],
"@mariozechner/clipboard-win32-x64-msvc": ["@mariozechner/clipboard-win32-x64-msvc@0.3.9", "", { "os": "win32", "cpu": "x64" }, "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA=="],
"@markmend/ast": ["@markmend/ast@1.0.4", "", { "dependencies": { "@markmend/core": "1.0.4", "@types/mdast": "^4.0.4", "mdast-util-from-markdown": "^2.0.3", "mdast-util-frontmatter": "^2.0.1", "mdast-util-gfm": "^3.1.0", "mdast-util-math": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "micromark-extension-cjk-friendly": "^2.0.1", "micromark-extension-cjk-friendly-gfm-strikethrough": "^2.0.1", "micromark-extension-frontmatter": "^2.0.0", "micromark-extension-gfm": "^3.0.0", "micromark-extension-math": "^3.1.0", "micromark-util-types": "^2.0.2" } }, "sha512-t62H5gsFKhK10O9kss3UyKB77ARkoCsksXPqIreDM8mwY7wdvaHZrYTqwkmWEyf9JqCRGX9sIs4hHlnUCxBdIA=="],
"@markmend/core": ["@markmend/core@1.0.4", "", { "dependencies": { "marked": "^18.0.7" } }, "sha512-x6ik8HhJLUmNjuNhfWT+jNahRnqY5zn2a8bwkqBH4NbvCWzG0S7Z2uWyZAa76IAEkgwXJjPC1uz3ZKbQDQ+/cg=="],
@ -942,8 +1059,20 @@
"@mermaid-js/parser": ["@mermaid-js/parser@1.2.0", "", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA=="],
"@mistralai/mistralai": ["@mistralai/mistralai@2.6.3", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-trace-otlp-http": "^0.220.0", "@opentelemetry/resources": "^2.9.0", "@opentelemetry/sdk-trace-base": "^2.9.0" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-http", "@opentelemetry/resources", "@opentelemetry/sdk-trace-base"] }, "sha512-4nOSDZTJXnmfsDtPFZEX4tAaJwIP2xyZwka9XK7ueNmOX0DuVhu2Ph2uWEeBMaNjpXbnVgmXLZ2Q58vFd8gsgg=="],
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
"@modelcontextprotocol/client": ["@modelcontextprotocol/client@2.0.0-beta.5", "", { "dependencies": { "@modelcontextprotocol/core": "2.0.0-beta.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "jose": "^6.1.3", "pkce-challenge": "^5.0.0", "zod": "^4.2.0" } }, "sha512-YuuNm5f2TMoFQRje1UqVP8TJRjijCXMz4ckvoVpx1cUXuBEmykWQ2d8R536pek6UKcXT41T5nWc4qR1JFIbEmg=="],
"@modelcontextprotocol/core": ["@modelcontextprotocol/core@2.0.0-beta.5", "", { "dependencies": { "zod": "^4.2.0" } }, "sha512-HKbY9XTbsDy1Y6r2I55TGE3JEapM0vg96e1MUmBIF9LGjos5gjhcIrTz1yvBPLg2aFKHjwhUAQfRdrCEnPxNew=="],
"@modelcontextprotocol/ext-apps": ["@modelcontextprotocol/ext-apps@1.7.5", "", { "dependencies": { "@standard-schema/spec": "^1.1.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.29.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-TjPH2S2y5UEGKhmI6+XGFuqfqOV4ppe1x6DA3txnUaEWkgtA4G5vo14jGKFZmegdkZ1H4QMLyujLvoU1BEdnAg=="],
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.30.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA=="],
"@mongodb-js/zstd": ["@mongodb-js/zstd@7.0.0", "", { "dependencies": { "node-addon-api": "^8.5.0", "prebuild-install": "^7.1.3" } }, "sha512-mQ2s0pYYiav+tzCDR05Zptem8Ey2v8s11lri5RKGhTtL4COVCvVCk5vtyRYNT+9L8qSfyOqqefF9UtnW8mC5jA=="],
"@multiformats/dns": ["@multiformats/dns@1.0.15", "", { "dependencies": { "@dnsquery/dns-packet": "^6.1.1", "@libp2p/interface": "^3.1.0", "hashlru": "^2.3.0", "p-queue": "^9.0.0", "progress-events": "^1.0.0", "uint8arrays": "^6.1.1" } }, "sha512-W0zAMABtAn+3chgFcPGvllKND7M6GblMAAFcJQTy+iMmGiyFErZYPzAh2b50Y3SFf340iFV7+ckVgZkGfUyxzA=="],
"@multiformats/mafmt": ["@multiformats/mafmt@12.1.6", "", { "dependencies": { "@multiformats/multiaddr": "^12.0.0" } }, "sha512-tlJRfL21X+AKn9b5i5VnaTD6bNttpSpcqwKVmDmSHLwxoz97fAHaepqFOk/l1fIu94nImIXneNbhsJx/RQNIww=="],
@ -990,6 +1119,8 @@
"@open-pencil/fig": ["@open-pencil/fig@workspace:packages/fig"],
"@open-pencil/harness": ["@open-pencil/harness@workspace:packages/harness"],
"@open-pencil/kiwi": ["@open-pencil/kiwi@workspace:packages/kiwi"],
"@open-pencil/mcp": ["@open-pencil/mcp@workspace:packages/mcp"],
@ -1002,6 +1133,10 @@
"@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@3.0.0", "", { "peerDependencies": { "ai": "^7.0.0", "zod": "^3.25.76 || ^4.1.8" } }, "sha512-m9XTSWoODH2RM5OsZpaGiN7QRR8cdP5paBWq699Tu3JVmGPBKT8xF8XwV0ZBVVsjikD/JgWfak4VSsTR4wAVbg=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="],
"@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.137.0", "", { "os": "android", "cpu": "arm" }, "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA=="],
"@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.137.0", "", { "os": "android", "cpu": "arm64" }, "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw=="],
@ -1172,6 +1307,8 @@
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@pkgr/core": ["@pkgr/core@0.1.2", "", {}, "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ=="],
"@playwright/test": ["@playwright/test@1.62.1", "", { "dependencies": { "playwright": "1.62.1" }, "bin": { "playwright": "cli.js" } }, "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ=="],
"@promptbook/utils": ["@promptbook/utils@0.69.5", "", { "dependencies": { "spacetrim": "0.11.59" } }, "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ=="],
@ -1196,7 +1333,7 @@
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="],
"@publint/pack": ["@publint/pack@0.1.6", "", { "dependencies": { "tinyexec": "^1.2.4" } }, "sha512-3uVNyGcVplhPZSLVyeIpL7+cIRn1YCSNHLG/rUIlBQMVH8YuN9++YF+5+UDIIO9RW98dujiUoTltO7RDB5bFJA=="],
"@publint/pack": ["@publint/pack@0.1.7", "", { "dependencies": { "tinyexec": "^1.3.0" } }, "sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g=="],
"@puppeteer/browsers": ["@puppeteer/browsers@2.13.2", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.4", "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw=="],
@ -1318,6 +1455,8 @@
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
"@silvia-odwyer/photon-node": ["@silvia-odwyer/photon-node@0.3.4", "", {}, "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA=="],
"@sinclair/typebox": ["@sinclair/typebox@0.34.52", "", {}, "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw=="],
"@sindresorhus/fnv1a": ["@sindresorhus/fnv1a@3.1.0", "", {}, "sha512-KV321z5m/0nuAg83W1dPLy85HpHDk7Sdi4fJbwvacWsEhAh+rZUW4ZfGcXmUIvjZg4ss2bcwNlRhJ7GBEUG08w=="],
@ -1326,6 +1465,24 @@
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="],
"@smithy/core": ["@smithy/core@3.33.2", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-CUGXpnPkVdjUCbix+83sWLW9VFgQOm44MDOx/ihITJMAnOZKvL8YYIc7DR9pP/tZ8CIRvMiON/TucvygqbHO3w=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg=="],
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.7.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw=="],
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.11.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-avwAh9HM3h2lcfjvP3zYIZGf+XVgLQ91wOJ2qoFbNpW1UZeZb33aGlhTZvtkANHfcGhJroRY64525OjfgOg30g=="],
"@smithy/signature-v4": ["@smithy/signature-v4@5.7.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-P7Ki6px6OOrxVtx8K7nLmyx4SlXUW/uTKDdMG44UHefmPGSRMBKe2v+TM59WdLcpUIrBrnuCsIqiM2MbsZjmhw=="],
"@smithy/types": ["@smithy/types@4.17.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA=="],
"@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@storybook/addon-a11y": ["@storybook/addon-a11y@10.5.9", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.5.9" } }, "sha512-MvXkoJIVRcZrqhWi+wynYAmFkvTWm5aHdJfYohhgPeEJVDRF91zejtOsN7zPRE/lQvBDY+IiomKxeRN3UhWNVw=="],
@ -1456,6 +1613,10 @@
"@testing-library/user-event": ["@testing-library/user-event@14.6.5", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-FhqjldLTpteueBaKflhNFlMT3+PM0O5fiBUivht6b9CZ1eesJyy7+g3Jr7XwJzt/Hip3ZG5hWwK1MX1FuDiE4w=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
"@trickfilm400/rollup-plugin-off-main-thread": ["@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1", "", { "dependencies": { "ejs": "^3.1.10", "json5": "^2.2.3", "magic-string": "^0.30.21", "string.prototype.matchall": "^4.0.12" } }, "sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q=="],
@ -1818,7 +1979,7 @@
"@yuku-toolchain/types": ["@yuku-toolchain/types@0.8.7", "", {}, "sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA=="],
"@zip.js/zip.js": ["@zip.js/zip.js@2.8.52", "", {}, "sha512-ZVXpmXRROrjz/0Glm9homDpSUAiavgpdnR+0tGlTtvtIsqs/d7f7qqbkOxrRKfUaNML68YXb4reIA3vZjul3uQ=="],
"@zip.js/zip.js": ["@zip.js/zip.js@2.8.53", "", {}, "sha512-Vr55wt21XKD0kZFCQEZOtbViUMUK/U0MH2MDsHxu6FSyYk3Wu+afh+f9xD01x0kerDNfHmUVrNpcgYAgtnRtjA=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
@ -1832,7 +1993,7 @@
"agentfmt": ["agentfmt@0.1.3", "", { "dependencies": { "picocolors": "^1.1.1" } }, "sha512-BVSZr8ovigxjYewuBx42Ep4RJj00HURaQYUQ+id41sgb7r0PMUSEWsnD/+h96xj6t6v2WVyPuGPH0K6RtjftKQ=="],
"ai": ["ai@7.0.67", "", { "dependencies": { "@ai-sdk/gateway": "4.0.53", "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-83Y/OFx7rylrrgzCsaDPlGeL3QC8qrFQ51LBs6MXm2JVxQLLkrvKmPa1/hOzgyCHM8dsB/2rE1a6OGPNRpmTLQ=="],
"ai": ["ai@7.0.68", "", { "dependencies": { "@ai-sdk/gateway": "4.0.54", "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9QuZOT77wzoxxUC0NcueXhCo3HUHA/1pApIJ9VRyE+9/K+3Innkq4kVhrd9aEnwIviJz2Nga063m+UTsPSdOyw=="],
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
@ -1936,6 +2097,8 @@
"beautiful-mermaid": ["beautiful-mermaid@1.1.3", "", { "dependencies": { "elkjs": "^0.11.0", "entities": "^7.0.1" } }, "sha512-TItrtrAyHp1vwFfFVYauWGrquouk/6SS21Aq3RsxindSYZODcN4xYrPZD6BiZRU+o5mKJzDPz9MUSMvELdylyg=="],
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
"bind-event-listener": ["bind-event-listener@3.0.0", "", {}, "sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q=="],
@ -1950,6 +2113,8 @@
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
"brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
@ -1964,6 +2129,8 @@
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
@ -2018,6 +2185,8 @@
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
@ -2208,10 +2377,14 @@
"decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
"dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="],
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"deepmerge-ts": ["deepmerge-ts@7.1.6", "", {}, "sha512-gQhL1ksGBLQbHeAo47YU6cs2ahd3Pv+8PFYoWogNZbQnNwQyOh9Ad5kbeHFiWwbKdeWQJ5Z7Y7mwb9ew2hXBLw=="],
@ -2292,6 +2465,8 @@
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
"edge-paths": ["edge-paths@3.0.5", "", { "dependencies": { "@types/which": "^2.0.1", "which": "^2.0.2" } }, "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg=="],
"edgedriver": ["edgedriver@6.3.0", "", { "dependencies": { "@wdio/logger": "^9.18.0", "@zip.js/zip.js": "^2.8.11", "decamelize": "^6.0.1", "edge-paths": "^3.0.5", "fast-xml-parser": "^5.3.3", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "which": "^6.0.0" }, "bin": { "edgedriver": "bin/edgedriver.js" } }, "sha512-ggEQL+oEyIcM4nP2QC3AtCQ04o4kDNefRM3hja0odvlPSnsaxiruMxEZ93v3gDCKWYW6BXUr51PPradb+3nffw=="],
@ -2302,7 +2477,7 @@
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.409", "", {}, "sha512-ChI4N44d0B4A6C8prnNjMOaGgE59fUyEVYcRYm2XEXIjMbbvF5i9UL1cblDbpGqiU0uS8FE8UcKxqZqTXdmzbQ=="],
"electron-to-chromium": ["electron-to-chromium@1.5.411", "", {}, "sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg=="],
"elkjs": ["elkjs@0.11.1", "", {}, "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg=="],
@ -2388,6 +2563,8 @@
"exit-hook": ["exit-hook@4.0.0", "", {}, "sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ=="],
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
"expect": ["expect@30.4.1", "", { "dependencies": { "@jest/expect-utils": "30.4.1", "@jest/get-type": "30.1.0", "jest-matcher-utils": "30.4.1", "jest-message-util": "30.4.1", "jest-mock": "30.4.1", "jest-util": "30.4.1" } }, "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA=="],
"expect-webdriverio": ["expect-webdriverio@5.7.0", "", { "dependencies": { "@vitest/snapshot": "^4.1.7", "deep-eql": "^5.0.2", "expect": "^30.4.1", "jest-matcher-utils": "^30.4.1" }, "peerDependencies": { "@wdio/globals": "^9.0.0", "@wdio/logger": "^9.0.0", "webdriverio": "^9.0.0" } }, "sha512-jLOTrJoPBC3Wtd83ryHMbRcEajMGtAnn6OWtSnoJoDLt16nHvxVdjcI4Bc0n+1KAOr8DvQtlJ55f0M48kJtEpw=="],
@ -2440,10 +2617,14 @@
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
"fflate": ["fflate@0.8.3", "", {}, "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="],
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
"file-type": ["file-type@21.3.4", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g=="],
"filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
@ -2474,6 +2655,8 @@
"formatly": ["formatly@0.3.0", "", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="],
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
"framer-motion": ["framer-motion@13.1.0", "", { "dependencies": { "motion-dom": "^13.0.0", "motion-utils": "^13.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-QSZrF0Id3QGuHJ+OL+9PSY9pk86C8ERFalwAGSchzTm65+ZoGH/RM26lmEARLljcHj2lqhv0jZOOks+EI3COOw=="],
@ -2482,6 +2665,8 @@
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
"fs-extra": ["fs-extra@11.4.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
@ -2496,6 +2681,10 @@
"fzstd": ["fzstd@0.1.1", "", {}, "sha512-dkuVSOKKwh3eas5VkJy1AW1vFpet8TA/fGmVA5krThl8YcOVE/8ZIoEA1+U1vEn5ckxxhLirSdY837azmbaNHA=="],
"gaxios": ["gaxios@7.3.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ=="],
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
"geckodriver": ["geckodriver@6.1.1", "", { "dependencies": { "@wdio/logger": "^9.18.0", "@zip.js/zip.js": "^2.8.11", "decamelize": "^6.0.1", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "modern-tar": "^0.7.3" }, "bin": { "geckodriver": "bin/geckodriver.js" } }, "sha512-/AcCyc9o9o6hUbudaSJM2iOtXbxSLqQPOb4GrPvEN40cjraUeaX/j5kH3mSgwiroyMn7qzx2wM61AQ2XY8j3sA=="],
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
@ -2528,6 +2717,8 @@
"get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="],
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
"glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
@ -2538,6 +2729,10 @@
"gonzales-pe": ["gonzales-pe@4.3.0", "", { "dependencies": { "minimist": "^1.2.5" }, "bin": { "gonzales": "bin/gonzales.js" } }, "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ=="],
"google-auth-library": ["google-auth-library@10.9.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw=="],
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
@ -2584,7 +2779,7 @@
"hookable": ["hookable@6.1.1", "", {}, "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ=="],
"hosted-git-info": ["hosted-git-info@8.1.0", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw=="],
"hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="],
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
@ -2614,7 +2809,7 @@
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"ignore": ["ignore@7.0.6", "", {}, "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw=="],
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"image-size": ["image-size@1.2.1", "", { "dependencies": { "queue": "6.0.2" }, "bin": { "image-size": "bin/image-size.js" } }, "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw=="],
@ -2634,6 +2829,8 @@
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"inquirer": ["inquirer@12.11.1", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", "@inquirer/prompts": "^7.10.1", "@inquirer/type": "^3.0.10", "mute-stream": "^2.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw=="],
"interface-datastore": ["interface-datastore@8.3.2", "", { "dependencies": { "interface-store": "^6.0.0", "uint8arrays": "^5.1.0" } }, "sha512-R3NLts7pRbJKc3qFdQf+u40hK8XWc0w4Qkx3OFEstC80VoaDUABY/dXA2EJPhtNC+bsrf41Ehvqb6+pnIclyRA=="],
@ -2830,10 +3027,14 @@
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
@ -2852,6 +3053,12 @@
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
"just-bash": ["just-bash@2.14.5", "", { "dependencies": { "diff": "^8.0.2", "fast-xml-parser": "^5.7.3", "file-type": "^21.2.0", "ini": "^6.0.0", "minimatch": "^10.1.1", "modern-tar": "^0.7.3", "papaparse": "^5.5.3", "quickjs-emscripten": "^0.32.0", "re2js": "^1.2.1", "seek-bzip": "^2.0.0", "smol-toml": "^1.6.0", "sprintf-js": "^1.1.3", "sql.js": "^1.13.0", "turndown": "^7.2.2", "yaml": "^2.8.2" }, "optionalDependencies": { "@mongodb-js/zstd": "^7.0.0", "node-liblzma": "^2.0.3" }, "bin": { "just-bash": "dist/bin/just-bash.js", "just-bash-shell": "dist/bin/shell/shell.js" } }, "sha512-MCBGnRlDeZ/MM7mcw+ZuSGFMBsggajrmKz6e/hrOAN7syvVZkjiY+Vh2wyCwN/CdcnAX5SxbiQB51n5nrQuX+g=="],
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
"katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="],
"khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
@ -3080,6 +3287,8 @@
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
"minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="],
@ -3094,6 +3303,8 @@
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
"mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
"mocha": ["mocha@10.8.2", "", { "dependencies": { "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", "chokidar": "^3.5.3", "debug": "^4.3.5", "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^8.1.0", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^5.1.6", "ms": "^2.1.3", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", "yargs": "^16.2.0", "yargs-parser": "^20.2.9", "yargs-unparser": "^2.0.0" }, "bin": { "mocha": "bin/mocha.js", "_mocha": "bin/_mocha" } }, "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg=="],
@ -3134,14 +3345,28 @@
"nanostores": ["nanostores@1.5.1", "", {}, "sha512-DNIX+HyFpo14fKGe0NsX9/aPzdKGiSZwX5xEMpDwQrDdo2iTiUYunOCCQLYwJrziKVe8ZOtVvcv0dEVI8lMx2g=="],
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"netmask": ["netmask@2.1.1", "", {}, "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA=="],
"node-abi": ["node-abi@3.94.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g=="],
"node-addon-api": ["node-addon-api@8.9.2", "", {}, "sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-emoji": ["node-emoji@2.2.0", "", { "dependencies": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", "emojilib": "^2.4.0", "skin-tone": "^2.0.0" } }, "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw=="],
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
"node-liblzma": ["node-liblzma@2.2.0", "", { "dependencies": { "node-addon-api": "^8.5.0", "node-gyp-build": "^4.8.4" }, "bin": { "nxz": "lib/cli/nxz.js" } }, "sha512-s0KzNOWwOJJgPG6wxg6cKohnAl9Wk/oW1KrQaVzJBjQwVcUGPQCzpR46Ximygjqj/3KhOrtJXnYMp/xYAXp75g=="],
"node-releases": ["node-releases@2.0.53", "", {}, "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ=="],
"node-sarif-builder": ["node-sarif-builder@4.1.0", "", { "dependencies": { "@types/sarif": "^2.1.7", "fs-extra": "^11.1.1" } }, "sha512-IWqZF6u0EI/07HTBm+zZ+MgXgWl09dnSJRGaDCPBSlOqilDcx6pj3Mpb3HvPN8V2Gr+ISw7ZrMsL7STWs1F++w=="],
@ -3186,6 +3411,8 @@
"open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"openai": ["openai@6.26.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA=="],
"opentype.js": ["opentype.js@2.0.0", "", { "bin": { "ot": "bin/ot" } }, "sha512-kCyjv6xdDY1W/jLWZ/L3QhhTlKUqDZMQ5+Jdlw12b3dXkKNpYBqqlMMj0YDQPShWFTMwgZI1hG14kN3XUDSg/A=="],
"own-keys": ["own-keys@1.0.2", "", { "dependencies": { "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg=="],
@ -3224,6 +3451,8 @@
"pako": ["pako@2.2.0", "", {}, "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w=="],
"papaparse": ["papaparse@5.6.0", "", {}, "sha512-N2vuNQAYGK1/4vs6HJX86+VYU6OkiSTgdJz3JQfTk1y51cFCO/U8gnaeTF4iNE4r57Tt0sV47dUua1/19pxO6Q=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
@ -3238,6 +3467,8 @@
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
"partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="],
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
"path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
@ -3268,6 +3499,8 @@
"performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="],
"pi-mcp-adapter": ["pi-mcp-adapter@2.12.1", "", { "dependencies": { "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.2.2", "@modelcontextprotocol/sdk": "^1.29.0", "cross-spawn": "^7.0.6", "open": "^10.2.0", "recheck": "^4.5.0", "smol-toml": "^1.6.1", "zod": "^3.25.0 || ^4.0.0" }, "peerDependencies": { "@earendil-works/pi-ai": "*", "@earendil-works/pi-tui": "*", "typebox": "*" }, "optionalPeers": ["@earendil-works/pi-ai", "@earendil-works/pi-tui", "typebox"], "bin": { "pi-mcp-adapter": "cli.js" } }, "sha512-lB0ANetzF0ld+l8ibgF6KcT7bj1LKvhgKig9LHMvr1DEBark6xZ36Jeb+JLib49/M03sBaCH5s3Jcmcy/OO5wg=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
@ -3298,6 +3531,8 @@
"pptxgenjs": ["pptxgenjs@4.0.1", "", { "dependencies": { "@types/node": "^22.8.1", "https": "^1.0.0", "image-size": "^1.2.1", "jszip": "^3.10.1" } }, "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A=="],
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
"precinct": ["precinct@12.3.2", "", { "dependencies": { "@dependents/detective-less": "^5.0.3", "commander": "^12.1.0", "detective-amd": "^6.1.0", "detective-cjs": "^6.1.1", "detective-es6": "^5.0.2", "detective-postcss": "^8.0.3", "detective-sass": "^6.0.2", "detective-scss": "^5.0.2", "detective-stylus": "^5.0.1", "detective-typescript": "^14.1.2", "detective-vue2": "^2.3.0", "module-definition": "^6.0.2", "node-source-walk": "^7.0.2", "postcss": "^8.5.14", "typescript": "^5.9.3" }, "bin": { "precinct": "bin/cli.js" } }, "sha512-JbJevI1K80z8e/WIyDt/4vUN/4qcfBSKKqOjJA4mosPPPb7zODKRJQV7YN7apVWN3k58nZYm/vEsLgEGYmnxwg=="],
"pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="],
@ -3318,6 +3553,8 @@
"promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
"property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="],
"protobufjs": ["protobufjs@7.5.5", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg=="],
@ -3374,6 +3611,10 @@
"quick-lru": ["quick-lru@7.3.0", "", {}, "sha512-k9lSsjl36EJdK7I06v7APZCbyGT2vMTsYSRX1Q2nbYmnkBqgUhRkAuzH08Ciotteu/PLJmIF2+tti7o3C/ts2g=="],
"quickjs-emscripten": ["quickjs-emscripten@0.32.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-release-sync": "0.32.0", "quickjs-emscripten-core": "0.32.0" } }, "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA=="],
"quickjs-emscripten-core": ["quickjs-emscripten-core@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg=="],
"quote-unquote": ["quote-unquote@1.0.0", "", {}, "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg=="],
"race-event": ["race-event@1.6.1", "", { "dependencies": { "abort-error": "^1.0.1" } }, "sha512-vi7WH5g5KoTFpu2mme/HqZiWH14XSOtg5rfp6raBskBHl7wnmy3F/biAIyY5MsK+BHWhoPhxtZ1Y2R7OHHaWyQ=="],
@ -3390,7 +3631,9 @@
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
"re2js": ["re2js@2.8.6", "", {}, "sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg=="],
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
"re2js": ["re2js@1.4.0", "", {}, "sha512-KTOIcZTSOpOxbu3i0+T6mFQ6tkxXKlTxfcMFs1trQbsMnG84qNq+DjXr8Afu+FEFjvF1NNlldpC7roPyazFI8g=="],
"react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="],
@ -3414,6 +3657,18 @@
"recast": ["recast@0.23.21", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw=="],
"recheck": ["recheck@4.5.0", "", { "dependencies": { "synckit": "0.9.2" }, "optionalDependencies": { "recheck-jar": "4.5.0", "recheck-linux-x64": "4.5.0", "recheck-macos-arm64": "4.5.0", "recheck-macos-x64": "4.5.0", "recheck-windows-x64": "4.5.0" } }, "sha512-kPnbOV6Zfx9a25AZ++28fI1q78L/UVRQmmuazwVRPfiiqpMs+WbOU69Shx820XgfKWfak0JH75PUvZMFtRGSsw=="],
"recheck-jar": ["recheck-jar@4.5.0", "", {}, "sha512-Ad7oCQmY8cQLzd3QVNXjzZ+S6MbImGhR4AaW2yiGzteOfMV45522rt6nSzFyt8p3mCEaMcm/4MoZrMSxUcCbrA=="],
"recheck-linux-x64": ["recheck-linux-x64@4.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-52kXsR/v+IbGIKYYFZfSZcgse/Ci9IA2HnuzrtvRRcfODkcUGe4n72ESQ8nOPwrdHFg9i4j9/YyPh1HWWgpJ6A=="],
"recheck-macos-arm64": ["recheck-macos-arm64@4.5.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-qIyK3dRuLkORQvv0b59fZZRXweSmjjWaoA4K8Kgifz0anMBH4pqsDV6plBlgjcRmW9yC12wErIRzifREaKnk2w=="],
"recheck-macos-x64": ["recheck-macos-x64@4.5.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1wp/eiLxcjC/Ex4wurlrS/LGzt8IiF4TiK5sEjldu4HVAKdNCnnmsS9a5vFpfcikDz4ZuZlLlTi1VbQTxHlwZg=="],
"recheck-windows-x64": ["recheck-windows-x64@4.5.0", "", { "os": "win32", "cpu": "x64" }, "sha512-ekBKwAp0oKkMULn5zgmHEYLwSJfkfb95AbTtbDkQazNkqYw9PRD/mVyFUR6Ff2IeRyZI0gxy+N2AKBISWydhug=="],
"recursive-readdir": ["recursive-readdir@2.2.3", "", { "dependencies": { "minimatch": "^3.0.5" } }, "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA=="],
"redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="],
@ -3468,7 +3723,7 @@
"ret": ["ret@0.5.0", "", {}, "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw=="],
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
@ -3522,6 +3777,8 @@
"section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
"seek-bzip": ["seek-bzip@2.0.0", "", { "dependencies": { "commander": "^6.0.0" }, "bin": { "seek-bunzip": "bin/seek-bunzip", "seek-table": "bin/seek-bzip-table" } }, "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg=="],
"semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
@ -3576,6 +3833,10 @@
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"skin-tone": ["skin-tone@2.0.0", "", { "dependencies": { "unicode-emoji-modifier-base": "^1.0.0" } }, "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA=="],
@ -3616,7 +3877,9 @@
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
"sql.js": ["sql.js@1.14.2", "", {}, "sha512-3ZGPovObMFrdw79zrUHbfdE/DLIsy8jdNdssmMSQuRAymedU6q84asPt0kgiqrdMYlPegDItiIMfmIXzZnYFcw=="],
"stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
@ -3668,6 +3931,8 @@
"strnum": ["strnum@2.4.2", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw=="],
"strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="],
"style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="],
"stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="],
@ -3690,6 +3955,8 @@
"swrv": ["swrv@1.2.0", "", { "peerDependencies": { "vue": ">=3.2.26 < 4" } }, "sha512-lH/g4UcNyj+7lzK4eRGT4C68Q4EhQ6JtM9otPRIASfhhzfLWtbZPHcMuhuba7S9YVYuxkMUGImwMyGpfbkH07A=="],
"synckit": ["synckit@0.9.2", "", { "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" } }, "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw=="],
"tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
"tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
@ -3742,6 +4009,8 @@
"token-stream": ["token-stream@1.0.0", "", {}, "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg=="],
"token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
"tokenx": ["tokenx@1.6.0", "", {}, "sha512-CKTjk345ajvBAUp5xUI9a5KKN0zU0lBueVHQbCskH1Hp6WkUKsPW2qGCYNs0pxNyfzxfo+IIjdt2W4sMbw/qBw=="],
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
@ -3754,6 +4023,8 @@
"trystero": ["trystero@0.22.0", "", { "dependencies": { "@noble/secp256k1": "^3.0.0", "@supabase/supabase-js": "^2.75.0", "@waku/sdk": "^0.0.35", "firebase": "^12.4.0", "mqtt": "^5.14.1" } }, "sha512-VscO7kaTFWNLmuxu1Au1kIxX6FzkVeXcL3+mhb9MaCSz8fm4T5MFWkdfDOujMtNK4iztupQ5AGEqGniP/I8Gvw=="],
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
"ts-dedent": ["ts-dedent@2.3.0", "", {}, "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg=="],
@ -3770,6 +4041,10 @@
"tsx": ["tsx@4.23.12", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
"turndown": ["turndown@7.2.4", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ=="],
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
"twirlwind": ["twirlwind@0.3.0", "", {}, "sha512-+pFwkOQG1euPgEmuR3lTQXwkr/2aK91ycf4p7dV11qYM1LbEnYUb1c4zewpimV52sSj7AfgBVmsoHp30ZRygLA=="],
@ -3786,6 +4061,8 @@
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
"typebox": ["typebox@1.3.16", "", {}, "sha512-Jac8dgnin+g2p1w1v9sk92Wvp49ZqsmSgNhrXDK+RqrovsjiORMZvYZ8t8id9b9XQp6LMfaAwnIRSHGsM+3FMw=="],
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
"typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
@ -3804,6 +4081,8 @@
"uint8-varint": ["uint8-varint@2.0.5", "", { "dependencies": { "uint8arraylist": "^2.0.0", "uint8arrays": "^5.0.0" } }, "sha512-jeFLbL/x30wBRnWjKE1qVBXeumG46r7XmYkpis955lTQ+blccGKFrOsSMHlxePwYB1pI7L8YPHz1t4jLxEs3nA=="],
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
"uint8arraylist": ["uint8arraylist@2.4.9", "", { "dependencies": { "uint8arrays": "^5.0.1" } }, "sha512-KxWjyEFzchzik3aoQlK66oaoxIReoMo5bQRm1fcjBUZvE8xv/tyR3CTKhjh6K/faV8VaF6hd5pjr45CzbwuwkA=="],
"uint8arrays": ["uint8arrays@5.1.1", "", { "dependencies": { "multiformats": "^13.0.0" } }, "sha512-9muQwa4wZG4dKi9gMAIBtnk2Pw87SRpvWTH6lOGm19V2Uqxr4uomUf2PGqPnWc+qs06sN8owUU4jfcoWOcfwVQ=="],
@ -3942,6 +4221,8 @@
"weald": ["weald@1.1.3", "", { "dependencies": { "ms": "^4.0.0-nightly.202508271359", "supports-color": "^10.0.0" } }, "sha512-vMWtNbYuPb58NeG2+0sKA0Een4VMDwzf+3oHqh68buWRSOMUBlUeRb11LhV28czV+DUpJHRykifijZDuS9bInA=="],
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
"web-tree-sitter": ["web-tree-sitter@0.26.12", "", {}, "sha512-fvqTNZQBGUgUgfP0mHw+iHf9Yf6bRQrp0A3pSf2v/hSKxkT1beCoIWoLVmlPL7O6dmySfSb/t1aJoJvrgTRStw=="],
"web-vitals": ["web-vitals@4.2.4", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="],
@ -4076,8 +4357,12 @@
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@ai-sdk/harness/ai": ["ai@7.0.67", "", { "dependencies": { "@ai-sdk/gateway": "4.0.53", "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-83Y/OFx7rylrrgzCsaDPlGeL3QC8qrFQ51LBs6MXm2JVxQLLkrvKmPa1/hOzgyCHM8dsB/2rE1a6OGPNRpmTLQ=="],
"@arethetypeswrong/core/typescript": ["typescript@5.6.1-rc", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ=="],
"@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1111.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.8", "@aws-sdk/nested-clients": "^3.997.43", "@aws-sdk/types": "^3.974.4", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-JfljgoVtl+s3Qy21n9a7Z48uCQaOXcN74KJ3TEQfPoB293GrXFSt6HSQJF1sTZ8c/5QedEvd3NjJQMO4u9qa5A=="],
"@babel/core/@babel/generator": ["@babel/generator@7.29.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg=="],
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
@ -4098,14 +4383,38 @@
"@babel/traverse/@babel/generator": ["@babel/generator@7.29.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg=="],
"@earendil-works/pi-agent-core/@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.80.10", "", { "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@google/genai": "1.52.0", "@mistralai/mistralai": "2.2.6", "@opentelemetry/api": "1.9.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", "typebox": "1.1.38" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-Moe/H8c87yacDGK9dPbWphZNjVsrb3nTrIHycOQJAkFEnY9PYxOOd74+ny44kATfPU9Dm7aTHefar3pZF+UKUA=="],
"@earendil-works/pi-agent-core/typebox": ["typebox@1.1.38", "", {}, "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA=="],
"@earendil-works/pi-coding-agent/@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.80.10", "", { "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@google/genai": "1.52.0", "@mistralai/mistralai": "2.2.6", "@opentelemetry/api": "1.9.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", "typebox": "1.1.38" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-Moe/H8c87yacDGK9dPbWphZNjVsrb3nTrIHycOQJAkFEnY9PYxOOd74+ny44kATfPU9Dm7aTHefar3pZF+UKUA=="],
"@earendil-works/pi-coding-agent/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
"@earendil-works/pi-coding-agent/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
"@earendil-works/pi-coding-agent/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
"@earendil-works/pi-coding-agent/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
"@earendil-works/pi-coding-agent/typebox": ["typebox@1.1.38", "", {}, "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA=="],
"@earendil-works/pi-coding-agent/undici": ["undici@8.5.0", "", {}, "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg=="],
"@earendil-works/pi-tui/marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="],
"@feature-sliced/filesystem/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"@firebase/app/idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="],
"@firebase/firestore/re2js": ["re2js@2.8.6", "", {}, "sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg=="],
"@firebase/installations/idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="],
"@firebase/messaging/idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="],
"@google/genai/p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
"@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@ -4138,7 +4447,7 @@
"@rollup/plugin-terser/serialize-javascript": ["serialize-javascript@7.1.0", "", {}, "sha512-RNEqWOyhhUQYN9V1GfHwu9AR/g+NTciH6Z5u3/no6X3/w+04J2lVDL+svFQVXgXrEGBMG2puMVN3gq2SNGuTGw=="],
"@shikijs/vitepress-twoslash/magic-string": ["magic-string@1.2.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-ptco+HFxTLgjafSLim2LojBSwfg5feBjd+SqyiwdGkzC38UPdZy3zgrHMI2CoTf5fJL38tbHMYWVzIH8BxGqJw=="],
"@shikijs/vitepress-twoslash/magic-string": ["magic-string@1.2.1", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vCfXkt3lIJha02CjPT1igeysyHVfCsEpIeD20O+X9aJ2hML3/kKx8E9Iv1FB+aMSAlDOEAtpRWzuooQCrYwdUg=="],
"@storybook/vue3-vite/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
@ -4270,6 +4579,8 @@
"easy-table/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ecdsa-sig-formatter/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"edgedriver/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="],
"encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
@ -4290,8 +4601,6 @@
"gray-matter/js-yaml": ["js-yaml@3.15.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag=="],
"hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
"is-expression/acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="],
@ -4312,6 +4621,10 @@
"jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"jwa/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"jws/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
@ -4356,10 +4669,16 @@
"mqtt/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"node-fetch/data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
"normalize-package-data/hosted-git-info": ["hosted-git-info@8.1.0", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="],
"p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
"parse5-parser-stream/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
@ -4370,14 +4689,22 @@
"pptxgenjs/@types/node": ["@types/node@22.20.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q=="],
"prebuild-install/tar-fs": ["tar-fs@2.1.5", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw=="],
"precinct/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
"precinct/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
"proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
"rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
"read-pkg/normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="],
"read-pkg/parse-json": ["parse-json@7.1.1", "", { "dependencies": { "@babel/code-frame": "^7.21.4", "error-ex": "^1.3.2", "json-parse-even-better-errors": "^3.0.0", "lines-and-columns": "^2.0.3", "type-fest": "^3.8.0" } }, "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw=="],
@ -4402,6 +4729,8 @@
"safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"seek-bzip/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="],
"serialize-error/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
@ -4434,6 +4763,8 @@
"tsx/esbuild": ["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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="],
"tunnel-agent/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="],
"unplugin-raw/unplugin": ["unplugin@3.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.4", "webpack-virtual-modules": "^0.6.2" }, "peerDependencies": { "@farmfe/core": "*", "@rspack/core": "*", "bun-types-no-globals": "*", "esbuild": "*", "rolldown": "*", "rollup": "*", "unloader": "*", "vite": "*", "webpack": "*" }, "optionalPeers": ["@farmfe/core", "@rspack/core", "bun-types-no-globals", "esbuild", "rolldown", "rollup", "unloader", "vite", "webpack"] }, "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg=="],
@ -4466,8 +4797,6 @@
"webdriverio/@types/node": ["@types/node@20.19.43", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA=="],
"websocket-driver/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
@ -4494,10 +4823,32 @@
"yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
"@ai-sdk/harness/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.53", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4ZgwJjv/lGA7ywlD/3oYg6XMkIy1VvVKiSRYB83PyLDApwMGXSF+oT7D5lPYJFhNE5Vr3W2H+SwCFuTsbfgn9w=="],
"@babel/generator/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@8.0.0", "", {}, "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg=="],
"@babel/generator/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@8.0.4", "", {}, "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg=="],
"@earendil-works/pi-agent-core/@earendil-works/pi-ai/@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/eventstream-handler-node": "^3.972.16", "@aws-sdk/middleware-eventstream": "^3.972.12", "@aws-sdk/middleware-websocket": "^3.972.19", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ=="],
"@earendil-works/pi-agent-core/@earendil-works/pi-ai/@mistralai/mistralai": ["@mistralai/mistralai@2.2.6", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ=="],
"@earendil-works/pi-agent-core/@earendil-works/pi-ai/@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="],
"@earendil-works/pi-coding-agent/@earendil-works/pi-ai/@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/eventstream-handler-node": "^3.972.16", "@aws-sdk/middleware-eventstream": "^3.972.12", "@aws-sdk/middleware-websocket": "^3.972.19", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ=="],
"@earendil-works/pi-coding-agent/@earendil-works/pi-ai/@mistralai/mistralai": ["@mistralai/mistralai@2.2.6", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ=="],
"@earendil-works/pi-coding-agent/@earendil-works/pi-ai/@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="],
"@earendil-works/pi-coding-agent/glob/minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="],
"@earendil-works/pi-coding-agent/glob/path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
"@google/genai/p-retry/@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
"@google/genai/p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"@inquirer/core/wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
@ -4694,10 +5045,14 @@
"mocha/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="],
"normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"parse5-parser-stream/parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
"pptxgenjs/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
"read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="],
"read-pkg/parse-json/json-parse-even-better-errors": ["json-parse-even-better-errors@3.0.2", "", {}, "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ=="],
@ -4906,6 +5261,14 @@
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"@earendil-works/pi-agent-core/@earendil-works/pi-ai/@aws-sdk/client-bedrock-runtime/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="],
"@earendil-works/pi-agent-core/@earendil-works/pi-ai/@aws-sdk/client-bedrock-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.11.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-avwAh9HM3h2lcfjvP3zYIZGf+XVgLQ91wOJ2qoFbNpW1UZeZb33aGlhTZvtkANHfcGhJroRY64525OjfgOg30g=="],
"@earendil-works/pi-coding-agent/@earendil-works/pi-ai/@aws-sdk/client-bedrock-runtime/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="],
"@earendil-works/pi-coding-agent/@earendil-works/pi-ai/@aws-sdk/client-bedrock-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.11.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-avwAh9HM3h2lcfjvP3zYIZGf+XVgLQ91wOJ2qoFbNpW1UZeZb33aGlhTZvtkANHfcGhJroRY64525OjfgOg30g=="],
"@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"@libp2p/crypto/@libp2p/interface/@multiformats/multiaddr/uint8-varint": ["uint8-varint@3.0.0", "", { "dependencies": { "uint8arraylist": "^3.0.1", "uint8arrays": "^6.1.0" } }, "sha512-S4DdpXBaLwKcFo7f0bWzWfHjbZ/i3QhM842qn+ZvHjxqFCfUcEB9SQNcmI69S+zMlcmIcKxsk9Iyw77S2Kxv6Q=="],
@ -4996,6 +5359,8 @@
"glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"gray-matter/js-yaml/argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
"mocha/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"mocha/find-up/locate-path/p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
@ -5004,6 +5369,10 @@
"mocha/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"prebuild-install/tar-fs/tar-stream/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"prebuild-install/tar-fs/tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"read-pkg/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"readdir-glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
@ -5028,6 +5397,8 @@
"mocha/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"prebuild-install/tar-fs/tar-stream/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"storybook/oxc-parser/@oxc-parser/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@waku/enr/@libp2p/peer-id/@libp2p/crypto/@libp2p/interface/@multiformats/multiaddr/uint8-varint": ["uint8-varint@3.0.0", "", { "dependencies": { "uint8arraylist": "^3.0.1", "uint8arrays": "^6.1.0" } }, "sha512-S4DdpXBaLwKcFo7f0bWzWfHjbZ/i3QhM842qn+ZvHjxqFCfUcEB9SQNcmI69S+zMlcmIcKxsk9Iyw77S2Kxv6Q=="],

3
desktop/.gitignore vendored
View file

@ -1,3 +1,6 @@
# Harness sidecar build output
/binaries/
# Cargo build output
/target/

View file

@ -60,6 +60,7 @@
{ "name": "codex-acp", "cmd": "codex-acp", "args": true },
{ "name": "gemini", "cmd": "gemini", "args": true },
{ "name": "openpencil-mcp-http", "cmd": "openpencil-mcp-http", "args": true },
{ "name": "binaries/openpencil-harness", "sidecar": true, "args": false },
{ "name": "cmd", "cmd": "cmd", "args": true }
]
},

View file

@ -4,9 +4,9 @@
"version": "0.14.0",
"identifier": "net.dannote.open-pencil",
"build": {
"beforeDevCommand": "bun run generate:tauri-menu && bun run dev",
"beforeDevCommand": "bun run generate:tauri-menu && bun run build:harness-sidecar && bun run dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "bun run generate:tauri-menu && bun run build",
"beforeBuildCommand": "bun run generate:tauri-menu && bun run build:harness-sidecar && bun run build",
"frontendDist": "../dist"
},
"app": {
@ -26,6 +26,7 @@
"active": true,
"targets": "all",
"createUpdaterArtifacts": true,
"externalBin": ["binaries/openpencil-harness"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",

View file

@ -13,6 +13,7 @@
"packages/vue",
"packages/cli",
"packages/mcp",
"packages/harness",
"packages/docs",
"tools/docs"
],
@ -24,11 +25,12 @@
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"tauri": "tauri",
"build:harness-sidecar": "bun tools/harness-sidecar/src/build.ts",
"build:native-test": "bun run build:packages && bun tauri build --debug --no-bundle --features native-test --config desktop/tauri.native-test.conf.json",
"test:native": "bun run build:native-test && wdio run wdio.conf.ts",
"lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/scene-graph/src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/pen/src/ packages/kiwi/src/ packages/fig/src/",
"lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ .storybook/ src/ packages/scene-graph/src/ packages/scene-graph/scripts/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/pen/src/ packages/pen/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests/ scripts/ tools/",
"format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ .storybook/ src/ packages/scene-graph/src/ packages/scene-graph/scripts/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/pen/src/ packages/pen/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests scripts/ tools/",
"lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/scene-graph/src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/harness/src/ packages/dom-css/src/ packages/pen/src/ packages/kiwi/src/ packages/fig/src/",
"lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ .storybook/ src/ packages/scene-graph/src/ packages/scene-graph/scripts/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/harness/src/ packages/harness/tests/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/pen/src/ packages/pen/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests/ scripts/ tools/",
"format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ .storybook/ src/ packages/scene-graph/src/ packages/scene-graph/scripts/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/harness/src/ packages/harness/tests/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/pen/src/ packages/pen/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests scripts/ tools/",
"format:check": "bun run format && status=$(git status --porcelain -uall) && test -z \"$status\" || (echo \"$status\" && exit 1)",
"check": "bun run build:packages && bun run lint && tsgo --noEmit && bun run check:vue && bun run check:native-test && bun run check:i18n && bun run check:docs && bun run check:packages && bun run check:deps && bun run check:audit && bun run check:secrets && bun run check:monorepo && bun run check:arch && bun run test:type-shapes && bun run test:tools && bun run test:dupes",
"check:deps": "knip --include unlisted,unresolved,binaries",
@ -51,10 +53,10 @@
"test:unit:heavy": "bash -lc 'mapfile -t test_files < <(bun tools/unit-tests/src/list.ts all --heavy-only); bun test --timeout 180000 \"${test_files[@]}\"'",
"test:coverage": "bun test --coverage ./tests/engine",
"test:type-shapes": "bun tools/type-shapes/src/index.ts",
"test:tools": "bun tools/test.ts",
"test:tools": "bun tools/test.ts && bun --filter @open-pencil/harness test",
"test:dupes": "jscpd packages/scene-graph/src packages/pen/src packages/core/src packages/cli/src packages/dom-css/src src --min-lines 5 --min-tokens 50 --format typescript --threshold 0",
"test:packages": "bun tools/package-quality/src/check/metadata.ts && bun tools/package-quality/src/check/publint.ts && bun tools/package-quality/src/check/attw.ts && bun tools/package-quality/src/smoke.ts",
"build:packages": "bun --filter @open-pencil/scene-graph build && bun --filter @open-pencil/pen build && bun --filter @open-pencil/kiwi build && bun --filter @open-pencil/fig build && bun --filter @open-pencil/core build && bun --filter @open-pencil/dom-css build && bun --filter @open-pencil/vue build && bun --filter @open-pencil/mcp build && bun --filter @open-pencil/cli build",
"build:packages": "bun --filter @open-pencil/scene-graph build && bun --filter @open-pencil/pen build && bun --filter @open-pencil/kiwi build && bun --filter @open-pencil/fig build && bun --filter @open-pencil/core build && bun --filter @open-pencil/dom-css build && bun --filter @open-pencil/vue build && bun --filter @open-pencil/mcp build && bun --filter @open-pencil/cli build && bun --filter @open-pencil/harness build",
"open-pencil": "bun packages/cli/src/index.ts",
"docs:dev": "bun --filter @open-pencil/docs dev",
"docs:build": "bun --filter @open-pencil/docs build",
@ -88,6 +90,7 @@
"@open-pencil/core": "workspace:*",
"@open-pencil/dom-css": "workspace:*",
"@open-pencil/fig": "workspace:*",
"@open-pencil/harness": "workspace:*",
"@open-pencil/kiwi": "workspace:*",
"@open-pencil/pen": "workspace:*",
"@open-pencil/scene-graph": "workspace:*",

View file

@ -187,6 +187,7 @@ export type AIProviderID =
| 'zai'
| 'minimax'
| 'anthropic-compatible'
| 'harness:pi'
| `acp:${ACPAgentID}`
export interface ModelOption {
@ -208,7 +209,18 @@ export interface AIProviderDef {
supportsCustomModel?: boolean
}
export const HARNESS_PROVIDER_ID = 'harness:pi' as const
export const AI_PROVIDERS: AIProviderDef[] = [
{
id: HARNESS_PROVIDER_ID,
name: 'Pi',
keyPlaceholder: 'Provider API key',
keyURL: '',
defaultModel: '',
supportsCustomModel: true,
models: []
},
{
id: 'openrouter',
name: 'OpenRouter',

View file

@ -0,0 +1,18 @@
# @open-pencil/harness
Private Node runtime for coding-agent harness sessions. It owns the backend-neutral session lifecycle, opaque resume-state persistence, and the JSONL sidecar protocol used by host applications.
The first backend uses AI SDK `HarnessAgent`, Pi, and local `just-bash`. Pi runs in the Node host process; `just-bash` provides an isolated in-memory workspace and shell without requiring cloud infrastructure.
## Current scope
- Backend-neutral, streaming session service.
- Atomic, bounded persistence of opaque harness resume state.
- JSONL stdio sidecar transport.
- Pi + `just-bash` backend.
The package is not wired into the desktop application yet. Credentials are supplied to the sidecar process at runtime and are never written to resume-state storage.
## Local sandbox limitation
`just-bash` is process-local and in-memory. Multi-turn sessions work while the sidecar remains alive, but its sandbox cannot be reattached after a process restart. Persisted opaque state establishes the session contract; durable restart recovery requires a persistent sandbox provider in a later integration.

View file

@ -0,0 +1,32 @@
{
"name": "@open-pencil/harness",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.mts",
"bun": "./src/index.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"bin": {
"openpencil-harness": "./dist/stdio.mjs"
},
"scripts": {
"build": "bunx tsdown --config tsdown.config.ts",
"check": "bun run typecheck && bun run test && bun run build",
"test": "bun test tests",
"typecheck": "bunx tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@ai-sdk/harness": "1.0.74",
"@ai-sdk/harness-pi": "1.0.76",
"@ai-sdk/sandbox-just-bash": "1.0.74"
},
"devDependencies": {
"@types/bun": "^1.3.14",
"typescript": "~5.8.3"
}
}

View file

@ -0,0 +1,176 @@
import { createPi } from '@ai-sdk/harness-pi'
import type { PiAuthOptions } from '@ai-sdk/harness-pi'
import { HarnessAgent } from '@ai-sdk/harness/agent'
import type { HarnessAgentResumeSessionState, HarnessAgentSession } from '@ai-sdk/harness/agent'
import { createJustBashSandbox } from '@ai-sdk/sandbox-just-bash'
import type { TextStreamPart, ToolSet } from 'ai'
import type { HarnessSessionConfiguration, JSONValue } from '../protocol'
function optional<T>(key: string, value: T | undefined): Record<string, T> {
return value === undefined ? {} : { [key]: value }
}
function restoreEnvironment(previous: Map<string, string | undefined>): void {
for (const [name, value] of previous) {
if (value === undefined) Reflect.deleteProperty(process.env, name)
else process.env[name] = value
}
}
async function withEnvironment<T>(
environment: Record<string, string>,
run: () => Promise<T>
): Promise<T> {
const previous = new Map<string, string | undefined>()
for (const [name, value] of Object.entries(environment)) {
previous.set(name, process.env[name])
process.env[name] = value
}
try {
return await run()
} finally {
restoreEnvironment(previous)
}
}
import type { BackendEvent, BackendSession, HarnessBackend, HarnessResumeState } from './types'
export type PiThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
export interface PiHarnessBackendOptions {
auth?: PiAuthOptions
apiKey?: string
model?: string
thinkingLevel?: PiThinkingLevel
agentDir?: string
mcpServers?: Record<string, unknown>
instructions?: string
permissionMode?: 'allow-all' | 'allow-reads' | 'allow-edits'
}
function asJSONValue(value: unknown): JSONValue {
if (value === undefined || typeof value === 'bigint' || typeof value === 'function') return null
try {
const clone = structuredClone(value)
JSON.stringify(clone)
return clone as JSONValue
} catch {
return null
}
}
function mapPart(part: TextStreamPart<ToolSet>): BackendEvent | undefined {
if (part.type === 'text-delta') return { type: 'text-delta', text: part.text }
if (part.type === 'reasoning-delta') return { type: 'reasoning-delta', text: part.text }
if (part.type === 'tool-call') {
return {
type: 'tool-call',
toolCallId: part.toolCallId,
toolName: part.toolName,
input: asJSONValue(part.input)
}
}
if (part.type === 'tool-result') {
return {
type: 'tool-result',
toolCallId: part.toolCallId,
toolName: part.toolName,
output: asJSONValue(part.output)
}
}
if (part.type === 'finish') return { type: 'finish', finishReason: part.finishReason }
if (part.type === 'error') {
return {
type: 'error',
message: part.error instanceof Error ? part.error.message : String(part.error)
}
}
return undefined
}
class PiBackendSession implements BackendSession {
readonly sessionId: string
readonly isResume: boolean
constructor(
private readonly agent: HarnessAgent,
private readonly session: HarnessAgentSession
) {
this.sessionId = session.sessionId
this.isResume = session.isResume
}
async *runTurn(prompt: string, signal?: AbortSignal): AsyncIterable<BackendEvent> {
const result = await this.agent.stream({ session: this.session, prompt, abortSignal: signal })
for await (const part of result.fullStream) {
const event = mapPart(part)
if (event) yield event
}
}
async stop(): Promise<HarnessResumeState> {
return (await this.session.stop()) as HarnessResumeState
}
async destroy(): Promise<void> {
await this.session.destroy()
}
}
export class PiHarnessBackend implements HarnessBackend {
readonly id = 'pi'
readonly capabilities = {
adapter: 'pi',
sandboxes: ['just-bash'],
structuredOutput: false,
sessionResume: 'live-process',
turnContinuation: true
} as const
constructor(private readonly defaults: PiHarnessBackendOptions = {}) {}
async createSession(options: {
sessionId: string
resumeState?: HarnessResumeState
configuration?: HarnessSessionConfiguration
signal?: AbortSignal
}): Promise<BackendSession> {
const configuration = options.configuration
if (!configuration) throw new Error('Harness configuration is required')
const thinkingLevel =
typeof configuration.settings?.thinkingLevel === 'string'
? (configuration.settings.thinkingLevel as PiThinkingLevel)
: undefined
const permissionMode =
typeof configuration.settings?.permissionMode === 'string'
? (configuration.settings.permissionMode as PiHarnessBackendOptions['permissionMode'])
: undefined
const environment: Record<string, string> = this.defaults.apiKey
? { AI_GATEWAY_API_KEY: this.defaults.apiKey }
: {}
return withEnvironment(environment, async () => {
const harness = createPi({
...optional('auth', this.defaults.auth),
...optional('model', configuration.model),
...optional('thinkingLevel', thinkingLevel ?? this.defaults.thinkingLevel),
...optional('agentDir', this.defaults.agentDir),
...optional('mcpServers', options.configuration?.mcpServers ?? this.defaults.mcpServers)
})
const agent = new HarnessAgent({
harness,
sandbox: createJustBashSandbox({ cwd: '/workspace' }),
sandboxConfig: { workDir: 'workspace' },
...optional('instructions', configuration.instructions ?? this.defaults.instructions),
permissionMode: permissionMode ?? this.defaults.permissionMode ?? 'allow-edits'
})
const session = await agent.createSession({
sessionId: options.sessionId,
...(options.resumeState === undefined
? {}
: { resumeFrom: options.resumeState as HarnessAgentResumeSessionState }),
...(options.signal === undefined ? {} : { abortSignal: options.signal })
})
return new PiBackendSession(agent, session)
})
}
}

View file

@ -0,0 +1,41 @@
import type {
HarnessAdapterID,
HarnessProviderCapability,
HarnessSessionConfiguration,
JSONValue
} from '../protocol'
export type HarnessResumeState = {
type: 'resume-session'
harnessId: string
specificationVersion: 'harness-v1'
data: JSONValue
continueFrom?: JSONValue
}
export interface BackendSession {
readonly sessionId: string
readonly isResume: boolean
runTurn(prompt: string, signal?: AbortSignal): AsyncIterable<BackendEvent>
stop(): Promise<HarnessResumeState>
destroy(): Promise<void>
}
export type BackendEvent =
| { type: 'text-delta'; text: string }
| { type: 'reasoning-delta'; text: string }
| { type: 'tool-call'; toolCallId: string; toolName: string; input: JSONValue }
| { type: 'tool-result'; toolCallId: string; toolName: string; output: JSONValue }
| { type: 'finish'; finishReason: string }
| { type: 'error'; message: string }
export interface HarnessBackend {
readonly id: HarnessAdapterID
readonly capabilities: HarnessProviderCapability
createSession(options: {
sessionId: string
resumeState?: HarnessResumeState
configuration?: HarnessSessionConfiguration
signal?: AbortSignal
}): Promise<BackendSession>
}

View file

@ -0,0 +1,22 @@
export type {
BackendEvent,
BackendSession,
HarnessBackend,
HarnessResumeState
} from './backends/types'
export { PiHarnessBackend, type PiHarnessBackendOptions } from './backends/pi'
export {
MAX_PROMPT_LENGTH,
MAX_PROTOCOL_LINE_BYTES,
parseHarnessRequest,
type HarnessAdapterID,
type HarnessProviderCapability,
type HarnessRequest,
type HarnessSandboxID,
type HarnessSessionConfiguration,
type HarnessSidecarMessage,
type HarnessTurnEvent,
type JSONValue
} from './protocol'
export { HarnessSessionService } from './service'
export { FileResumeStateStore, type ResumeStateStore } from './session-store'

View file

@ -0,0 +1,150 @@
export type JSONPrimitive = null | boolean | number | string
export type JSONValue = JSONPrimitive | JSONValue[] | { [key: string]: JSONValue }
export const HARNESS_ADAPTER_IDS = ['pi'] as const
export const HARNESS_SANDBOX_IDS = ['just-bash'] as const
export type HarnessAdapterID = (typeof HARNESS_ADAPTER_IDS)[number]
export type HarnessSandboxID = (typeof HARNESS_SANDBOX_IDS)[number]
export interface HarnessSessionConfiguration {
adapter: HarnessAdapterID
sandbox: HarnessSandboxID
model: string
settings?: Record<string, JSONValue>
mcpServers?: Record<string, Record<string, JSONValue>>
instructions?: string
}
export interface HarnessProviderCapability {
adapter: HarnessAdapterID
sandboxes: readonly HarnessSandboxID[]
structuredOutput: boolean
sessionResume: 'live-process' | 'persistent'
turnContinuation: boolean
}
export interface HarnessTurnInput {
sessionId: string
prompt: string
}
export type HarnessRequest =
| {
id: string
method: 'service.capabilities'
params?: Record<string, never>
}
| {
id: string
method: 'session.create'
params: { sessionId: string; configuration: HarnessSessionConfiguration }
}
| { id: string; method: 'session.turn'; params: HarnessTurnInput }
| { id: string; method: 'session.cancel'; params: { sessionId: string } }
| { id: string; method: 'session.stop'; params: { sessionId: string } }
| { id: string; method: 'session.destroy'; params: { sessionId: string } }
| { id: string; method: 'service.shutdown'; params?: Record<string, never> }
export type HarnessTurnEvent =
| { type: 'text-delta'; text: string }
| { type: 'reasoning-delta'; text: string }
| { type: 'tool-call'; toolCallId: string; toolName: string; input: JSONValue }
| { type: 'tool-result'; toolCallId: string; toolName: string; output: JSONValue }
| { type: 'finish'; finishReason: string }
| { type: 'error'; message: string }
export type HarnessSidecarMessage =
| { type: 'response'; id: string; result?: JSONValue; error?: string }
| { type: 'turn.event'; id: string; event: HarnessTurnEvent }
export const MAX_PROTOCOL_LINE_BYTES = 1024 * 1024
export const MAX_PROMPT_LENGTH = 256 * 1024
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
function requireString(record: Record<string, unknown>, key: string): string {
const value = record[key]
if (typeof value !== 'string' || value.length === 0) {
throw new Error(`Expected non-empty string at ${key}`)
}
return value
}
function isAdapterID(value: unknown): value is HarnessAdapterID {
return typeof value === 'string' && HARNESS_ADAPTER_IDS.includes(value as HarnessAdapterID)
}
function isSandboxID(value: unknown): value is HarnessSandboxID {
return typeof value === 'string' && HARNESS_SANDBOX_IDS.includes(value as HarnessSandboxID)
}
function parseJSONRecord(value: unknown, field: string): Record<string, JSONValue> | undefined {
if (value === undefined) return undefined
if (!isRecord(value)) throw new Error(`Expected object at ${field}`)
try {
JSON.stringify(value)
return structuredClone(value) as Record<string, JSONValue>
} catch {
throw new Error(`Expected JSON-compatible object at ${field}`)
}
}
function parseConfiguration(value: unknown): HarnessSessionConfiguration {
if (!isRecord(value)) throw new Error('Expected Harness session configuration')
if (!isAdapterID(value.adapter)) throw new Error('Unknown Harness adapter')
if (!isSandboxID(value.sandbox)) throw new Error('Unknown Harness sandbox')
const mcpServers = parseJSONRecord(value.mcpServers, 'mcpServers')
return {
adapter: value.adapter,
sandbox: value.sandbox,
model: requireString(value, 'model'),
...(parseJSONRecord(value.settings, 'settings')
? { settings: parseJSONRecord(value.settings, 'settings') }
: {}),
...(mcpServers ? { mcpServers: mcpServers as Record<string, Record<string, JSONValue>> } : {}),
...(typeof value.instructions === 'string' ? { instructions: value.instructions } : {})
}
}
function parseSessionParams(value: unknown): { sessionId: string } {
if (!isRecord(value)) throw new Error('Expected request params')
return { sessionId: requireString(value, 'sessionId') }
}
export function parseHarnessRequest(line: string): HarnessRequest {
if (Buffer.byteLength(line, 'utf8') > MAX_PROTOCOL_LINE_BYTES) {
throw new Error('Harness request exceeds the protocol size limit')
}
const parsed: unknown = JSON.parse(line)
if (!isRecord(parsed)) throw new Error('Expected a request object')
const id = requireString(parsed, 'id')
const method = requireString(parsed, 'method')
if (method === 'service.capabilities' || method === 'service.shutdown') {
return { id, method, params: {} }
}
if (method === 'session.create') {
const params = parseSessionParams(parsed.params)
if (!isRecord(parsed.params)) throw new Error('Expected request params')
return {
id,
method,
params: { ...params, configuration: parseConfiguration(parsed.params.configuration) }
}
}
if (method === 'session.cancel' || method === 'session.stop' || method === 'session.destroy') {
return { id, method, params: parseSessionParams(parsed.params) }
}
if (method === 'session.turn') {
if (!isRecord(parsed.params)) throw new Error('Expected request params')
const sessionId = requireString(parsed.params, 'sessionId')
const prompt = requireString(parsed.params, 'prompt')
if (prompt.length > MAX_PROMPT_LENGTH) throw new Error('Harness prompt exceeds the size limit')
return { id, method, params: { sessionId, prompt } }
}
throw new Error(`Unknown harness method: ${method}`)
}

View file

@ -0,0 +1,105 @@
import type { BackendEvent, BackendSession, HarnessBackend } from './backends/types'
import type { HarnessSessionConfiguration, JSONValue } from './protocol'
import type { ResumeStateStore } from './session-store'
export class HarnessSessionService {
private readonly sessions = new Map<string, BackendSession>()
private readonly turns = new Map<string, AbortController>()
constructor(
private readonly backends: ReadonlyMap<string, HarnessBackend>,
private readonly store: ResumeStateStore
) {}
capabilities(): JSONValue {
return this.backends.size
? [...this.backends.values()].map((backend) => ({
...backend.capabilities,
sandboxes: [...backend.capabilities.sandboxes]
}))
: []
}
async createSession(
sessionId: string,
configuration: HarnessSessionConfiguration,
signal?: AbortSignal
): Promise<{ isResume: boolean }> {
if (this.sessions.has(sessionId))
throw new Error(`Harness session already active: ${sessionId}`)
const backend = this.backends.get(configuration.adapter)
if (!backend) throw new Error(`Harness adapter is unavailable: ${configuration.adapter}`)
const resumeState = await this.store.load(sessionId)
if (resumeState && resumeState.harnessId !== backend.id) {
throw new Error(`Harness state belongs to ${resumeState.harnessId}, not ${backend.id}`)
}
const session = await backend.createSession({
sessionId,
resumeState,
configuration,
signal
})
this.sessions.set(sessionId, session)
return { isResume: session.isResume }
}
async *runTurn(
sessionId: string,
prompt: string,
signal?: AbortSignal
): AsyncIterable<BackendEvent> {
const session = this.requireSession(sessionId)
if (this.turns.has(sessionId)) throw new Error(`Harness turn already active: ${sessionId}`)
const controller = new AbortController()
this.turns.set(sessionId, controller)
const abort = () => controller.abort()
if (signal?.aborted) abort()
else signal?.addEventListener('abort', abort, { once: true })
try {
yield* session.runTurn(prompt, controller.signal)
} finally {
signal?.removeEventListener('abort', abort)
if (this.turns.get(sessionId) === controller) this.turns.delete(sessionId)
}
}
cancelTurn(sessionId: string): void {
this.requireSession(sessionId)
this.turns.get(sessionId)?.abort()
}
async stopSession(sessionId: string): Promise<void> {
const session = this.requireSession(sessionId)
const state = await session.stop()
await this.store.save(sessionId, state)
this.sessions.delete(sessionId)
}
async destroySession(sessionId: string): Promise<void> {
const session = this.requireSession(sessionId)
await session.destroy()
await this.store.remove(sessionId)
this.sessions.delete(sessionId)
}
async shutdown(): Promise<void> {
const sessions = [...this.sessions.entries()]
const results = await Promise.allSettled(
sessions.map(async ([sessionId, session]) => {
const state = await session.stop()
await this.store.save(sessionId, state)
this.sessions.delete(sessionId)
})
)
const errors = results
.filter((result) => result.status === 'rejected')
.map((result) => result.reason)
if (errors.length) throw new AggregateError(errors, 'Harness shutdown failed')
}
private requireSession(sessionId: string): BackendSession {
const session = this.sessions.get(sessionId)
if (!session) throw new Error(`Harness session is not active: ${sessionId}`)
return session
}
}

View file

@ -0,0 +1,79 @@
import { randomBytes } from 'node:crypto'
import { mkdir, open, readFile, rename, rm } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import type { HarnessResumeState } from './backends/types'
const MAX_RESUME_STATE_BYTES = 1024 * 1024
const SESSION_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/
export interface ResumeStateStore {
load(sessionId: string): Promise<HarnessResumeState | undefined>
save(sessionId: string, state: HarnessResumeState): Promise<void>
remove(sessionId: string): Promise<void>
}
function statePath(root: string, sessionId: string): string {
if (!SESSION_ID_PATTERN.test(sessionId)) throw new Error('Invalid harness session ID')
return join(root, `${sessionId}.json`)
}
function isResumeState(value: unknown): value is HarnessResumeState {
if (typeof value !== 'object' || value === null || !('type' in value)) return false
if (!('harnessId' in value) || !('specificationVersion' in value) || !('data' in value)) {
return false
}
return (
value.type === 'resume-session' &&
typeof value.harnessId === 'string' &&
value.specificationVersion === 'harness-v1'
)
}
export class FileResumeStateStore implements ResumeStateStore {
constructor(private readonly root: string) {}
async load(sessionId: string): Promise<HarnessResumeState | undefined> {
const path = statePath(this.root, sessionId)
try {
const content = await readFile(path, 'utf8')
if (Buffer.byteLength(content, 'utf8') > MAX_RESUME_STATE_BYTES) {
throw new Error('Persisted harness state exceeds the size limit')
}
const parsed: unknown = JSON.parse(content)
if (!isResumeState(parsed)) throw new Error('Persisted harness state is invalid')
return parsed
} catch (error) {
if (
typeof error === 'object' &&
error !== null &&
'code' in error &&
error.code === 'ENOENT'
) {
return undefined
}
throw error
}
}
async save(sessionId: string, state: HarnessResumeState): Promise<void> {
const path = statePath(this.root, sessionId)
const content = JSON.stringify(state)
if (Buffer.byteLength(content, 'utf8') > MAX_RESUME_STATE_BYTES) {
throw new Error('Harness state exceeds the persistence size limit')
}
await mkdir(dirname(path), { recursive: true, mode: 0o700 })
const temporaryPath = `${path}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`
const handle = await open(temporaryPath, 'wx', 0o600)
try {
await handle.writeFile(content, 'utf8')
} finally {
await handle.close()
}
await rename(temporaryPath, path)
}
async remove(sessionId: string): Promise<void> {
await rm(statePath(this.root, sessionId), { force: true })
}
}

View file

@ -0,0 +1,132 @@
#!/usr/bin/env node
import { homedir } from 'node:os'
import { join } from 'node:path'
import { createInterface } from 'node:readline'
import { PiHarnessBackend } from '#harness/backends/pi'
import type { HarnessRequest, HarnessSidecarMessage } from '#harness/protocol'
import { parseHarnessRequest } from '#harness/protocol'
import { HarnessSessionService } from '#harness/service'
import { FileResumeStateStore } from '#harness/session-store'
const stateRoot =
process.env.OPENPENCIL_HARNESS_STATE_DIR ?? join(homedir(), '.open-pencil', 'harness-sessions')
const agentDir = process.env.OPENPENCIL_HARNESS_AGENT_DIR
const apiKey = process.env.OPENPENCIL_HARNESS_API_KEY
const backend = new PiHarnessBackend({
...(agentDir ? { agentDir } : {}),
...(apiKey ? { apiKey } : {})
})
const service = new HarnessSessionService(
new Map([[backend.id, backend]]),
new FileResumeStateStore(stateRoot)
)
async function emit(message: HarnessSidecarMessage): Promise<void> {
if (process.stdout.write(`${JSON.stringify(message)}\n`)) return
await new Promise<void>((resolve) => {
process.stdout.once('drain', resolve)
})
}
function messageFor(error: unknown): string {
return error instanceof Error ? error.message : String(error)
}
async function dispatch(line: string): Promise<boolean> {
let requestId = 'unknown'
try {
const request = parseHarnessRequest(line)
requestId = request.id
if (request.method === 'service.capabilities') {
await emit({
type: 'response',
id: request.id,
result: service.capabilities()
})
return true
}
if (request.method === 'session.create') {
const result = await service.createSession(
request.params.sessionId,
request.params.configuration
)
await emit({
type: 'response',
id: request.id,
result: { isResume: result.isResume }
})
return true
}
if (request.method === 'session.turn') {
for await (const event of service.runTurn(request.params.sessionId, request.params.prompt)) {
await emit({ type: 'turn.event', id: request.id, event })
}
await emit({ type: 'response', id: request.id, result: { completed: true } })
return true
}
if (request.method === 'session.cancel') {
service.cancelTurn(request.params.sessionId)
await emit({ type: 'response', id: request.id, result: { cancelled: true } })
return true
}
if (request.method === 'session.stop') {
await service.stopSession(request.params.sessionId)
await emit({ type: 'response', id: request.id, result: { stopped: true } })
return true
}
if (request.method === 'session.destroy') {
await service.destroySession(request.params.sessionId)
await emit({ type: 'response', id: request.id, result: { destroyed: true } })
return true
}
await service.shutdown()
await emit({ type: 'response', id: request.id, result: { shutdown: true } })
return false
} catch (error) {
await emit({ type: 'response', id: requestId, error: messageFor(error) })
return true
}
}
const input = createInterface({ input: process.stdin, crlfDelay: Infinity })
let queue = Promise.resolve(true)
const activeTurns = new Set<Promise<boolean>>()
input.on('line', (line) => {
let request: HarnessRequest | undefined
try {
request = parseHarnessRequest(line)
} catch (error) {
console.warn('Deferring malformed Harness request to protocol error handling:', error)
}
if (request?.method === 'session.turn') {
const turn = dispatch(line).finally(() => activeTurns.delete(turn))
activeTurns.add(turn)
return
}
queue = queue.then(async (keepRunning) => {
if (!keepRunning) return false
const next = await dispatch(line)
if (!next) input.close()
return next
})
})
let closing: Promise<void> | undefined
async function closeSidecar(): Promise<void> {
closing ??= queue
.then(async () => {
await Promise.allSettled(activeTurns)
await service.shutdown()
return undefined
})
.catch((error) => {
process.stderr.write(`${messageFor(error)}\n`)
process.exitCode = 1
})
await closing
}
input.on('close', () => void closeSidecar())
process.on('SIGTERM', () => input.close())
process.on('SIGINT', () => input.close())

View file

@ -0,0 +1,61 @@
import { describe, expect, test } from 'bun:test'
import { MAX_PROMPT_LENGTH, parseHarnessRequest } from '../src/protocol'
describe('harness sidecar protocol', () => {
test('parses a provider-neutral session configuration', () => {
expect(
parseHarnessRequest(
JSON.stringify({
id: 'request-1',
method: 'session.create',
params: {
sessionId: 'session-1',
configuration: {
adapter: 'pi',
sandbox: 'just-bash',
model: 'provider/model',
settings: { thinkingLevel: 'high' }
}
}
})
)
).toMatchObject({
method: 'session.create',
params: {
configuration: { adapter: 'pi', sandbox: 'just-bash', model: 'provider/model' }
}
})
})
test('parses a turn request', () => {
expect(
parseHarnessRequest(
JSON.stringify({
id: 'request-1',
method: 'session.turn',
params: { sessionId: 'session-1', prompt: 'Inspect the document' }
})
)
).toEqual({
id: 'request-1',
method: 'session.turn',
params: { sessionId: 'session-1', prompt: 'Inspect the document' }
})
})
test('rejects unknown methods and oversized prompts', () => {
expect(() =>
parseHarnessRequest(JSON.stringify({ id: 'request-1', method: 'unknown' }))
).toThrow('Unknown harness method')
expect(() =>
parseHarnessRequest(
JSON.stringify({
id: 'request-1',
method: 'session.turn',
params: { sessionId: 'session-1', prompt: 'x'.repeat(MAX_PROMPT_LENGTH + 1) }
})
)
).toThrow('prompt exceeds')
})
})

View file

@ -0,0 +1,146 @@
import { describe, expect, test } from 'bun:test'
import type {
BackendEvent,
BackendSession,
HarnessBackend,
HarnessResumeState
} from '../src/backends/types'
import type { HarnessSessionConfiguration } from '../src/protocol'
import { HarnessSessionService } from '../src/service'
import type { ResumeStateStore } from '../src/session-store'
const configuration: HarnessSessionConfiguration = {
adapter: 'pi',
sandbox: 'just-bash',
model: 'test/model'
}
class MemoryStore implements ResumeStateStore {
readonly states = new Map<string, HarnessResumeState>()
async load(sessionId: string): Promise<HarnessResumeState | undefined> {
return this.states.get(sessionId)
}
async save(sessionId: string, state: HarnessResumeState): Promise<void> {
this.states.set(sessionId, state)
}
async remove(sessionId: string): Promise<void> {
this.states.delete(sessionId)
}
}
class FakeSession implements BackendSession {
readonly isResume: boolean
destroyed = false
constructor(
readonly sessionId: string,
isResume: boolean
) {
this.isResume = isResume
}
async *runTurn(prompt: string): AsyncIterable<BackendEvent> {
yield { type: 'text-delta', text: `reply:${prompt}` }
yield { type: 'finish', finishReason: 'stop' }
}
async stop(): Promise<HarnessResumeState> {
return {
type: 'resume-session',
harnessId: 'pi',
specificationVersion: 'harness-v1',
data: { sessionId: this.sessionId }
}
}
async destroy(): Promise<void> {
this.destroyed = true
}
}
class FakeBackend implements HarnessBackend {
readonly id = 'pi' as const
readonly capabilities = {
adapter: 'pi',
sandboxes: ['just-bash'],
structuredOutput: false,
sessionResume: 'live-process',
turnContinuation: true
} as const
sessions: FakeSession[] = []
async createSession(options: {
sessionId: string
resumeState?: HarnessResumeState
configuration?: HarnessSessionConfiguration
}): Promise<BackendSession> {
const session = new FakeSession(options.sessionId, options.resumeState !== undefined)
this.sessions.push(session)
return session
}
}
describe('HarnessSessionService', () => {
test('reports adapter and sandbox capabilities', () => {
const backend = new FakeBackend()
const service = new HarnessSessionService(new Map([[backend.id, backend]]), new MemoryStore())
expect(service.capabilities()).toEqual([
{
adapter: 'pi',
sandboxes: ['just-bash'],
structuredOutput: false,
sessionResume: 'live-process',
turnContinuation: true
}
])
})
test('streams turns and resumes from opaque persisted state', async () => {
const backend = new FakeBackend()
const store = new MemoryStore()
const service = new HarnessSessionService(new Map([[backend.id, backend]]), store)
expect(await service.createSession('session-1', configuration)).toEqual({ isResume: false })
const events: BackendEvent[] = []
for await (const event of service.runTurn('session-1', 'hello')) events.push(event)
expect(events).toEqual([
{ type: 'text-delta', text: 'reply:hello' },
{ type: 'finish', finishReason: 'stop' }
])
await service.stopSession('session-1')
expect(store.states.get('session-1')?.data).toEqual({ sessionId: 'session-1' })
expect(await service.createSession('session-1', configuration)).toEqual({ isResume: true })
})
test('destroys a session and removes resumability', async () => {
const backend = new FakeBackend()
const store = new MemoryStore()
const service = new HarnessSessionService(new Map([[backend.id, backend]]), store)
await service.createSession('session-1', configuration)
await service.stopSession('session-1')
await service.createSession('session-1', configuration)
await service.destroySession('session-1')
expect(store.states.has('session-1')).toBeFalse()
expect(backend.sessions.at(-1)?.destroyed).toBeTrue()
})
test('rejects persisted state from another backend', async () => {
const store = new MemoryStore()
store.states.set('session-1', {
type: 'resume-session',
harnessId: 'other',
specificationVersion: 'harness-v1',
data: {}
})
const service = new HarnessSessionService(new Map([['pi', new FakeBackend()]]), store)
await expect(service.createSession('session-1', configuration)).rejects.toThrow(
'belongs to other'
)
})
})

View file

@ -0,0 +1,53 @@
import { describe, expect, test } from 'bun:test'
import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import type { HarnessResumeState } from '../src/backends/types'
import { FileResumeStateStore } from '../src/session-store'
const state: HarnessResumeState = {
type: 'resume-session',
harnessId: 'fake',
specificationVersion: 'harness-v1',
data: { opaque: 'state' }
}
describe('FileResumeStateStore', () => {
test('atomically persists and removes opaque state', async () => {
const root = await mkdtemp(join(tmpdir(), 'open-pencil-harness-'))
try {
const store = new FileResumeStateStore(root)
expect(await store.load('session-1')).toBeUndefined()
await store.save('session-1', state)
expect(await store.load('session-1')).toEqual(state)
expect(JSON.parse(await readFile(join(root, 'session-1.json'), 'utf8'))).toEqual(state)
await store.remove('session-1')
expect(await store.load('session-1')).toBeUndefined()
} finally {
await rm(root, { recursive: true, force: true })
}
})
test('concurrent saves leave valid state', async () => {
const root = await mkdtemp(join(tmpdir(), 'open-pencil-harness-'))
try {
const store = new FileResumeStateStore(root)
await Promise.all([
store.save('session-1', state),
store.save('session-1', { ...state, data: { opaque: 'newer' } })
])
expect(await store.load('session-1')).toMatchObject({
type: 'resume-session',
harnessId: 'fake'
})
} finally {
await rm(root, { recursive: true, force: true })
}
})
test('rejects path traversal session IDs', async () => {
const store = new FileResumeStateStore(tmpdir())
await expect(store.save('../escape', state)).rejects.toThrow('Invalid harness session ID')
})
})

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ESNext", "DOM"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true,
"esModuleInterop": true,
"types": ["bun"],
"paths": {
"#harness/*": ["./src/*"],
"@open-pencil/harness": ["./src/index.ts"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,19 @@
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: {
index: './src/index.ts',
stdio: './src/stdio.ts'
},
platform: 'node',
format: ['esm'],
dts: true,
sourcemap: true,
clean: true,
outDir: './dist',
treeshake: false,
deps: {
neverBundle: [/^@ai-sdk\//, /^node:/],
onlyBundle: false
}
})

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "Denkstufe",
"harnessThinkingOff": "Aus",
"harnessThinkingMinimal": "Minimal",
"harnessThinkingLow": "Niedrig",
"harnessThinkingMedium": "Mittel",
"harnessThinkingHigh": "Hoch",
"harnessThinkingExtraHigh": "Sehr hoch",
"harnessToolPermissions": "Tool-Berechtigungen",
"harnessPermissionReads": "Vor Änderungen und Befehlen fragen",
"harnessPermissionEdits": "Vor Befehlen fragen",
"harnessPermissionAll": "Alle Tools zulassen"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "Nivel de razonamiento",
"harnessThinkingOff": "Desactivado",
"harnessThinkingMinimal": "Mínimo",
"harnessThinkingLow": "Bajo",
"harnessThinkingMedium": "Medio",
"harnessThinkingHigh": "Alto",
"harnessThinkingExtraHigh": "Muy alto",
"harnessToolPermissions": "Permisos de herramientas",
"harnessPermissionReads": "Preguntar antes de editar y ejecutar comandos",
"harnessPermissionEdits": "Preguntar antes de ejecutar comandos",
"harnessPermissionAll": "Permitir todas las herramientas"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "Niveau de réflexion",
"harnessThinkingOff": "Désactivé",
"harnessThinkingMinimal": "Minimal",
"harnessThinkingLow": "Faible",
"harnessThinkingMedium": "Moyen",
"harnessThinkingHigh": "Élevé",
"harnessThinkingExtraHigh": "Très élevé",
"harnessToolPermissions": "Autorisations des outils",
"harnessPermissionReads": "Demander avant les modifications et les commandes",
"harnessPermissionEdits": "Demander avant les commandes",
"harnessPermissionAll": "Autoriser tous les outils"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "Livello di ragionamento",
"harnessThinkingOff": "Disattivato",
"harnessThinkingMinimal": "Minimo",
"harnessThinkingLow": "Basso",
"harnessThinkingMedium": "Medio",
"harnessThinkingHigh": "Alto",
"harnessThinkingExtraHigh": "Molto alto",
"harnessToolPermissions": "Autorizzazioni degli strumenti",
"harnessPermissionReads": "Chiedi prima di modifiche e comandi",
"harnessPermissionEdits": "Chiedi prima dei comandi",
"harnessPermissionAll": "Consenti tutti gli strumenti"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "思考レベル",
"harnessThinkingOff": "オフ",
"harnessThinkingMinimal": "最小",
"harnessThinkingLow": "低",
"harnessThinkingMedium": "中",
"harnessThinkingHigh": "高",
"harnessThinkingExtraHigh": "最高",
"harnessToolPermissions": "ツール権限",
"harnessPermissionReads": "編集とコマンドの前に確認",
"harnessPermissionEdits": "コマンドの前に確認",
"harnessPermissionAll": "すべてのツールを許可"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "Poziom rozumowania",
"harnessThinkingOff": "Wyłączony",
"harnessThinkingMinimal": "Minimalny",
"harnessThinkingLow": "Niski",
"harnessThinkingMedium": "Średni",
"harnessThinkingHigh": "Wysoki",
"harnessThinkingExtraHigh": "Bardzo wysoki",
"harnessToolPermissions": "Uprawnienia narzędzi",
"harnessPermissionReads": "Pytaj przed zmianami i poleceniami",
"harnessPermissionEdits": "Pytaj przed poleceniami",
"harnessPermissionAll": "Zezwalaj na wszystkie narzędzia"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "Уровень рассуждений",
"harnessThinkingOff": "Выключен",
"harnessThinkingMinimal": "Минимальный",
"harnessThinkingLow": "Низкий",
"harnessThinkingMedium": "Средний",
"harnessThinkingHigh": "Высокий",
"harnessThinkingExtraHigh": "Очень высокий",
"harnessToolPermissions": "Разрешения инструментов",
"harnessPermissionReads": "Спрашивать перед изменениями и командами",
"harnessPermissionEdits": "Спрашивать перед командами",
"harnessPermissionAll": "Разрешить все инструменты"
}

View file

@ -297,5 +297,16 @@
"codeUpdatedLive": "Updated live",
"codePreviewFailed": "Preview failed",
"codeGeneratedReadOnly": "Generated, read only",
"codeReset": "Reset"
"codeReset": "Reset",
"harnessThinkingLevel": "思考级别",
"harnessThinkingOff": "关闭",
"harnessThinkingMinimal": "最低",
"harnessThinkingLow": "低",
"harnessThinkingMedium": "中",
"harnessThinkingHigh": "高",
"harnessThinkingExtraHigh": "极高",
"harnessToolPermissions": "工具权限",
"harnessPermissionReads": "编辑和执行命令前询问",
"harnessPermissionEdits": "执行命令前询问",
"harnessPermissionAll": "允许所有工具"
}

View file

@ -219,6 +219,17 @@ export const dialogMessageDefaults = {
selectDesignModel: 'Select design model',
modelNeedsCredential: 'Needs key',
modelAgentConnection: 'Agent',
harnessThinkingLevel: 'Thinking level',
harnessThinkingOff: 'Off',
harnessThinkingMinimal: 'Minimal',
harnessThinkingLow: 'Low',
harnessThinkingMedium: 'Medium',
harnessThinkingHigh: 'High',
harnessThinkingExtraHigh: 'Extra high',
harnessToolPermissions: 'Tool permissions',
harnessPermissionReads: 'Ask before edits and commands',
harnessPermissionEdits: 'Ask before commands',
harnessPermissionAll: 'Allow all tools',
saveModel: 'Save model',
deleteModel: 'Delete model',
deleteModelDescription: 'Delete this model and remove its role assignments?',

View file

@ -37,9 +37,12 @@ export const unsplashKeyStatus = ref<CredentialStatus>('missing')
const credentialRevision = ref(0)
export const isACPProvider = computed(() => providerID.value.startsWith('acp:'))
export const isHarnessProvider = computed(() => providerID.value === 'harness:pi')
export const isAgentProvider = computed(() => isACPProvider.value || isHarnessProvider.value)
export const isConfigured = computed(() => {
if (isACPProvider.value) return IS_TAURI
if (isHarnessProvider.value) return IS_TAURI && apiKeyStatus.value === 'configured'
if (apiKeyStatus.value !== 'configured') return false
const needsBaseURL =
providerID.value === 'openai-compatible' || providerID.value === 'anthropic-compatible'

View file

@ -15,7 +15,7 @@ import {
import { resolveLanguageModelID } from '@/app/ai/chat/model'
import { buildReasoningProviderOptions, type AIProviderOptions } from '@/app/ai/chat/reasoning'
import SYSTEM_PROMPT from '@/app/ai/chat/system-prompt.md?raw'
import { createAIModelRuntime } from '@/app/ai/models'
import { createAIModelRuntime, resolveModelConnectionAPIKey } from '@/app/ai/models'
import { MAX_AGENT_STEPS, createAITools, recordStepUsage, resetRunSteps } from '@/app/ai/tools'
import type { getActiveEditorStore } from '@/app/editor/active-store'
@ -24,6 +24,7 @@ type EditorStore = ReturnType<typeof getActiveEditorStore>
type ChatSessionOptions = {
isConfigured: ComputedRef<boolean>
isACPProvider: ComputedRef<boolean>
isHarnessProvider: ComputedRef<boolean>
providerID: Ref<AIProviderID>
credentialsReady: Promise<void>
getActiveEditorStore: () => EditorStore
@ -120,6 +121,7 @@ export function createToolLoopTransport({
export function createChatSessionManager({
isConfigured,
isACPProvider,
isHarnessProvider,
providerID,
credentialsReady,
getActiveEditorStore
@ -130,6 +132,7 @@ export function createChatSessionManager({
let currentChatMessages = new WeakMap<EditorStore, UIMessage[]>()
let chat: Chat<UIMessage> | null = null
let acpTransportInstance: { destroy(): Promise<void> } | null = null
let harnessTransportInstance: { destroy(): Promise<void> } | null = null
let overrideTransport: (() => ChatTransport<UIMessage>) | null = null
function handleChatFinish({
@ -154,18 +157,58 @@ export function createChatSessionManager({
currentChatMessages = new WeakMap()
}
async function destroyAgentTransports(): Promise<void> {
const acp = acpTransportInstance
const harness = harnessTransportInstance
acpTransportInstance = null
harnessTransportInstance = null
const results = await Promise.allSettled([acp?.destroy(), harness?.destroy()])
const errors = results
.filter((result) => result.status === 'rejected')
.map((result) => result.reason)
if (errors.length) throw new AggregateError(errors, 'Agent transport teardown failed')
}
async function createActiveACPTransport() {
await acpTransportInstance?.destroy()
await destroyAgentTransports()
const transport = await createACPTransport(providerID.value)
acpTransportInstance = transport
return transport as ChatTransport<UIMessage>
}
async function createActiveHarnessTransport() {
await destroyAgentTransports()
const runtime = await createAIModelRuntime('design')
if (runtime?.kind !== 'harness') throw new Error('The Design agent is not configured for Pi')
const [{ HarnessChatTransport }, { buildPiMCPServers }, { getActiveTabId }] = await Promise.all(
[import('@/app/ai/harness/transport'), import('@/app/integrations/mcp'), import('@/app/tabs')]
)
const apiKey = await resolveModelConnectionAPIKey(runtime.role.connection.id)
if (!apiKey) throw new Error('Credential is unavailable for the Pi agent')
const model = runtime.role.profile.customModelID || runtime.role.profile.modelID
const transport = new HarnessChatTransport(
`tab-${getActiveTabId()}-${runtime.role.profile.id}`,
{
adapter: 'pi',
sandbox: 'just-bash',
model,
settings: {
thinkingLevel: runtime.role.profile.harnessThinkingLevel ?? 'medium',
permissionMode: runtime.role.profile.harnessPermissionMode ?? 'allow-edits'
},
instructions: SYSTEM_PROMPT,
mcpServers: await buildPiMCPServers()
},
{ OPENPENCIL_HARNESS_API_KEY: apiKey }
)
harnessTransportInstance = transport
return transport as ChatTransport<UIMessage>
}
async function createTransport(store: EditorStore) {
if (overrideTransport) return overrideTransport()
void acpTransportInstance?.destroy()
acpTransportInstance = null
await destroyAgentTransports()
const runtime = await createAIModelRuntime('design')
if (runtime?.kind !== 'direct') {
@ -196,9 +239,10 @@ export function createChatSessionManager({
if (!chat || transportDirty || currentChatStore !== store) {
const messages = currentChatMessages.get(store)
const transport: ChatTransport<UIMessage> = isACPProvider.value
? await createActiveACPTransport()
: await createTransport(store)
let transport: ChatTransport<UIMessage>
if (isACPProvider.value) transport = await createActiveACPTransport()
else if (isHarnessProvider.value) transport = await createActiveHarnessTransport()
else transport = await createTransport(store)
chat = new Chat<UIMessage>({
transport,
messages,
@ -213,8 +257,9 @@ export function createChatSessionManager({
return chat
}
function resetChat() {
async function resetChat() {
if (currentChatStore) currentChatMessages.delete(currentChatStore)
await destroyAgentTransports()
failure.value = null
chat = null
currentChatStore = null

View file

@ -10,6 +10,7 @@ import {
customBaseURL,
customModelID,
isACPProvider,
isHarnessProvider,
isConfigured,
maxOutputTokens,
modelID,
@ -33,6 +34,7 @@ const activeTab = ref<'design' | 'code' | 'ai'>('design')
const chatSession = createChatSessionManager({
isConfigured,
isACPProvider,
isHarnessProvider,
providerID,
credentialsReady,
getActiveEditorStore

View file

@ -0,0 +1,67 @@
import type { HarnessSidecarMessage } from '@open-pencil/harness'
export type HarnessChild = {
write(data: number[]): Promise<void>
kill(): Promise<void>
}
export type HarnessProcess = {
child: HarnessChild
messages: ReadableStream<HarnessSidecarMessage>
send(request: object): Promise<void>
}
export async function spawnHarnessProcess(options: {
environment: Record<string, string>
onUnexpectedClose: () => void
}): Promise<HarnessProcess> {
const { Command } = await import('@tauri-apps/plugin-shell')
const command = Command.sidecar('binaries/openpencil-harness', [], {
encoding: 'raw',
env: options.environment
})
let buffer = ''
let controller: ReadableStreamDefaultController<HarnessSidecarMessage> | undefined
const decoder = new TextDecoder()
const messages = new ReadableStream<HarnessSidecarMessage>({
start(streamController) {
controller = streamController
}
})
function flush(chunk: Uint8Array): void {
buffer += decoder.decode(chunk, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() ?? ''
for (const line of lines) {
if (!line.trim()) continue
try {
controller?.enqueue(JSON.parse(line) as HarnessSidecarMessage)
} catch (error) {
console.warn('[Harness] Ignoring malformed sidecar output:', error)
}
}
}
command.stdout.on('data', (raw: Uint8Array | number[]) => {
flush(raw instanceof Uint8Array ? raw : new Uint8Array(raw))
})
command.stderr.on('data', (raw: Uint8Array | number[] | string) => {
const text = typeof raw === 'string' ? raw : decoder.decode(new Uint8Array(raw))
console.error('[Harness]', text)
})
command.on('close', () => {
controller?.close()
options.onUnexpectedClose()
})
const child = await command.spawn()
return {
child,
messages,
async send(request) {
await child.write(Array.from(new TextEncoder().encode(`${JSON.stringify(request)}\n`)))
}
}
}

View file

@ -0,0 +1,256 @@
import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'
import type {
HarnessSessionConfiguration,
HarnessSidecarMessage,
HarnessTurnEvent
} from '@open-pencil/harness'
import { spawnHarnessProcess, type HarnessProcess } from './process'
interface PendingRequest {
controller?: ReadableStreamDefaultController<UIMessageChunk>
resolve: (message: HarnessSidecarMessage) => void
reject: (error: Error) => void
textId: string
textStarted: boolean
reasoningStarted: boolean
}
function requestID(): string {
if (typeof crypto === 'undefined' || typeof crypto.getRandomValues !== 'function') {
throw new TypeError('Harness requests require Web Crypto')
}
const bytes = crypto.getRandomValues(new Uint8Array(16))
return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('')
}
function mapEvent(event: HarnessTurnEvent, pending: PendingRequest): UIMessageChunk[] {
const chunks: UIMessageChunk[] = []
if (event.type === 'text-delta') {
if (!pending.textStarted) {
chunks.push({ type: 'text-start', id: pending.textId })
pending.textStarted = true
}
chunks.push({ type: 'text-delta', id: pending.textId, delta: event.text })
} else if (event.type === 'reasoning-delta') {
const id = `reasoning-${pending.textId}`
if (!pending.reasoningStarted) {
chunks.push({ type: 'reasoning-start', id })
pending.reasoningStarted = true
}
chunks.push({ type: 'reasoning-delta', id, delta: event.text })
} else if (event.type === 'tool-call') {
chunks.push({
type: 'tool-input-available',
toolCallId: event.toolCallId,
toolName: event.toolName,
input: event.input,
providerExecuted: true
})
} else if (event.type === 'tool-result') {
chunks.push({
type: 'tool-output-available',
toolCallId: event.toolCallId,
output: event.output,
providerExecuted: true
})
} else if (event.type === 'error') {
chunks.push({ type: 'error', errorText: event.message })
}
return chunks
}
export class HarnessChatTransport implements ChatTransport<UIMessage> {
private process: HarnessProcess | null = null
private readonly pending = new Map<string, PendingRequest>()
private sessionCreated = false
private destroyed = false
constructor(
private readonly sessionId: string,
private readonly configuration: HarnessSessionConfiguration,
private readonly environment: Record<string, string>
) {}
async sendMessages({
messages,
abortSignal
}: Parameters<ChatTransport<UIMessage>['sendMessages']>[0]): Promise<
ReadableStream<UIMessageChunk>
> {
const process = await this.ensureProcess()
if (!this.sessionCreated) {
await this.request({
method: 'session.create',
params: { sessionId: this.sessionId, configuration: this.configuration }
})
this.sessionCreated = true
}
const lastUserMessage = [...messages].reverse().find((message) => message.role === 'user')
const text =
lastUserMessage?.parts
.filter((part): part is { type: 'text'; text: string } => part.type === 'text')
.map((part) => part.text)
.join('\n') ?? ''
const requestId = requestID()
return new ReadableStream<UIMessageChunk>({
start: (controller) => {
const pending = this.createPending(requestId)
pending.controller = controller
this.pending.set(requestId, pending)
controller.enqueue({ type: 'start' })
controller.enqueue({ type: 'start-step' })
const cancel = () => {
void process
.send({
id: requestID(),
method: 'session.cancel',
params: { sessionId: this.sessionId }
})
.catch((error) =>
this.failAll(error instanceof Error ? error : new Error(String(error)))
)
}
if (abortSignal?.aborted) {
cancel()
controller.enqueue({ type: 'finish-step' })
controller.enqueue({ type: 'finish', finishReason: 'stop' })
controller.close()
this.pending.delete(requestId)
return
}
abortSignal?.addEventListener('abort', cancel, { once: true })
void process.send({
id: requestId,
method: 'session.turn',
params: { sessionId: this.sessionId, prompt: text }
})
}
})
}
async reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {
return null
}
async destroy(): Promise<void> {
if (this.destroyed) return
this.destroyed = true
const process = this.process
const sessionCreated = this.sessionCreated
this.process = null
this.sessionCreated = false
if (process) {
if (sessionCreated) {
await process
.send({
id: requestID(),
method: 'session.destroy',
params: { sessionId: this.sessionId }
})
.catch(() => undefined)
}
await process.child.kill().catch(() => undefined)
}
for (const pending of this.pending.values()) pending.reject(new Error('Harness stopped'))
this.pending.clear()
}
private createPending(id: string): PendingRequest {
return {
resolve: () => undefined,
reject: () => undefined,
textId: `text-${id}`,
textStarted: false,
reasoningStarted: false
}
}
private async request(request: {
method: string
params: object
}): Promise<HarnessSidecarMessage> {
const process = await this.ensureProcess()
const id = requestID()
return new Promise((resolve, reject) => {
this.pending.set(id, {
resolve,
reject,
textId: `text-${id}`,
textStarted: false,
reasoningStarted: false
})
void process.send({ id, ...request }).catch(reject)
})
}
private async ensureProcess(): Promise<HarnessProcess> {
if (this.process) return this.process
this.destroyed = false
const environment = this.environment
const process = await spawnHarnessProcess({
environment,
onUnexpectedClose: () => this.failAll(new Error('Harness process exited unexpectedly'))
})
this.process = process
void this.readMessages(process)
return process
}
private async readMessages(process: HarnessProcess): Promise<void> {
try {
const reader = process.messages.getReader()
let next = await reader.read()
while (!next.done) {
const message = next.value
if (message.type === 'turn.event') {
const pending = this.pending.get(message.id)
if (pending?.controller) {
for (const chunk of mapEvent(message.event, pending)) pending.controller.enqueue(chunk)
}
} else {
const pending = this.pending.get(message.id)
if (pending) {
this.pending.delete(message.id)
if (message.error) {
const error = new Error(message.error)
pending.controller?.enqueue({ type: 'error', errorText: error.message })
pending.reject(error)
} else {
if (pending.reasoningStarted) {
pending.controller?.enqueue({
type: 'reasoning-end',
id: `reasoning-${pending.textId}`
})
}
if (pending.textStarted) {
pending.controller?.enqueue({ type: 'text-end', id: pending.textId })
}
pending.controller?.enqueue({ type: 'finish-step' })
pending.controller?.enqueue({ type: 'finish', finishReason: 'stop' })
pending.resolve(message)
}
pending.controller?.close()
}
}
next = await reader.read()
}
} catch (error) {
this.failAll(error instanceof Error ? error : new Error(String(error)))
}
}
private failAll(error: Error): void {
if (this.destroyed) return
this.process = null
this.sessionCreated = false
for (const pending of this.pending.values()) {
pending.controller?.enqueue({ type: 'error', errorText: error.message })
pending.controller?.close()
pending.reject(error)
}
this.pending.clear()
}
}

View file

@ -47,17 +47,22 @@ function normalizeModel(id: string, model: ModelsDevModel): ModelOption {
}
}
async function loadCatalog(fetcher: typeof fetch): Promise<ModelsDevCatalog | null> {
async function loadCatalog(
fetcher: typeof fetch,
options: { useCache: boolean }
): Promise<ModelsDevCatalog | null> {
if (options.useCache) {
const cached = await readCacheJSON<ModelsDevCatalog>(
MODELS_DEV_CACHE_KEY,
MODELS_DEV_CACHE_TTL_MS
)
if (cached) return cached
}
try {
const response = await fetcher(MODELS_DEV_URL)
if (!response.ok) throw new Error(`models.dev catalog request failed: ${response.status}`)
const catalog = (await response.json()) as ModelsDevCatalog
await writeCacheJSON(MODELS_DEV_CACHE_KEY, catalog)
if (options.useCache) await writeCacheJSON(MODELS_DEV_CACHE_KEY, catalog)
return catalog
} catch {
return null
@ -81,12 +86,17 @@ function modelIDCandidates(providerKey: string, modelID: string): string[] {
export async function resolveModelsDevModel(
providerID: AIProviderID,
modelID: string,
fetcher: typeof fetch = fetch
fetcher?: typeof fetch
): Promise<ModelOption | null> {
const providerKeys = PROVIDER_KEYS[providerID]
if (!providerKeys?.length || !modelID) return null
catalogPromise ??= loadCatalog(fetcher)
const catalog = await catalogPromise
const nativeFetch = typeof globalThis.fetch === 'function' ? globalThis.fetch : undefined
const resolvedFetcher = fetcher ?? nativeFetch
if (!resolvedFetcher) return null
const useCache = resolvedFetcher === nativeFetch
const catalog = await (useCache
? (catalogPromise ??= loadCatalog(resolvedFetcher, { useCache: true }))
: loadCatalog(resolvedFetcher, { useCache: false }))
if (!catalog) return null
for (const providerKey of providerKeys) {

View file

@ -21,7 +21,12 @@ export type ACPModelRuntime = {
role: ResolvedAIModelRole
}
export type AIModelRuntime = DirectAIModelRuntime | ACPModelRuntime
export type HarnessModelRuntime = {
kind: 'harness'
role: ResolvedAIModelRole
}
export type AIModelRuntime = DirectAIModelRuntime | ACPModelRuntime | HarnessModelRuntime
export function modelConnectionCredentialRef(connection: AIModelConnection): CredentialRef {
return providerCredentialRef(connection.providerID, connection.credentialProfileId)
@ -60,6 +65,12 @@ export async function createAIModelRuntime(role: AIModelRole): Promise<AIModelRu
if (role === 'vision' && !resolved.profile.capabilities.includes('vision')) {
throw new Error('The Vision model must support image input')
}
if (resolved.connection.providerID === 'harness:pi') {
if (role !== 'design') {
throw new Error('Harness agents can only be assigned to the Design agent role')
}
return { kind: 'harness', role: resolved }
}
if (resolved.connection.providerID.startsWith('acp:')) {
if (role !== 'design') {
throw new Error('ACP agents can only be assigned to the Design agent role')

View file

@ -12,17 +12,21 @@ import {
readLegacyAIModelStorage,
writeAIModelSettingsStorage
} from '@/app/ai/models/storage'
import type {
AIModelCapability,
AIModelConnection,
AIModelProfile,
AIModelProfileDraft,
AIModelProfileId,
AIModelRole,
AIModelRoleAssignment,
AIModelSettings,
OptionalAIModelRole,
ResolvedAIModelRole
import {
HARNESS_PERMISSION_MODES,
HARNESS_THINKING_LEVELS,
type AIModelCapability,
type AIModelConnection,
type AIModelProfile,
type AIModelProfileDraft,
type AIModelProfileId,
type AIModelRole,
type AIModelRoleAssignment,
type AIModelSettings,
type OptionalAIModelRole,
type ResolvedAIModelRole,
type HarnessPermissionMode,
type HarnessThinkingLevel
} from '@/app/ai/models/types'
const LEGACY_CONNECTION_ID = 'connection-default'
@ -44,6 +48,18 @@ function isAPIType(value: unknown): value is 'completions' | 'responses' {
return value === 'completions' || value === 'responses'
}
function isHarnessThinkingLevel(value: unknown): value is HarnessThinkingLevel {
return (
typeof value === 'string' && HARNESS_THINKING_LEVELS.includes(value as HarnessThinkingLevel)
)
}
function isHarnessPermissionMode(value: unknown): value is HarnessPermissionMode {
return (
typeof value === 'string' && HARNESS_PERMISSION_MODES.includes(value as HarnessPermissionMode)
)
}
function isCapability(value: unknown): value is AIModelCapability {
return value === 'tools' || value === 'vision'
}
@ -88,6 +104,12 @@ function parseProfile(value: unknown, connectionIds: Set<string>): AIModelProfil
customModelID: stringValue(value.customModelID),
maxOutputTokens: normalizedMaxOutputTokens(value.maxOutputTokens),
reasoningEffort: stringValue(value.reasoningEffort).trim() || undefined,
harnessThinkingLevel: isHarnessThinkingLevel(value.harnessThinkingLevel)
? value.harnessThinkingLevel
: undefined,
harnessPermissionMode: isHarnessPermissionMode(value.harnessPermissionMode)
? value.harnessPermissionMode
: undefined,
capabilities: [...new Set(capabilities)]
}
}
@ -126,9 +148,10 @@ function parseSettings(value: unknown): AIModelSettings | null {
const profileId = assignment === 'design' ? resolvedDesign : assignment
const profile = models.find((candidate) => candidate.id === profileId)
const connection = connections.find((candidate) => candidate.id === profile?.connectionId)
const invalidACP = connection?.providerID.startsWith('acp:')
const invalidAgent =
connection?.providerID.startsWith('acp:') || connection?.providerID === 'harness:pi'
const invalidVision = role === 'vision' && !profile?.capabilities.includes('vision')
if (invalidACP || invalidVision) assignments[role] = null
if (invalidAgent || invalidVision) assignments[role] = null
}
return { version: 1, connections, models, assignments }
}
@ -212,6 +235,11 @@ export function designModelProfiles(): AIModelProfile[] {
return aiModelSettings.value.models.filter(isDesignModelProfile)
}
export function isAgentModelProfile(profile: AIModelProfile | null): boolean {
const providerID = profile ? modelConnection(profile.connectionId)?.providerID : undefined
return Boolean(providerID?.startsWith('acp:') || providerID === 'harness:pi')
}
export function isACPModelProfile(profile: AIModelProfile | null): boolean {
return Boolean(profile && modelConnection(profile.connectionId)?.providerID.startsWith('acp:'))
}
@ -279,6 +307,8 @@ function draftForProfile(
customAPIType: connection.customAPIType,
maxOutputTokens: profile.maxOutputTokens,
reasoningEffort: profile.reasoningEffort ?? '',
harnessThinkingLevel: profile.harnessThinkingLevel ?? 'medium',
harnessPermissionMode: profile.harnessPermissionMode ?? 'allow-edits',
capabilities: [...profile.capabilities]
}
}
@ -297,6 +327,8 @@ function newProfileDraft(connection: AIModelConnection | null): AIModelProfileDr
customAPIType: connection?.customAPIType ?? 'completions',
maxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS,
reasoningEffort: '',
harnessThinkingLevel: 'medium',
harnessPermissionMode: 'allow-edits',
capabilities: ['tools']
}
}
@ -331,6 +363,10 @@ export function saveModelProfileDraft(draft: AIModelProfileDraft): AIModelProfil
customModelID: draft.customModelID.trim(),
maxOutputTokens: normalizedMaxOutputTokens(draft.maxOutputTokens),
reasoningEffort: draft.reasoningEffort.trim() || undefined,
harnessThinkingLevel:
draft.providerID === 'harness:pi' ? draft.harnessThinkingLevel : undefined,
harnessPermissionMode:
draft.providerID === 'harness:pi' ? draft.harnessPermissionMode : undefined,
capabilities: [...new Set(draft.capabilities)]
}
const index = aiModelSettings.value.models.findIndex((model) => model.id === profile.id)
@ -402,7 +438,7 @@ export function setModelRoleAssignment(role: AIModelRole, assignment: AIModelRol
assignment === 'design'
? modelProfile(aiModelSettings.value.assignments.design)
: modelProfile(assignment)
if (isACPModelProfile(profile)) return
if (isAgentModelProfile(profile)) return
if (role === 'vision' && !profile?.capabilities.includes('vision')) return
}
aiModelSettings.value.assignments[role] = assignment

View file

@ -2,6 +2,11 @@ import type { AIProviderID } from '@open-pencil/core/constants'
export const AI_MODEL_ROLES = ['design', 'review', 'fast', 'vision'] as const
export const AI_MODEL_CAPABILITIES = ['tools', 'vision'] as const
export const HARNESS_THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh'] as const
export const HARNESS_PERMISSION_MODES = ['allow-reads', 'allow-edits', 'allow-all'] as const
export type HarnessThinkingLevel = (typeof HARNESS_THINKING_LEVELS)[number]
export type HarnessPermissionMode = (typeof HARNESS_PERMISSION_MODES)[number]
export type AIModelRole = (typeof AI_MODEL_ROLES)[number]
export type AIModelProfileId = `model-${string}`
@ -25,6 +30,8 @@ export type AIModelProfile = {
customModelID: string
maxOutputTokens: number
reasoningEffort?: string
harnessThinkingLevel?: HarnessThinkingLevel
harnessPermissionMode?: HarnessPermissionMode
capabilities: AIModelCapability[]
}
@ -53,6 +60,8 @@ export type AIModelProfileDraft = {
customAPIType: 'completions' | 'responses'
maxOutputTokens: number
reasoningEffort: string
harnessThinkingLevel: HarnessThinkingLevel
harnessPermissionMode: HarnessPermissionMode
capabilities: AIModelCapability[]
}

View file

@ -10,7 +10,7 @@ import {
} from '@/app/ai/providers/compatible'
import type { ModelProviderAdapter } from '@/app/ai/providers/types'
type DirectProviderID = Exclude<AIProviderID, `acp:${string}`>
type DirectProviderID = Exclude<AIProviderID, `acp:${string}` | `harness:${string}`>
const MODEL_PROVIDER_ADAPTERS = {
openrouter: {
@ -52,12 +52,12 @@ const MODEL_PROVIDER_ADAPTERS = {
} satisfies Record<DirectProviderID, ModelProviderAdapter>
function isDirectProviderID(providerID: AIProviderID): providerID is DirectProviderID {
return !providerID.startsWith('acp:')
return !providerID.startsWith('acp:') && providerID !== 'harness:pi'
}
export function modelProviderAdapter(providerID: AIProviderID): ModelProviderAdapter {
if (!isDirectProviderID(providerID)) {
throw new Error('ACP providers do not use direct API models')
throw new Error('ACP providers and Harness agents do not use direct API models')
}
return MODEL_PROVIDER_ADAPTERS[providerID]
}

View file

@ -1,3 +1,4 @@
export * from './pi'
export * from './runtime'
export * from './store'
export type * from './types'

View file

@ -0,0 +1,51 @@
import { AUTOMATION_HTTP_PORT } from '@open-pencil/core/constants'
import { getAutomationAuthToken } from '@/app/automation/mcp/spawn'
import { appCredentialServices } from '@/app/settings/credentials/app'
import { enabledMCPConnections } from './store'
import type { MCPConnection } from './types'
export type PiMCPServer = {
url: string
headers?: Record<string, string>
auth: false
}
async function externalPiMCPServer(connection: MCPConnection): Promise<PiMCPServer> {
const headers: Record<string, string> = {}
if (connection.authentication.type === 'bearer') {
const token = await appCredentialServices.resolver.resolve(
connection.authentication.credentialRef
)
if (!token) throw new Error(`MCP connection "${connection.name}" needs a bearer token`)
headers.Authorization = `Bearer ${token}`
}
return {
url: connection.transport.url,
...(Object.keys(headers).length ? { headers } : {}),
auth: false
}
}
export async function buildPiMCPServers(options?: {
authorizationToken?: string | null
}): Promise<Record<string, PiMCPServer>> {
const authorizationToken =
options && 'authorizationToken' in options
? options.authorizationToken
: await getAutomationAuthToken()
const entries = await Promise.all(
enabledMCPConnections.value.map(
async (connection) => [connection.name, await externalPiMCPServer(connection)] as const
)
)
return {
...Object.fromEntries(entries),
'open-pencil': {
url: `http://127.0.0.1:${AUTOMATION_HTTP_PORT}/mcp`,
...(authorizationToken ? { headers: { Authorization: `Bearer ${authorizationToken}` } } : {}),
auth: false
}
}
}

View file

@ -17,7 +17,8 @@ export function providerCredentialRef(
if (providerId.startsWith('acp:')) {
throw new Error('ACP agents do not use API-key credentials')
}
return credentialRef(providerId, 'api-key', profileId)
const integrationId = providerId === 'harness:pi' ? 'harness-pi' : providerId
return credentialRef(integrationId, 'api-key', profileId)
}
export const PEXELS_CREDENTIAL = credentialRef('pexels', 'api-key')

View file

@ -242,7 +242,9 @@ function handleClearChat() {
clearChatFailure()
clearImageAttachmentPresentations()
chat.value = null
resetChat()
void resetChat().catch((error: unknown) => {
console.error('Chat reset error:', error)
})
clearToolLogEntries()
clearACPDebugLog()
}

View file

@ -76,8 +76,11 @@ function removeImage(index: number) {
}
const isStreaming = computed(() => disabled || status === 'streaming' || status === 'submitted')
const isACPProvider = computed(() => providerID.value.startsWith('acp:'))
const acpAgentName = computed(() => {
const isAgentProvider = computed(
() => providerID.value.startsWith('acp:') || providerID.value === 'harness:pi'
)
const agentName = computed(() => {
if (providerID.value === 'harness:pi') return 'Pi'
const agentId = providerID.value.replace('acp:', '')
return ACP_AGENTS.find((a) => a.id === agentId)?.name ?? agentId
})
@ -200,10 +203,10 @@ function handleSubmit(e: Event) {
<template #model>
<div class="flex min-w-0 items-center">
<template v-if="isACPProvider">
<template v-if="isAgentProvider">
<div class="flex min-w-0 items-center gap-1 px-1.5 text-[10px] text-muted">
<icon-lucide-bot class="size-3 shrink-0" />
<span class="truncate">{{ acpAgentName }}</span>
<span class="truncate">{{ agentName }}</span>
</div>
</template>
<ChatProfileSelect

View file

@ -14,6 +14,7 @@ const editing = ref(false)
const editingProfileId = ref<string>()
const statusByConnection = ref<Record<string, CredentialStatus>>({})
function providerName(providerID: string): string {
if (providerID === 'harness:pi') return 'Pi'
if (providerID.startsWith('acp:')) {
const agentID = providerID.slice('acp:'.length)
return ACP_AGENTS.find((agent) => agent.id === agentID)?.name ?? providerID

View file

@ -56,13 +56,16 @@ const connectionTestStatus = ref<'idle' | 'testing' | 'success' | 'error'>('idle
const connectionTestReason = ref<ProviderConnectionTestFailureReason | null>(null)
const deleteOpen = ref(false)
const advancedOpen = ref(Boolean(draft.customModelID.trim()))
const customModelSelected = ref(Boolean(draft.customModelID.trim()))
const customModelSelected = ref(
Boolean(draft.customModelID.trim()) || draft.providerID === 'harness:pi'
)
const catalogModel = ref<(typeof providerDef.value.models)[number] | null>(null)
const providerDef = computed(
() => AI_PROVIDERS.find((provider) => provider.id === draft.providerID) ?? AI_PROVIDERS[0]
)
const isACP = computed(() => draft.providerID.startsWith('acp:'))
const isHarness = computed(() => draft.providerID === 'harness:pi')
const supportsReasoningEffort = computed(() =>
['openai', 'openai-compatible', 'openrouter'].includes(draft.providerID)
)
@ -81,6 +84,7 @@ const selectedModelValue = computed(() =>
customModelSelected.value ? CUSTOM_MODEL_VALUE : draft.modelID
)
const knownModel = computed(() => {
if (isACP.value) return null
if (draft.customModelID.trim()) return catalogModel.value
return (
catalogModel.value ??
@ -111,7 +115,7 @@ const canSave = computed(
: Boolean(draft.modelID.trim())))
)
const canTest = computed(() => {
if (isACP.value) return false
if (isACP.value || isHarness.value) return false
if (!keyInput.value.trim() && !hasExistingKey.value) return false
if (providerDef.value.supportsCustomBaseURL && !draft.customBaseURL.trim()) return false
return customModelSelected.value
@ -146,6 +150,10 @@ function effectiveModelID(): string {
}
async function refreshCatalogModel(): Promise<void> {
if (isACP.value) {
catalogModel.value = null
return
}
const providerID = draft.providerID
const modelID = effectiveModelID()
catalogModel.value = await resolveModelsDevModel(providerID, modelID)
@ -165,10 +173,10 @@ function updateProvider(providerID: AIProviderID): void {
const provider = AI_PROVIDERS.find((definition) => definition.id === providerID)
draft.modelID = provider?.defaultModel ?? ''
draft.customModelID = ''
customModelSelected.value = providerID === 'harness:pi'
draft.customBaseURL = ''
draft.customAPIType = 'completions'
advancedOpen.value = false
customModelSelected.value = false
advancedOpen.value = providerID === 'harness:pi'
if (providerID.startsWith('acp:')) {
draft.capabilities = ['tools']
if (!draft.name.trim()) draft.name = providerDisplayName.value
@ -392,7 +400,11 @@ void refreshKeyStatus()
/>
</template>
<CollapsibleRoot v-model:open="advancedOpen" class="rounded border border-border">
<CollapsibleRoot
v-if="!isACP"
v-model:open="advancedOpen"
class="rounded border border-border"
>
<CollapsibleTrigger
class="flex w-full items-center gap-2 px-2.5 py-2 text-left text-[11px] text-muted hover:text-surface"
>
@ -433,6 +445,33 @@ void refreshKeyStatus()
</div>
</div>
<ProviderSettingsField v-if="isHarness" :label="dialogs.harnessThinkingLevel">
<AppSelect
v-model="draft.harnessThinkingLevel"
:label="dialogs.harnessThinkingLevel"
:options="[
{ value: 'off', label: dialogs.harnessThinkingOff },
{ value: 'minimal', label: dialogs.harnessThinkingMinimal },
{ value: 'low', label: dialogs.harnessThinkingLow },
{ value: 'medium', label: dialogs.harnessThinkingMedium },
{ value: 'high', label: dialogs.harnessThinkingHigh },
{ value: 'xhigh', label: dialogs.harnessThinkingExtraHigh }
]"
/>
</ProviderSettingsField>
<ProviderSettingsField v-if="isHarness" :label="dialogs.harnessToolPermissions">
<AppSelect
v-model="draft.harnessPermissionMode"
:label="dialogs.harnessToolPermissions"
:options="[
{ value: 'allow-reads', label: dialogs.harnessPermissionReads },
{ value: 'allow-edits', label: dialogs.harnessPermissionEdits },
{ value: 'allow-all', label: dialogs.harnessPermissionAll }
]"
/>
</ProviderSettingsField>
<ProviderSettingsField v-if="supportsReasoningEffort" :label="dialogs.reasoningEffort">
<ProviderSettingsInput
v-model="draft.reasoningEffort"

View file

@ -4,7 +4,7 @@ import { useI18n } from '@open-pencil/vue'
import {
aiModelSettings,
isACPModelProfile,
isAgentModelProfile,
modelProfile,
setModelRoleAssignment,
type AIModelRole,
@ -49,7 +49,7 @@ function optionsForRole(role: AIModelRole) {
const profiles = aiModelSettings.value.models
.filter((profile) => {
if (role === 'design') return profile.capabilities.includes('tools')
if (isACPModelProfile(profile)) return false
if (isAgentModelProfile(profile)) return false
if (role === 'vision') return profile.capabilities.includes('vision')
return true
})
@ -58,7 +58,7 @@ function optionsForRole(role: AIModelRole) {
const design = modelProfile(aiModelSettings.value.assignments.design)
const canInherit =
!isACPModelProfile(design) && (role !== 'vision' || design?.capabilities.includes('vision'))
!isAgentModelProfile(design) && (role !== 'vision' || design?.capabilities.includes('vision'))
return [
...(canInherit ? [{ value: SAME_AS_DESIGN, label: dialogs.value.modelRoleUseDesign }] : []),
{ value: NO_MODEL, label: dialogs.value.noModel },

View file

@ -62,6 +62,7 @@ const providerDef = computed(
)
const displayName = computed(() => {
if (providerID.value === 'harness:pi') return 'Pi'
if (providerID.value.startsWith('acp:')) {
const agentId = providerID.value.replace('acp:', '')
return ACP_AGENTS.find((agent) => agent.id === agentId)?.name ?? providerID.value
@ -83,7 +84,7 @@ const groups = computed(() => {
}
result.push({
label: acpAgents.value.length ? 'API key' : undefined,
label: acpAgents.value.length ? 'Providers' : undefined,
items: [...AI_PROVIDERS]
.sort((left, right) => left.name.localeCompare(right.name))
.map((provider) => ({

View file

@ -141,6 +141,36 @@ describe('AI model profiles and role assignments', () => {
expect(modelSettingsSnapshot().connections).toHaveLength(2)
})
test('stores multiple Harness provider profiles with independent model IDs', () => {
const first = createModelProfileDraft()
Object.assign(first, {
name: 'Pi Sonnet',
providerID: 'harness:pi',
modelID: '',
customModelID: 'anthropic/claude-sonnet-4.6',
harnessThinkingLevel: 'medium',
harnessPermissionMode: 'allow-edits'
})
const second = createModelProfileDraft()
Object.assign(second, {
name: 'Pi custom',
providerID: 'harness:pi',
modelID: '',
customModelID: 'custom/provider-model',
harnessThinkingLevel: 'high',
harnessPermissionMode: 'allow-reads'
})
const savedFirst = saveModelProfileDraft(first)
const savedSecond = saveModelProfileDraft(second)
expect(savedFirst.customModelID).toBe('anthropic/claude-sonnet-4.6')
expect(savedSecond.customModelID).toBe('custom/provider-model')
expect(savedFirst.harnessThinkingLevel).toBe('medium')
expect(savedSecond.harnessThinkingLevel).toBe('high')
expect(savedFirst.harnessPermissionMode).toBe('allow-edits')
expect(savedSecond.harnessPermissionMode).toBe('allow-reads')
})
test('keeps ACP agents exclusive to the Design role', () => {
const settings = modelSettingsSnapshot()
settings.connections.push({

View file

@ -63,12 +63,13 @@ describe('model provider registry', () => {
expect(requestBody).toContain('"model":"MiniMax-M3"')
})
test('registers every direct provider without handling ACP agents as models', () => {
for (const provider of AI_PROVIDERS) {
test('registers every direct provider without handling agent runtimes as models', () => {
for (const provider of AI_PROVIDERS.filter((entry) => entry.id !== 'harness:pi')) {
expect(modelProviderAdapter(provider.id).create).toBeFunction()
}
expect(() => modelProviderAdapter('harness:pi')).toThrow('Harness agents')
expect(() => modelProviderAdapter('acp:claude-code')).toThrow(
'ACP providers do not use direct API models'
'ACP providers and Harness agents do not use direct API models'
)
})
})

View file

@ -0,0 +1,45 @@
import { describe, expect, test } from 'bun:test'
import { replaceMCPConnectionSettings, setMCPConnectionCredential } from '@/app/integrations/mcp'
import { buildPiMCPServers } from '@/app/integrations/mcp/pi'
import { installTauriMockWindow } from '#tests/helpers/tauri/mocks'
installTauriMockWindow()
describe('Pi MCP configuration', () => {
test('adapts the built-in and external MCP servers to Pi native configuration', async () => {
replaceMCPConnectionSettings({
version: 1,
connections: [
{
id: 'mcp-example',
name: 'example',
enabled: true,
transport: { type: 'streamable-http', url: 'https://example.com/mcp' },
authentication: {
type: 'bearer',
credentialRef: {
integrationId: 'mcp',
profileId: 'mcp-example',
field: 'bearer-token'
}
}
}
]
})
await setMCPConnectionCredential('mcp-example', 'external-token')
const servers = await buildPiMCPServers({ authorizationToken: 'built-in-token' })
expect(servers.example).toEqual({
url: 'https://example.com/mcp',
headers: { Authorization: 'Bearer external-token' },
auth: false
})
expect(servers['open-pencil']).toEqual({
url: expect.stringContaining('/mcp'),
headers: { Authorization: 'Bearer built-in-token' },
auth: false
})
})
})

View file

@ -0,0 +1,54 @@
import { afterEach, describe, expect, test } from 'bun:test'
import { spawnHarnessProcess } from '@/app/ai/harness/process'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri/mocks'
afterEach(async () => {
await clearTauriMocks()
})
describe('Harness sidecar process', () => {
test('spawns the bundled sidecar and keeps credentials in process environment', async () => {
let onEvent: ((event: unknown) => void) | undefined
const calls: Array<{ cmd: string; args: unknown }> = []
await mockTauriIPC((cmd, args) => {
calls.push({ cmd, args })
if (cmd === 'plugin:shell|spawn') {
expect(args).toMatchObject({
program: expect.stringContaining('openpencil-harness'),
args: [],
options: {
encoding: 'raw',
env: { OPENPENCIL_HARNESS_API_KEY: 'secret' }
}
})
onEvent = (args as { onEvent: { onmessage: (event: unknown) => void } }).onEvent.onmessage
return 45
}
return null
})
const process = await spawnHarnessProcess({
environment: { OPENPENCIL_HARNESS_API_KEY: 'secret' },
onUnexpectedClose: () => undefined
})
await process.send({ id: 'one', method: 'service.shutdown' })
const reader = process.messages.getReader()
const encoded = new TextEncoder().encode(
'\n' + JSON.stringify({ type: 'response', id: 'one', result: { ok: true } }) + '\n'
)
onEvent?.({ event: 'Stdout', payload: Array.from(encoded.slice(0, 12)) })
onEvent?.({ event: 'Stdout', payload: Array.from(encoded.slice(12)) })
expect((await reader.read()).value).toEqual({
type: 'response',
id: 'one',
result: { ok: true }
})
expect(
JSON.stringify(calls.find((call) => call.cmd === 'plugin:shell|stdin_write'))
).not.toContain('secret')
})
})

View file

@ -0,0 +1,5 @@
{
"name": "@open-pencil/harness-sidecar-tools",
"private": true,
"type": "module"
}

View file

@ -0,0 +1,41 @@
import { execFileSync } from 'node:child_process'
import { chmod, mkdir } from 'node:fs/promises'
import { join } from 'node:path'
const hostTarget = (): string => {
if (process.platform === 'darwin')
return process.arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin'
if (process.platform === 'linux' && process.arch === 'x64') return 'x86_64-unknown-linux-gnu'
if (process.platform === 'win32') {
return process.arch === 'arm64' ? 'aarch64-pc-windows-msvc' : 'x86_64-pc-windows-msvc'
}
throw new Error(`Unsupported Harness sidecar host: ${process.platform}/${process.arch}`)
}
const target = process.argv[2] ?? process.env.TAURI_ENV_TARGET_TRIPLE ?? hostTarget()
const bunTargetByRustTarget: Record<string, string> = {
'aarch64-apple-darwin': 'bun-darwin-arm64',
'x86_64-apple-darwin': 'bun-darwin-x64',
'x86_64-unknown-linux-gnu': 'bun-linux-x64',
'x86_64-pc-windows-msvc': 'bun-windows-x64',
'aarch64-pc-windows-msvc': 'bun-windows-arm64'
}
const bunTarget = bunTargetByRustTarget[target]
if (!bunTarget) throw new Error(`Unsupported Harness sidecar target: ${target}`)
const extension = target.includes('windows') ? '.exe' : ''
const output = join('desktop', 'binaries', `openpencil-harness-${target}${extension}`)
await mkdir(join('desktop', 'binaries'), { recursive: true })
execFileSync(
'bun',
[
'build',
'--compile',
`--target=${bunTarget}`,
'packages/harness/src/stdio.ts',
'--outfile',
output
],
{ stdio: 'inherit' }
)
if (!extension) await chmod(output, 0o755)

View file

@ -37,6 +37,9 @@
"#mcp/*": [
"./packages/mcp/src/*"
],
"@open-pencil/harness": [
"./packages/harness/src/index.ts"
],
"#dom-css/*": [
"./packages/dom-css/src/*"
],