Compare commits

...

36 Commits

Author SHA1 Message Date
Aiden Cline 9fdd4fddb6 fix(session): use session selection for commands 2026-08-17 02:49:33 +00:00
opencode-agent[bot] c5e58b38a8 fix(app): omit shell prompts when copying commands (#42958)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-17 12:33:43 +10:00
Aiden Cline 1f86a6d3f0 fix(core): restore OpenAI request headers (#42959) 2026-08-16 21:25:48 -05:00
Luke Parker be8f975a99 fix(app): reduce session spinner CPU usage (#42952) 2026-08-17 11:04:02 +10:00
opencode-agent[bot] c7abb340f1 chore: generate 2026-08-16 23:37:10 +00:00
opencode-agent[bot] 45771a7e39 chore(util): log spawned processes (#42948)
Co-authored-by: Dax Raad <d@ironbay.co>
2026-08-16 19:36:02 -04:00
Dax 39810da936 docs: reorganize v2 documentation (#42947) 2026-08-16 19:35:48 -04:00
Luke Parker 01c8bf20f9 fix(app): correct background subagent status (#42944) 2026-08-17 09:20:51 +10:00
opencode-agent[bot] 9241a79dc9 chore: generate 2026-08-16 22:50:26 +00:00
Luke Parker e73fa8f4b7 fix(app): clarify skill timeline presentation (#42945) 2026-08-17 08:49:18 +10:00
opencode-agent[bot] fd2699b4d7 fix(tui): clarify saved permission copy (#41144)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
2026-08-16 17:14:19 -05:00
Aiden Cline ec10d71f22 fix(core): keep plan mode reminders in sync with session agent (#42652) 2026-08-16 16:01:02 -05:00
opencode-agent[bot] ca589273c7 fix(core): preserve tool-result images for AI SDK providers (#42849)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-08-16 15:20:37 -05:00
opencode-agent[bot] 75a979ec5c fix(ai): pass media through protocol lowering (#42921)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-16 15:18:43 -05:00
Dax Raad d4f10fa9be fix(release): stop advancing next npm tags 2026-08-16 11:24:58 -04:00
opencode-agent[bot] 08dd3f51ed fix(app): space no git status (#42868)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-16 08:14:58 +00:00
Dax Raad 0e99cb987a fix(desktop): use built CLI during beta build 2026-08-16 03:20:15 -04:00
opencode-agent[bot] 7731d1235d fix(cli): clarify debug config output (#42860)
Co-authored-by: Dax Raad <d@ironbay.co>
2026-08-16 02:39:47 -04:00
opencode-agent[bot] 174c0a742c chore: generate 2026-08-16 06:39:30 +00:00
Dax b080f216a5 feat(cli): add native CPU profiling (#42862) 2026-08-16 02:38:23 -04:00
Simon Klee 0d7fe6e074 fix(cli): omit undefined compile executable path 2026-08-16 08:03:11 +02:00
opencode-agent[bot] 01b7b53eeb chore: generate 2026-08-16 05:32:10 +00:00
Dax Raad 613c570a3b feat(cli): add heap snapshot signal 2026-08-16 01:30:44 -04:00
Dax Raad a48d44955e fix(cli): suppress compiled runtime warnings 2026-08-16 01:03:25 -04:00
Dax Raad 8251934007 fix(core): batch initial streamed delta 2026-08-15 19:26:16 -04:00
Dax Raad 42e345e1bc fix(cli): resolve Bun canary compile assets 2026-08-15 18:56:03 -04:00
Dax 6a7d6c5adc refactor(core): use numeric event timestamps (#42828) 2026-08-15 18:48:58 -04:00
Dax Raad 9e22c40fff feat: align V2 beta release channels 2026-08-15 18:40:23 -04:00
opencode-agent[bot] 467722c2f9 fix(app): use tree directory picker everywhere (#42820)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-16 08:19:44 +10:00
Luke Parker d9dfceddf8 fix(app): release virtualized timeline elements (#42825) 2026-08-16 08:19:06 +10:00
Dax 4fee4d7d86 fix(core): batch streamed session deltas 2026-08-15 18:16:01 -04:00
Luke Parker 61d7f942b5 fix(app): show new session header immediately (#42822) 2026-08-16 08:04:28 +10:00
Dax Raad d8e1753330 chore(cli): build binaries with Bun 1.4 2026-08-15 17:51:09 -04:00
opencode-agent[bot] d94d520f45 fix(tui): make running subagents clickable (#42797)
Co-authored-by: Dax Raad <mail@thdxr.com>
2026-08-15 19:54:40 +00:00
Dax Raad fcc1e9c42f docs(core): add MCP setup guidance 2026-08-15 15:13:28 -04:00
opencode-agent[bot] 64b9ba339e docs(console): prohibit abusive multi-account use (#42813)
Co-authored-by: Dax Raad <mail@thdxr.com>
2026-08-15 15:08:52 -04:00
149 changed files with 2940 additions and 3128 deletions
+10 -3
View File
@@ -1,6 +1,10 @@
name: "Setup Bun" name: "Setup Bun"
description: "Setup Bun with caching and install dependencies" description: "Setup Bun with caching and install dependencies"
inputs: inputs:
bun-version:
description: "Bun version to install instead of the root packageManager version"
required: false
default: ""
install-flags: install-flags:
description: "Additional flags to pass to 'bun install'" description: "Additional flags to pass to 'bun install'"
required: false required: false
@@ -20,19 +24,22 @@ runs:
shell: bash shell: bash
run: | run: |
if [ "$RUNNER_ARCH" = "X64" ]; then if [ "$RUNNER_ARCH" = "X64" ]; then
V=$(node -p "require('./package.json').packageManager.split('@')[1]") V="${{ inputs.bun-version }}"
if [ -z "$V" ]; then V=$(node -p "require('./package.json').packageManager.split('@')[1]"); fi
TAG=$([ "$V" = "canary" ] && echo "canary" || echo "bun-v${V}")
case "$RUNNER_OS" in case "$RUNNER_OS" in
macOS) OS=darwin ;; macOS) OS=darwin ;;
Linux) OS=linux ;; Linux) OS=linux ;;
Windows) OS=windows ;; Windows) OS=windows ;;
esac esac
echo "url=https://github.com/oven-sh/bun/releases/download/bun-v${V}/bun-${OS}-x64-baseline.zip" >> "$GITHUB_OUTPUT" echo "url=https://github.com/oven-sh/bun/releases/download/${TAG}/bun-${OS}-x64-baseline.zip" >> "$GITHUB_OUTPUT"
fi fi
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version-file: ${{ !steps.bun-url.outputs.url && 'package.json' || '' }} bun-version: ${{ !steps.bun-url.outputs.url && inputs.bun-version || '' }}
bun-version-file: ${{ !steps.bun-url.outputs.url && !inputs.bun-version && 'package.json' || '' }}
bun-download-url: ${{ steps.bun-url.outputs.url }} bun-download-url: ${{ steps.bun-url.outputs.url }}
- name: Get cache directory - name: Get cache directory
-37
View File
@@ -1,37 +0,0 @@
name: beta
on:
workflow_dispatch:
schedule:
- cron: "0 * * * *"
jobs:
sync:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Setup Git Committer
id: setup-git-committer
uses: ./.github/actions/setup-git-committer
with:
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
- name: Install OpenCode
run: bun i -g opencode-ai
- name: Sync beta branch
env:
GH_TOKEN: ${{ steps.setup-git-committer.outputs.token }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
run: bun script/beta.ts
+23 -5
View File
@@ -7,6 +7,7 @@ on:
- ci - ci
- dev - dev
- beta - beta
- v2
- fix/npm-native-binary-install - fix/npm-native-binary-install
- snapshot-* - snapshot-*
workflow_dispatch: workflow_dispatch:
@@ -32,7 +33,7 @@ permissions:
packages: write packages: write
env: env:
OPENCODE_CHANNEL: ${{ (github.ref_name == 'v2' && 'next') || '' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'v2' && 'dev') || '' }}
jobs: jobs:
version: version:
@@ -45,6 +46,13 @@ jobs:
- uses: ./.github/actions/setup-bun - uses: ./.github/actions/setup-bun
- name: Deploy update service
if: github.ref_name == 'v2' || github.ref_name == 'beta'
working-directory: packages/updates
run: bun run deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- name: Setup git committer - name: Setup git committer
id: committer id: committer
uses: ./.github/actions/setup-git-committer uses: ./.github/actions/setup-git-committer
@@ -74,13 +82,15 @@ jobs:
build-cli: build-cli:
needs: version needs: version
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'beta' if: github.repository == 'anomalyco/opencode'
steps: steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with: with:
fetch-tags: true fetch-tags: true
- uses: ./.github/actions/setup-bun - uses: ./.github/actions/setup-bun
with:
bun-version: canary # Bun 1.4 until its stable release is published
- name: Setup git committer - name: Setup git committer
id: committer id: committer
@@ -102,6 +112,7 @@ jobs:
id: build id: build
run: ./packages/cli/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} run: ./packages/cli/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }}
env: env:
BUN_COMPILE_RELEASE: canary
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_RELEASE: ${{ needs.version.outputs.release }} OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
GH_REPO: ${{ needs.version.outputs.repo }} GH_REPO: ${{ needs.version.outputs.repo }}
@@ -185,7 +196,7 @@ jobs:
build-node-cli: build-node-cli:
needs: version needs: version
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'beta' if: github.repository == 'anomalyco/opencode'
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -335,6 +346,7 @@ jobs:
build-electron: build-electron:
needs: needs:
- version - version
- sign-cli-macos
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2' if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2'
continue-on-error: false continue-on-error: false
env: env:
@@ -373,6 +385,12 @@ jobs:
steps: steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name == 'beta'
with:
name: opencode-preview-cli
path: packages/cli/dist
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0 - uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
if: runner.os == 'macOS' if: runner.os == 'macOS'
with: with:
@@ -431,6 +449,7 @@ jobs:
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
OPENCODE_CLI_TARGET: ${{ matrix.settings.target }} OPENCODE_CLI_TARGET: ${{ matrix.settings.target }}
OPENCODE_CLI_DIST: ${{ (github.ref_name == 'beta' && format('{0}/packages/cli/dist', github.workspace)) || '' }}
- name: Build - name: Build
run: bun run build run: bun run build
@@ -447,6 +466,7 @@ jobs:
VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }} VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }}
VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }} VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }}
OPENCODE_CLI_TARGET: ${{ matrix.settings.target }} OPENCODE_CLI_TARGET: ${{ matrix.settings.target }}
OPENCODE_CLI_DIST: ${{ github.workspace }}/packages/cli/dist
- name: Package - name: Package
if: needs.version.outputs.release if: needs.version.outputs.release
@@ -569,13 +589,11 @@ jobs:
path: packages/opencode/dist path: packages/opencode/dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'beta'
with: with:
name: opencode-preview-cli name: opencode-preview-cli
path: packages/cli/dist path: packages/cli/dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'beta'
with: with:
pattern: opencode-node-cli-* pattern: opencode-node-cli-*
path: packages/cli/dist/node path: packages/cli/dist/node
+1 -1
View File
@@ -9,7 +9,7 @@
- Run `bun run dev:live` from a development worktree to test its TUI against the currently elected `opencode2` background server and live sessions. - Run `bun run dev:live` from a development worktree to test its TUI against the currently elected `opencode2` background server and live sessions.
- Pass a directory after the script when needed, for example `bun run dev:live /path/to/project`. - Pass a directory after the script when needed, for example `bun run dev:live /path/to/project`.
- The script discovers the server with `opencode2 service status`, injects its private local credential from `opencode2 service get password`, and uses the `next` TUI storage channel so tabs and other client-local state match the installed client. - The script discovers the server with `opencode2 service status`, injects its private local credential from `opencode2 service get password`, and uses the `dev` TUI storage channel so tabs and other client-local state match the installed client.
- Prefer `dev:live` over plain `bun run dev` for this workflow. An implicit managed-service connection may replace the live server when the worktree client version differs; explicit `--server` warns and continues without replacing it. - Prefer `dev:live` over plain `bun run dev` for this workflow. An implicit managed-service connection may replace the live server when the worktree client version differs; explicit `--server` warns and continues without replacing it.
## V2 TUI Stories ## V2 TUI Stories
+218 -62
View File
@@ -1028,7 +1028,7 @@
"packages/www": { "packages/www": {
"name": "@opencode-ai/www", "name": "@opencode-ai/www",
"dependencies": { "dependencies": {
"blume": "1.1.4", "blume": "1.5.1",
}, },
"devDependencies": { "devDependencies": {
"@astrojs/cloudflare": "14.1.4", "@astrojs/cloudflare": "14.1.4",
@@ -1289,6 +1289,12 @@
"@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.4", "", { "dependencies": { "yaml": "^2.8.3" } }, "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A=="], "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.4", "", { "dependencies": { "yaml": "^2.8.3" } }, "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A=="],
"@asyncapi/converter": ["@asyncapi/converter@2.0.2", "", { "dependencies": { "@asyncapi/parser": "^3.6.2", "js-yaml": "^3.14.1", "path": "^0.12.7" } }, "sha512-tFvT2ijEriTe9CPCMP32fh2VPzdQiUupK63MyW7UBDCtENANPZJ0aboZuVPhgBhmqo2TxcHWbZANpmXc5sEFiw=="],
"@asyncapi/parser": ["@asyncapi/parser@3.6.3", "", { "dependencies": { "@asyncapi/specs": "^6.11.1", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "3.21.0", "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.18.3", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", "@stoplight/spectral-ref-resolver": "^1.0.3", "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.18.0", "ajv-errors": "^3.0.0", "ajv-formats": "^2.1.1", "avsc": "^5.7.5", "js-yaml": "^4.3.1", "jsonpath-plus": "^10.0.7", "node-fetch": "2.6.7" } }, "sha512-MUC8xIUMcS2qNvqrqyx/ie0txu3d/OdIsrXs7UCzawdyR6P07gh35DpOqPz/z57s1UA3vERVpcheZYl3h8cVtw=="],
"@asyncapi/specs": ["@asyncapi/specs@6.11.1", "", { "dependencies": { "@types/json-schema": "^7.0.11" } }, "sha512-A3WBLqAKGoJ2+6FWFtpjBlCQ1oFCcs4GxF7zsIGvNqp/klGUHjlA3aAcZ9XMMpLGE8zPeYDz2x9FmO6DSuKraQ=="],
"@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="],
"@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="], "@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="],
@@ -1799,6 +1805,12 @@
"@js-temporal/polyfill": ["@js-temporal/polyfill@0.5.1", "", { "dependencies": { "jsbi": "^4.3.0" } }, "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ=="], "@js-temporal/polyfill": ["@js-temporal/polyfill@0.5.1", "", { "dependencies": { "jsbi": "^4.3.0" } }, "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ=="],
"@jsep-plugin/assignment": ["@jsep-plugin/assignment@1.3.0", "", { "peerDependencies": { "jsep": "^0.4.0||^1.0.0" } }, "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ=="],
"@jsep-plugin/regex": ["@jsep-plugin/regex@1.0.4", "", { "peerDependencies": { "jsep": "^0.4.0||^1.0.0" } }, "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg=="],
"@jsep-plugin/ternary": ["@jsep-plugin/ternary@1.1.4", "", { "peerDependencies": { "jsep": "^0.4.0||^1.0.0" } }, "sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg=="],
"@jsx-email/all": ["@jsx-email/all@2.2.3", "", { "dependencies": { "@jsx-email/body": "1.0.2", "@jsx-email/button": "1.0.4", "@jsx-email/column": "1.0.3", "@jsx-email/container": "1.0.2", "@jsx-email/font": "1.0.3", "@jsx-email/head": "1.0.2", "@jsx-email/heading": "1.0.2", "@jsx-email/hr": "1.0.2", "@jsx-email/html": "1.0.2", "@jsx-email/img": "1.0.2", "@jsx-email/link": "1.0.2", "@jsx-email/markdown": "2.0.4", "@jsx-email/preview": "1.0.2", "@jsx-email/render": "1.1.1", "@jsx-email/row": "1.0.2", "@jsx-email/section": "1.0.2", "@jsx-email/tailwind": "2.4.4", "@jsx-email/text": "1.0.2" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-OBvLe/hVSQc0LlMSTJnkjFoqs3bmxcC4zpy/5pT5agPCSKMvAKQjzmsc2xJ2wO73jSpRV1K/g38GmvdCfrhSoQ=="], "@jsx-email/all": ["@jsx-email/all@2.2.3", "", { "dependencies": { "@jsx-email/body": "1.0.2", "@jsx-email/button": "1.0.4", "@jsx-email/column": "1.0.3", "@jsx-email/container": "1.0.2", "@jsx-email/font": "1.0.3", "@jsx-email/head": "1.0.2", "@jsx-email/heading": "1.0.2", "@jsx-email/hr": "1.0.2", "@jsx-email/html": "1.0.2", "@jsx-email/img": "1.0.2", "@jsx-email/link": "1.0.2", "@jsx-email/markdown": "2.0.4", "@jsx-email/preview": "1.0.2", "@jsx-email/render": "1.1.1", "@jsx-email/row": "1.0.2", "@jsx-email/section": "1.0.2", "@jsx-email/tailwind": "2.4.4", "@jsx-email/text": "1.0.2" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-OBvLe/hVSQc0LlMSTJnkjFoqs3bmxcC4zpy/5pT5agPCSKMvAKQjzmsc2xJ2wO73jSpRV1K/g38GmvdCfrhSoQ=="],
"@jsx-email/body": ["@jsx-email/body@1.0.2", "", { "peerDependencies": { "react": "^18.2.0" } }, "sha512-NjR2tgLH4XGfGkm+O8kcVwi9MBqZsXZCLlmk3HlMux3/n/+a5zB+yhJqXWZBJl2i+6cSF+E2O6hK11ekyK9WWQ=="], "@jsx-email/body": ["@jsx-email/body@1.0.2", "", { "peerDependencies": { "react": "^18.2.0" } }, "sha512-NjR2tgLH4XGfGkm+O8kcVwi9MBqZsXZCLlmk3HlMux3/n/+a5zB+yhJqXWZBJl2i+6cSF+E2O6hK11ekyK9WWQ=="],
@@ -1985,6 +1997,8 @@
"@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="], "@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="],
"@openapi-contrib/openapi-schema-to-json-schema": ["@openapi-contrib/openapi-schema-to-json-schema@3.2.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" } }, "sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw=="],
"@openauthjs/openauth": ["@openauthjs/openauth@0.0.0-20250322224806", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-p5IWSRXvABcwocH2dNI0w8c1QJelIOFulwhKk+aLLFfUbs8u1pr7kQbYe8yCSM2+bcLHiwbogpUQc2ovrGwCuw=="], "@openauthjs/openauth": ["@openauthjs/openauth@0.0.0-20250322224806", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-p5IWSRXvABcwocH2dNI0w8c1QJelIOFulwhKk+aLLFfUbs8u1pr7kQbYe8yCSM2+bcLHiwbogpUQc2ovrGwCuw=="],
"@opencode-ai/ai": ["@opencode-ai/ai@workspace:packages/ai"], "@opencode-ai/ai": ["@opencode-ai/ai@workspace:packages/ai"],
@@ -2585,6 +2599,8 @@
"@scalar/validation": ["@scalar/validation@0.6.2", "", {}, "sha512-Sc1TkcwGV6aVCO51AyKeaGiP8gpwAHxEtO5d3tZzPV+KsnlC/YokQxFxwBrbIXw73k9hmcExnJyGu3k5i6n6VA=="], "@scalar/validation": ["@scalar/validation@0.6.2", "", {}, "sha512-Sc1TkcwGV6aVCO51AyKeaGiP8gpwAHxEtO5d3tZzPV+KsnlC/YokQxFxwBrbIXw73k9hmcExnJyGu3k5i6n6VA=="],
"@scarf/scarf": ["@scarf/scarf@1.4.0", "", {}, "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ=="],
"@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
"@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.36.0", "", { "dependencies": { "@sentry/core": "10.36.0" } }, "sha512-WILVR8HQBWOxbqLRuTxjzRCMIACGsDTo6jXvzA8rz6ezElElLmIrn3CFAswrESLqEEUa4CQHl5bLgSVJCRNweA=="], "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.36.0", "", { "dependencies": { "@sentry/core": "10.36.0" } }, "sha512-WILVR8HQBWOxbqLRuTxjzRCMIACGsDTo6jXvzA8rz6ezElElLmIrn3CFAswrESLqEEUa4CQHl5bLgSVJCRNweA=="],
@@ -2807,6 +2823,36 @@
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@stoplight/better-ajv-errors": ["@stoplight/better-ajv-errors@1.0.3", "", { "dependencies": { "jsonpointer": "^5.0.0", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA=="],
"@stoplight/json": ["@stoplight/json@3.21.0", "", { "dependencies": { "@stoplight/ordered-object-literal": "^1.0.3", "@stoplight/path": "^1.3.2", "@stoplight/types": "^13.6.0", "jsonc-parser": "~2.2.1", "lodash": "^4.17.21", "safe-stable-stringify": "^1.1" } }, "sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g=="],
"@stoplight/json-ref-readers": ["@stoplight/json-ref-readers@1.2.2", "", { "dependencies": { "node-fetch": "^2.6.0", "tslib": "^1.14.1" } }, "sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ=="],
"@stoplight/json-ref-resolver": ["@stoplight/json-ref-resolver@3.1.6", "", { "dependencies": { "@stoplight/json": "^3.21.0", "@stoplight/path": "^1.3.2", "@stoplight/types": "^12.3.0 || ^13.0.0", "@types/urijs": "^1.19.19", "dependency-graph": "~0.11.0", "fast-memoize": "^2.5.2", "immer": "^9.0.6", "lodash": "^4.17.21", "tslib": "^2.6.0", "urijs": "^1.19.11" } }, "sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A=="],
"@stoplight/ordered-object-literal": ["@stoplight/ordered-object-literal@1.0.5", "", {}, "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg=="],
"@stoplight/path": ["@stoplight/path@1.3.2", "", {}, "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ=="],
"@stoplight/spectral-core": ["@stoplight/spectral-core@1.23.1", "", { "dependencies": { "@scarf/scarf": "^1.4.0", "@stoplight/better-ajv-errors": "1.0.3", "@stoplight/json": "~3.21.0", "@stoplight/path": "1.3.2", "@stoplight/spectral-parsers": "^1.0.0", "@stoplight/spectral-ref-resolver": "^1.0.4", "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "~13.6.0", "@types/es-aggregate-error": "^1.0.2", "@types/json-schema": "^7.0.11", "ajv": "^8.18.0", "ajv-errors": "~3.0.0", "ajv-formats": "~2.1.1", "es-aggregate-error": "^1.0.7", "expr-eval-fork": "^3.0.1", "jsonpath-plus": "^10.3.0", "lodash": "^4.18.1", "lodash.topath": "^4.5.2", "minimatch": "^3.1.4", "nimma": "0.2.3", "pony-cause": "^1.1.1", "tslib": "^2.8.1" } }, "sha512-VLC8OhpO/pMJKb6IHhurxJjXO1qB56Ng1unIb8b+hNxdw0+SEcASvmR+RpjfHYX/jv/DfSaA1x8QhFBJBmqBOQ=="],
"@stoplight/spectral-formats": ["@stoplight/spectral-formats@1.8.5", "", { "dependencies": { "@scarf/scarf": "^1.4.0", "@stoplight/json": "^3.17.0", "@stoplight/spectral-core": "^1.23.0", "@types/json-schema": "^7.0.7", "tslib": "^2.8.1" } }, "sha512-xaC0rCH0p7/bzNJsz+JgLSj+Cp6uwYGWpePQxdLkF2G6a8Zyp3OyS7umkGYNiimEwKrOjvCNNTFJpeuiENZSBA=="],
"@stoplight/spectral-functions": ["@stoplight/spectral-functions@1.10.5", "", { "dependencies": { "@scarf/scarf": "^1.4.0", "@stoplight/better-ajv-errors": "1.0.3", "@stoplight/json": "^3.17.1", "@stoplight/spectral-core": "^1.23.0", "@stoplight/spectral-formats": "^1.8.1", "@stoplight/spectral-runtime": "^1.1.2", "ajv": "^8.18.0", "ajv-draft-04": "~1.0.0", "ajv-errors": "~3.0.0", "ajv-formats": "~2.1.1", "lodash": "^4.18.1", "tslib": "^2.8.1" } }, "sha512-vDCd0NJ93715bcUpZZ5vNHiyxd4cgHF6tuXsDiXOXKAByg+I1fR5/dMijEo6Ce1Lz95a+RZ22JKYhF1YuzVvuA=="],
"@stoplight/spectral-parsers": ["@stoplight/spectral-parsers@1.0.5", "", { "dependencies": { "@stoplight/json": "~3.21.0", "@stoplight/types": "^14.1.1", "@stoplight/yaml": "~4.3.0", "tslib": "^2.8.1" } }, "sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ=="],
"@stoplight/spectral-ref-resolver": ["@stoplight/spectral-ref-resolver@1.0.5", "", { "dependencies": { "@stoplight/json-ref-readers": "1.2.2", "@stoplight/json-ref-resolver": "~3.1.6", "@stoplight/spectral-runtime": "^1.1.2", "dependency-graph": "0.11.0", "tslib": "^2.8.1" } }, "sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA=="],
"@stoplight/spectral-runtime": ["@stoplight/spectral-runtime@1.1.6", "", { "dependencies": { "@stoplight/json": "^3.20.1", "@stoplight/path": "^1.3.2", "@stoplight/types": "^13.6.0", "lodash": "^4.18.1", "node-fetch": "^2.7.0", "tslib": "^2.8.1" } }, "sha512-Y8rEDyMN4bSMJCrDs2shdcVHYyCnH3FvXRP4dBhha4Z8iJv+JPp7KqOV/hwVB/hWFC209upiwj2oDmLfR0qCDg=="],
"@stoplight/types": ["@stoplight/types@13.20.0", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA=="],
"@stoplight/yaml": ["@stoplight/yaml@4.3.0", "", { "dependencies": { "@stoplight/ordered-object-literal": "^1.0.5", "@stoplight/types": "^14.1.1", "@stoplight/yaml-ast-parser": "0.0.50", "tslib": "^2.2.0" } }, "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w=="],
"@stoplight/yaml-ast-parser": ["@stoplight/yaml-ast-parser@0.0.50", "", {}, "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ=="],
"@storybook/addon-a11y": ["@storybook/addon-a11y@10.5.7", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.5.7" } }, "sha512-I30rsNz6aA3xg3811MEry40uJDHP3l5SOkfqtmNkp7y4NdqTDdKhmbhhDAZQX1WWEk6GeMBGr3AJ3TCz7r7JmQ=="], "@storybook/addon-a11y": ["@storybook/addon-a11y@10.5.7", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.5.7" } }, "sha512-I30rsNz6aA3xg3811MEry40uJDHP3l5SOkfqtmNkp7y4NdqTDdKhmbhhDAZQX1WWEk6GeMBGr3AJ3TCz7r7JmQ=="],
"@storybook/addon-docs": ["@storybook/addon-docs@10.5.7", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.5.7", "@storybook/icons": "^2.0.2", "@storybook/react-dom-shim": "10.5.7", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.5.7" }, "optionalPeers": ["@types/react"] }, "sha512-KNARJfjICaizinsR3INMEiipZm1ObYo+xw+E26gteu50Bcy2dIZUtk5uHY5XdtardU3AXX6yRXoBZ2HCY3lbHA=="], "@storybook/addon-docs": ["@storybook/addon-docs@10.5.7", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.5.7", "@storybook/icons": "^2.0.2", "@storybook/react-dom-shim": "10.5.7", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.5.7" }, "optionalPeers": ["@types/react"] }, "sha512-KNARJfjICaizinsR3INMEiipZm1ObYo+xw+E26gteu50Bcy2dIZUtk5uHY5XdtardU3AXX6yRXoBZ2HCY3lbHA=="],
@@ -3023,6 +3069,8 @@
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
"@types/es-aggregate-error": ["@types/es-aggregate-error@1.0.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg=="],
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
"@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
@@ -3127,6 +3175,8 @@
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@types/urijs": ["@types/urijs@1.19.26", "", {}, "sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg=="],
"@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
"@types/which": ["@types/which@3.0.4", "", {}, "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w=="], "@types/which": ["@types/which@3.0.4", "", {}, "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w=="],
@@ -3213,6 +3263,8 @@
"@webgpu/types": ["@webgpu/types@0.1.54", "", {}, "sha512-81oaalC8LFrXjhsczomEQ0u3jG+TqE6V9QHLA8GNZq/Rnot0KDugu3LhSYSlie8tSdooAN1Hov05asrUUp9qgg=="], "@webgpu/types": ["@webgpu/types@0.1.54", "", {}, "sha512-81oaalC8LFrXjhsczomEQ0u3jG+TqE6V9QHLA8GNZq/Rnot0KDugu3LhSYSlie8tSdooAN1Hov05asrUUp9qgg=="],
"@workflow/serde": ["@workflow/serde@4.1.0", "", {}, "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ=="],
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="],
"@yuuang/ffi-rs-android-arm64": ["@yuuang/ffi-rs-android-arm64@1.3.7", "", { "os": "android", "cpu": "arm64" }, "sha512-t6Wx3Xll6c07Nuk0k3xnZsxKFxlshm92i0U/BiTHc6kQbvu+fMJF+gKsj4yEj886jH51CM3EqZT9Xdhq9CdUVw=="], "@yuuang/ffi-rs-android-arm64": ["@yuuang/ffi-rs-android-arm64@1.3.7", "", { "os": "android", "cpu": "arm64" }, "sha512-t6Wx3Xll6c07Nuk0k3xnZsxKFxlshm92i0U/BiTHc6kQbvu+fMJF+gKsj4yEj886jH51CM3EqZT9Xdhq9CdUVw=="],
@@ -3265,6 +3317,8 @@
"ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="],
"ajv-errors": ["ajv-errors@3.0.0", "", { "peerDependencies": { "ajv": "^8.0.1" } }, "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ=="],
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
"ajv-i18n": ["ajv-i18n@4.2.0", "", { "peerDependencies": { "ajv": "^8.0.0-beta.0" } }, "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg=="], "ajv-i18n": ["ajv-i18n@4.2.0", "", { "peerDependencies": { "ajv": "^8.0.0-beta.0" } }, "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg=="],
@@ -3343,6 +3397,8 @@
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
"avsc": ["avsc@5.7.9", "", {}, "sha512-yOA4wFeI7ET3v32Di/sUybQ+ttP20JHSW3mxLuNGeO0uD6PPcvLrIQXSvy/rhJOWU5JrYh7U4OHplWMmtAtjMg=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="], "aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="],
@@ -3409,7 +3465,7 @@
"bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="],
"blume": ["blume@1.1.4", "", { "dependencies": { "@astrojs/check": "^0.9.0", "@astrojs/markdown-satteri": "^0.3.2", "@astrojs/mdx": "^7.0.0", "@astrojs/node": "^11.0.0", "@astrojs/react": "^6.0.0", "@astrojs/vercel": "^11.0.0", "@clack/prompts": "^1.7.0", "@iconify-json/lucide": "^1.2.115", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.3", "@modelcontextprotocol/sdk": "^1.29.0", "@orama/orama": "^3.1.18", "@pierre/diffs": "^1.2.11", "@scalar/astro": "^0.4.5", "@scalar/openapi-parser": "^0.28.8", "@scalar/openapi-types": "^0.9.1", "@shikijs/transformers": "^4.2.0", "@shikijs/twoslash": "^4.2.0", "@tailwindcss/typography": "^0.5.20", "@tailwindcss/vite": "^4", "@vercel/analytics": "^2.0.1", "ai": "^5.0.0", "astro": "^7.0.2", "babel-plugin-react-compiler": "^1.0.0", "citty": "^0.1.6", "consola": "^3.4.0", "dompurify": "^3.4.11", "epub-gen-memory": "^1.1.2", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "jiti": "^2.4.0", "js-yaml": "^4.1.0", "katex": "^0.17.0", "marked": "^18.0.5", "mermaid": "^11.15.0", "node-html-parser": "^9.0.0", "pagefind": "^1.3.0", "pathe": "^2.0.0", "react": "^19.0.0", "react-dom": "^19.0.0", "satteri": "^0.9.5", "shiki": "^4.2.0", "simple-icons": "^13.0.0", "tailwindcss": "^4", "takumi-js": "^2.2.1", "tinyglobby": "^0.2.10", "twoslash": "^0.3.9", "typescript": "^6.0.3", "undici": "^8.6.0", "zod": "^3.24.0" }, "peerDependencies": { "@ai-sdk/openai-compatible": "^1.0.41", "@astrojs/cloudflare": "^14.0.0", "@astrojs/netlify": "^8.0.0", "@astrojs/svelte": "^9.0.0", "@astrojs/vue": "^7.0.0", "@mixedbread/sdk": "^0.76.0", "@notionhq/client": "^2.2.15", "@openrouter/ai-sdk-provider": "^1.5.4", "@oramacloud/client": "^2.1.0", "@sanity/client": "^6.21.0", "algoliasearch": "^5.55.0", "flexsearch": "^0.8.0", "typesense": "^3.0.0" }, "optionalPeers": ["@ai-sdk/openai-compatible", "@astrojs/cloudflare", "@astrojs/netlify", "@astrojs/svelte", "@astrojs/vue", "@mixedbread/sdk", "@notionhq/client", "@openrouter/ai-sdk-provider", "@oramacloud/client", "@sanity/client", "algoliasearch", "flexsearch", "typesense"], "bin": { "blume": "bin/blume.mjs" } }, "sha512-boCWAMfuyc2788hjGJPrmxu4mNUojG+XkkPypjq+z8KErq1yV4XrHgET8bzVJtmOdpWVGLIjfzmbLniTePBU8Q=="], "blume": ["blume@1.5.1", "", { "dependencies": { "@astrojs/check": "^0.9.0", "@astrojs/markdown-satteri": "^0.3.2", "@astrojs/mdx": "^7.0.0", "@astrojs/node": "^11.0.0", "@astrojs/react": "^6.0.0", "@astrojs/vercel": "^11.0.3", "@asyncapi/converter": "^2.0.2", "@clack/prompts": "^1.7.0", "@iconify-json/lucide": "^1.2.115", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.3", "@modelcontextprotocol/sdk": "^1.29.0", "@orama/orama": "^3.1.18", "@pierre/diffs": "^1.2.11", "@scalar/astro": "^0.4.5", "@scalar/openapi-parser": "^0.28.8", "@scalar/openapi-types": "^0.9.1", "@shikijs/transformers": "^4.2.0", "@shikijs/twoslash": "^4.2.0", "@tailwindcss/typography": "^0.5.20", "@tailwindcss/vite": "^4", "@types/mdast": "^4.0.4", "@vercel/analytics": "^2.0.1", "ai": "^7.0.42", "astro": "^7.1.0", "babel-plugin-react-compiler": "^1.0.0", "chokidar": "^5.0.0", "citty": "^0.1.6", "consola": "^3.4.0", "cross-spawn": "^7.0.6", "dompurify": "^3.4.13", "dotenv": "^17.4.2", "epub-gen-memory": "^1.1.2", "fast-xml-parser": "^5.10.1", "get-tsconfig": "^4.14.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "html-escaper": "^3.0.3", "image-size": "^2.0.2", "jiti": "^2.4.0", "js-yaml": "^4.3.1", "katex": "^0.18.1", "markdown-table": "^3.0.4", "marked": "^18.0.5", "mdast-util-from-markdown": "^2.0.3", "mdast-util-gfm": "^3.1.0", "mdast-util-to-string": "^4.0.0", "medium-zoom": "^1.1.0", "mermaid": "^11.16.1", "micromark-extension-gfm": "^3.0.0", "nanotar": "^0.3.0", "node-html-parser": "^9.0.0", "openapi-sampler": "^1.7.4", "p-limit": "^7.3.1", "p-map": "^7.0.6", "p-retry": "^8.0.0", "package-manager-detector": "^1.8.0", "pagefind": "^1.3.0", "pathe": "^2.0.0", "perfect-debounce": "^2.1.0", "picomatch": "^4.0.5", "react": "^19.0.0", "react-dom": "^19.0.0", "robots-parser": "^3.0.1", "satteri": "^0.9.5", "semver": "^7.8.5", "sharp": "^0.35.3", "shiki": "^4.2.0", "simple-icons": "^13.0.0", "string-width": "^8.1.0", "tailwindcss": "^4.3.3", "takumi-js": "^2.2.1", "tinyglobby": "^0.2.10", "twoslash": "^0.3.9", "typescript": "^6.0.3", "ufo": "^1.6.4", "undici": "^8.9.0", "write-file-atomic": "^8.0.0", "zod": "^4.3.6" }, "peerDependencies": { "@ai-sdk/openai-compatible": "^3.0.0", "@astrojs/cloudflare": "^14.0.0", "@astrojs/netlify": "^8.0.0", "@astrojs/svelte": "^9.0.0", "@astrojs/vue": "^7.0.0", "@mixedbread/sdk": "^0.76.0", "@notionhq/client": "^2.2.15", "@openrouter/ai-sdk-provider": "^3.0.0", "@oramacloud/client": "^2.1.0", "@sanity/client": "^6.21.0 || ^7.0.0", "algoliasearch": "^5.55.0", "flexsearch": "^0.8.0", "typesense": "^3.0.0" }, "optionalPeers": ["@ai-sdk/openai-compatible", "@astrojs/cloudflare", "@astrojs/netlify", "@astrojs/svelte", "@astrojs/vue", "@mixedbread/sdk", "@notionhq/client", "@openrouter/ai-sdk-provider", "@oramacloud/client", "@sanity/client", "algoliasearch", "flexsearch", "typesense"], "bin": { "blume": "bin/blume.mjs" } }, "sha512-RnzoY+OHvchUJkXjF7s45SUW5rhuL81URWATEZoEl3psWeajsUu6JRyqDuy8bpYZjoth7P6gpS4IijZspv8Yrw=="],
"body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], "body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="],
@@ -3737,6 +3793,8 @@
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"dependency-graph": ["dependency-graph@0.11.0", "", {}, "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg=="],
"deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="], "deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="],
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
@@ -3791,7 +3849,7 @@
"dot-prop": ["dot-prop@8.0.2", "", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="], "dot-prop": ["dot-prop@8.0.2", "", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="],
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
"dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="], "dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="],
@@ -3877,6 +3935,8 @@
"es-abstract-get": ["es-abstract-get@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "es-object-atoms": "^1.1.2", "is-callable": "^1.2.7", "object-inspect": "^1.13.4" } }, "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg=="], "es-abstract-get": ["es-abstract-get@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "es-object-atoms": "^1.1.2", "is-callable": "^1.2.7", "object-inspect": "^1.13.4" } }, "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg=="],
"es-aggregate-error": ["es-aggregate-error@1.0.14", "", { "dependencies": { "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "globalthis": "^1.0.4", "has-property-descriptors": "^1.0.2", "set-function-name": "^2.0.2" } }, "sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA=="],
"es-array-method-boxes-properly": ["es-array-method-boxes-properly@1.0.0", "", {}, "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="], "es-array-method-boxes-properly": ["es-array-method-boxes-properly@1.0.0", "", {}, "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
@@ -3951,6 +4011,8 @@
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
"expr-eval-fork": ["expr-eval-fork@3.0.3", "", {}, "sha512-BhC+hbc5lIVjygr840n5DEkW3MQq7H9o+mc1/N7Z5uIiCFVyESLL5DIE7LNq4CYUNxy+XjA+3jRrL/h0Kt2xcg=="],
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"express-rate-limit": ["express-rate-limit@8.6.2", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A=="], "express-rate-limit": ["express-rate-limit@8.6.2", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A=="],
@@ -3977,6 +4039,8 @@
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-memoize": ["fast-memoize@2.5.2", "", {}, "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="],
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
@@ -3987,7 +4051,7 @@
"fast-xml-builder": ["fast-xml-builder@1.3.0", "", { "dependencies": { "path-expression-matcher": "^1.6.2", "xml-naming": "^0.3.0" } }, "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ=="], "fast-xml-builder": ["fast-xml-builder@1.3.0", "", { "dependencies": { "path-expression-matcher": "^1.6.2", "xml-naming": "^0.3.0" } }, "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ=="],
"fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], "fast-xml-parser": ["fast-xml-parser@5.10.1", "", { "dependencies": { "@nodable/entities": "^3.0.0", "fast-xml-builder": "^1.2.0", "is-unsafe": "^2.0.0", "path-expression-matcher": "^1.6.2", "strnum": "^2.4.1", "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw=="],
"fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
@@ -4027,6 +4091,8 @@
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
"foreach": ["foreach@2.0.6", "", {}, "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg=="],
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="], "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="],
@@ -4243,6 +4309,8 @@
"ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="], "ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="],
"image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="],
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
"immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="], "immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="],
@@ -4329,6 +4397,8 @@
"is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
"is-network-error": ["is-network-error@1.3.2", "", {}, "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
@@ -4401,6 +4471,8 @@
"jsbi": ["jsbi@4.3.2", "", {}, "sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew=="], "jsbi": ["jsbi@4.3.2", "", {}, "sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew=="],
"jsep": ["jsep@1.4.0", "", {}, "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw=="],
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], "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-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
@@ -4409,6 +4481,8 @@
"json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], "json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="],
"json-pointer": ["json-pointer@0.6.2", "", { "dependencies": { "foreach": "^2.0.4" } }, "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw=="],
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], "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-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=="],
@@ -4431,6 +4505,8 @@
"jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="],
"jsonpath-plus": ["jsonpath-plus@10.4.0", "", { "dependencies": { "@jsep-plugin/assignment": "^1.3.0", "@jsep-plugin/regex": "^1.0.4", "jsep": "^1.4.0" }, "bin": { "jsonpath": "bin/jsonpath-cli.js", "jsonpath-plus": "bin/jsonpath-cli.js" } }, "sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA=="],
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
@@ -4529,6 +4605,8 @@
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
"lodash.topath": ["lodash.topath@4.5.2", "", {}, "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg=="],
"loglevelnext": ["loglevelnext@6.0.0", "", {}, "sha512-FDl1AI2sJGjHHG3XKJd6sG3/6ncgiGCQ0YkW46nxe7SfqQq6hujd9CvFXIXtkGBUN83KPZ2KSOJK8q5P0bSSRQ=="], "loglevelnext": ["loglevelnext@6.0.0", "", {}, "sha512-FDl1AI2sJGjHHG3XKJd6sG3/6ncgiGCQ0YkW46nxe7SfqQq6hujd9CvFXIXtkGBUN83KPZ2KSOJK8q5P0bSSRQ=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
@@ -4613,6 +4691,8 @@
"media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], "media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="],
"medium-zoom": ["medium-zoom@1.1.0", "", {}, "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ=="],
"merge-anything": ["merge-anything@5.1.7", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ=="], "merge-anything": ["merge-anything@5.1.7", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ=="],
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
@@ -4767,12 +4847,16 @@
"nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], "nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="],
"nanotar": ["nanotar@0.3.0", "", {}, "sha512-Kv2JYYiCzt16Kt5QwAc9BFG89xfPNBx+oQL4GQXD9nLqPkZBiNaqaCWtwnbk/q7UVsTYevvM1b0UF8zmEI4pCg=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
"nf3": ["nf3@0.1.12", "", {}, "sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ=="], "nf3": ["nf3@0.1.12", "", {}, "sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ=="],
"nimma": ["nimma@0.2.3", "", { "dependencies": { "@jsep-plugin/regex": "^1.0.1", "@jsep-plugin/ternary": "^1.0.2", "astring": "^1.8.1", "jsep": "^1.2.0" }, "optionalDependencies": { "jsonpath-plus": "^6.0.1 || ^10.1.0", "lodash.topath": "^4.5.2" } }, "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA=="],
"nitro": ["nitro@3.0.1-alpha.1", "", { "dependencies": { "consola": "^3.4.2", "crossws": "^0.4.1", "db0": "^0.3.4", "h3": "2.0.1-rc.5", "jiti": "^2.6.1", "nf3": "^0.1.10", "ofetch": "^2.0.0-alpha.3", "ohash": "^2.0.11", "oxc-minify": "^0.96.0", "oxc-transform": "^0.96.0", "srvx": "^0.9.5", "undici": "^7.16.0", "unenv": "^2.0.0-rc.24", "unstorage": "^2.0.0-alpha.4" }, "peerDependencies": { "rolldown": "*", "rollup": "^4", "vite": "^7", "xml2js": "^0.6.2" }, "optionalPeers": ["rolldown", "rollup", "vite", "xml2js"], "bin": { "nitro": "dist/cli/index.mjs" } }, "sha512-U4AxIsXxdkxzkFrK0XAw0e5Qbojk8jQ50MjjRBtBakC4HurTtQoiZvF+lSe382jhuQZCfAyywGWOFa9QzXLFaw=="], "nitro": ["nitro@3.0.1-alpha.1", "", { "dependencies": { "consola": "^3.4.2", "crossws": "^0.4.1", "db0": "^0.3.4", "h3": "2.0.1-rc.5", "jiti": "^2.6.1", "nf3": "^0.1.10", "ofetch": "^2.0.0-alpha.3", "ohash": "^2.0.11", "oxc-minify": "^0.96.0", "oxc-transform": "^0.96.0", "srvx": "^0.9.5", "undici": "^7.16.0", "unenv": "^2.0.0-rc.24", "unstorage": "^2.0.0-alpha.4" }, "peerDependencies": { "rolldown": "*", "rollup": "^4", "vite": "^7", "xml2js": "^0.6.2" }, "optionalPeers": ["rolldown", "rollup", "vite", "xml2js"], "bin": { "nitro": "dist/cli/index.mjs" } }, "sha512-U4AxIsXxdkxzkFrK0XAw0e5Qbojk8jQ50MjjRBtBakC4HurTtQoiZvF+lSe382jhuQZCfAyywGWOFa9QzXLFaw=="],
"nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="], "nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
@@ -4861,6 +4945,8 @@
"openai": ["openai@6.49.0", "", { "peerDependencies": { "@aws-sdk/credential-provider-node": ">=3.972.0 <4", "@smithy/hash-node": ">=4.3.0 <5", "@smithy/signature-v4": ">=5.4.0 <6", "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["@aws-sdk/credential-provider-node", "@smithy/hash-node", "@smithy/signature-v4", "ws", "zod"] }, "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg=="], "openai": ["openai@6.49.0", "", { "peerDependencies": { "@aws-sdk/credential-provider-node": ">=3.972.0 <4", "@smithy/hash-node": ">=4.3.0 <5", "@smithy/signature-v4": ">=5.4.0 <6", "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["@aws-sdk/credential-provider-node", "@smithy/hash-node", "@smithy/signature-v4", "ws", "zod"] }, "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg=="],
"openapi-sampler": ["openapi-sampler@1.7.4", "", { "dependencies": { "@types/json-schema": "^7.0.7", "fast-xml-parser": "^5.5.1", "json-pointer": "0.6.2" } }, "sha512-CKS/rd5ucPCuEDbJnjGDXZTsuGWcmv53aCmQx7soZlPEONUGN4af0/dY5+THRFZraSEjeA78nlfzdFswC/N5SA=="],
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
"openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="], "openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="],
@@ -4899,7 +4985,7 @@
"p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="],
"p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="], "p-retry": ["p-retry@8.0.0", "", { "dependencies": { "is-network-error": "^1.3.0" } }, "sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A=="],
"p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
@@ -4933,6 +5019,8 @@
"pascal-case": ["pascal-case@3.1.2", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g=="], "pascal-case": ["pascal-case@3.1.2", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g=="],
"path": ["path@0.12.7", "", { "dependencies": { "process": "^0.11.1", "util": "^0.10.3" } }, "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q=="],
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
"path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
@@ -4965,6 +5053,8 @@
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
"perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="],
"piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
@@ -4993,6 +5083,8 @@
"points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="],
"pony-cause": ["pony-cause@1.1.1", "", {}, "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g=="],
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
"postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], "postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="],
@@ -5221,6 +5313,8 @@
"roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
"robots-parser": ["robots-parser@3.0.1", "", {}, "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ=="],
"robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
"rolldown": ["rolldown@1.2.3", "", { "dependencies": { "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.3", "@rolldown/binding-darwin-arm64": "1.2.3", "@rolldown/binding-darwin-x64": "1.2.3", "@rolldown/binding-freebsd-x64": "1.2.3", "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", "@rolldown/binding-linux-arm64-gnu": "1.2.3", "@rolldown/binding-linux-arm64-musl": "1.2.3", "@rolldown/binding-linux-ppc64-gnu": "1.2.3", "@rolldown/binding-linux-s390x-gnu": "1.2.3", "@rolldown/binding-linux-x64-gnu": "1.2.3", "@rolldown/binding-linux-x64-musl": "1.2.3", "@rolldown/binding-openharmony-arm64": "1.2.3", "@rolldown/binding-win32-arm64-msvc": "1.2.3", "@rolldown/binding-win32-x64-msvc": "1.2.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A=="], "rolldown": ["rolldown@1.2.3", "", { "dependencies": { "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.3", "@rolldown/binding-darwin-arm64": "1.2.3", "@rolldown/binding-darwin-x64": "1.2.3", "@rolldown/binding-freebsd-x64": "1.2.3", "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", "@rolldown/binding-linux-arm64-gnu": "1.2.3", "@rolldown/binding-linux-arm64-musl": "1.2.3", "@rolldown/binding-linux-ppc64-gnu": "1.2.3", "@rolldown/binding-linux-s390x-gnu": "1.2.3", "@rolldown/binding-linux-x64-gnu": "1.2.3", "@rolldown/binding-linux-x64-musl": "1.2.3", "@rolldown/binding-openharmony-arm64": "1.2.3", "@rolldown/binding-win32-arm64-msvc": "1.2.3", "@rolldown/binding-win32-x64-msvc": "1.2.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A=="],
@@ -5249,6 +5343,8 @@
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
"safe-stable-stringify": ["safe-stable-stringify@1.1.1", "", {}, "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="], "sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="],
@@ -5459,7 +5555,7 @@
"stripe": ["stripe@18.0.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-3Fs33IzKUby//9kCkCa1uRpinAoTvj6rJgQ2jrBEysoxEvfsclvXdna1amyEYbA2EKkjynuB4+L/kleCCaWTpA=="], "stripe": ["stripe@18.0.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-3Fs33IzKUby//9kCkCa1uRpinAoTvj6rJgQ2jrBEysoxEvfsclvXdna1amyEYbA2EKkjynuB4+L/kleCCaWTpA=="],
"strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], "strnum": ["strnum@2.4.1", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg=="],
"stubborn-fs": ["stubborn-fs@2.0.0", "", { "dependencies": { "stubborn-utils": "^1.0.1" } }, "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA=="], "stubborn-fs": ["stubborn-fs@2.0.0", "", { "dependencies": { "stubborn-utils": "^1.0.1" } }, "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA=="],
@@ -5681,6 +5777,8 @@
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"urijs": ["urijs@1.19.11", "", {}, "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="],
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
@@ -5689,8 +5787,12 @@
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="], "utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
"util": ["util@0.10.4", "", { "dependencies": { "inherits": "2.0.3" } }, "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"utility-types": ["utility-types@3.11.0", "", {}, "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw=="],
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
"uuid": ["uuid@14.0.1", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew=="], "uuid": ["uuid@14.0.1", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew=="],
@@ -5807,7 +5909,7 @@
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], "write-file-atomic": ["write-file-atomic@8.0.0", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-dYwyZredl67GyLLIHJnRM3h2PcOmN5SkcgC7eM5DPDEOEl6dLFqVrMg3F1Ea32usj4VSVZtd2H4MtKTNOf6nPg=="],
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
@@ -5999,6 +6101,12 @@
"@astrojs/vercel/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], "@astrojs/vercel/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
"@asyncapi/parser/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
"@asyncapi/parser/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="],
"@asyncapi/parser/node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="],
"@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.974.2", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA=="], "@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.974.2", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA=="],
"@aws-crypto/crc32c/@aws-sdk/types": ["@aws-sdk/types@3.974.2", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA=="], "@aws-crypto/crc32c/@aws-sdk/types": ["@aws-sdk/types@3.974.2", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA=="],
@@ -6117,8 +6225,6 @@
"@azure/core-http/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], "@azure/core-http/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
"@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.10.1", "", { "dependencies": { "@nodable/entities": "^3.0.0", "fast-xml-builder": "^1.2.0", "is-unsafe": "^2.0.0", "path-expression-matcher": "^1.6.2", "strnum": "^2.4.1", "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw=="],
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
@@ -6325,6 +6431,8 @@
"@scalar/types/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@scalar/types/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
"@sentry/bundler-plugin-core/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], "@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
"@sentry/bundler-plugin-core/magic-string": ["magic-string@0.30.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ=="], "@sentry/bundler-plugin-core/magic-string": ["magic-string@0.30.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ=="],
@@ -6365,6 +6473,8 @@
"@slack/web-api/p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="], "@slack/web-api/p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
"@slack/web-api/p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
"@solidjs/start/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], "@solidjs/start/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
"@solidjs/start/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], "@solidjs/start/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
@@ -6373,6 +6483,26 @@
"@solidjs/start/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], "@solidjs/start/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="],
"@stoplight/better-ajv-errors/leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
"@stoplight/json/jsonc-parser": ["jsonc-parser@2.2.1", "", {}, "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w=="],
"@stoplight/json-ref-readers/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"@stoplight/json-ref-resolver/immer": ["immer@9.0.21", "", {}, "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="],
"@stoplight/spectral-core/@stoplight/types": ["@stoplight/types@13.6.0", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ=="],
"@stoplight/spectral-core/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
"@stoplight/spectral-core/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"@stoplight/spectral-functions/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
"@stoplight/spectral-parsers/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
"@stoplight/yaml/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
"@storybook/addon-docs/react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], "@storybook/addon-docs/react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="],
"@storybook/addon-docs/react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="], "@storybook/addon-docs/react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="],
@@ -6435,6 +6565,8 @@
"app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], "app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
"app-builder-lib/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"app-builder-lib/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], "app-builder-lib/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
"app-builder-lib/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "app-builder-lib/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="],
@@ -6473,6 +6605,8 @@
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], "babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
"bin-links/write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="],
"blume/@astrojs/mdx": ["@astrojs/mdx@7.0.5", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@astrojs/markdown-remark": "7.2.2", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-satteri": "^0.3.1", "astro": "^7.0.0" }, "optionalPeers": ["@astrojs/markdown-satteri"] }, "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw=="], "blume/@astrojs/mdx": ["@astrojs/mdx@7.0.5", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@astrojs/markdown-remark": "7.2.2", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-satteri": "^0.3.1", "astro": "^7.0.0" }, "optionalPeers": ["@astrojs/markdown-satteri"] }, "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw=="],
"blume/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="], "blume/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="],
@@ -6481,23 +6615,35 @@
"blume/@shikijs/transformers": ["@shikijs/transformers@4.4.2", "", { "dependencies": { "@shikijs/core": "4.4.2", "@shikijs/types": "4.4.2" } }, "sha512-d81PJ9KkR1tVP95FH/9296HTtDo0mh76wv10u9T1YmsZq/UcXgt0OLdBszfUQ1i+umkRMCjDnFbFZU7/tCODTQ=="], "blume/@shikijs/transformers": ["@shikijs/transformers@4.4.2", "", { "dependencies": { "@shikijs/core": "4.4.2", "@shikijs/types": "4.4.2" } }, "sha512-d81PJ9KkR1tVP95FH/9296HTtDo0mh76wv10u9T1YmsZq/UcXgt0OLdBszfUQ1i+umkRMCjDnFbFZU7/tCODTQ=="],
"blume/ai": ["ai@7.0.64", "", { "dependencies": { "@ai-sdk/gateway": "4.0.51", "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-29Ufm56e53pRzIPueWzZiwJsgxmZStaBFpwEkVM89sqyIIax/pzDm+ez/ksJ/CcY9UCuYzvCQpE2zkFHICvS3w=="],
"blume/astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="], "blume/astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="],
"blume/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"blume/dompurify": ["dompurify@3.4.13", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ=="], "blume/dompurify": ["dompurify@3.4.13", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ=="],
"blume/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "blume/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="],
"blume/katex": ["katex@0.17.0", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Vdw0ATsQ9V+LuegM/BTwQqV/6cTl5lbGcIrU+BCgLxyf6bo38ybOr372tuSIxir3CN720flu1meYR6XzNMwQnw=="], "blume/katex": ["katex@0.18.4", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-IMPntbRLOU+eu88XDiFKqQ8Akhr9Tv7jDMXqPhjG9SI1JMA4DIgXk4x9k4skJz2NZJXBRbC+2pYBLj9olqcZow=="],
"blume/node-html-parser": ["node-html-parser@9.0.1", "", { "dependencies": { "css-select": "^5.1.0", "entities": "^8.0.0" } }, "sha512-QrdiYYm1NnLRXsMXThUgVcF/syWfWIgHFmy8hylWMGFbHFtnRuXLBxxGQvIm3xaIJMUDJ0ayOmT/FGJYT3pZIw=="], "blume/node-html-parser": ["node-html-parser@9.0.1", "", { "dependencies": { "css-select": "^5.1.0", "entities": "^8.0.0" } }, "sha512-QrdiYYm1NnLRXsMXThUgVcF/syWfWIgHFmy8hylWMGFbHFtnRuXLBxxGQvIm3xaIJMUDJ0ayOmT/FGJYT3pZIw=="],
"blume/p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="],
"blume/react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], "blume/react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="],
"blume/react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="], "blume/react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="],
"blume/sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.3", "@img/sharp-darwin-x64": "0.35.3", "@img/sharp-freebsd-wasm32": "0.35.3", "@img/sharp-libvips-darwin-arm64": "1.3.2", "@img/sharp-libvips-darwin-x64": "1.3.2", "@img/sharp-libvips-linux-arm": "1.3.2", "@img/sharp-libvips-linux-arm64": "1.3.2", "@img/sharp-libvips-linux-ppc64": "1.3.2", "@img/sharp-libvips-linux-riscv64": "1.3.2", "@img/sharp-libvips-linux-s390x": "1.3.2", "@img/sharp-libvips-linux-x64": "1.3.2", "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", "@img/sharp-libvips-linuxmusl-x64": "1.3.2", "@img/sharp-linux-arm": "0.35.3", "@img/sharp-linux-arm64": "0.35.3", "@img/sharp-linux-ppc64": "0.35.3", "@img/sharp-linux-riscv64": "0.35.3", "@img/sharp-linux-s390x": "0.35.3", "@img/sharp-linux-x64": "0.35.3", "@img/sharp-linuxmusl-arm64": "0.35.3", "@img/sharp-linuxmusl-x64": "0.35.3", "@img/sharp-webcontainers-wasm32": "0.35.3", "@img/sharp-win32-arm64": "0.35.3", "@img/sharp-win32-ia32": "0.35.3", "@img/sharp-win32-x64": "0.35.3" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="],
"blume/string-width": ["string-width@8.2.2", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg=="],
"blume/tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="],
"blume/typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], "blume/typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
"blume/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "blume/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
"body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
@@ -6545,6 +6691,8 @@
"dot-prop/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="], "dot-prop/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
"dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"duplexer2/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=="], "duplexer2/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=="],
"editorconfig/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], "editorconfig/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
@@ -6685,8 +6833,6 @@
"p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
"parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
@@ -6785,6 +6931,8 @@
"unzipper/fs-extra": ["fs-extra@11.3.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g=="], "unzipper/fs-extra": ["fs-extra@11.3.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g=="],
"util/inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="],
"venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.42" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-oMh5/bhu2ibsXqzHeLbJ+9Y7UQdOSrfOLPI+UA1vEcqXGYVBQfWxLe5jBUpsaL8GBBj/nmSeYGX6QPan8RfHiQ=="], "venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.42" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-oMh5/bhu2ibsXqzHeLbJ+9Y7UQdOSrfOLPI+UA1vEcqXGYVBQfWxLe5jBUpsaL8GBBj/nmSeYGX6QPan8RfHiQ=="],
"venice-ai-sdk-provider/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], "venice-ai-sdk-provider/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="],
@@ -7019,6 +7167,8 @@
"@astrojs/vercel/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], "@astrojs/vercel/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
"@asyncapi/parser/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"@aws-crypto/sha1-browser/@smithy/util-utf8/@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=="], "@aws-crypto/sha1-browser/@smithy/util-utf8/@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=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8/@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=="], "@aws-crypto/sha256-browser/@smithy/util-utf8/@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=="],
@@ -7033,6 +7183,8 @@
"@aws-sdk/client-lambda/@aws-sdk/core/@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="], "@aws-sdk/client-lambda/@aws-sdk/core/@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
"@aws-sdk/client-sts/@aws-sdk/core/fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="],
@@ -7091,10 +7243,6 @@
"@aws-sdk/token-providers/@aws-sdk/core/@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="], "@aws-sdk/token-providers/@aws-sdk/core/@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
"@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.4.1", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg=="],
"@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.4.1", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg=="],
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"@bruits/satteri-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], "@bruits/satteri-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
@@ -7413,6 +7561,8 @@
"@slack/web-api/p-queue/p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], "@slack/web-api/p-queue/p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
"@slack/web-api/p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"@solidjs/start/shiki/@shikijs/core": ["@shikijs/core@1.29.2", "", { "dependencies": { "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ=="], "@solidjs/start/shiki/@shikijs/core": ["@shikijs/core@1.29.2", "", { "dependencies": { "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ=="],
"@solidjs/start/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "oniguruma-to-es": "^2.2.0" } }, "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A=="], "@solidjs/start/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "oniguruma-to-es": "^2.2.0" } }, "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A=="],
@@ -7425,6 +7575,8 @@
"@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="], "@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="],
"@stoplight/spectral-core/minimatch/brace-expansion": ["brace-expansion@1.1.18", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw=="],
"@storybook/addon-docs/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "@storybook/addon-docs/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"@storybook/csf-plugin/unplugin/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], "@storybook/csf-plugin/unplugin/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="],
@@ -7515,6 +7667,12 @@
"blume/@shikijs/transformers/@shikijs/types": ["@shikijs/types@4.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ=="], "blume/@shikijs/transformers/@shikijs/types": ["@shikijs/types@4.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ=="],
"blume/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.51", "", { "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-tQxWGUbg/O3A5EgekJo/C2byLVQ1r+vL6QdEVWmxUSnPhdCupcOeDbQO1tv9Um40VrdwYuSWlYzcbWvtlG5bOA=="],
"blume/ai/@ai-sdk/provider": ["@ai-sdk/provider@4.0.7", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6or44XprPzKbr8zkmzosowSE0pxkvJcoojBL+mCZvPUt3kvXp3XSNqeVun9golb1acEfSo6yaEBRT18h2VU+1Q=="],
"blume/ai/@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=="],
"blume/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], "blume/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="],
"blume/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="], "blume/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="],
@@ -7535,12 +7693,8 @@
"blume/astro/neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], "blume/astro/neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="],
"blume/astro/p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="],
"blume/astro/p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="], "blume/astro/p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="],
"blume/astro/sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.3", "@img/sharp-darwin-x64": "0.35.3", "@img/sharp-freebsd-wasm32": "0.35.3", "@img/sharp-libvips-darwin-arm64": "1.3.2", "@img/sharp-libvips-darwin-x64": "1.3.2", "@img/sharp-libvips-linux-arm": "1.3.2", "@img/sharp-libvips-linux-arm64": "1.3.2", "@img/sharp-libvips-linux-ppc64": "1.3.2", "@img/sharp-libvips-linux-riscv64": "1.3.2", "@img/sharp-libvips-linux-s390x": "1.3.2", "@img/sharp-libvips-linux-x64": "1.3.2", "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", "@img/sharp-libvips-linuxmusl-x64": "1.3.2", "@img/sharp-linux-arm": "0.35.3", "@img/sharp-linux-arm64": "0.35.3", "@img/sharp-linux-ppc64": "0.35.3", "@img/sharp-linux-riscv64": "0.35.3", "@img/sharp-linux-s390x": "0.35.3", "@img/sharp-linux-x64": "0.35.3", "@img/sharp-linuxmusl-arm64": "0.35.3", "@img/sharp-linuxmusl-x64": "0.35.3", "@img/sharp-webcontainers-wasm32": "0.35.3", "@img/sharp-win32-arm64": "0.35.3", "@img/sharp-win32-ia32": "0.35.3", "@img/sharp-win32-x64": "0.35.3" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="],
"blume/astro/unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], "blume/astro/unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="],
"blume/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], "blume/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="],
@@ -7549,7 +7703,7 @@
"blume/astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], "blume/astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
"blume/astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "blume/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="],
"blume/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "blume/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
@@ -7559,6 +7713,42 @@
"blume/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "blume/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"blume/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="],
"blume/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="],
"blume/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="],
"blume/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="],
"blume/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="],
"blume/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="],
"blume/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="],
"blume/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="],
"blume/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="],
"blume/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="],
"blume/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="],
"blume/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="],
"blume/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="],
"blume/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="],
"blume/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="],
"blume/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="],
"blume/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="],
"blume/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="],
"builder-util/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "builder-util/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
@@ -8115,6 +8305,8 @@
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@aws-sdk/client-sts/@aws-sdk/core/fast-xml-parser/strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="],
@@ -8479,6 +8671,8 @@
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="], "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
"@stoplight/spectral-core/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], "app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
@@ -8505,6 +8699,8 @@
"blume/@shikijs/transformers/@shikijs/core/@shikijs/primitive": ["@shikijs/primitive@4.4.2", "", { "dependencies": { "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA=="], "blume/@shikijs/transformers/@shikijs/core/@shikijs/primitive": ["@shikijs/primitive@4.4.2", "", { "dependencies": { "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA=="],
"blume/ai/@ai-sdk/provider-utils/undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="],
"blume/astro/@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], "blume/astro/@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="],
"blume/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], "blume/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="],
@@ -8561,46 +8757,8 @@
"blume/astro/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], "blume/astro/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="],
"blume/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="],
"blume/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="],
"blume/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="],
"blume/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="],
"blume/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="],
"blume/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="],
"blume/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="],
"blume/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="],
"blume/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="],
"blume/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="],
"blume/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="],
"blume/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="],
"blume/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="],
"blume/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="],
"blume/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="],
"blume/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="],
"blume/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="],
"blume/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="],
"blume/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], "blume/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
"blume/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"blume/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], "blume/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
"blume/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], "blume/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
@@ -9059,8 +9217,6 @@
"babel-plugin-module-resolver/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "babel-plugin-module-resolver/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"blume/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="],
"blume/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], "blume/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="],
"blume/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], "blume/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
+1 -1
View File
@@ -2,7 +2,7 @@
exact = true exact = true
# Only install newly resolved package versions published at least 3 days ago. # Only install newly resolved package versions published at least 3 days ago.
minimumReleaseAge = 259200 minimumReleaseAge = 259200
minimumReleaseAgeExcludes = ["@ai-sdk/amazon-bedrock", "@ai-sdk/anthropic", "@opencode-ai/sdk", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-arm64-musl", "@opentui/core-linux-x64", "@opentui/core-linux-x64-musl", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid", "opentui-spinner", "gitlab-ai-provider", "opencode-gitlab-auth", "@ff-labs/fff-node", "@ff-labs/fff-bun", "@ff-labs/fff-bin-darwin-arm64", "@ff-labs/fff-bin-darwin-x64", "@ff-labs/fff-bin-linux-arm64-gnu", "@ff-labs/fff-bin-linux-arm64-musl", "@ff-labs/fff-bin-linux-x64-gnu", "@ff-labs/fff-bin-linux-x64-musl", "@ff-labs/fff-bin-win32-arm64", "@ff-labs/fff-bin-win32-x64", "@pierre/diffs", "@pierre/theming", "app-builder-lib", "dmg-builder", "electron-builder", "electron-publish"] minimumReleaseAgeExcludes = ["@ai-sdk/amazon-bedrock", "@ai-sdk/anthropic", "@opencode-ai/sdk", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-arm64-musl", "@opentui/core-linux-x64", "@opentui/core-linux-x64-musl", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid", "opentui-spinner", "gitlab-ai-provider", "opencode-gitlab-auth", "@ff-labs/fff-node", "@ff-labs/fff-bun", "@ff-labs/fff-bin-darwin-arm64", "@ff-labs/fff-bin-darwin-x64", "@ff-labs/fff-bin-linux-arm64-gnu", "@ff-labs/fff-bin-linux-arm64-musl", "@ff-labs/fff-bin-linux-x64-gnu", "@ff-labs/fff-bin-linux-x64-musl", "@ff-labs/fff-bin-win32-arm64", "@ff-labs/fff-bin-win32-x64", "@pierre/diffs", "@pierre/theming", "app-builder-lib", "dmg-builder", "electron-builder", "electron-publish", "blume"]
[test] [test]
root = "./do-not-run-tests-from-root" root = "./do-not-run-tests-from-root"
+4 -4
View File
@@ -15,13 +15,13 @@ Usage: install.sh [options]
Options: Options:
-h, --help Display this help message -h, --help Display this help message
-v, --version <version> Install a specific version (e.g., 0.0.0-next-17236) -v, --version <version> Install a specific version (e.g., 0.0.0-beta-17236)
-b, --binary <path> Install from a local binary instead of downloading -b, --binary <path> Install from a local binary instead of downloading
--no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.) --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
Examples: Examples:
curl -fsSL https://raw.githubusercontent.com/anomalyco/opencode/v2/install | bash curl -fsSL https://opencode.ai/v2/install | bash
curl -fsSL https://raw.githubusercontent.com/anomalyco/opencode/v2/install | bash -s -- --version 0.0.0-next-17236 curl -fsSL https://opencode.ai/v2/install | bash -s -- --version 0.0.0-beta-17236
./install --binary /path/to/opencode2 ./install --binary /path/to/opencode2
EOF EOF
} }
@@ -166,7 +166,7 @@ else
fi fi
if [ -z "$requested_version" ]; then if [ -z "$requested_version" ]; then
metadata=$(curl -fsSL https://registry.npmjs.org/@opencode-ai%2fcli/next || true) metadata=$(curl -fsSL https://registry.npmjs.org/@opencode-ai%2fcli/beta || true)
specific_version=$(echo "$metadata" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p') specific_version=$(echo "$metadata" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')
if [ -z "$specific_version" ]; then if [ -z "$specific_version" ]; then
+1 -1
View File
@@ -8,7 +8,7 @@
"packageManager": "bun@1.3.14", "packageManager": "bun@1.3.14",
"scripts": { "scripts": {
"dev": "bun run --cwd packages/cli --conditions=browser src/index.ts", "dev": "bun run --cwd packages/cli --conditions=browser src/index.ts",
"dev:live": "OPENCODE_TUI_CHANNEL=next OPENCODE_PASSWORD=\"$(opencode2 service get password)\" bun run dev --server \"$(opencode2 service status)\"", "dev:live": "OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode2 service get password)\" bun run dev --server \"$(opencode2 service status)\"",
"dev:desktop": "bun --cwd packages/desktop dev", "dev:desktop": "bun --cwd packages/desktop dev",
"dev:web": "bun --cwd packages/app dev", "dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev", "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
@@ -30,7 +30,6 @@ import { ToolSchemaProjection } from "./utils/tool-schema.js"
import { ToolStream } from "./utils/tool-stream.js" import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "anthropic-messages" const ADAPTER = "anthropic-messages"
const MEDIA_MIMES = new Set<string>([...ProviderShared.IMAGE_MIMES, ...ProviderShared.PDF_MIMES])
export const DEFAULT_BASE_URL = "https://api.anthropic.com/v1" export const DEFAULT_BASE_URL = "https://api.anthropic.com/v1"
export const PATH = "/messages" export const PATH = "/messages"
@@ -400,7 +399,7 @@ const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult
}) })
const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part: MediaPart) { const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part: MediaPart) {
const media = yield* ProviderShared.validateMedia("Anthropic Messages", part, MEDIA_MIMES) const media = ProviderShared.normalizeMedia(part)
if (media.mime === "application/pdf") if (media.mime === "application/pdf")
return { return {
type: "document" as const, type: "document" as const,
@@ -410,6 +409,8 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part: Me
data: media.base64, data: media.base64,
}, },
} satisfies AnthropicDocumentBlock } satisfies AnthropicDocumentBlock
if (!media.mime.startsWith("image/"))
return yield* invalid(`Anthropic Messages does not support media type ${part.mediaType}`)
return { return {
type: "image" as const, type: "image" as const,
source: { source: {
+2 -3
View File
@@ -24,7 +24,6 @@ import { Lifecycle } from "./utils/lifecycle.js"
import { ToolSchemaProjection } from "./utils/tool-schema.js" import { ToolSchemaProjection } from "./utils/tool-schema.js"
const ADAPTER = "gemini" const ADAPTER = "gemini"
const MEDIA_MIMES = new Set<string>(ProviderShared.MEDIA_MIMES)
// Google documents this sentinel for replaying Gemini 3 function calls after their original signature was lost. // Google documents this sentinel for replaying Gemini 3 function calls after their original signature was lost.
const SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator" const SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator"
export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta" export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
@@ -248,7 +247,7 @@ const lowerToolConfig = (toolChoice: NonNullable<LLMRequest["toolChoice"]>) =>
const lowerUserPart = Effect.fn("Gemini.lowerUserPart")(function* (part: TextPart | MediaPart) { const lowerUserPart = Effect.fn("Gemini.lowerUserPart")(function* (part: TextPart | MediaPart) {
if (part.type === "text") return { text: part.text } if (part.type === "text") return { text: part.text }
const media = yield* ProviderShared.validateMedia("Gemini", part, MEDIA_MIMES) const media = ProviderShared.normalizeMedia(part)
return { inlineData: { mimeType: media.mime, data: media.base64 } } return { inlineData: { mimeType: media.mime, data: media.base64 } }
}) })
@@ -353,7 +352,7 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request: LLMR
const media: GeminiInlineDataPart[] = [] const media: GeminiInlineDataPart[] = []
for (const item of content) { for (const item of content) {
if (item.type === "text") continue if (item.type === "text") continue
const value = yield* ProviderShared.validateToolFile("Gemini", item, MEDIA_MIMES) const value = ProviderShared.normalizeToolFile(item)
media.push({ inlineData: { mimeType: value.mime, data: value.base64 } }) media.push({ inlineData: { mimeType: value.mime, data: value.base64 } })
} }
parts.push({ parts.push({
+4 -5
View File
@@ -26,7 +26,6 @@ import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "open-responses" const ADAPTER = "open-responses"
const NAME = "Open Responses" const NAME = "Open Responses"
const MEDIA_MIMES = new Set<string>([...ProviderShared.IMAGE_MIMES, ...ProviderShared.PDF_MIMES])
export const PATH = "/responses" export const PATH = "/responses"
// ============================================================================= // =============================================================================
@@ -285,7 +284,7 @@ export interface Extension {
readonly name: string readonly name: string
readonly lowerMedia?: (input: { readonly lowerMedia?: (input: {
readonly part: MediaPart readonly part: MediaPart
readonly media: ProviderShared.ValidatedMedia readonly media: ProviderShared.NormalizedMedia
readonly request: LLMRequest readonly request: LLMRequest
}) => MediaInput | undefined }) => MediaInput | undefined
readonly messagePhase?: (value: unknown) => MessagePhase | null | undefined readonly messagePhase?: (value: unknown) => MessagePhase | null | undefined
@@ -380,13 +379,13 @@ const lowerMedia = Effect.fn("OpenResponses.lowerMedia")(function* (
request: LLMRequest, request: LLMRequest,
extension: Extension, extension: Extension,
) { ) {
const media = yield* ProviderShared.validateMedia(extension.name, part, MEDIA_MIMES) const media = ProviderShared.normalizeMedia(part)
const extended = extension.lowerMedia?.({ part, media, request }) const extended = extension.lowerMedia?.({ part, media, request })
if (extended) return extended if (extended) return extended
if (media.mime === "application/pdf") { if (!media.mime.startsWith("image/")) {
return { return {
type: "input_file" as const, type: "input_file" as const,
filename: part.filename ?? "document.pdf", filename: part.filename ?? (media.mime === "application/pdf" ? "document.pdf" : "file"),
file_data: media.dataUrl, file_data: media.dataUrl,
} }
} }
+3 -2
View File
@@ -28,7 +28,6 @@ import { ToolSchemaProjection } from "./utils/tool-schema.js"
import { ToolStream } from "./utils/tool-stream.js" import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "openai-chat" const ADAPTER = "openai-chat"
const IMAGE_MIMES = new Set<string>(ProviderShared.IMAGE_MIMES)
const RESERVED_REASONING_FIELDS = new Set(["role", "content", "tool_calls"]) const RESERVED_REASONING_FIELDS = new Set(["role", "content", "tool_calls"])
export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const DEFAULT_BASE_URL = "https://api.openai.com/v1"
export const PATH = "/chat/completions" export const PATH = "/chat/completions"
@@ -284,7 +283,9 @@ const lowerToolCall = (part: ToolCallPart): OpenAIChatAssistantToolCall => ({
}) })
const lowerMedia = Effect.fn("OpenAIChat.lowerMedia")(function* (part: MediaPart) { const lowerMedia = Effect.fn("OpenAIChat.lowerMedia")(function* (part: MediaPart) {
const media = yield* ProviderShared.validateMedia("OpenAI Chat", part, IMAGE_MIMES) const media = ProviderShared.normalizeMedia(part)
if (!media.mime.startsWith("image/"))
return yield* ProviderShared.invalidRequest(`OpenAI Chat does not support media type ${part.mediaType}`)
return { type: "image_url" as const, image_url: { url: media.dataUrl } } return { type: "image_url" as const, image_url: { url: media.dataUrl } }
}) })
+9 -44
View File
@@ -155,59 +155,24 @@ export const wrappedSystemUpdate = Effect.fn("ProviderShared.wrappedSystemUpdate
export const parseToolInput = (route: string, name: string, raw: string) => export const parseToolInput = (route: string, name: string, raw: string) =>
parseJson(route, raw || "{}", `Invalid JSON input for ${route} tool call ${name}`) parseJson(route, raw || "{}", `Invalid JSON input for ${route} tool call ${name}`)
export const IMAGE_MIMES = ["image/png", "image/jpeg", "image/gif", "image/webp"] as const export interface NormalizedMedia {
export const VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"] as const
export const AUDIO_MIMES = ["audio/wav", "audio/mp3", "audio/aiff", "audio/aac", "audio/ogg", "audio/flac"] as const
export const PDF_MIMES = ["application/pdf"] as const
export const MEDIA_MIMES = [...IMAGE_MIMES, ...VIDEO_MIMES, ...AUDIO_MIMES, ...PDF_MIMES] as const
export const MAX_MEDIA_ENCODED_BYTES = 28 * 1024 * 1024
export const MAX_MEDIA_DECODED_BYTES = 20 * 1024 * 1024
const base64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
export interface ValidatedMedia {
readonly mime: string readonly mime: string
readonly base64: string readonly base64: string
readonly dataUrl: string readonly dataUrl: string
readonly bytes: Uint8Array
} }
export const validateMedia = Effect.fn("ProviderShared.validateMedia")(function* ( export const normalizeMedia = (part: MediaPart): NormalizedMedia => {
route: string,
part: MediaPart,
supportedMimes: ReadonlySet<string>,
) {
const mime = part.mediaType.toLowerCase() const mime = part.mediaType.toLowerCase()
if (!supportedMimes.has(mime)) return yield* invalidRequest(`${route} does not support media type ${part.mediaType}`)
let base64: string
if (typeof part.data !== "string") { if (typeof part.data !== "string") {
if (part.data.byteLength > MAX_MEDIA_DECODED_BYTES) const base64 = Buffer.from(part.data).toString("base64")
return yield* invalidRequest(`${route} media exceeds the ${MAX_MEDIA_DECODED_BYTES} byte decoded limit`) return { mime, base64, dataUrl: `data:${mime};base64,${base64}` }
base64 = Buffer.from(part.data).toString("base64")
} else if (part.data.startsWith("data:")) {
const match = /^data:([^;,]+);base64,([A-Za-z0-9+/]*={0,2})$/s.exec(part.data)
if (!match) return yield* invalidRequest(`${route} media data URL must contain valid base64`)
if (match[1]!.toLowerCase() !== mime)
return yield* invalidRequest(`${route} media type ${part.mediaType} does not match data URL type ${match[1]}`)
base64 = match[2]!
} else {
base64 = part.data
} }
if (!part.data.startsWith("data:")) return { mime, base64: part.data, dataUrl: `data:${mime};base64,${part.data}` }
return { mime, base64: part.data.slice(part.data.indexOf(",") + 1), dataUrl: part.data }
}
if (Buffer.byteLength(base64, "utf8") > MAX_MEDIA_ENCODED_BYTES) export const normalizeToolFile = (part: Tool.FileContent) =>
return yield* invalidRequest(`${route} media exceeds the ${MAX_MEDIA_ENCODED_BYTES} byte encoded limit`) normalizeMedia({ type: "media", mediaType: part.mime, data: part.uri, filename: part.name })
if (!base64 || base64.length % 4 !== 0 || !base64Pattern.test(base64))
return yield* invalidRequest(`${route} media must contain valid base64`)
const bytes = Buffer.from(base64, "base64")
if (bytes.byteLength > MAX_MEDIA_DECODED_BYTES)
return yield* invalidRequest(`${route} media exceeds the ${MAX_MEDIA_DECODED_BYTES} byte decoded limit`)
if (bytes.toString("base64") !== base64) return yield* invalidRequest(`${route} media must contain canonical base64`)
return { mime, base64, dataUrl: `data:${mime};base64,${base64}`, bytes } satisfies ValidatedMedia
})
export const validateToolFile = (route: string, part: Tool.FileContent, supportedMimes: ReadonlySet<string>) =>
validateMedia(route, { type: "media", mediaType: part.mime, data: part.uri, filename: part.name }, supportedMimes)
export const trimBaseUrl = (value: string) => value.replace(/\/+$/, "") export const trimBaseUrl = (value: string) => value.replace(/\/+$/, "")
@@ -66,11 +66,7 @@ export const lower = Effect.fn("BedrockMedia.lower")(function* (part: MediaPart)
const mime = part.mediaType.toLowerCase() const mime = part.mediaType.toLowerCase()
const imageFormat = IMAGE_FORMATS[mime as keyof typeof IMAGE_FORMATS] const imageFormat = IMAGE_FORMATS[mime as keyof typeof IMAGE_FORMATS]
if (imageFormat) { if (imageFormat) {
const media = yield* ProviderShared.validateMedia( const media = ProviderShared.normalizeMedia(part)
"Bedrock Converse",
part,
new Set<string>(Object.keys(IMAGE_FORMATS)),
)
return { image: { format: imageFormat, source: { bytes: media.base64 } } } satisfies ImageBlock return { image: { format: imageFormat, source: { bytes: media.base64 } } } satisfies ImageBlock
} }
if (mime.startsWith("image/")) if (mime.startsWith("image/"))
@@ -79,11 +75,7 @@ export const lower = Effect.fn("BedrockMedia.lower")(function* (part: MediaPart)
if (documentFormat) { if (documentFormat) {
if (!part.filename) if (!part.filename)
return yield* ProviderShared.invalidRequest("Bedrock Converse document media requires a filename") return yield* ProviderShared.invalidRequest("Bedrock Converse document media requires a filename")
const media = yield* ProviderShared.validateMedia( const media = ProviderShared.normalizeMedia(part)
"Bedrock Converse",
part,
new Set<string>(Object.keys(DOCUMENT_FORMATS)),
)
return documentBlock(part.filename, documentFormat, media.base64) return documentBlock(part.filename, documentFormat, media.base64)
} }
return yield* ProviderShared.invalidRequest(`Bedrock Converse does not support media type ${part.mediaType}`) return yield* ProviderShared.invalidRequest(`Bedrock Converse does not support media type ${part.mediaType}`)
@@ -399,7 +399,7 @@ describe("Anthropic Messages route", () => {
}), }),
) )
it.effect("rejects unsupported media in tool-result content with a clear error", () => it.effect("rejects tool-result media that cannot be lowered", () =>
Effect.gen(function* () { Effect.gen(function* () {
const error = yield* compileRequest( const error = yield* compileRequest(
LLM.request({ LLM.request({
@@ -418,8 +418,7 @@ describe("Anthropic Messages route", () => {
}), }),
).pipe(Effect.flip) ).pipe(Effect.flip)
expect(error.message).toContain("Anthropic Messages") expect(error.message).toContain("Anthropic Messages does not support media type audio/mpeg")
expect(error.message).toContain("audio/mpeg")
}), }),
) )
+18 -24
View File
@@ -4,7 +4,6 @@ import { LLM, AIError, LLMRequest, Message, ToolCallPart, ToolDefinition, Usage
import { Auth, LLMClient } from "../../src/route.js" import { Auth, LLMClient } from "../../src/route.js"
import { compileRequest } from "../../src/route/client.js" import { compileRequest } from "../../src/route/client.js"
import * as Gemini from "../../src/protocols/gemini.js" import * as Gemini from "../../src/protocols/gemini.js"
import { ProviderShared } from "../../src/protocols/shared.js"
import { it } from "../lib/effect.js" import { it } from "../lib/effect.js"
import { fixedResponse } from "../lib/http.js" import { fixedResponse } from "../lib/http.js"
import { sseEvents, sseRaw } from "../lib/sse.js" import { sseEvents, sseRaw } from "../lib/sse.js"
@@ -291,35 +290,30 @@ describe("Gemini route", () => {
}), }),
) )
for (const [name, media] of [ it.effect("passes encoded media through without local validation", () =>
["mismatched data URL MIME", { mediaType: "image/png", data: "data:image/jpeg;base64,/9j/" }],
["malformed base64", { mediaType: "image/png", data: "%%%=" }],
["unsupported SVG", { mediaType: "image/svg+xml", data: "PHN2Zz4=" }],
] as const)
it.effect(`rejects ${name}`, () =>
Effect.gen(function* () {
const error = yield* compileRequest(
LLM.request({ model, messages: [Message.user({ type: "media", ...media })] }),
).pipe(Effect.flip)
expect(error.message).toMatch(/does not support|does not match|valid base64/)
}),
)
it.effect("rejects oversized image input", () =>
Effect.gen(function* () { Effect.gen(function* () {
const error = yield* compileRequest( const prepared = yield* compileRequest(
LLM.request({ LLM.request({
model, model,
messages: [ messages: [
Message.user({ Message.user([
type: "media", { type: "media", mediaType: "image/png", data: "%%%=" },
mediaType: "image/png", { type: "media", mediaType: "image/png", data: "data:image/jpeg;base64,/9j/" },
data: "A".repeat(ProviderShared.MAX_MEDIA_ENCODED_BYTES + 4), { type: "media", mediaType: "image/svg+xml", data: "PHN2Zz4=" },
}), ]),
], ],
}), }),
).pipe(Effect.flip) )
expect(error.message).toContain("encoded limit") expect(prepared.body.contents).toEqual([
{
role: "user",
parts: [
{ inlineData: { mimeType: "image/png", data: "%%%=" } },
{ inlineData: { mimeType: "image/png", data: "/9j/" } },
{ inlineData: { mimeType: "image/svg+xml", data: "PHN2Zz4=" } },
],
},
])
}), }),
) )
+29 -22
View File
@@ -527,35 +527,42 @@ describe("OpenAI Chat route", () => {
}), }),
) )
for (const [name, media] of [ it.effect("passes encoded image media through without local validation", () =>
["mismatched data URL MIME", { mediaType: "image/png", data: "data:image/jpeg;base64,/9j/" }], Effect.gen(function* () {
["malformed base64", { mediaType: "image/png", data: "not-base64" }], const prepared = yield* compileRequest(
["unsupported SVG", { mediaType: "image/svg+xml", data: "PHN2Zz4=" }], LLM.request({
] as const) model,
it.effect(`rejects ${name}`, () => messages: [
Effect.gen(function* () { Message.user([
const error = yield* compileRequest( { type: "media", mediaType: "image/png", data: "not-base64" },
LLM.request({ model, messages: [Message.user({ type: "media", ...media })] }), { type: "media", mediaType: "image/png", data: "data:image/jpeg;base64,/9j/" },
).pipe(Effect.flip) { type: "media", mediaType: "image/svg+xml", data: "PHN2Zz4=" },
expect(error.message).toMatch(/does not support|does not match|valid base64/) ]),
}), ],
) }),
)
expect(prepared.body.messages).toEqual([
{
role: "user",
content: [
{ type: "image_url", image_url: { url: "data:image/png;base64,not-base64" } },
{ type: "image_url", image_url: { url: "data:image/jpeg;base64,/9j/" } },
{ type: "image_url", image_url: { url: "data:image/svg+xml;base64,PHN2Zz4=" } },
],
},
])
}),
)
it.effect("rejects oversized image input", () => it.effect("rejects non-image media that cannot be lowered", () =>
Effect.gen(function* () { Effect.gen(function* () {
const error = yield* compileRequest( const error = yield* compileRequest(
LLM.request({ LLM.request({
model, model,
messages: [ messages: [Message.user({ type: "media", mediaType: "audio/mpeg", data: "AAECAw==" })],
Message.user({
type: "media",
mediaType: "image/png",
data: "A".repeat(ProviderShared.MAX_MEDIA_ENCODED_BYTES + 4),
}),
],
}), }),
).pipe(Effect.flip) ).pipe(Effect.flip)
expect(error.message).toContain("encoded limit") expect(error.message).toContain("OpenAI Chat does not support media type audio/mpeg")
}), }),
) )
@@ -1149,6 +1149,32 @@ describe("OpenAI Responses route", () => {
}), }),
) )
it.effect("passes large PDF tool-result content through", () =>
Effect.gen(function* () {
const base64 = "A".repeat(8_125_844)
const dataUrl = `data:application/pdf;base64,${base64}`
const prepared = yield* compileRequest(
LLM.request({
id: "req_tool_result_large_pdf",
model,
messages: [
Message.assistant([ToolCallPart.make({ id: "call_1", name: "read", input: {} })]),
Message.tool({
id: "call_1",
name: "read",
resultType: "content",
result: [{ type: "file", uri: dataUrl, mime: "application/pdf", name: "report.pdf" }],
}),
],
}),
)
expect(expectToolOutput(prepared.body).output).toEqual([
{ type: "input_file", filename: "report.pdf", file_data: dataUrl },
])
}),
)
it.effect("uses xAI inline file encoding for PDF tool results", () => it.effect("uses xAI inline file encoding for PDF tool results", () =>
Effect.gen(function* () { Effect.gen(function* () {
const prepared = yield* compileRequest( const prepared = yield* compileRequest(
@@ -1184,9 +1210,9 @@ describe("OpenAI Responses route", () => {
}), }),
) )
it.effect("rejects unsupported media in tool-result content with a clear error", () => it.effect("passes non-image tool-result content through as an input file", () =>
Effect.gen(function* () { Effect.gen(function* () {
const error = yield* compileRequest( const prepared = yield* compileRequest(
LLM.request({ LLM.request({
id: "req_tool_result_unsupported_media", id: "req_tool_result_unsupported_media",
model, model,
@@ -1200,10 +1226,11 @@ describe("OpenAI Responses route", () => {
}), }),
], ],
}), }),
).pipe(Effect.flip) )
expect(error.message).toContain("OpenAI Responses") expect(expectToolOutput(prepared.body).output).toEqual([
expect(error.message).toContain("audio/mpeg") { type: "input_file", filename: "file", file_data: "data:audio/mpeg;base64,AAECAw==" },
])
}), }),
) )
@@ -2394,17 +2421,28 @@ describe("OpenAI Responses route", () => {
}), }),
) )
it.effect("rejects unsupported user media content", () => it.effect("passes non-image user media through as an input file", () =>
Effect.gen(function* () { Effect.gen(function* () {
const error = yield* compileRequest( const prepared = yield* compileRequest(
LLM.request({ LLM.request({
id: "req_media", id: "req_media",
model, model,
messages: [Message.user({ type: "media", mediaType: "application/x-tar", data: "AAECAw==" })], messages: [Message.user({ type: "media", mediaType: "application/x-tar", data: "AAECAw==" })],
}), }),
).pipe(Effect.flip) )
expect(error.message).toContain("OpenAI Responses does not support media type application/x-tar") expect(prepared.body.input).toEqual([
{
role: "user",
content: [
{
type: "input_file",
filename: "file",
file_data: "data:application/x-tar;base64,AAECAw==",
},
],
},
])
}), }),
) )
@@ -1,60 +0,0 @@
import { expect, test } from "@playwright/test"
import type { Page } from "@playwright/test"
import { fixture, pageMessages } from "../smoke/session-timeline.fixture"
import { mockOpenCodeServer } from "../utils/mock-server"
import { expectAppVisible } from "../utils/waits"
const NAMES = ["alpha-service", "bravo-web", "charlie-api", "delta-tools", "echo-infra", "foxtrot-docs"]
const worktrees = NAMES.map((name) => `/opencode-demo/${name}`)
// The sixth project sits outside the five-item recent cap, so it is only reachable if the
// dialog hands every recent project to the list filter instead of a pre-truncated slice.
const OUTSIDE_CAP = "foxtrot-docs"
// Dialog rows carry data-directory-path; the sidebar project list does not, so this
// scopes assertions to the picker instead of matching the sidebar entry of the same name.
const rows = (page: Page) => page.locator("[data-directory-path]")
const row = (page: Page, name: string) => page.locator(`[data-directory-path*="${name}"]`)
async function openProjectDialog(page: Page) {
await mockOpenCodeServer(page, {
sessions: fixture.sessions,
provider: fixture.provider,
directory: fixture.directory,
project: fixture.project,
pageMessages,
fileList: () => [],
findFiles: () => [],
})
await page.addInitScript((dirs) => {
localStorage.setItem(
"opencode.global.dat:server",
JSON.stringify({
projects: { local: dirs.map((worktree: string) => ({ worktree, expanded: false })) },
lastProject: {},
}),
)
}, worktrees)
await page.goto("/")
const add = page.getByRole("button", { name: "Add project" }).first()
await expectAppVisible(add)
await add.click()
await expect(rows(page)).toHaveCount(5)
return page.getByRole("textbox").last()
}
test("searches every recent project, not just the five most recent", async ({ page }) => {
const search = await openProjectDialog(page)
await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
await search.fill("foxtrot")
await expect(row(page, OUTSIDE_CAP)).toHaveCount(1)
})
test("still caps the idle recent list at five projects", async ({ page }) => {
await openProjectDialog(page)
await expect(row(page, NAMES[4])).toHaveCount(1)
await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
})
@@ -1,10 +1,15 @@
import { expect, test } from "@playwright/test" import { expect, test } from "@playwright/test"
import type { SessionMessageAssistant, SessionMessageInfo } from "@opencode-ai/client/promise" import type { SessionMessageAssistant, SessionMessageInfo } from "@opencode-ai/client/promise"
import { session, sessionID, setupTimeline } from "../performance/timeline-stability/fixture" import { event, session, sessionID, setupTimeline } from "../performance/timeline-stability/fixture"
const user = { id: "msg_user", type: "user", text: "Run it", time: { created: 1 } } satisfies SessionMessageInfo const user = { id: "msg_user", type: "user", text: "Run it", time: { created: 1 } } satisfies SessionMessageInfo
const assistant = (completed: boolean, tool = false, childID?: string): SessionMessageAssistant => ({ const assistant = (
completed: boolean,
tool = false,
childID?: string,
background = false,
): SessionMessageAssistant => ({
id: "msg_assistant", id: "msg_assistant",
type: "assistant", type: "assistant",
agent: "build", agent: "build",
@@ -15,7 +20,11 @@ const assistant = (completed: boolean, tool = false, childID?: string): SessionM
type: "tool", type: "tool",
id: "call_subagent", id: "call_subagent",
name: "subagent", name: "subagent",
state: { status: "running", input: {}, metadata: childID ? { sessionID: childID } : {} }, state: {
status: "running",
input: { description: "Inspect code", ...(background ? { background: true } : {}) },
metadata: { status: "running", ...(childID ? { sessionID: childID } : {}) },
},
time: { created: 2 }, time: { created: 2 },
}, },
] ]
@@ -59,12 +68,17 @@ test("renders current protocol notices in CLI order", async ({ page }) => {
await expect(notices.nth(1)).toContainText("explore finished · Search code") await expect(notices.nth(1)).toContainText("explore finished · Search code")
await expect(notices.nth(2)).toContainText("Continuing after restart") await expect(notices.nth(2)).toContainText("Continuing after restart")
await expect(notices.nth(3)).toContainText("Skill · Review") await expect(notices.nth(3)).toContainText("Skill · Review")
await expect(notices).toHaveClass([/text-text-weak/, /text-text-weak/, /text-text-weak/, /text-text-weak/])
await expect(notices.locator(".text-text-strong")).toHaveCount(0)
expect(ownerWarnings).toEqual([]) expect(ownerWarnings).toEqual([])
}) })
test("moves blocking work to the background with Ctrl+B", async ({ page }) => { test("moves blocking work to the background with Ctrl+B", async ({ page }) => {
await setupTimeline(page, { currentMessages: [user, assistant(false, true)] }) await setupTimeline(page, { currentMessages: [user, assistant(false, true)] })
await expect(page.locator('[data-component="task-tool-card"]')).toBeVisible() const card = page.locator('[data-component="task-tool-card"]')
await expect(card).toBeVisible()
await expect(card).toContainText("Inspect code")
await expect(card).not.toContainText("(background)")
await expect(page.getByText("Called `subagent`", { exact: false })).toHaveCount(0) await expect(page.getByText("Called `subagent`", { exact: false })).toHaveCount(0)
await expect(page.locator('[data-component="background-tool-control"]')).toHaveCount(0) await expect(page.locator('[data-component="background-tool-control"]')).toHaveCount(0)
await expect(page.locator('[data-action="session-background-toggle"]')).toContainText("Move 1 subagent to background") await expect(page.locator('[data-action="session-background-toggle"]')).toContainText("Move 1 subagent to background")
@@ -77,6 +91,11 @@ test("moves blocking work to the background with Ctrl+B", async ({ page }) => {
await request await request
}) })
test("waits for completion before labeling requested background work", async ({ page }) => {
await setupTimeline(page, { currentMessages: [user, assistant(false, true, undefined, true)] })
await expect(page.locator('[data-component="task-tool-card"]')).not.toContainText("(background)")
})
test("navigates from a running subagent card and hides background controls in the child", async ({ page }) => { test("navigates from a running subagent card and hides background controls in the child", async ({ page }) => {
const childID = "ses_running_child" const childID = "ses_running_child"
await setupTimeline(page, { await setupTimeline(page, {
@@ -105,7 +124,7 @@ test("shows a badge for active background work", async ({ page }) => {
test("separates blocking and already-backgrounded work into two rows", async ({ page }) => { test("separates blocking and already-backgrounded work into two rows", async ({ page }) => {
const backgroundID = "ses_background_existing" const backgroundID = "ses_background_existing"
const blockingID = "ses_background_blocking" const blockingID = "ses_background_blocking"
await setupTimeline(page, { const timeline = await setupTimeline(page, {
currentMessages: [ currentMessages: [
user, user,
{ {
@@ -175,9 +194,16 @@ test("separates blocking and already-backgrounded work into two rows", async ({
}) })
const dock = page.locator('[data-component="session-background-dock"]') const dock = page.locator('[data-component="session-background-dock"]')
const backgroundCard = page.locator('[data-timeline-part-id="call_backgrounded"]')
await expect(dock).toContainText("Move 1 subagent to background") await expect(dock).toContainText("Move 1 subagent to background")
await expect(dock.getByText("Running 1 shell and 1 subagent in background", { exact: true })).toBeVisible() await expect(dock.getByText("Running 1 shell and 1 subagent in background", { exact: true })).toBeVisible()
await expect(backgroundCard).toContainText("Background task (background)")
await expect(backgroundCard.locator('[data-component="session-progress-indicator-v2"]')).toBeVisible()
await expect( await expect(
page.locator('[data-timeline-part-id="call_shell_backgrounded"] [data-component="text-shimmer"]'), page.locator('[data-timeline-part-id="call_shell_backgrounded"] [data-component="text-shimmer"]'),
).toHaveAttribute("data-active", "true") ).toHaveAttribute("data-active", "true")
await timeline.send(event("session.status", { sessionID: backgroundID, status: { type: "idle" } }))
await expect(backgroundCard.locator('[data-component="session-progress-indicator-v2"]')).toHaveCount(0)
await expect(backgroundCard).toContainText("Background task (background)")
}) })
@@ -115,6 +115,12 @@ test("labels V2 skill tools from IDs and result metadata", async ({ page }) => {
"aria-label", "aria-label",
"OpenCode", "OpenCode",
) )
for (const id of [pending, completed]) {
const skill = page.locator(`[data-timeline-part-id="${id}"]`)
await expect(skill.locator('[data-slot="skill-tool-label"]')).toHaveText("Skill")
await expect(skill.locator('[data-slot="skill-tool-separator"]')).toHaveText("·")
await expect(skill.locator('use[href="#opencode-icon-post-skill"]')).toBeVisible()
}
}) })
function questionInput() { function questionInput() {
@@ -72,7 +72,12 @@ test("creates a session in a new project and selects its model", async ({ page }
const addProject = page.locator('[data-action="home-add-project-row"]') const addProject = page.locator('[data-action="home-add-project-row"]')
await expectAppVisible(addProject) await expectAppVisible(addProject)
await addProject.click() await addProject.click()
await page.locator("[data-directory-path]").click() const directoryItem = page.getByRole("treeitem", { name: "NewProject" })
await expect(directoryItem).toBeVisible()
await directoryItem.click()
const selectFolder = page.getByRole("button", { name: "Select folder" })
await expect(selectFolder).toBeEnabled()
await selectFolder.click()
await page.locator('[data-action="home-new-session"]').click() await page.locator('[data-action="home-new-session"]').click()
await expectAppVisible(page.locator('[data-component="prompt-input-v2"]')) await expectAppVisible(page.locator('[data-component="prompt-input-v2"]'))
@@ -1,205 +0,0 @@
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { List } from "@opencode-ai/ui/list"
import type { ListRef } from "@opencode-ai/ui/list"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import { createMemo, createResource, createSignal } from "solid-js"
import { useLanguage } from "@/context/language"
import { ServerConnection } from "@/context/servers"
import { useGlobal } from "@/context/global"
import { cleanPickerInput, createDirectorySearch, displayPickerPath } from "./directory-picker-domain"
import type { Path } from "@/types"
interface DialogSelectDirectoryProps {
title?: string
multiple?: boolean
onSelect: (result: string | string[] | null) => void
server: ServerConnection.Any
}
const RECENT_PROJECT_LIMIT = 5
type Row = {
absolute: string
search: string
group: "recent" | "folders"
}
function toRow(absolute: string, home: string, group: Row["group"]): Row {
const full = displayPickerPath(absolute, "", "")
const tilde = displayPickerPath(full, "~", home)
const withSlash = (value: string) => {
if (!value) return ""
if (value.endsWith("/")) return value
return value + "/"
}
const search = Array.from(
new Set([full, withSlash(full), tilde, withSlash(tilde), getFilename(full)].filter(Boolean)),
).join("\n")
return { absolute: full, search, group }
}
function uniqueRows(rows: Row[]) {
const seen = new Set<string>()
return rows.filter((row) => {
if (seen.has(row.absolute)) return false
seen.add(row.absolute)
return true
})
}
export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
const global = useGlobal()
const { sync, sdk, ...serverCtx } = global.ensureServerCtx(props.server)
const dialog = useDialog()
const language = useLanguage()
const [filter, setFilter] = createSignal("")
let list: ListRef | undefined
const [fallbackPath] = createResource(
() => (!(sync.data.path.home || sync.data.path.directory) ? true : undefined),
() =>
sdk.api.location
.get()
.then(
(location): Path => ({
state: "",
config: "",
worktree: location.project.directory,
directory: location.directory,
home: "",
}),
)
.catch(() => undefined),
{ initialValue: undefined },
)
const home = createMemo(() => sync.data.path.home || fallbackPath()?.home || "")
const start = createMemo(
() => sync.data.path.home || sync.data.path.directory || fallbackPath()?.home || fallbackPath()?.directory,
)
const directories = createDirectorySearch({
sdk,
home,
base: start,
})
const recentProjects = createMemo(() => {
const projects = serverCtx.projects.list()
const byProject = new Map<string, number>()
for (const project of projects) {
let at = 0
const dirs = [project.worktree, ...(project.sandboxes ?? [])]
for (const directory of dirs) {
const sessions = sync.child(directory, { bootstrap: false })[0].session
for (const session of sessions) {
if (session.time.archived) continue
const updated = session.time.updated ?? session.time.created
if (updated > at) at = updated
}
}
byProject.set(project.worktree, at)
}
return projects
.map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
.sort((a, b) => b.at - a.at || a.index - b.index)
.map(({ project }) => {
const row = toRow(project.worktree, home(), "recent")
const name = project.name || getFilename(project.worktree)
return {
...row,
search: `${row.search}\n${name}`,
}
})
})
const items = async (value: string) => {
const results = await directories(value)
const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
// Cap the idle list only. Once a query narrows the results, every project stays searchable.
const recent = recentProjects()
const visible = value ? recent : recent.slice(0, RECENT_PROJECT_LIMIT)
return uniqueRows([...visible, ...directoryRows])
}
function resolve(absolute: string) {
props.onSelect(props.multiple ? [absolute] : absolute)
dialog.close()
}
return (
<Dialog title={props.title ?? language.t("command.project.open")}>
<List
class="px-3"
search={{ placeholder: language.t("dialog.directory.search.placeholder"), autofocus: true }}
emptyMessage={language.t("dialog.directory.empty")}
loadingMessage={language.t("common.loading")}
items={items}
key={(x) => x.absolute}
filterKeys={["search"]}
groupBy={(item) => item.group}
sortGroupsBy={(a, b) => {
if (a.category === b.category) return 0
return a.category === "recent" ? -1 : 1
}}
groupHeader={(group) =>
group.category === "recent" ? language.t("home.recentProjects") : language.t("command.project.open")
}
ref={(r) => (list = r)}
onFilter={(value) => setFilter(cleanPickerInput(value))}
onKeyEvent={(e, item) => {
if (e.key !== "Tab") return
if (e.shiftKey) return
if (!item) return
e.preventDefault()
e.stopPropagation()
const value = displayPickerPath(item.absolute, filter(), home())
list?.setFilter(value.endsWith("/") ? value : value + "/")
}}
onSelect={(path) => {
if (!path) return
resolve(path.absolute)
}}
>
{(item) => {
const path = displayPickerPath(item.absolute, filter(), home())
if (path === "~") {
return (
<div data-directory-path={item.absolute} class="w-full flex items-center justify-between rounded-md">
<div class="flex items-center gap-x-3 grow min-w-0">
<FileIcon node={{ path: item.absolute, type: "directory" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular min-w-0">
<span class="text-text-strong whitespace-nowrap">~</span>
<span class="text-text-weak whitespace-nowrap">/</span>
</div>
</div>
</div>
)
}
return (
<div data-directory-path={item.absolute} class="w-full flex items-center justify-between rounded-md">
<div class="flex items-center gap-x-3 grow min-w-0">
<FileIcon node={{ path: item.absolute, type: "directory" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular min-w-0">
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
{getDirectory(path)}
</span>
<span class="text-text-strong whitespace-nowrap">{getFilename(path)}</span>
<span class="text-text-weak whitespace-nowrap">/</span>
</div>
</div>
</div>
)
}}
</List>
</Dialog>
)
}
@@ -1,9 +1,7 @@
import { useDialog } from "@opencode-ai/ui/context/dialog" import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ServerConnection } from "@/context/servers" import { ServerConnection } from "@/context/servers"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useSettings } from "@/context/settings"
import { lazy } from "solid-js" import { lazy } from "solid-js"
import { DialogSelectDirectory } from "./dialog-select-directory"
import { directoryPickerKind } from "./directory-picker-policy" import { directoryPickerKind } from "./directory-picker-policy"
const DialogSelectDirectoryV2 = lazy(() => const DialogSelectDirectoryV2 = lazy(() =>
@@ -19,7 +17,6 @@ type DirectoryPickerInput = {
export function useDirectoryPicker() { export function useDirectoryPicker() {
const platform = usePlatform() const platform = usePlatform()
const settings = useSettings()
const dialog = useDialog() const dialog = useDialog()
return (input: DirectoryPickerInput) => { return (input: DirectoryPickerInput) => {
@@ -36,10 +33,6 @@ export function useDirectoryPicker() {
const cancel = () => { const cancel = () => {
if (!selected) input.onSelect(null) if (!selected) input.onSelect(null)
} }
if (platform.platform === "desktop" && settings.general.newLayoutDesigns()) { dialog.show(() => <DialogSelectDirectoryV2 {...input} onSelect={onSelect} />, cancel)
dialog.show(() => <DialogSelectDirectoryV2 {...input} onSelect={onSelect} />, cancel)
return
}
dialog.show(() => <DialogSelectDirectory {...input} onSelect={onSelect} />, cancel)
} }
} }
@@ -534,11 +534,14 @@ describe("prompt submit worktree selection", () => {
id: expect.stringMatching(/^msg_/), id: expect.stringMatching(/^msg_/),
command: "review", command: "review",
arguments: "staged changes", arguments: "staged changes",
agent: "agent",
model: { id: "model", providerID: "provider", variant: "high" },
files: [], files: [],
}, },
]) ])
expect(switchedAgents).toEqual([{ sessionID: "session-1", agent: "agent" }])
expect(switchedModels).toEqual([
{ sessionID: "session-1", model: { id: "model", providerID: "provider", variant: "high" } },
])
expect(sessionRequestOrder).toEqual(["agent", "model"])
expect(serverSessionSyncs).toBe(0) expect(serverSessionSyncs).toBe(0)
}) })
@@ -61,23 +61,39 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
input.serverSync.session.set("session_status", input.draft.sessionID, { type: "idle" }) input.serverSync.session.set("session_status", input.draft.sessionID, { type: "idle" })
} }
const select = async () => {
const session = input.session()
if (session?.agent !== input.draft.agent) {
await input.api.switchAgent({ sessionID: input.draft.sessionID, agent: input.draft.agent })
}
if (
session?.model?.providerID === input.draft.model.providerID &&
session.model.id === input.draft.model.modelID &&
(session.model.variant ?? "default") === (input.draft.variant ?? "default")
)
return
await input.api.switchModel({
sessionID: input.draft.sessionID,
model: {
id: input.draft.model.modelID,
providerID: input.draft.model.providerID,
variant: input.draft.variant,
},
})
}
const [head, ...tail] = text.split(" ") const [head, ...tail] = text.split(" ")
const cmd = head?.startsWith("/") ? head.slice(1) : undefined const cmd = head?.startsWith("/") ? head.slice(1) : undefined
if (cmd && input.sync.data.command.find((item) => item.name === cmd)) { if (cmd && input.sync.data.command.find((item) => item.name === cmd)) {
setBusy() setBusy()
try { try {
await select()
const messageID = Identifier.ascending("message") const messageID = Identifier.ascending("message")
await input.api.command({ await input.api.command({
sessionID: input.draft.sessionID, sessionID: input.draft.sessionID,
id: messageID, id: messageID,
command: cmd, command: cmd,
arguments: tail.join(" "), arguments: tail.join(" "),
agent: input.draft.agent,
model: {
id: input.draft.model.modelID,
providerID: input.draft.model.providerID,
variant: input.draft.variant,
},
files: await Promise.all( files: await Promise.all(
images.map(async (attachment) => ({ images.map(async (attachment) => ({
uri: await blobDataUrl(attachment.blob, attachment.mime), uri: await blobDataUrl(attachment.blob, attachment.mime),
@@ -118,24 +134,7 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
}) })
try { try {
const session = input.session() await select()
if (session?.agent !== input.draft.agent) {
await input.api.switchAgent({ sessionID: input.draft.sessionID, agent: input.draft.agent })
}
if (
session?.model?.providerID !== input.draft.model.providerID ||
session.model.id !== input.draft.model.modelID ||
(session.model.variant ?? "default") !== (input.draft.variant ?? "default")
) {
await input.api.switchModel({
sessionID: input.draft.sessionID,
model: {
id: input.draft.model.modelID,
providerID: input.draft.model.providerID,
variant: input.draft.variant,
},
})
}
const admitted = await input.api.prompt({ const admitted = await input.api.prompt({
sessionID: input.draft.sessionID, sessionID: input.draft.sessionID,
@@ -469,14 +468,23 @@ export function createPromptSubmit(input: PromptSubmitInput) {
clearInput() clearInput()
const messageID = Identifier.ascending("message") const messageID = Identifier.ascending("message")
submissionServerSync.session.set("session_status", session.id, { type: "busy" }) submissionServerSync.session.set("session_status", session.id, { type: "busy" })
void submissionSDK.api.session void (async () => {
.command({ if (session.agent !== agent)
await submissionSDK.api.session.switchAgent({ sessionID: session.id, agent })
if (
session.model?.providerID !== model.providerID ||
session.model.id !== model.modelID ||
(session.model.variant ?? "default") !== (variant ?? "default")
)
await submissionSDK.api.session.switchModel({
sessionID: session.id,
model: { id: model.modelID, providerID: model.providerID, variant },
})
await submissionSDK.api.session.command({
sessionID: session.id, sessionID: session.id,
id: messageID, id: messageID,
command: commandName, command: commandName,
arguments: args.join(" "), arguments: args.join(" "),
agent,
model: { id: model.modelID, providerID: model.providerID, variant },
files: await Promise.all( files: await Promise.all(
images.map(async (attachment) => ({ images.map(async (attachment) => ({
uri: await blobDataUrl(attachment.blob, attachment.mime), uri: await blobDataUrl(attachment.blob, attachment.mime),
@@ -484,14 +492,14 @@ export function createPromptSubmit(input: PromptSubmitInput) {
})), })),
), ),
}) })
.catch((err) => { })().catch((err) => {
submissionServerSync.session.set("session_status", session.id, { type: "idle" }) submissionServerSync.session.set("session_status", session.id, { type: "idle" })
showToast({ showToast({
title: language.t("prompt.toast.commandSendFailed.title"), title: language.t("prompt.toast.commandSendFailed.title"),
description: formatServerError(err, language.t, language.t("common.requestFailed")), description: formatServerError(err, language.t, language.t("common.requestFailed")),
})
restoreInput()
}) })
restoreInput()
})
return return
} }
} }
@@ -60,7 +60,11 @@ export function NewSessionView(props: {
<Show <Show
when={props.workspace.bar.visible()} when={props.workspace.bar.visible()}
fallback={ fallback={
<PromptGitStatus branch={props.workspace.bar.branch()} noGit={!props.workspace.project.git()} /> <PromptGitStatus
branch={props.workspace.bar.branch()}
noGit={!props.workspace.project.git()}
class="ms-1"
/>
} }
> >
<PromptWorkspaceSelector <PromptWorkspaceSelector
@@ -94,7 +94,7 @@ export function createTimelineController(input: {
fallback: language.t("command.session.new"), fallback: language.t("command.session.new"),
}) })
}) })
const showHeader = createMemo(() => !!(titleValue() || input.session.data.parentID())) const showHeader = createMemo(() => !!input.session.identity.sessionID())
const projection = createTimelineProjection({ const projection = createTimelineProjection({
messages: input.session.history.messages, messages: input.session.history.messages,
userMessages: input.userMessages, userMessages: input.userMessages,
@@ -1057,9 +1057,12 @@ function MessageTimelineView(
<TimelineRowFrame row={noticeRow()}> <TimelineRowFrame row={noticeRow()}>
<Show when={content()}> <Show when={content()}>
{(content) => ( {(content) => (
<div data-slot="session-timeline-notice" class={`w-full pt-3 pb-1 text-13-regular ${turnPadding()}`}> <div
<span class="text-13-medium text-text-strong">{content().label}</span> data-slot="session-timeline-notice"
<Show when={content().data}>{(data) => <span class="text-text-weak"> · {data()}</span>}</Show> class={`w-full pt-3 pb-1 text-13-regular text-text-weak ${turnPadding()}`}
>
<span class="text-13-medium">{content().label}</span>
<Show when={content().data}>{(data) => <span> · {data()}</span>}</Show>
</div> </div>
)} )}
</Show> </Show>
@@ -1203,6 +1206,8 @@ function MessageTimelineView(
onCleanup(() => { onCleanup(() => {
if (contentMeasureFrame !== undefined) cancelAnimationFrame(contentMeasureFrame) if (contentMeasureFrame !== undefined) cancelAnimationFrame(contentMeasureFrame)
// Solid runs cleanup before it disconnects the row, so defer TanStack's null-ref cleanup.
queueMicrotask(() => virtualizer.measureElement(null))
}) })
return ( return (
+2 -1
View File
@@ -5,7 +5,8 @@ const fs = require("fs")
const path = require("path") const path = require("path")
const os = require("os") const os = require("os")
const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"] const forwardedSignals =
process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGHUP"] : ["SIGINT", "SIGTERM", "SIGHUP", "SIGUSR1"]
function run(target) { function run(target) {
const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" }) const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })
+71 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bun #!/usr/bin/env bun
import { $ } from "bun" import { $ } from "bun"
import { rm } from "fs/promises" import { mkdir, rm } from "fs/promises"
import path from "path" import path from "path"
import { Script } from "@opencode-ai/script" import { Script } from "@opencode-ai/script"
import { createSolidTransformPlugin } from "@opentui/solid/bun-plugin" import { createSolidTransformPlugin } from "@opentui/solid/bun-plugin"
@@ -27,6 +27,7 @@ const requestedTarget = process.argv.find((arg) => arg.startsWith("--target="))?
const skipInstall = process.argv.includes("--skip-install") const skipInstall = process.argv.includes("--skip-install")
const skipWebUi = process.argv.includes("--skip-web-ui") const skipWebUi = process.argv.includes("--skip-web-ui")
const solidPlugin = createSolidTransformPlugin() const solidPlugin = createSolidTransformPlugin()
const releaseAssets = new Map<string, Promise<Map<string, string>>>()
const allTargets: { const allTargets: {
os: string os: string
@@ -99,6 +100,7 @@ for (const item of targets) {
} }
const target = targetName(item) const target = targetName(item)
const name = target.replace(binary, "cli") const name = target.replace(binary, "cli")
const executablePath = await compileExecutable(item)
console.log(`building ${name}`) console.log(`building ${name}`)
const result = await Bun.build({ const result = await Bun.build({
entrypoints: ["./src/index.ts"], entrypoints: ["./src/index.ts"],
@@ -115,8 +117,9 @@ for (const item of targets) {
autoloadTsconfig: true, autoloadTsconfig: true,
autoloadPackageJson: true, autoloadPackageJson: true,
target: target.replace(binary, "bun") as Bun.Build.CompileTarget, target: target.replace(binary, "bun") as Bun.Build.CompileTarget,
...(executablePath ? { executablePath } : {}),
outfile: path.join(outdir, name, "bin", binary), outfile: path.join(outdir, name, "bin", binary),
execArgv: [`--user-agent=${binary}/${Script.version}`, "--use-system-ca", "--"], execArgv: [`--user-agent=${binary}/${Script.version}`, "--use-system-ca", "--no-warnings", "--"],
windows: {}, windows: {},
}, },
define: { define: {
@@ -154,6 +157,72 @@ for (const item of targets) {
await verifyArtifact(path.join(outdir, name)) await verifyArtifact(path.join(outdir, name))
} }
async function compileExecutable(item: (typeof allTargets)[number]) {
const release = process.env.BUN_COMPILE_RELEASE
if (!release) return
const platform = item.os === "win32" ? "windows" : item.os
const name = [
"bun",
platform,
item.arch === "arm64" ? "aarch64" : item.arch,
item.abi,
item.avx2 === false ? "baseline" : undefined,
]
.filter(Boolean)
.join("-")
const cache = path.join(outdir, ".bun", release)
const executable = path.join(cache, name, item.os === "win32" ? "bun.exe" : "bun")
if (await Bun.file(executable).exists()) return executable
await mkdir(cache, { recursive: true })
const archive = path.join(cache, `${name}.zip`)
const assets = await compileReleaseAssets(release)
const url = assets.get(`${name}.zip`)
if (!url) throw new Error(`Bun release ${release} does not include ${name}.zip`)
const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN
const response = await fetch(url, {
headers: { Accept: "application/octet-stream", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
})
if (!response.ok) throw new Error(`Failed to download ${name} from Bun release ${release}: ${response.status}`)
await Bun.write(archive, response)
await $`unzip -oq ${archive} -d ${cache}`
await rm(archive)
return executable
}
function compileReleaseAssets(release: string) {
const existing = releaseAssets.get(release)
if (existing) return existing
const pending = fetch(`https://api.github.com/repos/oven-sh/bun/releases/tags/${release}?cache=${Date.now()}`)
.then(async (response) => {
if (!response.ok) throw new Error(`Failed to resolve Bun release ${release}: ${response.status}`)
const data: unknown = await response.json()
if (typeof data !== "object" || data === null || !("assets" in data) || !Array.isArray(data.assets)) {
throw new Error(`Bun release ${release} returned invalid metadata`)
}
return new Map(
data.assets
.filter(
(asset): asset is { name: string; url: string } =>
typeof asset === "object" &&
asset !== null &&
"name" in asset &&
typeof asset.name === "string" &&
"url" in asset &&
typeof asset.url === "string",
)
.map((asset) => [asset.name, asset.url]),
)
})
.catch((error) => {
releaseAssets.delete(release)
throw error
})
releaseAssets.set(release, pending)
return pending
}
function targetName(item: (typeof allTargets)[number]) { function targetName(item: (typeof allTargets)[number]) {
return [ return [
binary, binary,
+6 -3
View File
@@ -14,9 +14,12 @@ async function published(name: string, version: string) {
async function publish(dir: string, name: string, version: string) { async function publish(dir: string, name: string, version: string) {
if (process.platform !== "win32") await $`chmod -R 755 .`.cwd(dir) if (process.platform !== "win32") await $`chmod -R 755 .`.cwd(dir)
if (await published(name, version)) return console.log(`already published ${name}@${version}`) const exists = await published(name, version)
await $`bun pm pack`.cwd(dir) if (exists) console.log(`already published ${name}@${version}`)
await $`npm publish *.tgz --access public --tag ${Script.channel}`.cwd(dir) if (!exists) {
await $`bun pm pack`.cwd(dir)
await $`npm publish *.tgz --access public --tag ${Script.channel}`.cwd(dir)
}
} }
async function publishDistribution(input: { root: string; name: string; binary: string; packagePrefix: string }) { async function publishDistribution(input: { root: string; name: string; binary: string; packagePrefix: string }) {
+6 -3
View File
@@ -1,5 +1,6 @@
import { Argument, Flag } from "effect/unstable/cli" import { Argument, Command, Flag } from "effect/unstable/cli"
import { Spec } from "../framework/spec" import { Spec } from "../framework/spec"
import { GlobalFlags } from "./global-flags"
declare const OPENCODE_CLI_NAME: string | undefined declare const OPENCODE_CLI_NAME: string | undefined
@@ -26,7 +27,7 @@ const PermissionParams = {
), ),
} }
export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", { const Root = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", {
description: "OpenCode 2.0 preview command line interface", description: "OpenCode 2.0 preview command line interface",
params: { params: {
...ServerParams, ...ServerParams,
@@ -70,7 +71,7 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
description: "Debugging and troubleshooting tools", description: "Debugging and troubleshooting tools",
commands: [ commands: [
Spec.make("agents", { description: "List all agents" }), Spec.make("agents", { description: "List all agents" }),
Spec.make("config", { description: "Show resolved configuration" }), Spec.make("config", { description: "List configuration sources" }),
], ],
}), }),
Spec.make("console", { Spec.make("console", {
@@ -277,3 +278,5 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
}), }),
], ],
}) })
export const Commands = { ...Root, spec: Root.spec.pipe(Command.withGlobalFlags(GlobalFlags.all)) }
+12
View File
@@ -0,0 +1,12 @@
export * as GlobalFlags from "./global-flags"
import { Flag, GlobalFlag } from "effect/unstable/cli"
export const CpuProfile = GlobalFlag.setting("cpu-profile")({
flag: Flag.string("cpu-profile").pipe(
Flag.withDescription("Write a CPU profile to this path when the process stops"),
Flag.optional,
),
})
export const all = [CpuProfile] as const
+49 -27
View File
@@ -5,7 +5,7 @@ import { TuiKeybind } from "@opencode-ai/tui/config/v1/keybind"
import { Definitions } from "@opencode-ai/tui/config/keybind" import { Definitions } from "@opencode-ai/tui/config/keybind"
import { Effect, FileSystem, Option, Schema } from "effect" import { Effect, FileSystem, Option, Schema } from "effect"
import { randomUUID } from "crypto" import { randomUUID } from "crypto"
import { createScanner, parse, parseTree, type Node, type ParseError } from "jsonc-parser" import { applyEdits, createScanner, modify, parse, parseTree, type Node, type ParseError } from "jsonc-parser"
import path from "path" import path from "path"
import { Info } from "./schema" import { Info } from "./schema"
@@ -41,33 +41,55 @@ export const run = Effect.fn("cli.config.migrate")(function* (input: {
if (errors.length) return if (errors.length) return
const config = Option.getOrUndefined(decodeRecord(value)) const config = Option.getOrUndefined(decodeRecord(value))
if (config === undefined) return if (config === undefined) return
const terminal = Option.getOrUndefined(decodeRecord(config.terminal))
const legacyCopy = terminal?.copy_on_select
const copy =
typeof legacyCopy === "boolean" && terminal?.copy === undefined ? (legacyCopy ? "select" : "manual") : undefined
const renamed =
typeof legacyCopy !== "boolean"
? text
: [
...(copy === undefined ? [] : [{ path: ["terminal", "copy"], value: copy }]),
{ path: ["terminal", "copy_on_select"], value: undefined },
].reduce(
(text, edit) =>
applyEdits(
text,
modify(text, edit.path, edit.value, { formattingOptions: { tabSize: 2, insertSpaces: true } }),
),
text,
)
const keybinds = Option.getOrUndefined(decodeRecord(config.keybinds)) const keybinds = Option.getOrUndefined(decodeRecord(config.keybinds))
if (keybinds === undefined) return const updated =
const deduped = findKeybindObjects(text) keybinds === undefined
.slice(0, -1) ? renamed
.reduce((text) => { : Object.keys(keybinds).reduce(
const property = findKeybindObjects(text)[0] (text, name) => {
return property === undefined ? text : removeProperty(text, property) const target =
}, text) TuiKeybind.CommandMap[name as keyof typeof TuiKeybind.CommandMap] ??
const updated = Object.keys(keybinds).reduce((text, name) => { (name in Definitions || LegacyKeybindTargets.has(name) ? name : undefined)
const target = if (target === undefined) return text
TuiKeybind.CommandMap[name as keyof typeof TuiKeybind.CommandMap] ?? const properties = findKeybindProperties(text, name)
(name in Definitions || LegacyKeybindTargets.has(name) ? name : undefined) if (!properties.length) return text
if (target === undefined) return text const remove = !(target in Definitions) || (target !== name && target in keybinds)
const properties = findKeybindProperties(text, name) // The parser gives the final duplicate precedence, so remove earlier properties before renaming it.
if (!properties.length) return text const cleaned = properties.slice(0, remove ? properties.length : -1).reduce((text) => {
const remove = !(target in Definitions) || (target !== name && target in keybinds) const property = findKeybindProperties(text, name)[0]
// The parser gives the final duplicate precedence, so remove earlier properties before renaming it. return property === undefined ? text : removeProperty(text, property)
const updated = properties.slice(0, remove ? properties.length : -1).reduce((text) => { }, text)
const property = findKeybindProperties(text, name)[0] if (remove) return cleaned
return property === undefined ? text : removeProperty(text, property) if (target === name) return cleaned
}, text) const key = findKeybindProperties(cleaned, name)[0]?.children?.[0]
if (remove) return updated if (key === undefined) return text
if (target === name) return updated return cleaned.slice(0, key.offset) + JSON.stringify(target) + cleaned.slice(key.offset + key.length)
const key = findKeybindProperties(updated, name)[0]?.children?.[0] },
if (key === undefined) return text findKeybindObjects(renamed)
return updated.slice(0, key.offset) + JSON.stringify(target) + updated.slice(key.offset + key.length) .slice(0, -1)
}, deduped) .reduce((text) => {
const property = findKeybindObjects(text)[0]
return property === undefined ? text : removeProperty(text, property)
}, renamed),
)
if (updated === text) return if (updated === text) return
const updatedErrors: ParseError[] = [] const updatedErrors: ParseError[] = []
const info = Option.getOrUndefined(decodeInfo(parse(updated, updatedErrors, { allowTrailingComma: true }))) const info = Option.getOrUndefined(decodeInfo(parse(updated, updatedErrors, { allowTrailingComma: true })))
+45
View File
@@ -0,0 +1,45 @@
export * as CpuProfile from "./cpu-profile"
import { Effect, FileSystem } from "effect"
import { Session } from "node:inspector"
import path from "node:path"
export function run<A, E, R>(file: string, effect: Effect.Effect<A, E, R>) {
const target = path.resolve(file)
return Effect.acquireUseRelease(
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
yield* fs.makeDirectory(path.dirname(target), { recursive: true })
const session = new Session()
session.connect()
yield* command(session, "Profiler.enable")
yield* command(session, "Profiler.start")
yield* Effect.logInfo("CPU profile started", { path: target })
return session
}),
() => effect,
(session) =>
Effect.tryPromise(
() =>
new Promise<void>((resolve, reject) => {
session.post("Profiler.stop", (error, result) => {
session.disconnect()
if (error) return reject(error)
Bun.write(target, JSON.stringify(result.profile)).then(() => resolve(), reject)
})
}),
).pipe(
Effect.andThen(Effect.logInfo("CPU profile written", { path: target })),
Effect.catchCause((cause) => Effect.logError("Failed to write CPU profile", { path: target, cause })),
),
)
}
function command(session: Session, method: "Profiler.enable" | "Profiler.start") {
return Effect.tryPromise(
() =>
new Promise<void>((resolve, reject) => {
session.post(method, (error) => (error ? reject(error) : resolve()))
}),
)
}
+20 -2
View File
@@ -1,10 +1,13 @@
import { Effect, FileSystem, Scope } from "effect" import { Effect, FileSystem, Option, Scope } from "effect"
import { Command } from "effect/unstable/cli" import { Command } from "effect/unstable/cli"
import { Spec } from "./spec" import { Spec } from "./spec"
import { Global } from "@opencode-ai/util/global" import { Global } from "@opencode-ai/util/global"
import { Updater } from "../services/updater" import { Updater } from "../services/updater"
import { Config } from "../config" import { Config } from "../config"
import { Npm } from "@opencode-ai/util/npm" import { Npm } from "@opencode-ai/util/npm"
import { GlobalFlags } from "../commands/global-flags"
import { CpuProfile } from "../cpu-profile"
import path from "node:path"
export type Input<Value> = export type Input<Value> =
Value extends Spec.Node<infer _Name, infer Command, infer _Commands> Value extends Spec.Node<infer _Name, infer Command, infer _Commands>
@@ -86,7 +89,22 @@ function provide(node: Spec.Any, handlers: ReadonlyArray<LazyHandler>): Provided
? node.spec.pipe( ? node.spec.pipe(
Command.withHandler((input) => Command.withHandler((input) =>
Effect.gen(function* () { Effect.gen(function* () {
yield* Effect.flatMap(Effect.promise(handler.load), (module) => module.default(input)) const module = yield* Effect.promise(handler.load)
const cpuProfile = Option.getOrUndefined(yield* GlobalFlags.CpuProfile)
if (!cpuProfile) return yield* module.default(input)
const target = path.resolve(cpuProfile)
const previous = process.env.OPENCODE_CPU_PROFILE
process.env.OPENCODE_CPU_PROFILE = target
return yield* (
node.name === "serve" ? CpuProfile.run(target, module.default(input)) : module.default(input)
).pipe(
Effect.ensuring(
Effect.sync(() => {
if (previous === undefined) delete process.env.OPENCODE_CPU_PROFILE
else process.env.OPENCODE_CPU_PROFILE = previous
}),
),
)
}), }),
), ),
) )
+37
View File
@@ -0,0 +1,37 @@
import { Global } from "@opencode-ai/util/global"
import { Effect, Queue } from "effect"
import path from "node:path"
export const listen = Effect.gen(function* () {
const global = yield* Global.Service
if (process.platform === "win32") return
const signals = yield* Queue.dropping<void>(1)
yield* Effect.acquireRelease(
Effect.sync(() => {
const handler = () => Queue.offerUnsafe(signals, undefined)
process.on("SIGUSR1", handler)
return handler
}),
(handler) => Effect.sync(() => process.off("SIGUSR1", handler)),
)
yield* Queue.take(signals).pipe(
Effect.andThen(
Effect.suspend(() => {
const file = path.join(
global.log,
`heap-${process.pid}-${new Date().toISOString().replace(/[:.]/g, "")}.heapsnapshot`,
)
return Effect.gen(function* () {
yield* Effect.logInfo("writing heap snapshot", { path: file })
const { writeHeapSnapshot } = yield* Effect.tryPromise(() => import("node:v8"))
yield* Effect.try(() => writeHeapSnapshot(file))
yield* Effect.logInfo("heap snapshot written", { path: file })
}).pipe(Effect.catchCause((cause) => Effect.logError("failed to write heap snapshot", { path: file, cause })))
}),
),
Effect.forever,
Effect.forkScoped({ startImmediately: true }),
)
})
export * as Heap from "./heap"
+10 -6
View File
@@ -12,6 +12,7 @@ import { Global } from "@opencode-ai/util/global"
import { AppProcess } from "@opencode-ai/util/process" import { AppProcess } from "@opencode-ai/util/process"
import { Config } from "./config" import { Config } from "./config"
import { Npm } from "@opencode-ai/util/npm" import { Npm } from "@opencode-ai/util/npm"
import { Heap } from "./heap"
const Handlers = Runtime.handlers(Commands, { const Handlers = Runtime.handlers(Commands, {
$: () => import("./commands/handlers/default"), $: () => import("./commands/handlers/default"),
@@ -54,13 +55,16 @@ const Handlers = Runtime.handlers(Commands, {
serve: () => import("./commands/handlers/serve"), serve: () => import("./commands/handlers/serve"),
}) })
Effect.logInfo("cli starting", { Effect.gen(function* () {
version: OPENCODE_VERSION, yield* Heap.listen
channel: OPENCODE_CHANNEL, yield* Effect.logInfo("cli starting", {
local: OPENCODE_LOCAL, version: OPENCODE_VERSION,
args: process.argv.slice(2), channel: OPENCODE_CHANNEL,
local: OPENCODE_LOCAL,
args: process.argv.slice(2),
})
return yield* Runtime.run(Commands, Handlers, { version: OPENCODE_VERSION })
}).pipe( }).pipe(
Effect.flatMap(() => Runtime.run(Commands, Handlers, { version: OPENCODE_VERSION })),
Effect.annotateLogs({ role: "cli" }), Effect.annotateLogs({ role: "cli" }),
Effect.provide(Config.layer), Effect.provide(Config.layer),
Effect.provide(Updater.layer), Effect.provide(Updater.layer),
+1 -1
View File
@@ -84,7 +84,7 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
database: { database: {
path: path:
process.env.OPENCODE_DB ?? process.env.OPENCODE_DB ??
(["latest", "beta", "next", "prod"].includes(OPENCODE_CHANNEL) || (["latest", "dev", "beta", "next", "prod"].includes(OPENCODE_CHANNEL) ||
process.env.OPENCODE_DISABLE_CHANNEL_DB === "1" || process.env.OPENCODE_DISABLE_CHANNEL_DB === "1" ||
process.env.OPENCODE_DISABLE_CHANNEL_DB === "true" process.env.OPENCODE_DISABLE_CHANNEL_DB === "true"
? "opencode.db" ? "opencode.db"
+8 -3
View File
@@ -25,12 +25,12 @@ const decodeInfo = Schema.decodeUnknownEffect(Schema.fromJsonString(Info))
const decodeRegistration = Schema.decodeUnknownEffect(Schema.fromJsonString(Service.Info)) const decodeRegistration = Schema.decodeUnknownEffect(Schema.fromJsonString(Service.Info))
export function filename(channel = OPENCODE_CHANNEL) { export function filename(channel = OPENCODE_CHANNEL) {
if (channel === "latest" || channel === "next") return "service.json" if (channel === "latest" || channel === "dev" || channel === "beta" || channel === "next") return "service.json"
return `service-${channel.replace(/[^a-zA-Z0-9._-]/g, "-")}.json` return `service-${channel.replace(/[^a-zA-Z0-9._-]/g, "-")}.json`
} }
export function defaultPort(channel = OPENCODE_CHANNEL) { export function defaultPort(channel = OPENCODE_CHANNEL) {
if (channel === "latest" || channel === "next") return 0xc0de if (channel === "latest" || channel === "dev" || channel === "beta" || channel === "next") return 0xc0de
if (channel === "local") return 0xc0df if (channel === "local") return 0xc0df
return 10_000 + (Number.parseInt(Hash.fast(channel).slice(0, 8), 16) % 50_000) return 10_000 + (Number.parseInt(Hash.fast(channel).slice(0, 8), 16) % 50_000)
} }
@@ -104,7 +104,12 @@ export const options = Effect.fnUntraced(function* (input: { readonly checkVersi
return { return {
file, file,
version: input.checkVersion ? OPENCODE_VERSION : undefined, version: input.checkVersion ? OPENCODE_VERSION : undefined,
command: [...selfCommand(), "serve", "--service"], command: [
...selfCommand(),
"serve",
"--service",
...(process.env.OPENCODE_CPU_PROFILE ? ["--cpu-profile", process.env.OPENCODE_CPU_PROFILE] : []),
],
} }
}) })
+2 -1
View File
@@ -35,7 +35,8 @@ describe("updater", () => {
test("accepts strict release version variants", () => { test("accepts strict release version variants", () => {
expect(action("v1.2.3", " 1.2.4\n", true)).toBe("upgrade") expect(action("v1.2.3", " 1.2.4\n", true)).toBe("upgrade")
expect(action("1.2.3-alpha.1", "1.2.3-alpha.2", true)).toBe("upgrade") expect(action("1.2.3-alpha.1", "1.2.3-alpha.2", true)).toBe("upgrade")
expect(action("0.0.0-next-17403", "0.0.0-next-17403.2", true)).toBe("upgrade") expect(action("0.0.0-dev-17403", "0.0.0-dev-17403.2", true)).toBe("upgrade")
expect(action("0.0.0-next-17403", "0.0.0-beta-17404", true)).toBe("upgrade")
expect(action("1.2.3+old", "1.2.3+new", true)).toBe("none") expect(action("1.2.3+old", "1.2.3+new", true)).toBe("none")
expect(action("v1.2.3+old", "1.2.3", true)).toBe("none") expect(action("v1.2.3+old", "1.2.3", true)).toBe("none")
}) })
+40
View File
@@ -210,6 +210,46 @@ test("migrates legacy keybind names in an existing cli.json", async () => {
} }
}) })
test("migrates copy_on_select in an existing cli.json", async () => {
for (const item of [
{ legacy: true, expected: "select" },
{ legacy: false, expected: "manual" },
{ legacy: true, copy: "manual", expected: "manual" },
] as const) {
const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim())
const file = path.join(directory, "cli.json")
await Bun.write(
file,
`{
// Preserve this comment
"terminal": {
${item.copy === undefined ? "" : `"copy": ${JSON.stringify(item.copy)},`}
"copy_on_select": ${item.legacy}
}
}
`,
)
try {
const config = await run(
directory,
Effect.gen(function* () {
const service = yield* Config.Service
return yield* service.get()
}),
)
expect(config.terminal).toEqual({ copy: item.expected })
const text = await Bun.file(file).text()
expect(text).toContain("// Preserve this comment")
expect(text).not.toContain("copy_on_select")
expect(parse(text).terminal).toEqual({ copy: item.expected })
} finally {
await Bun.$`rm -rf ${directory}`
}
}
})
test("uses migrated keybinds when persistence fails", async () => { test("uses migrated keybinds when persistence fails", async () => {
const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim()) const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim())
const file = path.join(directory, "cli.json") const file = path.join(directory, "cli.json")
+2 -1
View File
@@ -10,9 +10,10 @@ describe("debug config command", () => {
expect(debug.exitCode).toBe(0) expect(debug.exitCode).toBe(0)
expect(debug.stdout).toContain("config") expect(debug.stdout).toContain("config")
expect(debug.stdout).toContain("Show resolved configuration") expect(debug.stdout).toContain("List configuration sources")
expect(config.exitCode).toBe(0) expect(config.exitCode).toBe(0)
expect(config.stdout).toContain("opencode debug config [flags]") expect(config.stdout).toContain("opencode debug config [flags]")
expect(config.stdout).toContain("List configuration sources")
}) })
test("prints config entries from the invoking directory without reordering permissions", async () => { test("prints config entries from the invoking directory without reordering permissions", async () => {
+27
View File
@@ -11,12 +11,37 @@ import { ServiceConfig } from "../src/services/service-config"
test("managed service ports are stable per installation channel", () => { test("managed service ports are stable per installation channel", () => {
expect(ServiceConfig.defaultPort("latest")).toBe(0xc0de) expect(ServiceConfig.defaultPort("latest")).toBe(0xc0de)
expect(ServiceConfig.defaultPort("dev")).toBe(0xc0de)
expect(ServiceConfig.defaultPort("beta")).toBe(0xc0de)
expect(ServiceConfig.defaultPort("next")).toBe(0xc0de) expect(ServiceConfig.defaultPort("next")).toBe(0xc0de)
expect(ServiceConfig.defaultPort("local")).toBe(0xc0df) expect(ServiceConfig.defaultPort("local")).toBe(0xc0df)
expect(ServiceConfig.defaultPort("preview-a")).toBe(ServiceConfig.defaultPort("preview-a")) expect(ServiceConfig.defaultPort("preview-a")).toBe(ServiceConfig.defaultPort("preview-a"))
expect(ServiceConfig.defaultPort("preview-a")).not.toBe(ServiceConfig.defaultPort("preview-b")) expect(ServiceConfig.defaultPort("preview-a")).not.toBe(ServiceConfig.defaultPort("preview-b"))
}) })
test("managed service forwards the CPU profile path to the server", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-profile-"))
const profile = path.join(root, "server.cpuprofile")
try {
const previous = process.env.OPENCODE_CPU_PROFILE
process.env.OPENCODE_CPU_PROFILE = profile
try {
const options = await Effect.runPromise(
ServiceConfig.options().pipe(
Effect.provide(Global.layerWith({ config: path.join(root, "config"), state: path.join(root, "state") })),
Effect.provide(NodeFileSystem.layer),
),
)
expect(options.command.slice(-2)).toEqual(["--cpu-profile", profile])
} finally {
if (previous === undefined) delete process.env.OPENCODE_CPU_PROFILE
else process.env.OPENCODE_CPU_PROFILE = previous
}
} finally {
await fs.rm(root, { recursive: true, force: true })
}
})
test("local channel stores service config with the local service filename", async () => { test("local channel stores service config with the local service filename", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-")) const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-"))
try { try {
@@ -37,6 +62,8 @@ test("local channel stores service config with the local service filename", asyn
test("service filenames share release channels and identify preview channels", () => { test("service filenames share release channels and identify preview channels", () => {
expect(ServiceConfig.filename("latest")).toBe("service.json") expect(ServiceConfig.filename("latest")).toBe("service.json")
expect(ServiceConfig.filename("dev")).toBe("service.json")
expect(ServiceConfig.filename("beta")).toBe("service.json")
expect(ServiceConfig.filename("next")).toBe("service.json") expect(ServiceConfig.filename("next")).toBe("service.json")
expect(ServiceConfig.filename("local")).toBe("service-local.json") expect(ServiceConfig.filename("local")).toBe("service-local.json")
expect(ServiceConfig.filename("preview-a")).toBe("service-preview-a.json") expect(ServiceConfig.filename("preview-a")).toBe("service-preview-a.json")
+40 -43
View File
@@ -20,7 +20,6 @@ import type { InstructionEntry } from "@opencode-ai/schema/instruction-entry"
import type { Schema } from "effect" import type { Schema } from "effect"
import type { EventLog } from "@opencode-ai/schema/event-log" import type { EventLog } from "@opencode-ai/schema/event-log"
import type { Shell } from "@opencode-ai/schema/shell" import type { Shell } from "@opencode-ai/schema/shell"
import type { DateTime } from "effect"
import type { Provider } from "@opencode-ai/schema/provider" import type { Provider } from "@opencode-ai/schema/provider"
import type { Integration } from "@opencode-ai/schema/integration" import type { Integration } from "@opencode-ai/schema/integration"
import type { Form } from "@opencode-ai/schema/form" import type { Form } from "@opencode-ai/schema/form"
@@ -193,8 +192,6 @@ export type Endpoint5_13Input = {
readonly id?: SessionMessage.ID | undefined readonly id?: SessionMessage.ID | undefined
readonly command: string readonly command: string
readonly arguments?: string | undefined readonly arguments?: string | undefined
readonly agent?: Agent.ID | undefined
readonly model?: Model.Ref | undefined
readonly files?: ReadonlyArray<PromptInput.FileAttachment> | undefined readonly files?: ReadonlyArray<PromptInput.FileAttachment> | undefined
readonly agents?: ReadonlyArray<AgentAttachment> | undefined readonly agents?: ReadonlyArray<AgentAttachment> | undefined
readonly skills?: ReadonlyArray<PromptInput.SkillAttachment> | undefined readonly skills?: ReadonlyArray<PromptInput.SkillAttachment> | undefined
@@ -316,7 +313,7 @@ export type Endpoint5_31Output =
| ( | (
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.created" readonly type: "session.created"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -336,7 +333,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.agent.selected" readonly type: "session.agent.selected"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -349,7 +346,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.model.selected" readonly type: "session.model.selected"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -362,7 +359,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.moved" readonly type: "session.moved"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -376,7 +373,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.renamed" readonly type: "session.renamed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -385,7 +382,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.deleted" readonly type: "session.deleted"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -394,7 +391,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.forked" readonly type: "session.forked"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -410,7 +407,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.inbox.delivered" readonly type: "session.inbox.delivered"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -419,7 +416,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.inbox.enqueued" readonly type: "session.inbox.enqueued"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -432,7 +429,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.inbox.cancelled" readonly type: "session.inbox.cancelled"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -441,7 +438,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.inbox.delivery.changed" readonly type: "session.inbox.delivery.changed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -454,7 +451,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.execution.started" readonly type: "session.execution.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -463,7 +460,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.execution.succeeded" readonly type: "session.execution.succeeded"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -472,7 +469,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.execution.failed" readonly type: "session.execution.failed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -484,7 +481,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.execution.interrupted" readonly type: "session.execution.interrupted"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -493,7 +490,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.instructions.updated" readonly type: "session.instructions.updated"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -506,7 +503,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.synthetic" readonly type: "session.synthetic"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -520,7 +517,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.skill.activated" readonly type: "session.skill.activated"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -534,7 +531,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.shell.started" readonly type: "session.shell.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -543,7 +540,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.shell.ended" readonly type: "session.shell.ended"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -561,7 +558,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.step.started" readonly type: "session.step.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -576,7 +573,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.step.ended" readonly type: "session.step.ended"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -598,7 +595,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.step.failed" readonly type: "session.step.failed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -622,7 +619,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.text.started" readonly type: "session.text.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -635,7 +632,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.text.ended" readonly type: "session.text.ended"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -650,7 +647,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.reasoning.started" readonly type: "session.reasoning.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -664,7 +661,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.reasoning.ended" readonly type: "session.reasoning.ended"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -679,7 +676,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.tool.input.started" readonly type: "session.tool.input.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -693,7 +690,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.tool.input.ended" readonly type: "session.tool.input.ended"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -707,7 +704,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.tool.called" readonly type: "session.tool.called"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -723,7 +720,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.tool.success" readonly type: "session.tool.success"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -759,7 +756,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.tool.failed" readonly type: "session.tool.failed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -798,7 +795,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.retry.scheduled" readonly type: "session.retry.scheduled"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -813,7 +810,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.compaction.started" readonly type: "session.compaction.started"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -827,7 +824,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.compaction.ended" readonly type: "session.compaction.ended"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -841,7 +838,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.compaction.failed" readonly type: "session.compaction.failed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -855,7 +852,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.revert.staged" readonly type: "session.revert.staged"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -864,7 +861,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.revert.cleared" readonly type: "session.revert.cleared"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -873,7 +870,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.revert.committed" readonly type: "session.revert.committed"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -882,7 +879,7 @@ export type Endpoint5_31Output =
} }
| { | {
readonly id: Event.ID readonly id: Event.ID
readonly created: DateTime.Utc readonly created: number
readonly metadata?: { readonly [x: string]: unknown } | undefined readonly metadata?: { readonly [x: string]: unknown } | undefined
readonly type: "session.usage.recorded" readonly type: "session.usage.recorded"
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version } readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
@@ -423,8 +423,6 @@ const Endpoint5_13 = (raw: RawClient["server.session"]) => (input: Endpoint5_13I
id: input["id"], id: input["id"],
command: input["command"], command: input["command"],
arguments: input["arguments"], arguments: input["arguments"],
agent: input["agent"],
model: input["model"],
files: input["files"], files: input["files"],
agents: input["agents"], agents: input["agents"],
skills: input["skills"], skills: input["skills"],
@@ -627,8 +627,6 @@ export function make(options: ClientOptions) {
id: input["id"], id: input["id"],
command: input["command"], command: input["command"],
arguments: input["arguments"], arguments: input["arguments"],
agent: input["agent"],
model: input["model"],
files: input["files"], files: input["files"],
agents: input["agents"], agents: input["agents"],
skills: input["skills"], skills: input["skills"],
@@ -3511,8 +3511,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3534,8 +3532,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3557,8 +3553,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3576,58 +3570,10 @@ export type SessionCommandInput = {
readonly delivery?: ("steer" | "queue") | null readonly delivery?: ("steer" | "queue") | null
readonly resume?: boolean | null readonly resume?: boolean | null
}["arguments"] }["arguments"]
readonly agent?: {
readonly id?: string | null
readonly command: string
readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{
readonly uri: string
readonly name?: string
readonly description?: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly skills?: ReadonlyArray<{
readonly id: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly delivery?: ("steer" | "queue") | null
readonly resume?: boolean | null
}["agent"]
readonly model?: {
readonly id?: string | null
readonly command: string
readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{
readonly uri: string
readonly name?: string
readonly description?: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly skills?: ReadonlyArray<{
readonly id: string
readonly mention?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly delivery?: ("steer" | "queue") | null
readonly resume?: boolean | null
}["model"]
readonly files?: { readonly files?: {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3649,8 +3595,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3672,8 +3616,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3695,8 +3637,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -3718,8 +3658,6 @@ export type SessionCommandInput = {
readonly id?: string | null readonly id?: string | null
readonly command: string readonly command: string
readonly arguments?: string | null readonly arguments?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly files?: ReadonlyArray<{ readonly files?: ReadonlyArray<{
readonly uri: string readonly uri: string
readonly name?: string readonly name?: string
@@ -21,7 +21,7 @@ export default function TermsOfService() {
<section data-component="brand-content"> <section data-component="brand-content">
<article data-component="terms-of-service"> <article data-component="terms-of-service">
<h1>Terms of Use</h1> <h1>Terms of Use</h1>
<p class="effective-date">Effective date: Mar 6, 2026</p> <p class="effective-date">Effective date: Aug 15, 2026</p>
<p> <p>
Welcome to OpenCode. Please read on to learn the rules and restrictions that govern your use of Welcome to OpenCode. Please read on to learn the rules and restrictions that govern your use of
@@ -154,6 +154,11 @@ export default function TermsOfService() {
is dangerous, harmful, fraudulent, deceptive, threatening, harassing, defamatory, obscene, or is dangerous, harmful, fraudulent, deceptive, threatening, harassing, defamatory, obscene, or
otherwise objectionable; otherwise objectionable;
</li> </li>
<li>
creates, maintains, or uses accounts in bulk, or creates, maintains, or uses multiple accounts to
circumvent usage limits, access restrictions, billing obligations, promotions, suspensions, or any
other restriction or policy applicable to the Services;
</li>
<li>automatically or programmatically extracts data or Output (defined below);</li> <li>automatically or programmatically extracts data or Output (defined below);</li>
<li>Represent that the Output was human-generated when it was not;</li> <li>Represent that the Output was human-generated when it was not;</li>
<li> <li>
+43 -5
View File
@@ -435,7 +435,22 @@ function prompt(request: LLMRequest): LanguageModelV3Prompt {
.map((part) => part.text) .map((part) => part.text)
.filter(Boolean) .filter(Boolean)
.join("\n\n") .join("\n\n")
const messages = request.messages.flatMap(message) const pending: UserContent = []
const messages = request.messages.flatMap((input, index) => {
if (input.role !== "tool") return message(input)
const lowered = toolMessage(input)
pending.push(...lowered.media)
if (request.messages[index + 1]?.role === "tool" || pending.length === 0) return lowered.messages
const media = [...pending]
pending.length = 0
return [
...lowered.messages,
{
role: "user" as const,
content: [{ type: "text" as const, text: "Attached media from tool result:" }, ...media],
},
]
})
if (!system.length) return messages if (!system.length) return messages
return [{ role: "system", content: system }, ...messages] return [{ role: "system", content: system }, ...messages]
} }
@@ -448,10 +463,33 @@ function message(input: LLMRequest["messages"][number]): LanguageModelV3Message[
return [{ role: "user", content: input.content.flatMap(userPart) }] return [{ role: "user", content: input.content.flatMap(userPart) }]
case "assistant": case "assistant":
return [{ role: "assistant", content: input.content.flatMap(assistantPart) }] return [{ role: "assistant", content: input.content.flatMap(assistantPart) }]
case "tool": { case "tool":
const content = input.content.flatMap(toolResultPart) return toolMessage(input).messages
return content.length ? [{ role: "tool", content }] : [] }
} }
function toolMessage(input: LLMRequest["messages"][number]) {
const media: UserContent = []
const content = input.content.flatMap((part) => {
if (part.type !== "tool-result" || part.result.type !== "content") return toolResultPart(part)
const value = part.result.value.filter((item) => {
if (item.type !== "file") return true
if (!item.mime.startsWith("image/") && item.mime !== "application/pdf") return true
const data = /^data:[^;,]+(?:;[^,]*)*;base64,(.*)$/s.exec(item.uri)?.[1] ?? item.uri
media.push({ type: "file", mediaType: item.mime, data, filename: item.name })
return false
})
return toolResultPart({
...part,
result:
value.length === 0
? { type: "text", value: "Media attached in the following user message." }
: { ...part.result, value },
})
})
return {
messages: content.length ? ([{ role: "tool", content }] satisfies LanguageModelV3Message[]) : [],
media,
} }
} }
+10 -10
View File
@@ -1,6 +1,6 @@
export * as Bus from "./bus.js" export * as Bus from "./bus.js"
import { Cause, Context, DateTime, Effect, Layer, Option, PubSub, Schema, Stream } from "effect" import { Cause, Clock, Context, Effect, Layer, Option, PubSub, Schema, Stream } from "effect"
import { Event } from "@opencode-ai/schema/event" import { Event } from "@opencode-ai/schema/event"
import type { EventLog } from "@opencode-ai/schema/event-log" import type { EventLog } from "@opencode-ai/schema/event-log"
import { and, asc, eq, gt, lte, sql } from "drizzle-orm" import { and, asc, eq, gt, lte, sql } from "drizzle-orm"
@@ -47,7 +47,7 @@ export const reserveSequence = Effect.fn("Bus.reserveSequence")(function* (
export type SerializedEvent = { export type SerializedEvent = {
readonly id: Event.ID readonly id: Event.ID
readonly type: string readonly type: string
readonly created?: DateTime.Utc readonly created?: number
readonly seq: number readonly seq: number
readonly aggregateID: string readonly aggregateID: string
readonly data: Record<string, unknown> readonly data: Record<string, unknown>
@@ -74,7 +74,7 @@ const decodeSerializedEvent = (event: SerializedEvent): Event.Payload => {
} }
return { return {
id: event.id, id: event.id,
created: event.created ?? DateTime.makeUnsafe(0), created: event.created ?? 0,
type: definition.type, type: definition.type,
durable: envelope(event.aggregateID, event.seq, definition.durable.version), durable: envelope(event.aggregateID, event.seq, definition.durable.version),
data: Schema.decodeUnknownSync(definition.data)(event.data), data: Schema.decodeUnknownSync(definition.data)(event.data),
@@ -283,7 +283,7 @@ export function configured(options?: Options) {
if ( if (
stored?.id === event.id && stored?.id === event.id &&
stored.type === versionedType(definition.type, durable.version) && stored.type === versionedType(definition.type, durable.version) &&
stored.created === DateTime.toEpochMillis(event.created ?? DateTime.makeUnsafe(0)) && stored.created === (event.created ?? 0) &&
isDeepStrictEqual(stored.data, encoded) isDeepStrictEqual(stored.data, encoded)
) { ) {
if (input.ownerID && row?.ownerID == null) { if (input.ownerID && row?.ownerID == null) {
@@ -358,7 +358,7 @@ export function configured(options?: Options) {
id: event.id, id: event.id,
aggregate_id: aggregateID, aggregate_id: aggregateID,
seq, seq,
created: DateTime.toEpochMillis(event.created ?? DateTime.makeUnsafe(0)), created: event.created ?? 0,
type: versionedType(definition.type, durable.version), type: versionedType(definition.type, durable.version),
data: encoded, data: encoded,
}, },
@@ -455,7 +455,7 @@ export function configured(options?: Options) {
definition, definition,
{ {
id: options?.id ?? Event.ID.create(), id: options?.id ?? Event.ID.create(),
created: yield* DateTime.now, created: yield* Clock.currentTimeMillis,
...(options?.metadata ? { metadata: options.metadata } : {}), ...(options?.metadata ? { metadata: options.metadata } : {}),
type: definition.type, type: definition.type,
...(location ? { location } : {}), ...(location ? { location } : {}),
@@ -491,7 +491,7 @@ export function configured(options?: Options) {
commit: options?.commit, commit: options?.commit,
event: { event: {
id: options?.id ?? Event.ID.create(), id: options?.id ?? Event.ID.create(),
created: yield* DateTime.now, created: yield* Clock.currentTimeMillis,
...(options?.metadata ? { metadata: options.metadata } : {}), ...(options?.metadata ? { metadata: options.metadata } : {}),
type: definition.type, type: definition.type,
...(location ? { location } : {}), ...(location ? { location } : {}),
@@ -571,7 +571,7 @@ export function configured(options?: Options) {
id: event.id, id: event.id,
aggregate_id: aggregateID, aggregate_id: aggregateID,
seq, seq,
created: DateTime.toEpochMillis(event.created), created: event.created,
type: versionedType(item.definition.type, item.definition.durable.version), type: versionedType(item.definition.type, item.definition.durable.version),
data: encoded, data: encoded,
}) })
@@ -619,7 +619,7 @@ export function configured(options?: Options) {
Effect.gen(function* () { Effect.gen(function* () {
const payload = { const payload = {
id: event.id, id: event.id,
created: event.created ?? DateTime.makeUnsafe(0), created: event.created ?? 0,
type: definition.type, type: definition.type,
data: Schema.decodeUnknownSync(definition.data)(event.data), data: Schema.decodeUnknownSync(definition.data)(event.data),
} as Event.Payload } as Event.Payload
@@ -733,7 +733,7 @@ export function configured(options?: Options) {
return [ return [
decodeSerializedEvent({ decodeSerializedEvent({
id: event.id, id: event.id,
created: DateTime.makeUnsafe(event.created), created: event.created,
aggregateID: event.aggregate_id, aggregateID: event.aggregate_id,
seq: event.seq, seq: event.seq,
type: event.type, type: event.type,
+31 -3
View File
@@ -1,6 +1,6 @@
export * as PlanPlugin from "./plan.js" export * as PlanPlugin from "./plan.js"
import { ToolFailure } from "@opencode-ai/ai" import { Message, ToolFailure } from "@opencode-ai/ai"
import { define } from "@opencode-ai/plugin/effect/plugin" import { define } from "@opencode-ai/plugin/effect/plugin"
import { Effect, Stream } from "effect" import { Effect, Stream } from "effect"
import { Agent } from "../agent.js" import { Agent } from "../agent.js"
@@ -38,13 +38,33 @@ export const Plugin = define({
}) })
}) })
// Compaction and committed reverts can strip reminders while the session's agent stays
// put. Reconcile per request, appending near the tail so the cached prefix stays warm.
yield* ctx.session.hook("context", (event) => {
const reminder = lastReminder(event.messages)
const missing = event.agent === plan && reminder !== enter
const stale = event.agent !== plan && reminder === enter
const text = missing ? enter : stale ? leave : undefined
if (!text) return Effect.void
// Before the user's prompt, matching where agent-switch reminders land.
const at = event.messages.at(-1)?.role === "user" ? event.messages.length - 1 : event.messages.length
event.messages.splice(at, 0, Message.user(text))
return ctx.session
.synthetic({ sessionID: event.sessionID, text, resume: false })
.pipe(
Effect.catchCause((cause) =>
Effect.logWarning("failed to persist Plan mode reminder", { sessionID: event.sessionID, cause }),
),
)
})
yield* ctx.event.subscribe().pipe( yield* ctx.event.subscribe().pipe(
Stream.filter( Stream.filter(
(event): event is SessionEvent.Created | SessionEvent.AgentSelected => (event): event is SessionEvent.Created | SessionEvent.AgentSelected =>
event.type === "session.created" || event.type === "session.agent.selected", event.type === "session.created" || event.type === "session.agent.selected",
), ),
Stream.runForEach((event) => { Stream.runForEach((event) => {
const text = reminder(event) const text = switchReminder(event)
if (!text) return Effect.void if (!text) return Effect.void
return ctx.session return ctx.session
.synthetic({ .synthetic({
@@ -63,7 +83,7 @@ export const Plugin = define({
}), }),
}) })
function reminder(event: SessionEvent.Created | SessionEvent.AgentSelected) { function switchReminder(event: SessionEvent.Created | SessionEvent.AgentSelected) {
if (event.type === "session.created") { if (event.type === "session.created") {
if (event.data.agent !== plan) return if (event.data.agent !== plan) return
return enter return enter
@@ -72,3 +92,11 @@ function reminder(event: SessionEvent.Created | SessionEvent.AgentSelected) {
if (event.data.agent === plan) return enter if (event.data.agent === plan) return enter
if (event.data.previous === plan) return leave if (event.data.previous === plan) return leave
} }
function lastReminder(messages: ReadonlyArray<Message>) {
return messages.reduce<string | undefined>((found, message) => {
const part = message.role === "user" && message.content.length === 1 ? message.content[0] : undefined
if (part?.type !== "text") return found
return part.text === enter || part.text === leave ? part.text : found
}, undefined)
}
@@ -5,6 +5,7 @@ import { App } from "../../app.js"
import { Credential } from "../../credential.js" import { Credential } from "../../credential.js"
import { Bus } from "../../bus.js" import { Bus } from "../../bus.js"
import { Integration } from "../../integration.js" import { Integration } from "../../integration.js"
import { Model } from "../../model.js"
import { OauthCallbackPage } from "../../oauth/page.js" import { OauthCallbackPage } from "../../oauth/page.js"
import { Provider } from "../../provider.js" import { Provider } from "../../provider.js"
import type { PluginInternal } from "../internal.js" import type { PluginInternal } from "../internal.js"
@@ -229,6 +230,16 @@ export const OpenAIPlugin = define({
}) })
} }
}) })
yield* ctx.session.hook("http.request", (evt) =>
Effect.sync(() => {
if (!chatgpt || evt.model.providerID !== Provider.ID.openai) return
const url = new URL(evt.request.url)
evt.request.headers.set("originator", "opencode")
evt.request.headers.set("session-id", evt.sessionID)
if (url.origin !== "https://api.openai.com") return
evt.request = new Request(`${codexBaseURL}${url.pathname.replace(/^\/v1/, "")}${url.search}`, evt.request)
}),
)
const refresh = () => loading.withPermit(load().pipe(Effect.andThen(ctx.catalog.reload()))) const refresh = () => loading.withPermit(load().pipe(Effect.andThen(ctx.catalog.reload())))
yield* bus.subscribe(Integration.Event.ConnectionUpdated).pipe( yield* bus.subscribe(Integration.Event.ConnectionUpdated).pipe(
Stream.filter((event) => event.data.integrationID === Integration.ID.make("openai")), Stream.filter((event) => event.data.integrationID === Integration.ID.make("openai")),
@@ -63,6 +63,33 @@ examples, but do not fetch it to determine the V2 configuration shape.
See the [full configuration guide](https://opencode.ai/v2/docs/config) for See the [full configuration guide](https://opencode.ai/v2/docs/config) for
every field, examples, config locations, and links to dedicated feature guides. every field, examples, config locations, and links to dedicated feature guides.
## [MCP servers](https://opencode.ai/v2/docs/mcp-servers)
Configure MCP servers under `mcp.servers`. Prefer the CLI because it preserves
unrelated configuration. Use `--global` when the user asks to set up a service
for themselves without limiting it to the current project; omit it when they
explicitly want project-local configuration.
```sh
opencode2 mcp add <name> --global --url <remote-url>
opencode2 mcp list
```
Remote servers use OAuth by default. If `mcp list` reports that a server needs
authentication, run the OAuth flow and then verify the connection:
```sh
opencode2 mcp auth <name>
opencode2 mcp list
```
The auth command prints an authorization URL, waits for the browser redirect,
and stores credentials outside the OpenCode configuration. Do not ask for or
store an API key when the server supports OAuth. Use header-based credentials
only when OAuth is unavailable or the user explicitly requires them, and use an
environment substitution such as `{env:MCP_API_KEY}` instead of writing a
secret into configuration.
## [V1 to V2 migration](https://opencode.ai/v2/docs/migrate-v1) ## [V1 to V2 migration](https://opencode.ai/v2/docs/migrate-v1)
For any request to migrate OpenCode configuration, agents, commands, skills, For any request to migrate OpenCode configuration, agents, commands, skills,
+2 -4
View File
@@ -233,8 +233,6 @@ export interface Interface {
sessionID: SessionSchema.ID sessionID: SessionSchema.ID
command: string command: string
arguments?: string arguments?: string
agent?: Agent.ID
model?: Model.Ref
files?: PromptInput.Prompt["files"] files?: PromptInput.Prompt["files"]
agents?: PromptInput.Prompt["agents"] agents?: PromptInput.Prompt["agents"]
skills?: PromptInput.Prompt["skills"] skills?: PromptInput.Prompt["skills"]
@@ -621,13 +619,13 @@ const layer = Layer.effect(
const evaluated = yield* commands.evaluate({ name: input.command, arguments: input.arguments }) const evaluated = yield* commands.evaluate({ name: input.command, arguments: input.arguments })
// TODO(v2 commands): decide whether command-level subtask/background execution belongs in v2 commands. // TODO(v2 commands): decide whether command-level subtask/background execution belongs in v2 commands.
const agent = command.agent ?? input.agent const agent = command.agent
const commandAgent = yield* Effect.gen(function* () { const commandAgent = yield* Effect.gen(function* () {
if (!command.agent) return undefined if (!command.agent) return undefined
const agents = yield* Agent.Service.pipe(Effect.provide(locations.get(session.location))) const agents = yield* Agent.Service.pipe(Effect.provide(locations.get(session.location)))
return yield* agents.get(Agent.ID.make(command.agent)) return yield* agents.get(Agent.ID.make(command.agent))
}) })
const model = command.model ?? commandAgent?.model ?? input.model const model = command.model ?? commandAgent?.model
if (agent !== undefined && session.agent !== Agent.ID.make(agent)) if (agent !== undefined && session.agent !== Agent.ID.make(agent))
yield* result.switchAgent({ sessionID: input.sessionID, agent: Agent.ID.make(agent) }) yield* result.switchAgent({ sessionID: input.sessionID, agent: Agent.ID.make(agent) })
if (model !== undefined) yield* result.switchModel({ sessionID: input.sessionID, model }) if (model !== undefined) yield* result.switchModel({ sessionID: input.sessionID, model })
+3 -3
View File
@@ -161,7 +161,7 @@ export const admit = Effect.fn("SessionInbox.admit")(function* (
const base = { const base = {
id: request.id, id: request.id,
sessionID: request.sessionID, sessionID: request.sessionID,
timeCreated: event.created, timeCreated: DateTime.makeUnsafe(event.created),
} }
return Effect.succeed(Info.make({ ...base, ...request.item })) return Effect.succeed(Info.make({ ...base, ...request.item }))
}), }),
@@ -196,7 +196,7 @@ export const projectAdmitted = Effect.fn("SessionInbox.projectAdmitted")(functio
readonly id: SessionMessage.ID readonly id: SessionMessage.ID
readonly sessionID: SessionSchema.ID readonly sessionID: SessionSchema.ID
readonly item: Item readonly item: Item
readonly timeCreated: DateTime.Utc readonly timeCreated: number
}, },
) { ) {
const message = yield* db const message = yield* db
@@ -222,7 +222,7 @@ export const projectAdmitted = Effect.fn("SessionInbox.projectAdmitted")(functio
: encodeMove(request.item.payload), : encodeMove(request.item.payload),
delivery: request.item.delivery, delivery: request.item.delivery,
enqueued_seq: request.enqueuedSeq, enqueued_seq: request.enqueuedSeq,
time_created: DateTime.toEpochMillis(request.timeCreated), time_created: request.timeCreated,
}) })
.onConflictDoNothing() .onConflictDoNothing()
.returning({ id: SessionInboxTable.id }) .returning({ id: SessionInboxTable.id })
+22 -21
View File
@@ -26,6 +26,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
type DraftTool = WritableDraft<SessionMessage.AssistantTool> type DraftTool = WritableDraft<SessionMessage.AssistantTool>
type DraftText = WritableDraft<SessionMessage.AssistantText> type DraftText = WritableDraft<SessionMessage.AssistantText>
type DraftReasoning = WritableDraft<SessionMessage.AssistantReasoning> type DraftReasoning = WritableDraft<SessionMessage.AssistantReasoning>
const created = DateTime.makeUnsafe(event.created)
const latestTool = (assistant: DraftAssistant | undefined, id?: string) => const latestTool = (assistant: DraftAssistant | undefined, id?: string) =>
assistant?.content.findLast( assistant?.content.findLast(
@@ -70,7 +71,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
metadata: event.metadata, metadata: event.metadata,
agent: event.data.agent, agent: event.data.agent,
previous, previous,
time: { created: event.created }, time: { created },
}), }),
) )
}) })
@@ -85,7 +86,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
metadata: event.metadata, metadata: event.metadata,
model: event.data.model, model: event.data.model,
previous, previous,
time: { created: event.created }, time: { created },
}), }),
) )
}) })
@@ -101,7 +102,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
projectID: event.data.projectID, projectID: event.data.projectID,
subpath: event.data.subpath, subpath: event.data.subpath,
previous: yield* adapter.getLocation(), previous: yield* adapter.getLocation(),
time: { created: event.created }, time: { created },
}), }),
) )
}) })
@@ -126,7 +127,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
text: event.data.text, text: event.data.text,
description: `Instructions updated: ${Object.keys(event.data.delta).join(", ")}`, description: `Instructions updated: ${Object.keys(event.data.delta).join(", ")}`,
metadata: event.metadata, metadata: event.metadata,
time: { created: event.created }, time: { created },
}), }),
) )
}, },
@@ -138,7 +139,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
metadata: event.data.metadata, metadata: event.data.metadata,
id: SessionMessage.ID.fromEvent(event.id), id: SessionMessage.ID.fromEvent(event.id),
type: "synthetic", type: "synthetic",
time: { created: event.created }, time: { created },
}), }),
) )
}, },
@@ -151,7 +152,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
name: event.data.name, name: event.data.name,
text: event.data.text, text: event.data.text,
metadata: event.metadata, metadata: event.metadata,
time: { created: event.created }, time: { created },
}), }),
) )
}, },
@@ -164,7 +165,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
shellID: event.data.shell.id, shellID: event.data.shell.id,
command: event.data.shell.command, command: event.data.shell.command,
status: event.data.shell.status, status: event.data.shell.status,
time: { created: event.created }, time: { created },
}), }),
) )
}, },
@@ -177,7 +178,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
draft.status = event.data.shell.status draft.status = event.data.shell.status
draft.exit = event.data.shell.exit draft.exit = event.data.shell.exit
draft.output = event.data.output draft.output = event.data.output
draft.time.completed = event.created draft.time.completed = created
}), }),
) )
} }
@@ -205,7 +206,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
yield* adapter.updateAssistant( yield* adapter.updateAssistant(
produce(currentAssistant, (draft) => { produce(currentAssistant, (draft) => {
draft.retry = undefined draft.retry = undefined
draft.time.completed = event.created draft.time.completed = created
}), }),
) )
} }
@@ -216,7 +217,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
agent: event.data.agent, agent: event.data.agent,
model: event.data.model, model: event.data.model,
metadata: event.metadata, metadata: event.metadata,
time: { created: event.created }, time: { created },
content: [], content: [],
snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined, snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
}), }),
@@ -225,7 +226,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
}, },
"session.step.ended": (event) => { "session.step.ended": (event) => {
return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => {
draft.time.completed = event.created draft.time.completed = created
draft.finish = event.data.finish draft.finish = event.data.finish
draft.cost = event.data.cost draft.cost = event.data.cost
draft.tokens = event.data.tokens draft.tokens = event.data.tokens
@@ -239,7 +240,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
}, },
"session.step.failed": (event) => { "session.step.failed": (event) => {
return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => {
draft.time.completed = event.created draft.time.completed = created
draft.finish = "error" draft.finish = "error"
draft.error = castDraft(event.data.error) draft.error = castDraft(event.data.error)
draft.retry = undefined draft.retry = undefined
@@ -277,7 +278,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
type: "tool", type: "tool",
id: event.data.id, id: event.data.id,
name: event.data.name, name: event.data.name,
time: { created: event.created }, time: { created },
state: SessionMessage.ToolStateStreaming.make({ status: "streaming", input: "" }), state: SessionMessage.ToolStateStreaming.make({ status: "streaming", input: "" }),
}), }),
), ),
@@ -296,7 +297,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
if (match) { if (match) {
match.executed = event.data.executed match.executed = event.data.executed
match.providerState = event.data.state match.providerState = event.data.state
match.time.ran = event.created match.time.ran = created
match.state = castDraft( match.state = castDraft(
SessionMessage.ToolStateRunning.make({ SessionMessage.ToolStateRunning.make({
status: "running", status: "running",
@@ -315,7 +316,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
if (match && match.state.status === "running") { if (match && match.state.status === "running") {
match.executed = event.data.executed || match.executed === true match.executed = event.data.executed || match.executed === true
match.providerResultState = event.data.resultState match.providerResultState = event.data.resultState
match.time.completed = event.created match.time.completed = created
match.state = castDraft( match.state = castDraft(
SessionMessage.ToolStateCompleted.make({ SessionMessage.ToolStateCompleted.make({
status: "completed", status: "completed",
@@ -333,7 +334,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
if (match && (match.state.status === "streaming" || match.state.status === "running")) { if (match && (match.state.status === "streaming" || match.state.status === "running")) {
match.executed = event.data.executed || match.executed === true match.executed = event.data.executed || match.executed === true
match.providerResultState = event.data.resultState match.providerResultState = event.data.resultState
match.time.completed = event.created match.time.completed = created
match.state = castDraft( match.state = castDraft(
SessionMessage.ToolStateError.make({ SessionMessage.ToolStateError.make({
status: "error", status: "error",
@@ -354,7 +355,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
type: "reasoning", type: "reasoning",
text: "", text: "",
state: event.data.state, state: event.data.state,
time: { created: event.created }, time: { created },
}), }),
), ),
) )
@@ -365,7 +366,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
const match = latestReasoning(draft) const match = latestReasoning(draft)
if (match) { if (match) {
match.text = event.data.text match.text = event.data.text
match.time = { created: match.time?.created ?? event.created, completed: event.created } match.time = { created: match.time?.created ?? created, completed: created }
if (event.data.state !== undefined) match.state = event.data.state if (event.data.state !== undefined) match.state = event.data.state
} }
}) })
@@ -389,7 +390,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
reason: event.data.reason, reason: event.data.reason,
summary: "", summary: "",
recent: event.data.recent ?? "", recent: event.data.recent ?? "",
time: { created: event.created }, time: { created },
}), }),
), ),
"session.compaction.ended": (event) => { "session.compaction.ended": (event) => {
@@ -414,7 +415,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
reason: event.data.reason, reason: event.data.reason,
summary: event.data.text, summary: event.data.text,
recent: event.data.recent, recent: event.data.recent,
time: { created: event.created }, time: { created },
}), }),
) )
}) })
@@ -429,7 +430,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
metadata: current?.metadata ?? event.metadata, metadata: current?.metadata ?? event.metadata,
reason: event.data.reason, reason: event.data.reason,
error: event.data.error, error: event.data.error,
time: current?.time ?? { created: event.created }, time: current?.time ?? { created },
}) })
if (current?.status === "running") return yield* adapter.updateCompaction(failed) if (current?.status === "running") return yield* adapter.updateCompaction(failed)
yield* adapter.appendMessage(failed) yield* adapter.appendMessage(failed)
+14 -14
View File
@@ -162,8 +162,8 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
tokens_reasoning: 0, tokens_reasoning: 0,
tokens_cache_read: 0, tokens_cache_read: 0,
tokens_cache_write: 0, tokens_cache_write: 0,
time_created: DateTime.toEpochMillis(event.created), time_created: event.created,
time_updated: DateTime.toEpochMillis(event.created), time_updated: event.created,
}) })
.onConflictDoNothing() .onConflictDoNothing()
.returning({ sessionID: SessionTable.id }) .returning({ sessionID: SessionTable.id })
@@ -411,8 +411,8 @@ const layer = Layer.effectDiscard(
agent: event.data.agent, agent: event.data.agent,
model: event.data.model, model: event.data.model,
version: event.data.version, version: event.data.version,
time_created: DateTime.toEpochMillis(event.created), time_created: event.created,
time_updated: DateTime.toEpochMillis(event.created), time_updated: event.created,
}) })
.onConflictDoNothing() .onConflictDoNothing()
.returning({ sessionID: SessionTable.id }) .returning({ sessionID: SessionTable.id })
@@ -431,7 +431,7 @@ const layer = Layer.effectDiscard(
path: event.data.subpath, path: event.data.subpath,
...(event.data.projectID ? { project_id: event.data.projectID } : {}), ...(event.data.projectID ? { project_id: event.data.projectID } : {}),
workspace_id: event.data.location.workspaceID ? Workspace.ID.make(event.data.location.workspaceID) : null, workspace_id: event.data.location.workspaceID ? Workspace.ID.make(event.data.location.workspaceID) : null,
time_updated: DateTime.toEpochMillis(event.created), time_updated: event.created,
}) })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
@@ -487,7 +487,7 @@ const layer = Layer.effectDiscard(
yield* run(db, event) yield* run(db, event)
yield* db yield* db
.update(SessionTable) .update(SessionTable)
.set({ agent: event.data.agent, time_updated: DateTime.toEpochMillis(event.created) }) .set({ agent: event.data.agent, time_updated: event.created })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
.pipe(Effect.orDie) .pipe(Effect.orDie)
@@ -498,7 +498,7 @@ const layer = Layer.effectDiscard(
yield* run(db, event) yield* run(db, event)
yield* db yield* db
.update(SessionTable) .update(SessionTable)
.set({ model: event.data.model, time_updated: DateTime.toEpochMillis(event.created) }) .set({ model: event.data.model, time_updated: event.created })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
.pipe(Effect.orDie) .pipe(Effect.orDie)
@@ -507,7 +507,7 @@ const layer = Layer.effectDiscard(
yield* bus.project(SessionEvent.Renamed, (event) => yield* bus.project(SessionEvent.Renamed, (event) =>
db db
.update(SessionTable) .update(SessionTable)
.set({ title: event.data.title, time_updated: DateTime.toEpochMillis(event.created) }) .set({ title: event.data.title, time_updated: event.created })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
.pipe(Effect.orDie), .pipe(Effect.orDie),
@@ -535,7 +535,7 @@ const layer = Layer.effectDiscard(
files: input.payload.files, files: input.payload.files,
agents: input.payload.agents, agents: input.payload.agents,
skills: input.payload.skills, skills: input.payload.skills,
time: { created: event.created }, time: { created: DateTime.makeUnsafe(event.created) },
} }
: { : {
id: input.id, id: input.id,
@@ -543,7 +543,7 @@ const layer = Layer.effectDiscard(
text: input.payload.text, text: input.payload.text,
description: input.payload.description, description: input.payload.description,
metadata: input.payload.metadata, metadata: input.payload.metadata,
time: { created: event.created }, time: { created: DateTime.makeUnsafe(event.created) },
}, },
) )
}), }),
@@ -561,7 +561,7 @@ const layer = Layer.effectDiscard(
}) })
yield* db yield* db
.update(SessionTable) .update(SessionTable)
.set({ time_updated: DateTime.toEpochMillis(event.created) }) .set({ time_updated: event.created })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
.pipe(Effect.orDie) .pipe(Effect.orDie)
@@ -640,7 +640,7 @@ const layer = Layer.effectDiscard(
.update(SessionTable) .update(SessionTable)
.set({ .set({
revert: { ...revert, files: revert.files ? [...revert.files] : undefined }, revert: { ...revert, files: revert.files ? [...revert.files] : undefined },
time_updated: DateTime.toEpochMillis(event.created), time_updated: event.created,
}) })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
@@ -650,7 +650,7 @@ const layer = Layer.effectDiscard(
yield* bus.project(SessionEvent.RevertEvent.Cleared, (event) => yield* bus.project(SessionEvent.RevertEvent.Cleared, (event) =>
db db
.update(SessionTable) .update(SessionTable)
.set({ revert: null, time_updated: DateTime.toEpochMillis(event.created) }) .set({ revert: null, time_updated: event.created })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
.pipe(Effect.orDie, Effect.asVoid), .pipe(Effect.orDie, Effect.asVoid),
@@ -685,7 +685,7 @@ const layer = Layer.effectDiscard(
.pipe(Effect.orDie) .pipe(Effect.orDie)
yield* db yield* db
.update(SessionTable) .update(SessionTable)
.set({ revert: null, time_updated: DateTime.toEpochMillis(event.created) }) .set({ revert: null, time_updated: event.created })
.where(eq(SessionTable.id, event.data.sessionID)) .where(eq(SessionTable.id, event.data.sessionID))
.run() .run()
.pipe(Effect.orDie) .pipe(Effect.orDie)
@@ -1,5 +1,5 @@
import { type LLMEvent, type ProviderMetadata, type ToolResultValue } from "@opencode-ai/ai" import { type LLMEvent, type ProviderMetadata, type ToolResultValue } from "@opencode-ai/ai"
import { Effect } from "effect" import { Clock, Effect } from "effect"
import { Bus } from "../../bus.js" import { Bus } from "../../bus.js"
import { Model } from "../../model.js" import { Model } from "../../model.js"
import { SessionEvent } from "../event.js" import { SessionEvent } from "../event.js"
@@ -81,6 +81,7 @@ const hostedContent = (result: ToolResultValue): NonEmptyContent => {
* and consumers fold by id/ordinal rather than global position. * and consumers fold by id/ordinal rather than global position.
*/ */
export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, input: Input) => { export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, input: Input) => {
const deltaBatchInterval = 100
const tools = new Map< const tools = new Map<
string, string,
{ {
@@ -123,32 +124,56 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
const fragments = ( const fragments = (
name: string, name: string,
ended: (id: string, value: string, ordinal: number, state?: Record<string, unknown>) => Effect.Effect<void>, ended: (id: string, value: string, ordinal: number, state?: Record<string, unknown>) => Effect.Effect<void>,
delta?: (id: string, value: string, ordinal: number) => Effect.Effect<void>,
single = false, single = false,
) => { ) => {
const chunks = new Map< type Fragment = {
string, readonly ordinal: number
{ readonly ordinal: number; readonly values: string[]; state?: Record<string, unknown> } readonly values: string[]
>() pending: string
publishedAt?: number
state?: Record<string, unknown>
}
const chunks = new Map<string, Fragment>()
let nextOrdinal = 0 let nextOrdinal = 0
const start = (id: string, state?: Record<string, unknown>) => const start = (id: string, state?: Record<string, unknown>) =>
Effect.suspend(() => { Effect.suspend(() => {
if (chunks.has(id)) return Effect.die(new Error(`Duplicate ${name} start: ${id}`)) if (chunks.has(id)) return Effect.die(new Error(`Duplicate ${name} start: ${id}`))
if (single && chunks.size > 0) return Effect.die(new Error(`${name} start before end: ${id}`)) if (single && chunks.size > 0) return Effect.die(new Error(`${name} start before end: ${id}`))
const ordinal = nextOrdinal++ const ordinal = nextOrdinal++
chunks.set(id, { ordinal, values: [], state }) chunks.set(id, { ordinal, values: [], pending: "", state })
return Effect.succeed(ordinal) return Effect.succeed(ordinal)
}) })
const append = (id: string, value: string, state?: Record<string, unknown>) => const publishDelta = Effect.fnUntraced(function* (id: string, force = false) {
Effect.suspend(() => { if (!delta) return undefined
const current = chunks.get(id) const current = chunks.get(id)
if (!current) return Effect.die(new Error(`${name} delta before start: ${id}`)) if (!current) return yield* Effect.die(new Error(`${name} delta before start: ${id}`))
current.values.push(value) if (!current.pending) return undefined
if (state !== undefined) current.state = { ...current.state, ...state } const now = yield* Clock.currentTimeMillis
return Effect.succeed(current.ordinal) if (!force && current.publishedAt === undefined) {
}) current.publishedAt = now
return undefined
}
if (!force && current.publishedAt !== undefined && now - current.publishedAt < deltaBatchInterval)
return undefined
yield* delta(id, current.pending, current.ordinal)
current.pending = ""
current.publishedAt = now
return undefined
})
const append = Effect.fnUntraced(function* (id: string, value: string, state?: Record<string, unknown>) {
const current = chunks.get(id)
if (!current) return yield* Effect.die(new Error(`${name} delta before start: ${id}`))
current.values.push(value)
if (delta) current.pending += value
if (state !== undefined) current.state = { ...current.state, ...state }
yield* publishDelta(id)
return current.ordinal
})
const end = Effect.fnUntraced(function* (id: string, state?: Record<string, unknown>, value?: string) { const end = Effect.fnUntraced(function* (id: string, state?: Record<string, unknown>, value?: string) {
const current = chunks.get(id) const current = chunks.get(id)
if (!current) return yield* Effect.die(new Error(`${name} end before start: ${id}`)) if (!current) return yield* Effect.die(new Error(`${name} end before start: ${id}`))
yield* publishDelta(id, true)
yield* ended( yield* ended(
id, id,
value ?? current.values.join(""), value ?? current.values.join(""),
@@ -156,9 +181,10 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
state === undefined ? current.state : { ...current.state, ...state }, state === undefined ? current.state : { ...current.state, ...state },
) )
chunks.delete(id) chunks.delete(id)
return undefined
}) })
const flush = Effect.fnUntraced(function* () { const flush = Effect.fnUntraced(function* () {
for (const id of chunks.keys()) yield* end(id) for (const id of Array.from(chunks.keys())) yield* end(id)
}) })
return { start, append, end, flush, has: (id: string) => chunks.has(id) } return { start, append, end, flush, has: (id: string) => chunks.has(id) }
} }
@@ -175,6 +201,15 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
state, state,
}) })
}), }),
(_textID, value, ordinal) =>
Effect.gen(function* () {
yield* bus.publish(SessionEvent.Text.Delta, {
sessionID: input.sessionID,
assistantMessageID: yield* currentAssistantMessageID(),
ordinal,
delta: value,
})
}),
true, true,
) )
const reasoning = fragments( const reasoning = fragments(
@@ -189,6 +224,15 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
state, state,
}) })
}), }),
(_reasoningID, value, ordinal) =>
Effect.gen(function* () {
yield* bus.publish(SessionEvent.Reasoning.Delta, {
sessionID: input.sessionID,
assistantMessageID: yield* currentAssistantMessageID(),
ordinal,
delta: value,
})
}),
true, true,
) )
const toolInput = fragments("tool input", (id, value) => const toolInput = fragments("tool input", (id, value) =>
@@ -351,13 +395,7 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
}) })
return return
case "text-delta": case "text-delta":
const deltaTextOrdinal = yield* text.append(event.id, event.text, providerState(event.providerMetadata)) yield* text.append(event.id, event.text, providerState(event.providerMetadata))
yield* bus.publish(SessionEvent.Text.Delta, {
sessionID: input.sessionID,
assistantMessageID: yield* currentAssistantMessageID(),
ordinal: deltaTextOrdinal,
delta: event.text,
})
return return
case "text-end": case "text-end":
yield* text.end(event.id, providerState(event.providerMetadata)) yield* text.end(event.id, providerState(event.providerMetadata))
@@ -373,17 +411,7 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
}) })
return return
case "reasoning-delta": case "reasoning-delta":
const deltaReasoningOrdinal = yield* reasoning.append( yield* reasoning.append(event.id, event.text, providerState(event.providerMetadata))
event.id,
event.text,
providerState(event.providerMetadata),
)
yield* bus.publish(SessionEvent.Reasoning.Delta, {
sessionID: input.sessionID,
assistantMessageID: yield* currentAssistantMessageID(),
ordinal: deltaReasoningOrdinal,
delta: event.text,
})
return return
case "reasoning-end": case "reasoning-end":
yield* reasoning.end(event.id, providerState(event.providerMetadata)) yield* reasoning.end(event.id, providerState(event.providerMetadata))
@@ -399,12 +427,6 @@ export const createLLMEventPublisher = (bus: Pick<Bus.Interface, "publish">, inp
return yield* Effect.die(new Error(`Tool input name changed for ${event.id}: ${tool.name} -> ${event.name}`)) return yield* Effect.die(new Error(`Tool input name changed for ${event.id}: ${tool.name} -> ${event.name}`))
if (!toolInput.has(event.id)) return yield* Effect.die(new Error(`Tool input delta after end: ${event.id}`)) if (!toolInput.has(event.id)) return yield* Effect.die(new Error(`Tool input delta after end: ${event.id}`))
yield* toolInput.append(event.id, event.text) yield* toolInput.append(event.id, event.text)
yield* bus.publish(SessionEvent.Tool.Input.Delta, {
sessionID: input.sessionID,
assistantMessageID: tool.assistantMessageID,
id: event.id,
delta: event.text,
})
return return
} }
case "tool-input-end": case "tool-input-end":
+1 -3
View File
@@ -181,9 +181,7 @@ export const Plugin = {
) )
const background = input.background === true const background = input.background === true
yield* context.progress({ yield* context.progress({ sessionID: child.id, status: "running" })
metadata: { sessionID: child.id, status: "running" },
})
const run = Effect.gen(function* () { const run = Effect.gen(function* () {
// The child session owns its agent/model (set at create); prompt only admits input. // The child session owns its agent/model (set at create); prompt only admits input.
+61 -39
View File
@@ -1,5 +1,6 @@
import { APICallError } from "@ai-sdk/provider" import { APICallError } from "@ai-sdk/provider"
import type { LanguageModelV3, LanguageModelV3StreamPart } from "@ai-sdk/provider" import type { LanguageModelV3, LanguageModelV3StreamPart } from "@ai-sdk/provider"
import { createMistral } from "@ai-sdk/mistral"
import { AISDK } from "@opencode-ai/core/aisdk" import { AISDK } from "@opencode-ai/core/aisdk"
import { SessionRunnerRetry } from "@opencode-ai/core/session/runner/retry" import { SessionRunnerRetry } from "@opencode-ai/core/session/runner/retry"
import { toSessionError } from "@opencode-ai/core/session/to-session-error" import { toSessionError } from "@opencode-ai/core/session/to-session-error"
@@ -277,67 +278,88 @@ it.effect("projects replay metadata onto AI SDK prompt parts", () =>
}), }),
) )
it.effect("preserves tool result content in AI SDK prompts", () => it.effect("moves a tool image through the real Mistral provider as a user message", () =>
Effect.gen(function* () { Effect.gen(function* () {
const aisdk = yield* AISDK.Service const aisdk = yield* AISDK.Service
let body: { messages?: unknown[] } | undefined
const mockFetch = Object.assign(
async (_input: Parameters<typeof fetch>[0], init?: RequestInit) => {
body = JSON.parse(String(init?.body))
const chunks = [
{
id: "response-1",
created: 0,
model: "pixtral-large-latest",
choices: [{ index: 0, delta: { content: [{ type: "text", text: "I see it." }] } }],
},
{
id: "response-1",
created: 0,
model: "pixtral-large-latest",
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
},
]
return new Response(chunks.map((chunk) => `data: ${JSON.stringify(chunk)}\n\n`).join(""), {
headers: { "Content-Type": "text/event-stream" },
})
},
{ preconnect: fetch.preconnect },
)
yield* aisdk.hook.sdk((event) => { yield* aisdk.hook.sdk((event) => {
event.sdk = { languageModel: () => ({ provider: event.model.providerID }) } event.sdk = createMistral({ apiKey: "test", fetch: mockFetch })
}) })
const resolved = yield* aisdk.model(model("test-ai-sdk")) const resolved = yield* aisdk.model({
const prepared = yield* compileRequest( ...model("@ai-sdk/mistral"),
modelID: Model.ID.make("pixtral-large-latest"),
})
yield* LLMClient.generate(
LLM.request({ LLM.request({
model: resolved, model: resolved,
messages: [ messages: [
Message.user("Inspect the screenshot."),
Message.assistant({ type: "tool-call", id: "call_1", name: "screenshot", input: {} }),
Message.tool({ Message.tool({
type: "tool-result",
id: "call_1", id: "call_1",
name: "read", name: "screenshot",
result: { result: {
type: "content", type: "content",
value: [ value: [
{ type: "text", text: "attachments" }, { type: "text", text: "Screenshot captured" },
{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png", name: "pixel.png" }, { type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png", name: "screen.png" },
{
type: "file",
uri: "data:application/pdf;charset=utf-8;base64,JVBERg==",
mime: "application/pdf",
name: "document.pdf",
},
{ type: "file", uri: "data:audio/mpeg;base64,SUQz", mime: "audio/mpeg", name: "clip.mp3" },
{ type: "file", uri: "https://example.com/pixel.png", mime: "image/png" },
{ type: "file", uri: "https://example.com/document.pdf", mime: "application/pdf" },
], ],
}, },
}), }),
], ],
}), }),
) ).pipe(Effect.provide(client))
expect(prepared.body.prompt).toEqual([ expect(body?.messages).toEqual([
{ role: "user", content: [{ type: "text", text: "Inspect the screenshot." }] },
{
role: "assistant",
content: "",
tool_calls: [
{
id: "call_1",
type: "function",
function: { name: "screenshot", arguments: "{}" },
},
],
},
{ {
role: "tool", role: "tool",
name: "screenshot",
tool_call_id: "call_1",
content: '[{"type":"text","text":"Screenshot captured"}]',
},
{
role: "user",
content: [ content: [
{ { type: "text", text: "Attached media from tool result:" },
type: "tool-result", { type: "image_url", image_url: "data:image/png;base64,AAAA" },
toolCallId: "call_1",
toolName: "read",
output: {
type: "content",
value: [
{ type: "text", text: "attachments" },
{ type: "image-data", data: "AAAA", mediaType: "image/png" },
{
type: "file-data",
data: "JVBERg==",
mediaType: "application/pdf",
filename: "document.pdf",
},
{ type: "file-data", data: "SUQz", mediaType: "audio/mpeg", filename: "clip.mp3" },
{ type: "image-url", url: "https://example.com/pixel.png" },
{ type: "file-url", url: "https://example.com/document.pdf" },
],
},
},
], ],
}, },
]) ])
+25 -24
View File
@@ -1,5 +1,5 @@
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { Cause, DateTime, Deferred, Effect, Exit, Fiber, Layer, Option, Ref, Schema, Stream } from "effect" import { Cause, Deferred, Effect, Exit, Fiber, Layer, Option, Ref, Schema, Stream } from "effect"
import { Bus } from "@opencode-ai/core/bus" import { Bus } from "@opencode-ai/core/bus"
import { Event } from "@opencode-ai/schema/event" import { Event } from "@opencode-ai/schema/event"
import { Session } from "@opencode-ai/schema/session" import { Session } from "@opencode-ai/schema/session"
@@ -422,7 +422,7 @@ describe("Bus", () => {
const { db } = yield* Database.Service const { db } = yield* Database.Service
const aggregateID = Event.ID.create() const aggregateID = Event.ID.create()
yield* bus.publish(SyncMessage, { id: aggregateID, text: "first" }) const event = yield* bus.publish(SyncMessage, { id: aggregateID, text: "first" })
const rows = yield* db const rows = yield* db
.select() .select()
.from(EventTable) .from(EventTable)
@@ -433,6 +433,7 @@ describe("Bus", () => {
expect(rows).toHaveLength(1) expect(rows).toHaveLength(1)
expect(rows[0]?.type).toBe(Bus.versionedType(SyncMessage.type, 1)) expect(rows[0]?.type).toBe(Bus.versionedType(SyncMessage.type, 1))
expect(rows[0]?.aggregate_id).toBe(aggregateID) expect(rows[0]?.aggregate_id).toBe(aggregateID)
expect(rows[0]?.created).toBe(event.created)
}), }),
) )
@@ -706,7 +707,7 @@ describe("Bus", () => {
yield* bus.replay({ yield* bus.replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -726,7 +727,7 @@ describe("Bus", () => {
yield* bus.replay({ yield* bus.replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -763,7 +764,7 @@ describe("Bus", () => {
const exit = yield* bus const exit = yield* bus
.replay({ .replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 1, seq: 1,
aggregateID: envelopeAggregateID, aggregateID: envelopeAggregateID,
@@ -797,7 +798,7 @@ describe("Bus", () => {
yield* bus.replay({ yield* bus.replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -806,7 +807,7 @@ describe("Bus", () => {
const exit = yield* bus const exit = yield* bus
.replay({ .replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 5, seq: 5,
aggregateID, aggregateID,
@@ -831,14 +832,14 @@ describe("Bus", () => {
yield* bus.replay({ yield* bus.replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(SessionEvent.InstructionsUpdated.type, 2), type: Bus.versionedType(SessionEvent.InstructionsUpdated.type, 2),
seq: 0, seq: 0,
aggregateID, aggregateID,
data: { sessionID: aggregateID, delta: { "core/context": "0".repeat(64) } }, data: { sessionID: aggregateID, delta: { "core/context": "0".repeat(64) } },
}) })
expect(received[0]?.created).toEqual(DateTime.makeUnsafe(0)) expect(received[0]?.created).toBe(0)
}), }),
) )
@@ -867,7 +868,7 @@ describe("Bus", () => {
const exit = yield* bus const exit = yield* bus
.replay({ .replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: "unknown.event.1", type: "unknown.event.1",
seq: 0, seq: 0,
aggregateID: Event.ID.create(), aggregateID: Event.ID.create(),
@@ -895,7 +896,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 1, seq: 1,
aggregateID, aggregateID,
@@ -915,7 +916,7 @@ describe("Bus", () => {
const id = Event.ID.create() const id = Event.ID.create()
const replayed = { const replayed = {
id, id,
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -972,7 +973,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -1001,7 +1002,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 1, seq: 1,
aggregateID, aggregateID,
@@ -1012,7 +1013,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 2, seq: 2,
aggregateID, aggregateID,
@@ -1045,7 +1046,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -1058,7 +1059,7 @@ describe("Bus", () => {
.replay( .replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 1, seq: 1,
aggregateID, aggregateID,
@@ -1080,7 +1081,7 @@ describe("Bus", () => {
yield* bus.listen((event) => Effect.sync(() => received.push(event))) yield* bus.listen((event) => Effect.sync(() => received.push(event)))
const replayed = { const replayed = {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -1101,7 +1102,7 @@ describe("Bus", () => {
const aggregateID = Session.ID.create() const aggregateID = Session.ID.create()
const replayed = { const replayed = {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -1126,7 +1127,7 @@ describe("Bus", () => {
const id = Event.ID.create() const id = Event.ID.create()
yield* bus.replay({ yield* bus.replay({
id, id,
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -1136,7 +1137,7 @@ describe("Bus", () => {
const exit = yield* bus const exit = yield* bus
.replay({ .replay({
id, id,
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 1, seq: 1,
aggregateID, aggregateID,
@@ -1159,7 +1160,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
@@ -1170,7 +1171,7 @@ describe("Bus", () => {
yield* bus.replay( yield* bus.replay(
{ {
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 1, seq: 1,
aggregateID, aggregateID,
@@ -1232,7 +1233,7 @@ describe("Bus", () => {
yield* bus.replay({ yield* bus.replay({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Bus.versionedType(DurableMessage.type, 1), type: Bus.versionedType(DurableMessage.type, 1),
seq: 0, seq: 0,
aggregateID, aggregateID,
+1 -1
View File
@@ -1002,7 +1002,7 @@ test("reconciles only changed MCP server config", async () => {
const publishUpdate = () => const publishUpdate = () =>
PubSub.publish(updates, { PubSub.publish(updates, {
id: ID.create(), id: ID.create(),
created: DateTime.makeUnsafe(0), created: 0,
type: Event.Updated.type, type: Event.Updated.type,
data: {}, data: {},
} satisfies Payload<typeof Event.Updated>) } satisfies Payload<typeof Event.Updated>)
+180
View File
@@ -0,0 +1,180 @@
import { describe, expect } from "bun:test"
import { Message } from "@opencode-ai/ai"
import { DateTime, Effect, Stream } from "effect"
import type { SessionContext } from "@opencode-ai/plugin/effect/session"
import { Agent } from "@opencode-ai/core/agent"
import { Event } from "@opencode-ai/schema/event"
import { Model } from "@opencode-ai/core/model"
import { PlanPlugin } from "@opencode-ai/core/plugin/plan"
import { Provider } from "@opencode-ai/core/provider"
import { Session } from "@opencode-ai/core/session"
import { SessionEvent } from "@opencode-ai/core/session/event"
import { SessionInbox } from "@opencode-ai/core/session/inbox"
import { SessionMessage } from "@opencode-ai/core/session/message"
import { it } from "../lib/effect"
import { host } from "./host"
const sessionID = Session.ID.make("ses_plan_test")
const plan = Agent.ID.make("plan")
const build = Agent.ID.make("build")
const agentSelected = (agent: Agent.ID, previous: Agent.ID): SessionEvent.AgentSelected => ({
id: Event.ID.create(),
created: 0,
durable: { aggregateID: sessionID, seq: Event.Seq.make(0), version: Event.Version.make(1) },
type: "session.agent.selected",
data: { sessionID, agent, previous },
})
/** Runs the plan plugin against stubbed domains, capturing persisted reminders and the context hook. */
const run = Effect.fnUntraced(function* (events: ReadonlyArray<SessionEvent.AgentSelected> = []) {
const persisted = new Array<string>()
let contextHook: ((input: SessionContext) => Effect.Effect<void>) | undefined
yield* PlanPlugin.Plugin.effect(
host({
agent: {
get: () => Effect.die("unused agent.get"),
list: () => Effect.die("unused agent.list"),
reload: () => Effect.die("unused agent.reload"),
transform: () => Effect.succeed({ dispose: Effect.void }),
},
tool: {
transform: () => Effect.die("unused tool.transform"),
hook: () => Effect.succeed({ dispose: Effect.void }),
},
event: {
subscribe: () => Stream.fromIterable(events),
},
session: {
hook: (name, callback) => {
if (name === "context") contextHook = callback as (input: SessionContext) => Effect.Effect<void>
return Effect.succeed({ dispose: Effect.void })
},
synthetic: (input) => {
persisted.push(input.text)
return Effect.succeed(
SessionInbox.Synthetic.make({
id: SessionMessage.ID.make("msg_plan_test"),
sessionID,
timeCreated: DateTime.makeUnsafe(0),
type: "synthetic",
payload: { text: input.text },
delivery: "steer",
}),
)
},
},
}),
)
if (!contextHook) return yield* Effect.die("plan plugin did not register a context hook")
return { persisted, contextHook }
})
const request = (agent: Agent.ID, messages: Array<Message>): SessionContext => ({
sessionID,
agent,
model: { id: Model.ID.make("test-model"), providerID: Provider.ID.make("test") },
system: [],
messages,
tools: {},
})
const settle = (persisted: ReadonlyArray<string>, expected: number, remaining = 1000): Effect.Effect<void, Error> =>
Effect.gen(function* () {
if (persisted.length >= expected) return
if (remaining === 0) {
return yield* Effect.fail(new Error(`Timed out waiting for ${expected} reminders, saw ${persisted.length}`))
}
yield* Effect.promise(() => Bun.sleep(1))
yield* settle(persisted, expected, remaining - 1)
})
/** The exact reminder texts, derived from plugin behavior rather than duplicated here. */
const reminders = Effect.gen(function* () {
const planRun = yield* run()
yield* planRun.contextHook(request(plan, []))
const buildRun = yield* run()
yield* buildRun.contextHook(request(build, [Message.user(planRun.persisted[0]!)]))
return { enter: planRun.persisted[0]!, leave: buildRun.persisted[0]! }
})
describe("plan plugin reminders", () => {
it.effect("injects enter and leave reminders on agent switches", () =>
Effect.gen(function* () {
const { persisted } = yield* run([agentSelected(plan, build), agentSelected(build, plan)])
yield* settle(persisted, 2)
expect(persisted[0]).toContain("You are in Plan mode")
expect(persisted[1]).toContain("NO LONGER in Plan mode")
}),
)
it.effect("reconciles a missing enter reminder into the request and persists it", () =>
Effect.gen(function* () {
const { persisted, contextHook } = yield* run()
const messages = [Message.user("what agent are you?")]
yield* contextHook(request(plan, messages))
expect(messages).toHaveLength(2)
// Inserted before the user's prompt, matching where agent-switch reminders land.
const first = messages[0]?.content[0]
expect(first?.type === "text" && first.text).toContain("You are in Plan mode")
expect(persisted).toHaveLength(1)
}),
)
it.effect("does nothing when the transcript already has a live enter reminder", () =>
Effect.gen(function* () {
const { enter } = yield* reminders
const { persisted, contextHook } = yield* run()
const messages = [Message.user(enter), Message.user("hello")]
yield* contextHook(request(plan, messages))
expect(messages).toHaveLength(2)
expect(persisted).toHaveLength(0)
}),
)
it.effect("reconciles a stale enter reminder with a leave reminder", () =>
Effect.gen(function* () {
const { enter } = yield* reminders
const { persisted, contextHook } = yield* run()
const messages = [Message.user(enter), Message.user("ok implement it")]
yield* contextHook(request(build, messages))
expect(messages).toHaveLength(3)
const middle = messages[1]?.content[0]
expect(middle?.type === "text" && middle.text).toContain("NO LONGER in Plan mode")
expect(persisted).toHaveLength(1)
}),
)
it.effect("does nothing for non-plan sessions without plan history", () =>
Effect.gen(function* () {
const { persisted, contextHook } = yield* run()
const messages = [Message.user("hello")]
yield* contextHook(request(build, messages))
expect(messages).toHaveLength(1)
expect(persisted).toHaveLength(0)
}),
)
it.effect("does nothing when a leave reminder already follows the enter reminder", () =>
Effect.gen(function* () {
const { enter, leave } = yield* reminders
const { persisted, contextHook } = yield* run()
const messages = [Message.user(enter), Message.user(leave), Message.user("continue")]
yield* contextHook(request(build, messages))
expect(messages).toHaveLength(3)
expect(persisted).toHaveLength(0)
}),
)
it.effect("treats reminder text quoted inside a larger message as not live", () =>
Effect.gen(function* () {
const { enter } = yield* reminders
const { persisted, contextHook } = yield* run()
// Mirrors a compaction checkpoint quoting the reminder inside <recent-context>.
const messages = [Message.user(`<conversation-checkpoint>\n${enter}\n</conversation-checkpoint>`)]
yield* contextHook(request(plan, messages))
expect(messages).toHaveLength(2)
expect(persisted).toHaveLength(1)
}),
)
})
@@ -1,4 +1,6 @@
import { Money } from "@opencode-ai/schema/money" import { Money } from "@opencode-ai/schema/money"
import { Agent } from "@opencode-ai/schema/agent"
import { Session } from "@opencode-ai/schema/session"
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { Effect } from "effect" import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "@opencode-ai/core/catalog"
@@ -27,9 +29,14 @@ function required<T>(value: T | undefined): T {
return value return value
} }
const httpMiddlewareCount = Effect.fn(function* () { const http = Effect.fn(function* (providerID: Provider.ID, url: string) {
const hooks = yield* PluginHooks.Service const event = yield* (yield* PluginHooks.Service).trigger("session", "http.request", {
return Number(yield* hooks.has("session", "http.request")) + Number(yield* hooks.has("session", "http.response")) sessionID: Session.ID.make("ses_test"),
agent: Agent.ID.make("build"),
model: Model.Ref.make({ providerID, id: Model.ID.make("gpt-5.5") }),
request: new Request(url, { method: "POST", body: "{}" }),
})
return { url: event.request.url, headers: Object.fromEntries(event.request.headers.entries()) }
}) })
describe("OpenAIPlugin", () => { describe("OpenAIPlugin", () => {
@@ -103,13 +110,22 @@ describe("OpenAIPlugin", () => {
}) })
yield* addPlugin() yield* addPlugin()
const request = yield* http(Provider.ID.openai, "https://api.openai.com/v1/responses")
const custom = yield* http(Provider.ID.make("custom-openai"), "https://custom.example/v1/responses")
const proxy = yield* http(Provider.ID.openai, "https://proxy.example/v1/responses?region=us")
const provider = required(yield* catalog.provider.get(Provider.ID.openai)) const provider = required(yield* catalog.provider.get(Provider.ID.openai))
expect(provider.package).toBe("@opencode-ai/ai/providers/openai") expect(provider.package).toBe("@opencode-ai/ai/providers/openai")
expect(provider.settings).toMatchObject({ baseURL: "https://chatgpt.com/backend-api/codex" }) expect(provider.settings).toMatchObject({ baseURL: "https://chatgpt.com/backend-api/codex" })
expect(provider.headers).toMatchObject({ originator: "opencode", "chatgpt-account-id": "acct_123" }) expect(provider.headers).toMatchObject({ originator: "opencode", "chatgpt-account-id": "acct_123" })
expect(yield* httpMiddlewareCount()).toBe(0) expect(request.url).toBe("https://chatgpt.com/backend-api/codex/responses")
expect(request.headers).toMatchObject({ originator: "opencode", "session-id": "ses_test" })
expect(custom.headers).not.toHaveProperty("originator")
expect(proxy.url).toBe("https://proxy.example/v1/responses?region=us")
expect(proxy.headers).toMatchObject({ originator: "opencode", "session-id": "ses_test" })
const eligible = required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("gpt-5.5"))) const eligible = required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("gpt-5.5")))
expect(eligible.package).toBe("@opencode-ai/ai/providers/openai") expect(eligible.package).toBe("@opencode-ai/ai/providers/openai")
expect(eligible.headers).toMatchObject({ originator: "opencode", "chatgpt-account-id": "acct_123" })
expect(eligible.cost).toEqual([]) expect(eligible.cost).toEqual([])
expect(eligible.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 }) expect(eligible.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 })
expect(eligible.enabled).toBe(true) expect(eligible.enabled).toBe(true)
@@ -151,13 +167,15 @@ describe("OpenAIPlugin", () => {
}) })
yield* addPlugin() yield* addPlugin()
const request = yield* http(Provider.ID.openai, "https://api.openai.com/v1/responses")
const provider = required(yield* catalog.provider.get(Provider.ID.openai)) const provider = required(yield* catalog.provider.get(Provider.ID.openai))
const model = required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("gpt-5.5"))) const model = required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("gpt-5.5")))
expect(model.package).toBe("@opencode-ai/ai/providers/openai") expect(model.package).toBe("@opencode-ai/ai/providers/openai")
expect(model.enabled).toBe(true) expect(model.enabled).toBe(true)
expect(model.limit).toEqual({ context: 1_050_000, input: 922_000, output: 128_000 }) expect(model.limit).toEqual({ context: 1_050_000, input: 922_000, output: 128_000 })
expect(request.headers).not.toHaveProperty("originator")
expect(provider.headers).not.toHaveProperty("originator") expect(provider.headers).not.toHaveProperty("originator")
expect(yield* httpMiddlewareCount()).toBe(0)
expect(required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("gpt-4.1"))).enabled).toBe(true) expect(required(yield* catalog.model.get(Provider.ID.openai, Model.ID.make("gpt-4.1"))).enabled).toBe(true)
}), }),
) )
+1 -1
View File
@@ -599,7 +599,7 @@ describe("Session.create", () => {
.all() .all()
.pipe(Effect.orDie)).map((event) => ({ .pipe(Effect.orDie)).map((event) => ({
id: event.id, id: event.id,
created: DateTime.makeUnsafe(event.created), created: event.created,
aggregateID: event.aggregate_id, aggregateID: event.aggregate_id,
seq: event.seq, seq: event.seq,
type: event.type, type: event.type,
+1 -1
View File
@@ -760,7 +760,7 @@ describe("Session.prompt", () => {
yield* Effect.forEach( yield* Effect.forEach(
recorded.map((event) => ({ recorded.map((event) => ({
id: event.id, id: event.id,
created: DateTime.makeUnsafe(event.created), created: event.created,
aggregateID: event.aggregate_id, aggregateID: event.aggregate_id,
seq: event.seq, seq: event.seq,
type: event.type, type: event.type,
@@ -13,6 +13,8 @@ import { Provider } from "@opencode-ai/core/provider"
import { RelativePath } from "@opencode-ai/core/schema" import { RelativePath } from "@opencode-ai/core/schema"
import { Snapshot } from "@opencode-ai/core/snapshot" import { Snapshot } from "@opencode-ai/core/snapshot"
import { createLLMEventPublisher } from "@opencode-ai/core/session/runner/publish-llm-event" import { createLLMEventPublisher } from "@opencode-ai/core/session/runner/publish-llm-event"
import { it } from "./lib/effect"
import { TestClock } from "effect/testing"
const sessionID = Session.ID.make("ses_tool_event_test") const sessionID = Session.ID.make("ses_tool_event_test")
const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB" const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
@@ -201,6 +203,82 @@ test("reasoning state from start, empty delta, and end is merged", async () => {
}) })
}) })
it.effect("batches text deltas and flushes pending text before the terminal event", () =>
Effect.gen(function* () {
const { published, publisher } = capture()
yield* Effect.forEach(
[
LLMEvent.textStart({ id: "text" }),
LLMEvent.textDelta({ id: "text", text: "one" }),
LLMEvent.textDelta({ id: "text", text: " two" }),
LLMEvent.textDelta({ id: "text", text: " three" }),
],
publisher.publish,
{ discard: true },
)
expect(published.filter((event) => event.type === "session.text.delta")).toHaveLength(0)
yield* TestClock.adjust("99 millis")
expect(published.filter((event) => event.type === "session.text.delta")).toHaveLength(0)
yield* TestClock.adjust("1 millis")
yield* publisher.publish(LLMEvent.textDelta({ id: "text", text: " four" }))
expect(published.filter((event) => event.type === "session.text.delta").map((event) => event.data)).toMatchObject([
{ delta: "one two three four" },
])
yield* publisher.publish(LLMEvent.textDelta({ id: "text", text: " five" }))
yield* publisher.publish(LLMEvent.textEnd({ id: "text" }))
expect(published.slice(-2).map((event) => event.type)).toEqual(["session.text.delta", "session.text.ended.1"])
expect(published.at(-2)?.data).toMatchObject({ delta: " five" })
}),
)
it.effect("batches reasoning deltas and flushes pending reasoning before the terminal event", () =>
Effect.gen(function* () {
const { published, publisher } = capture()
yield* Effect.forEach(
[
LLMEvent.reasoningStart({ id: "reasoning" }),
LLMEvent.reasoningDelta({ id: "reasoning", text: "one" }),
LLMEvent.reasoningDelta({ id: "reasoning", text: " two" }),
LLMEvent.reasoningDelta({ id: "reasoning", text: " three" }),
LLMEvent.reasoningEnd({ id: "reasoning" }),
],
publisher.publish,
{ discard: true },
)
expect(
published.filter((event) => event.type === "session.reasoning.delta").map((event) => event.data),
).toMatchObject([{ delta: "one two three" }])
expect(published.slice(-2).map((event) => event.type)).toEqual([
"session.reasoning.delta",
"session.reasoning.ended.1",
])
}),
)
test("tool input deltas are accumulated without being published", async () => {
const { published, publisher } = capture()
await Effect.runPromise(
Effect.forEach(
[
LLMEvent.toolInputStart({ id: "call", name: "read" }),
LLMEvent.toolInputDelta({ id: "call", name: "read", text: '{"path":' }),
LLMEvent.toolInputDelta({ id: "call", name: "read", text: '"file.txt"}' }),
LLMEvent.toolInputEnd({ id: "call", name: "read" }),
],
publisher.publish,
{ discard: true },
),
)
expect(published.some((event) => event.type === "session.tool.input.delta")).toBe(false)
expect(published.find((event) => event.type === "session.tool.input.ended.1")?.data).toMatchObject({
text: '{"path":"file.txt"}',
})
})
test("provider-executed tool metadata is flattened using the route key", async () => { test("provider-executed tool metadata is flattened using the route key", async () => {
const { published, publisher } = capture("openai") const { published, publisher } = capture("openai")
await Effect.runPromise( await Effect.runPromise(
+34 -15
View File
@@ -75,7 +75,7 @@ import { SessionSystemPrompt } from "@opencode-ai/core/session/system-prompt"
import { ID } from "@opencode-ai/core/model" import { ID } from "@opencode-ai/core/model"
import { Location } from "@opencode-ai/core/location" import { Location } from "@opencode-ai/core/location"
import { Provider } from "@opencode-ai/core/provider" import { Provider } from "@opencode-ai/core/provider"
import { Cause, DateTime, Deferred, Effect, Exit, Fiber, Layer, Schema, Scope, Stream } from "effect" import { Cause, Deferred, Effect, Exit, Fiber, Layer, Schema, Scope, Stream } from "effect"
import { TestClock } from "effect/testing" import { TestClock } from "effect/testing"
import { HttpClientRequest, HttpClientResponse } from "effect/unstable/http" import { HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { asc, desc, eq } from "drizzle-orm" import { asc, desc, eq } from "drizzle-orm"
@@ -672,7 +672,7 @@ const replaySessionProjection = (id: Session.ID) =>
yield* Effect.forEach( yield* Effect.forEach(
recorded.map((event) => ({ recorded.map((event) => ({
id: event.id, id: event.id,
created: DateTime.makeUnsafe(event.created), created: event.created,
aggregateID: event.aggregate_id, aggregateID: event.aggregate_id,
seq: event.seq, seq: event.seq,
type: event.type, type: event.type,
@@ -686,7 +686,7 @@ const replaySessionProjection = (id: Session.ID) =>
type FragmentKind = "text" | "reasoning" | "tool input" type FragmentKind = "text" | "reasoning" | "tool input"
type FragmentFixture = { type FragmentFixture = {
readonly delta: Event.Definition readonly delta?: Event.Definition
readonly completeEvents: LLMEvent[] readonly completeEvents: LLMEvent[]
readonly partialEvents: LLMEvent[] readonly partialEvents: LLMEvent[]
readonly expectedAssistant: unknown readonly expectedAssistant: unknown
@@ -748,7 +748,6 @@ const fragmentFixture = (kind: FragmentKind, id: string, chunks: readonly string
] ]
const expectedContent = { type: "tool", id, state: { status: "streaming", input: text } } const expectedContent = { type: "tool", id, state: { status: "streaming", input: text } }
return { return {
delta: SessionEvent.Tool.Input.Delta,
partialEvents, partialEvents,
completeEvents: [...partialEvents, LLMEvent.toolInputEnd({ id, name: "echo" })], completeEvents: [...partialEvents, LLMEvent.toolInputEnd({ id, name: "echo" })],
expectedAssistant: { type: "assistant", content: [expectedContent] }, expectedAssistant: { type: "assistant", content: [expectedContent] },
@@ -767,20 +766,37 @@ const verifyEphemeralDeltas = (kind: FragmentKind) =>
const expectedContext = [{ type: "user", text: prompt }, fixture.expectedAssistant] const expectedContext = [{ type: "user", text: prompt }, fixture.expectedAssistant]
yield* admit(session, prompt) yield* admit(session, prompt)
const bus = yield* Bus.Service const bus = yield* Bus.Service
const live = yield* bus.subscribe(fixture.delta).pipe(Stream.take(32), Stream.runCollect, Effect.forkScoped) const live = fixture.delta
? yield* bus.subscribe(fixture.delta).pipe(Stream.take(1), Stream.runCollect, Effect.forkScoped)
: undefined
yield* Effect.yieldNow yield* Effect.yieldNow
yield* TestLLM.push(fixture.completeEvents) yield* TestLLM.push(fixture.completeEvents)
yield* session.resume(sessionID) yield* session.resume(sessionID)
const { db } = yield* Database.Service const { db } = yield* Database.Service
const deltas = yield* db const deltas = fixture.delta
.select({ type: EventTable.type }) ? yield* db
.from(EventTable) .select({ type: EventTable.type })
.where(eq(EventTable.type, Bus.versionedType(fixture.delta.type, 1))) .from(EventTable)
.all() .where(eq(EventTable.type, Bus.versionedType(fixture.delta.type, 1)))
.pipe(Effect.orDie) .all()
expect(Array.from(yield* Fiber.join(live))).toHaveLength(32) .pipe(Effect.orDie)
: []
if (live) {
const streamed = Array.from(yield* Fiber.join(live))
expect(streamed).toHaveLength(1)
expect(
streamed
.map((event) => {
if (!event.data || typeof event.data !== "object" || !("delta" in event.data))
throw new Error("Expected delta event")
if (typeof event.data.delta !== "string") throw new Error("Expected string delta")
return event.data.delta
})
.join(""),
).toBe(chunks.join(""))
}
expect(deltas).toHaveLength(0) expect(deltas).toHaveLength(0)
expect(yield* session.context(sessionID)).toMatchObject(expectedContext) expect(yield* session.context(sessionID)).toMatchObject(expectedContext)
@@ -1463,7 +1479,7 @@ describe("SessionRunnerLLM", () => {
yield* Effect.forEach( yield* Effect.forEach(
recorded.map((event) => ({ recorded.map((event) => ({
id: event.id, id: event.id,
created: DateTime.makeUnsafe(event.created), created: event.created,
aggregateID: event.aggregate_id, aggregateID: event.aggregate_id,
seq: event.seq, seq: event.seq,
type: event.type, type: event.type,
@@ -5219,8 +5235,11 @@ describe("SessionRunnerLLM", () => {
) )
for (const kind of fragmentKinds) { for (const kind of fragmentKinds) {
it.effect(`broadcasts provider ${kind} deltas without storing projection rewrites`, () => it.effect(
verifyEphemeralDeltas(kind), kind === "tool input"
? "does not broadcast provider tool input deltas"
: `batches provider ${kind} deltas without storing projection rewrites`,
() => verifyEphemeralDeltas(kind),
) )
it.effect(`durably closes partial ${kind} when the provider stream fails`, () => verifyPartialFlushOnFailure(kind)) it.effect(`durably closes partial ${kind} when the provider stream fails`, () => verifyPartialFlushOnFailure(kind))
+1 -1
View File
@@ -298,7 +298,7 @@ describe("SubagentTool", () => {
}) })
const child = yield* sessions.get(outputSessionID(settled.metadata)) const child = yield* sessions.get(outputSessionID(settled.metadata))
expect(settled.metadata).toEqual({ sessionID: child.id, status: "completed" }) expect(settled.metadata).toEqual({ sessionID: child.id, status: "completed" })
expect(progress[0]?.metadata).toEqual({ sessionID: child.id, status: "running" }) expect(progress[0]).toEqual({ sessionID: child.id, status: "running" })
expect(child).toMatchObject({ expect(child).toMatchObject({
parentID: parent.id, parentID: parent.id,
location: parent.location, location: parent.location,
+3 -2
View File
@@ -1,11 +1,12 @@
#!/usr/bin/env bun #!/usr/bin/env bun
import { $ } from "bun" import { $ } from "bun"
import { downloadCliToResources, resolveChannel } from "./utils" import { copyBuiltCliToResources, downloadCliToResources, resolveChannel } from "./utils"
const channel = resolveChannel() const channel = resolveChannel()
await $`bun ./scripts/copy-icons.ts ${channel}` await $`bun ./scripts/copy-icons.ts ${channel}`
await $`bun ./scripts/copy-metainfo.ts ${channel}` await $`bun ./scripts/copy-metainfo.ts ${channel}`
if (channel === "dev") await downloadCliToResources() if (channel === "dev") await downloadCliToResources()
if (channel === "beta") await downloadCliToResources("next") if (channel === "beta" && Bun.env.OPENCODE_CLI_DIST) await copyBuiltCliToResources(Bun.env.OPENCODE_CLI_DIST)
if (channel === "beta" && !Bun.env.OPENCODE_CLI_DIST) await downloadCliToResources("beta")
+13 -3
View File
@@ -3,7 +3,7 @@ import { chmod, copyFile, mkdtemp, rm } from "node:fs/promises"
import { tmpdir } from "node:os" import { tmpdir } from "node:os"
import { join } from "node:path" import { join } from "node:path"
const CLI_VERSION = "0.0.0-next-16365" const CLI_VERSION = "dev"
export type Channel = "dev" | "beta" | "prod" export type Channel = "dev" | "beta" | "prod"
@@ -74,18 +74,28 @@ export async function downloadCliToResources(version = CLI_VERSION, dest = windo
const directory = await mkdtemp(join(tmpdir(), "opencode-cli-")) const directory = await mkdtemp(join(tmpdir(), "opencode-cli-"))
try { try {
await $`bun install --no-save --cwd ${directory} ${`${cli.package}@${version}`} ${`--os=${cli.os}`} ${`--cpu=${cli.cpu}`}` await $`bun install --no-save --cwd ${directory} ${`${cli.package}@${version}`} ${`--os=${cli.os}`} ${`--cpu=${cli.cpu}`}`
await copyFile( await copyCliToResources(
join(directory, "node_modules", cli.package, "bin", cli.os === "win32" ? "opencode2.exe" : "opencode2"), join(directory, "node_modules", cli.package, "bin", cli.os === "win32" ? "opencode2.exe" : "opencode2"),
dest, dest,
) )
} finally { } finally {
await rm(directory, { recursive: true, force: true }) await rm(directory, { recursive: true, force: true })
} }
await prepareCli(dest)
console.log(`Copied ${cli.package}@${version} to ${dest}`) console.log(`Copied ${cli.package}@${version} to ${dest}`)
} }
export async function copyBuiltCliToResources(root: string, dest = windowsify("resources/opencode-cli")) {
const cli = getCurrentCli()
const directory = cli.package.replace("@opencode-ai/", "")
await copyCliToResources(join(root, directory, "bin", cli.os === "win32" ? "opencode2.exe" : "opencode2"), dest)
}
async function copyCliToResources(source: string, dest: string) {
await copyFile(source, dest)
await prepareCli(dest)
}
async function prepareCli(dest: string) { async function prepareCli(dest: string) {
if (process.platform !== "win32") await chmod(dest, 0o755) if (process.platform !== "win32") await chmod(dest, 0o755)
if (process.platform === "win32" && process.env.GITHUB_ACTIONS === "true") { if (process.platform === "win32" && process.env.GITHUB_ACTIONS === "true") {
@@ -27,7 +27,7 @@ test("installs and verifies the bundled CLI version", async () => {
await controller.installOpencode("Debian") await controller.installOpencode("Debian")
expect(installs).toEqual([["Debian", "0.0.0-next-16365"]]) expect(installs).toEqual([["Debian", "0.0.0-dev-16365"]])
expect(controller.getState().opencodeChecks.Debian?.matchesDesktop).toBe(true) expect(controller.getState().opencodeChecks.Debian?.matchesDesktop).toBe(true)
}) })
@@ -37,12 +37,12 @@ test("rejects a WSL CLI version that differs from the bundled version", async ()
testControllerOptions({ testControllerOptions({
installCli: async () => undefined, installCli: async () => undefined,
resolveCli: async () => "/home/me/.opencode/bin/opencode2", resolveCli: async () => "/home/me/.opencode/bin/opencode2",
readCliVersion: async () => "0.0.0-next-older", readCliVersion: async () => "0.0.0-dev-older",
}), }),
) )
await expect(controller.installOpencode("Debian")).rejects.toThrow( await expect(controller.installOpencode("Debian")).rejects.toThrow(
"OpenCode update finished but Debian still reports 0.0.0-next-older; expected 0.0.0-next-16365", "OpenCode update finished but Debian still reports 0.0.0-dev-older; expected 0.0.0-dev-16365",
) )
}) })
@@ -147,7 +147,7 @@ async function waitFor(check: () => boolean) {
function testControllerOptions(overrides: Partial<ControllerOptions> = {}): ControllerOptions { function testControllerOptions(overrides: Partial<ControllerOptions> = {}): ControllerOptions {
return { return {
cli: { version: "0.0.0-next-16365" }, cli: { version: "0.0.0-dev-16365" },
spawnSidecar: async () => ({ spawnSidecar: async () => ({
stop: async () => undefined, stop: async () => undefined,
onExit: () => undefined, onExit: () => undefined,
@@ -159,7 +159,7 @@ function testControllerOptions(overrides: Partial<ControllerOptions> = {}): Cont
writeServers: (servers: WslServerConfig[]) => { writeServers: (servers: WslServerConfig[]) => {
persistedServers = servers persistedServers = servers
}, },
readCliVersion: async () => "0.0.0-next-16365", readCliVersion: async () => "0.0.0-dev-16365",
resolveCli: async () => "/home/me/.opencode/bin/opencode2", resolveCli: async () => "/home/me/.opencode/bin/opencode2",
...overrides, ...overrides,
} }
-2
View File
@@ -341,8 +341,6 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
id: SessionMessage.ID.pipe(Schema.optional), id: SessionMessage.ID.pipe(Schema.optional),
command: Schema.String, command: Schema.String,
arguments: Schema.String.pipe(Schema.optional), arguments: Schema.String.pipe(Schema.optional),
agent: Agent.ID.pipe(Schema.optional),
model: Model.Ref.pipe(Schema.optional),
files: PromptInput.Prompt.fields.files, files: PromptInput.Prompt.fields.files,
agents: PromptInput.Prompt.fields.agents, agents: PromptInput.Prompt.fields.agents,
skills: PromptInput.Prompt.fields.skills, skills: PromptInput.Prompt.fields.skills,
+25 -1
View File
@@ -1,5 +1,25 @@
import { expect, test } from "bun:test" import { expect, test } from "bun:test"
import { isOpenCodeEvent } from "../src/groups/event.js" import { isOpenCodeEvent, type OpenCodeEvent, type OpenCodeEventEncoded } from "../src/groups/event.js"
type JsonShape<Value> = Value extends string | number | boolean | null
? Value
: Value extends undefined
? never
: Value extends ReadonlyArray<infer Item>
? ReadonlyArray<JsonShape<Item>>
: Value extends object
? {
readonly [Key in keyof Value as undefined extends Value[Key] ? never : Key]: JsonShape<Value[Key]>
} & {
readonly [Key in keyof Value as undefined extends Value[Key] ? Key : never]?: JsonShape<
Exclude<Value[Key], undefined>
>
}
: Value
// JSON.stringify omits undefined object properties, so normalize them before
// requiring every runtime event shape to fit its encoded wire contract.
const wireReady: [JsonShape<OpenCodeEvent>] extends [JsonShape<OpenCodeEventEncoded>] ? true : false = true
test("classifies public events by type", () => { test("classifies public events by type", () => {
expect(isOpenCodeEvent({ type: "server.connected" })).toBe(true) expect(isOpenCodeEvent({ type: "server.connected" })).toBe(true)
@@ -7,3 +27,7 @@ test("classifies public events by type", () => {
expect(isOpenCodeEvent({ type: "mcp.resources.changed" })).toBe(true) expect(isOpenCodeEvent({ type: "mcp.resources.changed" })).toBe(true)
expect(isOpenCodeEvent({ type: "mcp.tools.changed" })).toBe(false) expect(isOpenCodeEvent({ type: "mcp.tools.changed" })).toBe(false)
}) })
test("keeps public event runtime values within the encoded contract", () => {
expect(wireReady).toBe(true)
})
+4 -4
View File
@@ -4,7 +4,7 @@ import { Schema, SchemaTransformation } from "effect"
import { optional } from "./schema.js" import { optional } from "./schema.js"
import { ascending } from "./identifier.js" import { ascending } from "./identifier.js"
import { Location } from "./location.js" import { Location } from "./location.js"
import { DateTimeUtcFromMillis, statics } from "./schema.js" import { statics } from "./schema.js"
export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe( export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe(
Schema.brand("Event.ID"), Schema.brand("Event.ID"),
@@ -60,7 +60,7 @@ export type Data<D extends Definition> = Schema.Schema.Type<D["data"]>
type PayloadBase<D extends Definition> = { type PayloadBase<D extends Definition> = {
readonly id: ID readonly id: ID
readonly type: D["type"] readonly type: D["type"]
readonly created: typeof DateTimeUtcFromMillis.Type readonly created: number
readonly data: Data<D> readonly data: Data<D>
readonly location?: Location.Ref readonly location?: Location.Ref
readonly metadata?: Record<string, unknown> readonly metadata?: Record<string, unknown>
@@ -100,7 +100,7 @@ export function durable<
}) })
return Schema.Struct({ return Schema.Struct({
id: ID, id: ID,
created: DateTimeUtcFromMillis, created: Schema.Finite,
metadata: optional(Schema.Record(Schema.String, Schema.Unknown)), metadata: optional(Schema.Record(Schema.String, Schema.Unknown)),
type: Schema.Literal(input.type), type: Schema.Literal(input.type),
durable, durable,
@@ -125,7 +125,7 @@ export function ephemeral<
const data = Schema.Struct(input.schema) const data = Schema.Struct(input.schema)
return Schema.Struct({ return Schema.Struct({
id: ID, id: ID,
created: DateTimeUtcFromMillis, created: Schema.Finite,
metadata: optional(Schema.Record(Schema.String, Schema.Unknown)), metadata: optional(Schema.Record(Schema.String, Schema.Unknown)),
type: Schema.Literal(input.type), type: Schema.Literal(input.type),
location: optional(Location.Ref), location: optional(Location.Ref),
+1 -1
View File
@@ -34,7 +34,7 @@ const IS_PREVIEW = CHANNEL !== "latest"
const VERSION = await (async () => { const VERSION = await (async () => {
if (env.OPENCODE_VERSION) return env.OPENCODE_VERSION if (env.OPENCODE_VERSION) return env.OPENCODE_VERSION
if (IS_PREVIEW) return `0.0.0-${CHANNEL}-${previewBuildNumber()}` if (IS_PREVIEW) return `0.0.0-${CHANNEL}-${previewBuildNumber()}`
const version = await fetch("https://registry.npmjs.org/opencode-ai/latest") const version = await fetch("https://registry.npmjs.org/@opencode-ai%2fcli/latest")
.then((res) => { .then((res) => {
if (!res.ok) throw new Error(res.statusText) if (!res.ok) throw new Error(res.statusText)
return res.json() return res.json()
+2 -4
View File
@@ -2,7 +2,7 @@ export * as EventFeed from "./event-feed"
import { Bus } from "@opencode-ai/core/bus" import { Bus } from "@opencode-ai/core/bus"
import { Event } from "@opencode-ai/schema/event" import { Event } from "@opencode-ai/schema/event"
import { isOpenCodeEvent, OpenCodeEvent } from "@opencode-ai/protocol/groups/event" import { isOpenCodeEvent, type OpenCodeEvent } from "@opencode-ai/protocol/groups/event"
import { Cause, Context, Effect, Layer, Queue, Schema, Scope, Stream } from "effect" import { Cause, Context, Effect, Layer, Queue, Schema, Scope, Stream } from "effect"
export const SubscriberCapacity = 4_096 export const SubscriberCapacity = 4_096
@@ -26,10 +26,8 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/server/EventFeed") {} export class Service extends Context.Service<Service, Interface>()("@opencode/server/EventFeed") {}
const encode = Schema.encodeUnknownSync(OpenCodeEvent)
export function frame(event: OpenCodeEvent) { export function frame(event: OpenCodeEvent) {
return `data: ${JSON.stringify(encode(event))}\n\n` return `data: ${JSON.stringify(event)}\n\n`
} }
export const make = Effect.fn("EventFeed.make")(function* ( export const make = Effect.fn("EventFeed.make")(function* (
-2
View File
@@ -356,8 +356,6 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
id: ctx.payload.id, id: ctx.payload.id,
command: ctx.payload.command, command: ctx.payload.command,
arguments: ctx.payload.arguments, arguments: ctx.payload.arguments,
agent: ctx.payload.agent,
model: ctx.payload.model,
files: ctx.payload.files, files: ctx.payload.files,
agents: ctx.payload.agents, agents: ctx.payload.agents,
skills: ctx.payload.skills, skills: ctx.payload.skills,
+4 -7
View File
@@ -2,8 +2,7 @@ import { describe, expect, test } from "bun:test"
import { Agent } from "@opencode-ai/core/agent" import { Agent } from "@opencode-ai/core/agent"
import { Bus } from "@opencode-ai/core/bus" import { Bus } from "@opencode-ai/core/bus"
import { Event } from "@opencode-ai/schema/event" import { Event } from "@opencode-ai/schema/event"
import { OpenCodeEvent } from "@opencode-ai/protocol/groups/event" import { Deferred, Effect, Exit, Fiber, Option, Schema, Stream } from "effect"
import { DateTime, Deferred, Effect, Exit, Fiber, Option, Schema, Stream } from "effect"
import { it } from "../../core/test/lib/effect" import { it } from "../../core/test/lib/effect"
import { EventFeed } from "../src/event-feed" import { EventFeed } from "../src/event-feed"
@@ -11,14 +10,14 @@ const Internal = Bus.ephemeral({ type: "test.internal", schema: { value: Schema.
const event = (id: string): Event.Payload<typeof Agent.Event.Updated> => ({ const event = (id: string): Event.Payload<typeof Agent.Event.Updated> => ({
id: Event.ID.make(`evt_${id}`), id: Event.ID.make(`evt_${id}`),
created: DateTime.makeUnsafe(Date.now()), created: Date.now(),
type: Agent.Event.Updated.type, type: Agent.Event.Updated.type,
data: {}, data: {},
}) })
const internal = (value: string): Event.Payload<typeof Internal> => ({ const internal = (value: string): Event.Payload<typeof Internal> => ({
id: Event.ID.create(), id: Event.ID.create(),
created: DateTime.makeUnsafe(Date.now()), created: Date.now(),
type: Internal.type, type: Internal.type,
data: { value }, data: { value },
}) })
@@ -40,9 +39,7 @@ function makeSource() {
describe("EventFeed", () => { describe("EventFeed", () => {
test("preserves the public SSE frame encoding", () => { test("preserves the public SSE frame encoding", () => {
const payload = event("wire") const payload = event("wire")
expect(EventFeed.frame(payload)).toBe( expect(EventFeed.frame(payload)).toBe(`data: ${JSON.stringify(payload)}\n\n`)
`data: ${JSON.stringify(Schema.encodeUnknownSync(OpenCodeEvent)(payload))}\n\n`,
)
}) })
it.effect("encodes once and delivers the same frame to every subscriber", () => it.effect("encodes once and delivers the same frame to every subscriber", () =>
+1
View File
@@ -25,6 +25,7 @@
"./v2/prompt-input/types": "./src/v2/components/prompt-input/types.ts" "./v2/prompt-input/types": "./src/v2/components/prompt-input/types.ts"
}, },
"scripts": { "scripts": {
"generate:progress-indicator": "bun script/generate-session-progress-indicator.ts",
"typecheck": "tsgo -b", "typecheck": "tsgo -b",
"test": "bun test src --only-failures" "test": "bun test src --only-failures"
}, },
@@ -0,0 +1,133 @@
import { deflateSync } from "node:zlib"
const size = 16
const frameRate = 90
const duration = 1.2
const frameCount = frameRate * duration
const opacity = [0.2, 0.5, 0.75, 1]
// Each digit selects one dot opacity for one of the eight source key poses.
const poses = [
"0000000000003000031000321",
"0000000000003000320032100",
"0000000000333002100010000",
"3000023000123000000000000",
"1230001300003000000000000",
"0012300230003000000000000",
"0000100022003330000000000",
"0000000000003330002200001",
].map((pose) => Array.from(pose, (value) => opacity[Number(value)]))
const crcTable = Array.from({ length: 256 }, (_, value) =>
Array.from({ length: 8 }).reduce<number>((crc) => ((crc & 1) !== 0 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1), value),
)
await Bun.write(new URL("../src/v2/components/session-progress-indicator-v2-1x.png", import.meta.url), apng(1))
function apng(scale: number) {
const frameSize = size * scale
const header = Buffer.alloc(13)
header.writeUInt32BE(frameSize, 0)
header.writeUInt32BE(frameSize, 4)
header[8] = 8
header[9] = 6
const animation = Buffer.alloc(8)
animation.writeUInt32BE(frameCount, 0)
animation.writeUInt32BE(0, 4)
const chunks = [chunk("IHDR", header), chunk("acTL", animation)]
const sequence = { value: 0 }
Array.from({ length: frameCount }, (_, frame) => {
const control = Buffer.alloc(26)
control.writeUInt32BE(sequence.value++, 0)
control.writeUInt32BE(frameSize, 4)
control.writeUInt32BE(frameSize, 8)
control.writeUInt16BE(frame % 9 === 0 ? 12 : 11, 20)
control.writeUInt16BE(1000, 22)
chunks.push(chunk("fcTL", control))
const data = deflateSync(pixels(scale, frame), { level: 9 })
if (frame === 0) {
chunks.push(chunk("IDAT", data))
return
}
const frameData = Buffer.alloc(data.length + 4)
frameData.writeUInt32BE(sequence.value++, 0)
data.copy(frameData, 4)
chunks.push(chunk("fdAT", frameData))
})
return Buffer.concat([Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), ...chunks, chunk("IEND", Buffer.alloc(0))])
}
function pixels(scale: number, frame: number) {
const frameSize = size * scale
const stride = frameSize * 4 + 1
const pixels = Buffer.alloc(stride * frameSize)
const progress = (frame / frameCount) * poses.length
const current = Math.floor(progress)
const next = (current + 1) % poses.length
const mix = easeOut(progress - current)
Array.from({ length: frameSize }, (_, y) => (pixels[y * stride] = 0))
poses[current].forEach((value, index) => {
draw(
pixels,
stride,
(1 + (index % 5) * 3) * scale,
(1 + Math.floor(index / 5) * 3) * scale,
3 * scale,
value + (poses[next][index] - value) * mix,
)
})
return pixels
}
function draw(pixels: Buffer, stride: number, x: number, y: number, size: number, opacity: number) {
const left = Math.floor(x)
const top = Math.floor(y)
const right = Math.ceil(x + size)
const bottom = Math.ceil(y + size)
Array.from({ length: bottom - top }, (_, row) => top + row).forEach((py) =>
Array.from({ length: right - left }, (_, column) => left + column).forEach((px) => {
const coverageX = Math.max(0, Math.min(px + 1, x + size) - Math.max(px, x))
const coverageY = Math.max(0, Math.min(py + 1, y + size) - Math.max(py, y))
const offset = py * stride + 1 + px * 4
pixels[offset] = 255
pixels[offset + 1] = 255
pixels[offset + 2] = 255
pixels[offset + 3] = Math.round(opacity * coverageX * coverageY * 255)
}),
)
}
function easeOut(progress: number) {
const curve = (value: number, first: number, second: number) => {
const inverse = 1 - value
return 3 * inverse * inverse * value * first + 3 * inverse * value * value * second + value * value * value
}
const parameter = Array.from({ length: 16 }).reduce<[number, number]>(
(range) => {
const middle = (range[0] + range[1]) / 2
return curve(middle, 0, 0.58) < progress ? [middle, range[1]] : [range[0], middle]
},
[0, 1],
)
return curve((parameter[0] + parameter[1]) / 2, 0, 1)
}
function chunk(type: string, data: Buffer) {
const name = Buffer.from(type)
const result = Buffer.alloc(data.length + 12)
result.writeUInt32BE(data.length, 0)
name.copy(result, 4)
data.copy(result, 8)
result.writeUInt32BE(crc32(Buffer.concat([name, data])), data.length + 8)
return result
}
function crc32(data: Buffer) {
return (
(data.reduce<number>((crc, value) => crcTable[(crc ^ value) & 0xff] ^ (crc >>> 8), 0xffffffff) ^ 0xffffffff) >>> 0
)
}
@@ -418,6 +418,10 @@
white-space: pre-wrap; white-space: pre-wrap;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
[data-slot="bash-prompt"] {
user-select: none;
}
} }
[data-component="edit-trigger"], [data-component="edit-trigger"],
@@ -2000,17 +2000,25 @@ ToolRegistry.register({
const title = createMemo(() => agent().name ?? i18n.t("ui.tool.agent.default")) const title = createMemo(() => agent().name ?? i18n.t("ui.tool.agent.default"))
const tone = createMemo(() => agent().color) const tone = createMemo(() => agent().color)
const v2Tone = createMemo(() => agent().v2Color) const v2Tone = createMemo(() => agent().v2Color)
const background = createMemo(() => {
if (props.tool === "task") return props.metadata.background === true
return props.status === "completed" && props.metadata.status === "running"
})
const subtitle = createMemo(() => { const subtitle = createMemo(() => {
const value = const value =
typeof props.input.description === "string" && props.input.description typeof props.input.description === "string" && props.input.description
? props.input.description ? props.input.description
: childSessionId() : childSessionId()
if (!value) return value if (!value) return value
if (props.input.background === true || props.metadata.background === true || props.metadata.status === "running") if (background()) return `${value} (background)`
return `${value} (background)`
return value return value
}) })
const running = createMemo(() => props.status === "pending" || props.status === "running") const running = createMemo(() => {
if (props.status === "pending" || props.status === "running") return true
const id = childSessionId()
if (!id) return false
return (data.store.session_status[id]?.type ?? "idle") !== "idle"
})
const href = createMemo(() => sessionLink(childSessionId(), data.sessionHref)) const href = createMemo(() => sessionLink(childSessionId(), data.sessionHref))
const clickable = createMemo(() => !!(childSessionId() && (data.navigateToSession || href()))) const clickable = createMemo(() => !!(childSessionId() && (data.navigateToSession || href())))
@@ -2103,15 +2111,15 @@ ToolRegistry.register({
const pending = () => const pending = () =>
props.status === "pending" || props.status === "running" || props.metadata.status === "running" props.status === "pending" || props.status === "running" || props.metadata.status === "running"
const sawPending = pending() const sawPending = pending()
const command = () => props.input.command ?? props.metadata.command ?? ""
const text = createMemo(() => { const text = createMemo(() => {
const cmd = props.input.command ?? props.metadata.command ?? ""
const out = stripAnsi(props.output || props.metadata.output || "").replace(/\r\n?/g, "\n") const out = stripAnsi(props.output || props.metadata.output || "").replace(/\r\n?/g, "\n")
return `$ ${cmd}${out ? "\n\n" + out : ""}` return `${command()}${out ? "\n\n" + out : ""}`
}) })
const [copied, setCopied] = createSignal(false) const [copied, setCopied] = createSignal(false)
const handleCopy = async () => { const handleCopy = async () => {
const content = text() const content = command()
if (!content) return if (!content) return
if (await writeClipboard(content)) { if (await writeClipboard(content)) {
setCopied(true) setCopied(true)
@@ -2158,7 +2166,12 @@ ToolRegistry.register({
aria-label={i18n.t("ui.scrollView.ariaLabel")} aria-label={i18n.t("ui.scrollView.ariaLabel")}
> >
<pre data-slot="bash-pre"> <pre data-slot="bash-pre">
<code>{text()}</code> <code>
<span data-slot="bash-prompt" aria-hidden="true">
{"$ "}
</span>
{text()}
</code>
</pre> </pre>
</div> </div>
</div> </div>
@@ -2637,21 +2650,36 @@ ToolRegistry.register({
name: "skill", name: "skill",
render(props) { render(props) {
const i18n = useI18n() const i18n = useI18n()
const title = createMemo(() => skillToolName(props.input, props.metadata) || i18n.t("ui.tool.skill")) const name = createMemo(() => skillToolName(props.input, props.metadata))
const running = createMemo(() => props.status === "pending" || props.status === "running") const running = createMemo(() => props.status === "pending" || props.status === "running")
const titleContent = () => <TextShimmer text={title()} active={running()} />
const trigger = () => ( const trigger = () => (
<div data-slot="basic-tool-tool-info-structured"> <div data-slot="skill-tool-trigger" class="flex min-w-0 items-center gap-1.5">
<div data-slot="basic-tool-tool-info-main"> <Icon name="post-skill" size="small" class="shrink-0 text-v2-icon-icon-muted" />
<span data-slot="basic-tool-tool-title" class="capitalize agent-title"> <span
{titleContent()} data-slot="skill-tool-label"
</span> class="shrink-0 text-[13px] font-[530] leading-5 tracking-[-0.04px] text-v2-text-text-muted"
</div> >
{i18n.t("ui.tool.skill")}
</span>
<Show when={name()}>
{(name) => (
<>
<span data-slot="skill-tool-separator" aria-hidden="true" class="shrink-0 text-v2-text-text-muted">
·
</span>
<TextShimmer
as="bdi"
text={name()}
active={running()}
class="min-w-0 truncate text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-muted"
/>
</>
)}
</Show>
</div> </div>
) )
return <BasicTool icon="brain" status={props.status} trigger={trigger()} hideDetails /> return <BasicTool icon="post-skill" status={props.status} trigger={trigger()} hideDetails />
}, },
}) })
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -1,875 +1,25 @@
[data-component="session-progress-indicator-v2"] { [data-component="session-progress-indicator-v2"] {
--_duration: 1200ms;
display: block; display: block;
flex-shrink: 0; flex-shrink: 0;
color: var(--v2-icon-icon-muted, #808080); color: var(--v2-icon-icon-muted, #808080);
} }
[data-component="session-progress-indicator-v2"] [data-dot] { [data-component="session-progress-indicator-v2"] [data-frame-content] {
image-rendering: pixelated;
image-rendering: crisp-edges;
}
[data-component="session-progress-indicator-v2"] [data-reduced-motion] {
display: none;
fill: currentColor; fill: currentColor;
opacity: 0.2;
animation-duration: var(--_duration);
animation-timing-function: ease-out;
animation-iteration-count: infinite;
animation-fill-mode: both;
}
@keyframes session-progress-indicator-v2-dot-0 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 1;
}
50% {
opacity: 0.5;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="0"] {
animation-name: session-progress-indicator-v2-dot-0;
}
@keyframes session-progress-indicator-v2-dot-5 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.75;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="5"] {
animation-name: session-progress-indicator-v2-dot-5;
}
@keyframes session-progress-indicator-v2-dot-10 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 1;
}
37.5% {
opacity: 0.5;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="10"] {
animation-name: session-progress-indicator-v2-dot-10;
}
@keyframes session-progress-indicator-v2-dot-15 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.75;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="15"] {
animation-name: session-progress-indicator-v2-dot-15;
}
@keyframes session-progress-indicator-v2-dot-20 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 1;
}
25% {
opacity: 0.5;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="20"] {
animation-name: session-progress-indicator-v2-dot-20;
}
@keyframes session-progress-indicator-v2-dot-1 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.75;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="1"] {
animation-name: session-progress-indicator-v2-dot-1;
}
@keyframes session-progress-indicator-v2-dot-6 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 1;
}
50% {
opacity: 0.5;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="6"] {
animation-name: session-progress-indicator-v2-dot-6;
}
@keyframes session-progress-indicator-v2-dot-11 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 1;
}
37.5% {
opacity: 0.75;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="11"] {
animation-name: session-progress-indicator-v2-dot-11;
}
@keyframes session-progress-indicator-v2-dot-16 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 1;
}
25% {
opacity: 0.5;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="16"] {
animation-name: session-progress-indicator-v2-dot-16;
}
@keyframes session-progress-indicator-v2-dot-21 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.75;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="21"] {
animation-name: session-progress-indicator-v2-dot-21;
}
@keyframes session-progress-indicator-v2-dot-2 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 1;
}
62.5% {
opacity: 0.5;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="2"] {
animation-name: session-progress-indicator-v2-dot-2;
}
@keyframes session-progress-indicator-v2-dot-7 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 1;
}
62.5% {
opacity: 0.75;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="7"] {
animation-name: session-progress-indicator-v2-dot-7;
}
@keyframes session-progress-indicator-v2-dot-12 {
0% {
opacity: 1;
}
12.5% {
opacity: 1;
}
25% {
opacity: 1;
}
37.5% {
opacity: 1;
}
50% {
opacity: 1;
}
62.5% {
opacity: 1;
}
75% {
opacity: 1;
}
87.5% {
opacity: 1;
}
100% {
opacity: 1;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="12"] {
animation-name: session-progress-indicator-v2-dot-12;
}
@keyframes session-progress-indicator-v2-dot-17 {
0% {
opacity: 1;
}
12.5% {
opacity: 0.75;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 1;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="17"] {
animation-name: session-progress-indicator-v2-dot-17;
}
@keyframes session-progress-indicator-v2-dot-22 {
0% {
opacity: 1;
}
12.5% {
opacity: 0.5;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 1;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="22"] {
animation-name: session-progress-indicator-v2-dot-22;
}
@keyframes session-progress-indicator-v2-dot-3 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.75;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="3"] {
animation-name: session-progress-indicator-v2-dot-3;
}
@keyframes session-progress-indicator-v2-dot-8 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 1;
}
75% {
opacity: 0.75;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="8"] {
animation-name: session-progress-indicator-v2-dot-8;
}
@keyframes session-progress-indicator-v2-dot-13 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 1;
}
87.5% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="13"] {
animation-name: session-progress-indicator-v2-dot-13;
}
@keyframes session-progress-indicator-v2-dot-18 {
0% {
opacity: 0.5;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.75;
}
100% {
opacity: 0.5;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="18"] {
animation-name: session-progress-indicator-v2-dot-18;
}
@keyframes session-progress-indicator-v2-dot-23 {
0% {
opacity: 0.75;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.75;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="23"] {
animation-name: session-progress-indicator-v2-dot-23;
}
@keyframes session-progress-indicator-v2-dot-4 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 1;
}
75% {
opacity: 0.5;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="4"] {
animation-name: session-progress-indicator-v2-dot-4;
}
@keyframes session-progress-indicator-v2-dot-9 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.75;
}
87.5% {
opacity: 0.2;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="9"] {
animation-name: session-progress-indicator-v2-dot-9;
}
@keyframes session-progress-indicator-v2-dot-14 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 1;
}
87.5% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="14"] {
animation-name: session-progress-indicator-v2-dot-14;
}
@keyframes session-progress-indicator-v2-dot-19 {
0% {
opacity: 0.2;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.75;
}
100% {
opacity: 0.2;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="19"] {
animation-name: session-progress-indicator-v2-dot-19;
}
@keyframes session-progress-indicator-v2-dot-24 {
0% {
opacity: 0.5;
}
12.5% {
opacity: 0.2;
}
25% {
opacity: 0.2;
}
37.5% {
opacity: 0.2;
}
50% {
opacity: 0.2;
}
62.5% {
opacity: 0.2;
}
75% {
opacity: 0.2;
}
87.5% {
opacity: 0.5;
}
100% {
opacity: 0.5;
}
}
[data-component="session-progress-indicator-v2"] [data-dot="24"] {
animation-name: session-progress-indicator-v2-dot-24;
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
[data-component="session-progress-indicator-v2"] [data-dot] { [data-component="session-progress-indicator-v2"] [data-frame-content] {
animation: none; display: none;
} }
[data-component="session-progress-indicator-v2"] [data-dot="12"] { [data-component="session-progress-indicator-v2"] [data-reduced-motion] {
opacity: 1; display: block;
} }
} }
@@ -1,5 +1,6 @@
// @ts-nocheck // @ts-nocheck
import { SessionProgressIndicatorV2 } from "./session-progress-indicator-v2" import { SessionProgressIndicatorV2 } from "./session-progress-indicator-v2"
import { createSignal, onCleanup, onMount } from "solid-js"
const docs = `### Overview const docs = `### Overview
Animated 5×5 dot grid loader for in-progress session state. Animated 5×5 dot grid loader for in-progress session state.
@@ -10,7 +11,7 @@ Derived from Figma \`_sessionProgressIndicator\` with 8-frame rotation.
- Accepts standard SVG props. - Accepts standard SVG props.
### Behavior ### Behavior
- CSS keyframes drive per-dot opacity across 8 frames (1.2s loop). - A shared, pre-rendered alpha mask preserves the smooth opacity changes between 8 key poses (1.2s loop).
- Center dot stays at full opacity throughout the cycle. - Center dot stays at full opacity throughout the cycle.
### Accessibility ### Accessibility
@@ -48,6 +49,43 @@ export const Sizes = {
), ),
} }
export const StressTest = {
parameters: {
layout: "fullscreen",
},
render: () => <StressGrid />,
}
function StressGrid() {
const measure = () => ({
columns: Math.max(1, Math.floor((window.innerWidth + 4) / 20)),
rows: Math.max(1, Math.floor((window.innerHeight + 4) / 20)),
})
const [grid, setGrid] = createSignal(measure())
const resize = () => setGrid(measure())
onMount(() => window.addEventListener("resize", resize))
onCleanup(() => window.removeEventListener("resize", resize))
return (
<div
style={{
display: "grid",
width: "100vw",
height: "100vh",
overflow: "hidden",
gap: "4px",
"grid-template-columns": `repeat(${grid().columns}, 16px)`,
"grid-template-rows": `repeat(${grid().rows}, 16px)`,
"place-content": "center",
}}
>
{Array.from({ length: grid().columns * grid().rows }, () => (
<SessionProgressIndicatorV2 />
))}
</div>
)
}
export const OnDark = { export const OnDark = {
render: () => ( render: () => (
<div <div
@@ -1,18 +1,18 @@
import { For, splitProps, type ComponentProps } from "solid-js" import { createUniqueId, splitProps, type ComponentProps } from "solid-js"
import "./session-progress-indicator-v2.css" import "./session-progress-indicator-v2.css"
const grid = 5 const frames = new URL("./session-progress-indicator-v2-1x.png", import.meta.url).href
const dot = 2 const dots = Array.from({ length: 25 }, (_, index) => {
const gap = 1 const x = 1.5 + (index % 5) * 3
const origin = 1.5 const y = 1.5 + Math.floor(index / 5) * 3
const dots = Array.from({ length: grid * grid }, (_, index) => ({ return `M${x} ${y}h2v2h-2z`
index, }).join("")
x: origin + (index % grid) * (dot + gap),
y: origin + Math.floor(index / grid) * (dot + gap),
}))
export function SessionProgressIndicatorV2(props: ComponentProps<"svg">) { export function SessionProgressIndicatorV2(props: ComponentProps<"svg">) {
const [local, rest] = splitProps(props, ["class", "classList", "width", "height"]) const [local, rest] = splitProps(props, ["class", "classList", "width", "height"])
const id = createUniqueId()
const filter = `session-progress-indicator-filter-${id}`
const clip = `session-progress-indicator-clip-${id}`
return ( return (
<svg <svg
{...rest} {...rest}
@@ -26,7 +26,26 @@ export function SessionProgressIndicatorV2(props: ComponentProps<"svg">) {
data-component="session-progress-indicator-v2" data-component="session-progress-indicator-v2"
aria-hidden={rest["aria-hidden"] ?? "true"} aria-hidden={rest["aria-hidden"] ?? "true"}
> >
<For each={dots}>{(cell) => <rect data-dot={cell.index} x={cell.x} y={cell.y} width={dot} height={dot} />}</For> <defs>
<filter id={filter} filterUnits="userSpaceOnUse" x={0} y={0} width={16} height={16}>
<feFlood flood-color="currentColor" result="color" />
<feComposite in="color" in2="SourceAlpha" operator="in" />
</filter>
<clipPath id={clip}>
<path d={dots} />
</clipPath>
</defs>
<image
data-frame-content
href={frames}
x={0}
y={0}
width={16}
height={16}
filter={`url(#${filter})`}
clip-path={`url(#${clip})`}
/>
<rect data-reduced-motion x={7.5} y={7.5} width={2} height={2} />
</svg> </svg>
) )
} }
+3 -2
View File
@@ -550,7 +550,7 @@ function App(props: { pair?: DialogPairCredentials }) {
const offSelectionKeys = keymap.intercept( const offSelectionKeys = keymap.intercept(
"key", "key",
({ event }) => { ({ event }) => {
if (config.data.terminal?.copy_on_select ?? process.platform !== "win32") return if ((config.data.terminal?.copy ?? (process.platform === "win32" ? "manual" : "select")) === "select") return
Selection.handleSelectionKey(renderer, toast, event, clipboard) Selection.handleSelectionKey(renderer, toast, event, clipboard)
}, },
{ priority: 1 }, { priority: 1 },
@@ -571,7 +571,8 @@ function App(props: { pair?: DialogPairCredentials }) {
renderer.clearSelection() renderer.clearSelection()
} }
const terminalTitleEnabled = () => config.data.terminal?.title ?? true const terminalTitleEnabled = () => config.data.terminal?.title ?? true
const copyOnSelectEnabled = () => config.data.terminal?.copy_on_select ?? process.platform !== "win32" const copyOnSelectEnabled = () =>
(config.data.terminal?.copy ?? (process.platform === "win32" ? "manual" : "select")) === "select"
const pasteSummaryEnabled = () => config.data.prompt?.paste !== "full" const pasteSummaryEnabled = () => config.data.prompt?.paste !== "full"
const tabsVertical = () => const tabsVertical = () =>
config.data.tabs.layout === "vertical" && sessionTabsFitVertically(dimensions().width, preferredTabsWidth()) config.data.tabs.layout === "vertical" && sessionTabsFitVertically(dimensions().width, preferredTabsWidth())
+4 -5
View File
@@ -274,12 +274,11 @@ export const settings: Setting[] = [
keywords: ["terminal title", "tab title"], keywords: ["terminal title", "tab title"],
}, },
{ {
title: "Copy on select", title: "Copy behavior",
category: "Terminal", category: "Terminal",
path: ["terminal", "copy_on_select"], path: ["terminal", "copy"],
default: process.platform !== "win32", default: process.platform === "win32" ? "manual" : "select",
values: [false, true], values: ["manual", "select"],
labels: ["off", "on"],
keywords: ["selection", "clipboard"], keywords: ["selection", "clipboard"],
}, },
{ {
+10 -2
View File
@@ -1220,15 +1220,23 @@ export function Prompt(props: PromptProps) {
} else if (slashHead && isCommand) { } else if (slashHead && isCommand) {
move.startSubmit() move.startSubmit()
const model = { providerID: selection.providerID, id: selection.modelID, variant } const model = { providerID: selection.providerID, id: selection.modelID, variant }
if (session?.agent !== agent.id) await client.api.session.switchAgent({ sessionID, agent: agent.id })
const cancelCommit = local.model.trackSessionCommit(sessionID, model) const cancelCommit = local.model.trackSessionCommit(sessionID, model)
if (
session?.model?.providerID !== model.providerID ||
session.model.id !== model.id ||
(session.model.variant ?? "default") !== (model.variant ?? "default")
)
await client.api.session.switchModel({ sessionID, model }).catch((error) => {
cancelCommit()
throw error
})
void client.api.session void client.api.session
.command({ .command({
sessionID, sessionID,
command: slashHead.name, command: slashHead.name,
arguments: slashHead.arguments, arguments: slashHead.arguments,
agent: agent.id,
model,
files: store.prompt.files, files: store.prompt.files,
agents: store.prompt.agents, agents: store.prompt.agents,
skills: store.prompt.skills?.length ? store.prompt.skills : undefined, skills: store.prompt.skills?.length ? store.prompt.skills : undefined,
+3 -1
View File
@@ -103,7 +103,9 @@ export const Info = Schema.Struct({
terminal: Schema.optional( terminal: Schema.optional(
Schema.Struct({ Schema.Struct({
title: Schema.optional(Schema.Boolean).annotate({ description: "Update the terminal window title" }), title: Schema.optional(Schema.Boolean).annotate({ description: "Update the terminal window title" }),
copy_on_select: Schema.optional(Schema.Boolean).annotate({ description: "Copy selected terminal text" }), copy: Schema.optional(Schema.Literals(["manual", "select"])).annotate({
description: "Copy text manually or immediately after selecting it",
}),
}), }),
).annotate({ description: "Terminal integration settings" }), ).annotate({ description: "Terminal integration settings" }),
prompt: Schema.optional( prompt: Schema.optional(
+10 -10
View File
@@ -1647,8 +1647,6 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
) )
} }
const selected = await resolveSelectedModel(input, client, next)
if (next.variant && !selected) throw new Error("Cannot select a variant before selecting a model")
input.trace?.write("send.command", { sessionID: input.sessionID, messageID, command: command.name, delivery }) input.trace?.write("send.command", { sessionID: input.sessionID, messageID, command: command.name, delivery })
return client.session.command( return client.session.command(
{ {
@@ -1656,8 +1654,6 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
id: messageID, id: messageID,
command: command.name, command: command.name,
arguments: command.arguments, arguments: command.arguments,
agent: next.agent,
model: selected,
files: attachments.files.length ? attachments.files : undefined, files: attachments.files.length ? attachments.files : undefined,
agents: agents.length ? agents : undefined, agents: agents.length ? agents : undefined,
skills: skills.length ? skills : undefined, skills: skills.length ? skills : undefined,
@@ -1700,12 +1696,10 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
const client = sdk const client = sdk
if (next.agent) if (next.agent)
await client.session.switchAgent({ sessionID: input.sessionID, agent: next.agent }, { signal: next.signal }) await client.session.switchAgent({ sessionID: input.sessionID, agent: next.agent }, { signal: next.signal })
if (!next.prompt.command) { const selected = await resolveSelectedModel(input, client, next)
const selected = await resolveSelectedModel(input, client, next) if (next.variant && !selected) throw new Error("Cannot select a variant before selecting a model")
if (next.variant && !selected) throw new Error("Cannot select a variant before selecting a model") if (selected)
if (selected) await client.session.switchModel({ sessionID: input.sessionID, model: selected }, { signal: next.signal })
await client.session.switchModel({ sessionID: input.sessionID, model: selected }, { signal: next.signal })
}
mergePending(await admitPrompt(next, client, delivery)) mergePending(await admitPrompt(next, client, delivery))
settlementClient = client settlementClient = client
}, },
@@ -1744,6 +1738,12 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
return return
} }
if (command) { if (command) {
if (next.agent)
await client.session.switchAgent({ sessionID: input.sessionID, agent: next.agent }, { signal: next.signal })
const selected = await resolveSelectedModel(input, client, next)
if (next.variant && !selected) throw new Error("Cannot select a variant before selecting a model")
if (selected)
await client.session.switchModel({ sessionID: input.sessionID, model: selected }, { signal: next.signal })
await runTurnWait( await runTurnWait(
next, next,
messageID, messageID,
+2 -1
View File
@@ -207,7 +207,8 @@ export function DialogProvider(props: ParentProps) {
const toast = useToast() const toast = useToast()
const clipboard = useClipboard() const clipboard = useClipboard()
const config = useConfig() const config = useConfig()
const copyOnSelectEnabled = () => config.data.terminal?.copy_on_select ?? process.platform !== "win32" const copyOnSelectEnabled = () =>
(config.data.terminal?.copy ?? (process.platform === "win32" ? "manual" : "select")) === "select"
function copySelection() { function copySelection() {
const text = renderer.getSelection()?.getSelectedText() const text = renderer.getSelection()?.getSelectedText()
+3 -3
View File
@@ -154,14 +154,14 @@ function wildcardDirectory(value: string) {
export function permissionAlwaysLines(input: { action: string; save?: ReadonlyArray<string> }): string[] { export function permissionAlwaysLines(input: { action: string; save?: ReadonlyArray<string> }): string[] {
const save = input.save ?? [] const save = input.save ?? []
if (save.length === 1 && save[0] === "*") { if (save.length === 1 && save[0] === "*") {
return [`This will allow ${input.action} until OpenCode is restarted.`] return [`This will always allow ${input.action} for this project.`]
} }
return ["This will allow the following patterns until OpenCode is restarted.", ...save.map((item) => `- ${item}`)] return ["This will always allow the following patterns for this project.", ...save.map((item) => `- ${item}`)]
} }
export function permissionOptionLabel(option: "once" | "always" | "reject" | "confirm" | "cancel") { export function permissionOptionLabel(option: "once" | "always" | "reject" | "confirm" | "cancel") {
if (option === "once") return "Allow once" if (option === "once") return "Allow once"
if (option === "always") return "Allow always" if (option === "always") return "Always allow"
if (option === "reject") return "Reject" if (option === "reject") return "Reject"
if (option === "confirm") return "Confirm" if (option === "confirm") return "Confirm"
return "Cancel" return "Cancel"
+10
View File
@@ -62,6 +62,16 @@ test("shows the new session location default in settings", () => {
expect(settings.find((setting) => setting.path.join(".") === "session.new_location")?.default).toBe("launch") expect(settings.find((setting) => setting.path.join(".") === "session.new_location")?.default).toBe("launch")
}) })
test("validates terminal copy behavior", () => {
expect(decodeInfo({ terminal: { copy: "manual" } })).toEqual({ terminal: { copy: "manual" } })
expect(decodeInfo({ terminal: { copy: "select" } })).toEqual({ terminal: { copy: "select" } })
expect(() => decodeInfo({ terminal: { copy: "always" } })).toThrow()
const setting = settings.find((setting) => setting.path.join(".") === "terminal.copy")
expect(setting?.values).toEqual(["manual", "select"])
expect(setting?.default).toBe(process.platform === "win32" ? "manual" : "select")
})
test("uses command IDs as keybind keys", () => { test("uses command IDs as keybind keys", () => {
const config = resolve({ keybinds: { "session.list": "ctrl+l" } }, { terminalSuspend: true }) const config = resolve({ keybinds: { "session.list": "ctrl+l" } }, { terminalSuspend: true })
@@ -183,11 +183,11 @@ describe("run permission shared", () => {
test("formats always-allow copy for wildcard and explicit patterns", () => { test("formats always-allow copy for wildcard and explicit patterns", () => {
expect(permissionAlwaysLines(req({ action: "bash", save: ["*"] }))).toEqual([ expect(permissionAlwaysLines(req({ action: "bash", save: ["*"] }))).toEqual([
"This will allow bash until OpenCode is restarted.", "This will always allow bash for this project.",
]) ])
expect(permissionAlwaysLines(req({ save: ["src/**/*.ts", "src/**/*.tsx"] }))).toEqual([ expect(permissionAlwaysLines(req({ save: ["src/**/*.ts", "src/**/*.tsx"] }))).toEqual([
"This will allow the following patterns until OpenCode is restarted.", "This will always allow the following patterns for this project.",
"- src/**/*.ts", "- src/**/*.ts",
"- src/**/*.tsx", "- src/**/*.tsx",
]) ])
@@ -2855,8 +2855,6 @@ describe("V2 mini transport", () => {
id: "msg_cmd", id: "msg_cmd",
command: "deploy", command: "deploy",
arguments: "prod", arguments: "prod",
agent: "build",
model: { providerID: "test", id: "model" },
files: [ files: [
{ uri: "file:///tmp/context.txt", name: "context.txt" }, { uri: "file:///tmp/context.txt", name: "context.txt" },
{ {
@@ -2868,9 +2866,11 @@ describe("V2 mini transport", () => {
skills: [{ id: "api-design", mention: { start: 13, end: 24, text: "/api-design" } }], skills: [{ id: "api-design", mention: { start: 13, end: 24, text: "/api-design" } }],
delivery: "steer", delivery: "steer",
}) })
// Selection rides the command payload; no separate client-side switch. expect(client.session.switchAgent).toHaveBeenCalledWith({ sessionID: "ses_1", agent: "build" }, expect.anything())
expect(client.session.switchAgent).not.toHaveBeenCalled() expect(client.session.switchModel).toHaveBeenCalledWith(
expect(client.session.switchModel).not.toHaveBeenCalled() { sessionID: "ses_1", model: { providerID: "test", id: "model" } },
expect.anything(),
)
await transport.close() await transport.close()
}) })
+10 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import { channelsForRef, validGitHubClaims } from "./index" import { channelsForRef, resolveChannel, validGitHubClaims } from "./index"
const claims = { const claims = {
repository: "anomalyco/opencode", repository: "anomalyco/opencode",
@@ -19,6 +19,10 @@ describe("GitHub publish authorization", () => {
expect(channelsForRef(claims.ref)).toEqual(["dev", "latest"]) expect(channelsForRef(claims.ref)).toEqual(["dev", "latest"])
}) })
test("maps V2 development to the dev channel", () => {
expect(channelsForRef("refs/heads/v2")).toEqual(["dev"])
})
test("rejects another repository or workflow", () => { test("rejects another repository or workflow", () => {
expect(validGitHubClaims({ ...claims, repository_id: "1" })).toBe(false) expect(validGitHubClaims({ ...claims, repository_id: "1" })).toBe(false)
expect( expect(
@@ -33,3 +37,8 @@ describe("GitHub publish authorization", () => {
).toBe(false) ).toBe(false)
}) })
}) })
test("routes the retired next channel to beta", () => {
expect(resolveChannel("next")).toBe("beta")
expect(resolveChannel("dev")).toBe("dev")
})

Some files were not shown because too many files have changed in this diff Show More