Compare commits

..
Author SHA1 Message Date
Dax Raad fa012bebcb fix(project): preserve cached project identity 2026-05-26 17:48:19 -04:00
Dax Raad 390f49b4b8 fix(opencode): preserve session update time during project migration 2026-05-24 19:09:18 -04:00
opencode-agent[bot] 47f333299b chore: generate 2026-05-24 18:08:39 +00:00
Aiden ClineandGitHub db63eaf6ea tweak: make OPENCODE_EXPERIMENTAL_NATIVE_LLM separate from OPENCODE_EXPERIMENTAL (#29123) 2026-05-24 13:07:25 -05:00
opencode-agent[bot] 0cf99cf5f9 chore: update nix node_modules hashes 2026-05-24 02:24:16 +00:00
Aiden ClineandGitHub d16bfe850a chore(opencode): bump amazon bedrock provider (#29052) 2026-05-23 21:06:00 -05:00
opencode-agent[bot] 728eae9c25 chore: generate 2026-05-24 01:08:00 +00:00
Aiden ClineandGitHub eb84f461b8 fix(llm): split OpenAI reasoning summary blocks (#29000) 2026-05-23 20:06:45 -05:00
Kit LangtonandGitHub 0b3a1c2fdf test(opencode): simplify config effect tests (#29019) 2026-05-23 23:48:08 +00:00
opencode-agent[bot] 1ccd14b0e1 chore: update nix node_modules hashes 2026-05-23 20:51:28 +00:00
61e7cdfbff fix(opencode): bump google vertex provider for thought signatures (#29010)
Co-authored-by: Andrew Sweet <andrew.sweet@cantab.net>
2026-05-23 15:29:43 -05:00
opencode-agent[bot] 7fe7b9f258 chore: update nix node_modules hashes 2026-05-23 16:42:22 +00:00
a5ba1d075d chore(opencode): bump venice ai sdk provider (#29003)
Co-authored-by: dpuyosa <dpuyosa@users.noreply.github.com>
2026-05-23 11:28:04 -05:00
ba437069e6 fix(opencode): advertise configured shell timeout (#28998)
Co-authored-by: Nabs <nabil@instafork.com>
2026-05-23 10:43:39 -05:00
Frank 387c5a02c8 sync 2026-05-23 10:27:07 -04:00
Jérôme BenoitandGitHub 4b1b722e98 fix(nix): re-inherit patches in desktop, ad-hoc sign .app on darwin (#27869) 2026-05-23 09:20:21 -05:00
Frank 6b03be5468 sync 2026-05-23 03:57:22 -04:00
opencode-agent[bot] 7d2c1ce6c6 chore: generate 2026-05-23 04:49:36 +00:00
DaxandGitHub a9ef5a0fae feat(project): resolve remote-backed project identity (#28914) 2026-05-23 04:48:09 +00:00
Luke ParkerandGitHub 0cf9a5d20b fix(desktop): avoid white flash when restoring window (#28247) 2026-05-23 14:42:40 +10:00
opencode 2c4ad9f405 sync release versions for v1.15.10 2026-05-23 01:04:22 +00:00
Luke ParkerandGitHub 3bf054c1d9 fix(app): restore desktop prod legacy flows (#28919) 2026-05-22 20:40:47 -04:00
Aiden ClineandGitHub 14c511e380 fix(llm): stabilize anthropic tool result typecheck (#28909) 2026-05-22 17:14:40 -05:00
opencode-agent[bot] 5ee0238c4a chore: update nix node_modules hashes 2026-05-22 21:33:19 +00:00
opencode 871d38cbd7 sync release versions for v1.15.9 2026-05-22 21:18:49 +00:00
opencode-agent[bot] 1a329e4e67 chore: generate 2026-05-22 19:31:59 +00:00
James LongandGitHub bfb2d8dc76 fix(tui): when diff viewer closes always return to last route (#28903) 2026-05-22 15:30:31 -04:00
James LongandGitHub 8f7a6c4a00 fix(tui): refine diff view keyboard shortcuts (#28896) 2026-05-22 14:40:14 -04:00
opencode-agent[bot] 1857c73565 chore: generate 2026-05-22 18:25:01 +00:00
Shoubhit DashandGitHub 5f42351159 fix(provider): type default model failures (#28881) 2026-05-22 23:53:23 +05:30
Shoubhit DashandGitHub 968aaa3cfe fix(pty): expose missing session errors (#28884) 2026-05-22 23:53:03 +05:30
opencode-agent[bot] b8266e5819 chore: generate 2026-05-22 18:22:48 +00:00
James LongandGitHub ba746e36d8 fix(tui): empty states, context, and minor improvements to diff viewer (#28878) 2026-05-22 14:21:22 -04:00
opencode-agent[bot] d5068ba28e chore: generate 2026-05-22 17:50:23 +00:00
Shoubhit DashandGitHub 7265c46af6 fix(skill): type expected skill failures (#28885) 2026-05-22 23:18:52 +05:30
Shoubhit DashandGitHub 536ee857c6 fix(installation): type upgrade failures (#28883) 2026-05-22 23:18:17 +05:30
opencode-agent[bot] dda69d77e8 chore: generate 2026-05-22 17:47:18 +00:00
Shoubhit DashandGitHub 0e14404e5f fix(sync): map workspace warp not found (#28882) 2026-05-22 23:15:53 +05:30
opencode-agent[bot] 05f51bfe45 chore: generate 2026-05-22 17:44:34 +00:00
Shoubhit DashandGitHub aee552c043 fix(repository): type expected reference failures (#28880) 2026-05-22 23:13:14 +05:30
opencode-agent[bot] 4f6eaf859b chore: generate 2026-05-22 16:39:58 +00:00
Kit LangtonandGitHub d0cb58782f fix(llm): surface code, type, and nested fields on provider stream errors (#28757) 2026-05-22 12:37:55 -04:00
opencode-agent[bot] a3430db73a chore: generate 2026-05-22 16:37:46 +00:00
Shoubhit DashandGitHub 3e1972fd92 fix(httpapi): return project not found errors (#28856) 2026-05-22 22:06:20 +05:30
opencode-agent[bot] b368e5adbe chore: generate 2026-05-22 16:31:30 +00:00
James LongandGitHub 69e4f52272 fix(tui): interaction improvements to diff viewer (#28851) 2026-05-22 12:30:10 -04:00
opencode-agent[bot] 8a55920531 chore: generate 2026-05-22 16:25:15 +00:00
Kit LangtonandGitHub 9db90a0b76 fix(llm): emit structured image blocks for tool-result media in Anthropic Messages (#28755) 2026-05-22 12:23:41 -04:00
Kit LangtonandGitHub 700d012025 fix(llm): emit structured input_image content for tool-result media in OpenAI Responses (#28754) 2026-05-22 12:23:23 -04:00
Shoubhit DashandGitHub 59e486a917 fix(tui): restore question prompt key handling (#28835) 2026-05-22 21:47:13 +05:30
Aiden ClineandGitHub 8596967415 ci: "fix: exempt team members from compliance cleanup" (#28865) 2026-05-22 11:14:52 -05:00
opencode-agent[bot] 00038027c8 chore: generate 2026-05-22 15:31:46 +00:00
Shoubhit DashandGitHub 5cf597d583 fix(httpapi): return pty error bodies (#28838) 2026-05-22 21:00:20 +05:30
opencode-agent[bot] d92b8d8009 chore: generate 2026-05-22 13:52:40 +00:00
Dax Raad 854c535536 fix(tui): enable diff viewer by default 2026-05-22 09:50:30 -04:00
OpeOginniandGitHub 3cf955e9ad fix(desktop): correct user code extraction when URL contains colons (#28837) 2026-05-22 21:16:33 +08:00
opencode-agent[bot] 63f3e84792 chore: generate 2026-05-22 12:17:49 +00:00
Shoubhit DashandGitHub 0beb4de3e8 fix(httpapi): return mcp server not found errors (#28817) 2026-05-22 17:46:30 +05:30
Shoubhit DashandGitHub 51da3483a9 feat(tui): copy worktree path from palette (#28823) 2026-05-22 17:28:51 +05:30
opencode-agent[bot] 060fbc9ce7 chore: generate 2026-05-22 11:42:14 +00:00
7a769dab3a fix(mcp): include scope in clientMetadata and add callbackPort option (#28810)
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
2026-05-22 17:10:38 +05:30
opencode-agent[bot] 2663ecd39d chore: generate 2026-05-22 11:00:29 +00:00
Shoubhit DashandGitHub 4ce247eaba fix(httpapi): return request not found errors (#28693) 2026-05-22 16:29:12 +05:30
Brendan AllanandGitHub 76d9c2cd76 fix(app): debounce vcs refresh in session page (#28784) 2026-05-22 06:47:05 +00:00
Brendan AllanandGitHub f6101aef8a refactor(app): consolidate sdk and sync contexts (#28782) 2026-05-22 06:40:56 +00:00
Brendan AllanandGitHub f3874ec2f9 refactor(app): move tab navigation to titlebar and conditionally register project shortcuts (#28773) 2026-05-22 13:54:25 +08:00
Brendan AllanandGitHub 6466fcfdea refactor(app): simplify session routing and tab close handling (#28767) 2026-05-22 12:48:09 +08:00
Brendan AllanandGitHub 1f0390cfbb app: wrap provider data in Map to avoid store (#28765) 2026-05-22 12:23:16 +08:00
opencode-agent[bot] 9f06accfb4 chore: generate 2026-05-22 04:04:41 +00:00
7a9724496b fix(vertex): Vertex (Antropic) provider: use .rep.googleapis.com for continental multi-region endpoints (us, eu) (#28347)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-05-21 23:03:24 -05:00
Aiden ClineandGitHub 3e931152d1 tweak(tui): remove italics from thinking labels (#28737) 2026-05-21 22:22:51 -05:00
Frank ad1d14775d sync 2026-05-21 19:11:33 -04:00
Frank fcf4dff2ce sync 2026-05-21 19:09:26 -04:00
Frank 56714327f4 sync 2026-05-21 19:02:24 -04:00
Frank 21f338652d sync 2026-05-21 18:49:52 -04:00
Frank 8115f0c661 sync 2026-05-21 18:43:48 -04:00
opencode-agent[bot] 87d4cb07b0 chore: generate 2026-05-21 22:36:00 +00:00
James LongandGitHub ee008923f3 feat(tui): design revamp of diff viewer (#28728) 2026-05-21 18:34:32 -04:00
Frank bbbef0da1c sync 2026-05-21 18:27:45 -04:00
Aiden ClineandGitHub 1268f8657e fix: exempt team members from compliance cleanup 2026-05-21 16:15:45 -05:00
Frank 39e7ff932d sync 2026-05-21 17:03:54 -04:00
Kit LangtonandGitHub e92c4fb460 chore: drop dead imports across opencode/core/llm (#28720) 2026-05-21 17:03:12 -04:00
4b496066bf fix(opencode): update spinner color logic (#28032)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-05-21 16:02:08 -05:00
Frank e63dcd30f3 sync 2026-05-21 16:55:12 -04:00
Kit LangtonandGitHub 2935d1819e refactor(server): rename Fence.waitEffect to Fence.wait (#28717) 2026-05-21 20:48:15 +00:00
Kit LangtonandGitHub 86907e2e4a refactor(server): drop dead Fence.wait + redundant casts (#28710) 2026-05-21 16:35:44 -04:00
Frank 31d2d38d7a sync 2026-05-21 16:20:33 -04:00
Frank d21477d55a sync 2026-05-21 16:11:41 -04:00
Frank 80fa6e6c49 sync 2026-05-21 16:02:56 -04:00
Kit LangtonandGitHub b99787e95b refactor(opencode): fetch remote config with http client (#28661) 2026-05-21 15:53:44 -04:00
Frank 562d299a41 sync 2026-05-21 15:46:54 -04:00
opencode-agent[bot] 9ecb04e35b chore: generate 2026-05-21 19:19:25 +00:00
Kit LangtonandGitHub 231689c767 test(config): port env-var config tests to it.instance (#28706) 2026-05-21 19:17:29 +00:00
Frank d70942079a sync 2026-05-21 15:06:10 -04:00
Frank 0cc55c11ac sync 2026-05-21 14:53:56 -04:00
Kit LangtonandGitHub 40da77e77c test(opencode): cover native reasoning continuation (#28683) 2026-05-21 14:50:37 -04:00
Frank 0d2de7db65 sync 2026-05-21 14:25:43 -04:00
Frank a568f616c9 sync 2026-05-21 13:55:31 -04:00
opencode-agent[bot] e4957a78ea chore: update nix node_modules hashes 2026-05-21 17:27:59 +00:00
opencode-agent[bot] 7e35cff4f5 chore: generate 2026-05-21 17:25:26 +00:00
Shoubhit DashandGitHub 82b796ce31 fix(httpapi): return session busy error bodies (#28684) 2026-05-21 22:53:49 +05:30
Vladimir GlafirovandGitHub 6c24062d2f chore(deps): bump gitlab-ai-provider to 6.7.0 (#28218) 2026-05-21 12:12:39 -05:00
opencode-agent[bot] 3ca09ee3ef chore: generate 2026-05-21 16:50:49 +00:00
Kit LangtonandGitHub 003f1ce0f4 test(control-plane): finish porting workspace.test.ts to it.instance (#28665) 2026-05-21 12:48:59 -04:00
Kit LangtonandGitHub 61390dbb49 fix(llm): preserve native continuation metadata (#28678) 2026-05-21 11:57:45 -04:00
opencode a58c3c53a9 sync release versions for v1.15.7 2026-05-21 15:51:26 +00:00
Victor NavarroandGitHub 75ce8feff5 chore: upgrade sst version (#26224) 2026-05-21 17:19:15 +02:00
opencode-agent[bot] 6bee6ee755 chore: generate 2026-05-21 11:01:36 +00:00
Shoubhit DashandGitHub 9739d75892 fix(httpapi): handle corrupt v2 session messages (#28633) 2026-05-21 16:30:04 +05:30
opencode-agent[bot] 4a976482b1 chore: generate 2026-05-21 10:46:02 +00:00
Brendan AllanandGitHub 2caac055ef feat(desktop): add pinch zoom setting (#28632) 2026-05-21 10:44:30 +00:00
Shoubhit DashandGitHub 2697cb8001 fix(httpapi): remove config error middleware special case (#28631) 2026-05-21 15:57:25 +05:30
opencode-agent[bot] dbd709376b chore: generate 2026-05-21 10:15:54 +00:00
Shoubhit DashandGitHub fc08292136 fix(httpapi): add unknown error references (#28629) 2026-05-21 15:44:35 +05:30
JackandGitHub ee594ba55b docs: sync TUI config translations and Zen pricing (#28620) 2026-05-21 18:03:22 +08:00
opencode-agent[bot] 516af86d53 chore: generate 2026-05-21 09:43:40 +00:00
318 changed files with 9347 additions and 5386 deletions
-9
View File
@@ -21,15 +21,6 @@ jobs:
with:
node-version: "24"
# Workaround for Pulumi version conflict:
# GitHub runners have Pulumi 3.212.0+ pre-installed, which removed the -root flag
# from pulumi-language-nodejs (see https://github.com/pulumi/pulumi/pull/21065).
# SST 3.17.x uses Pulumi SDK 3.210.0 which still passes -root, causing a conflict.
# Removing the system language plugin forces SST to use its bundled compatible version.
# TODO: Remove when sst supports Pulumi >3.210.0
- name: Fix Pulumi version conflict
run: sudo rm -f /usr/local/bin/pulumi-language-nodejs
- run: bun sst deploy --stage=${{ github.ref_name }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+171 -148
View File
@@ -23,13 +23,13 @@
"oxlint-tsgolint": "0.21.0",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "3.18.10",
"sst": "4.13.1",
"turbo": "2.8.13",
},
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -44,6 +44,7 @@
"@solid-primitives/i18n": "2.2.1",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.5",
"@solid-primitives/scheduled": "1.5.3",
"@solid-primitives/scroll": "2.1.3",
"@solid-primitives/storage": "catalog:",
"@solid-primitives/timer": "1.4.4",
@@ -84,7 +85,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -119,7 +120,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -146,7 +147,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@ai-sdk/anthropic": "3.0.64",
"@ai-sdk/openai": "3.0.48",
@@ -168,7 +169,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -192,21 +193,21 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "1.15.6",
"version": "1.15.10",
"bin": {
"opencode": "./bin/opencode",
},
"dependencies": {
"@ai-sdk/alibaba": "1.0.17",
"@ai-sdk/amazon-bedrock": "4.0.96",
"@ai-sdk/amazon-bedrock": "4.0.107",
"@ai-sdk/anthropic": "3.0.71",
"@ai-sdk/azure": "3.0.49",
"@ai-sdk/cerebras": "2.0.41",
"@ai-sdk/cohere": "3.0.27",
"@ai-sdk/deepinfra": "2.0.41",
"@ai-sdk/gateway": "3.0.104",
"@ai-sdk/google": "3.0.63",
"@ai-sdk/google-vertex": "4.0.112",
"@ai-sdk/google": "3.0.75",
"@ai-sdk/google-vertex": "4.0.131",
"@ai-sdk/groq": "3.0.31",
"@ai-sdk/mistral": "3.0.27",
"@ai-sdk/openai": "3.0.53",
@@ -230,7 +231,7 @@
"ai-gateway-provider": "3.1.2",
"cross-spawn": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"immer": "11.1.4",
@@ -238,7 +239,7 @@
"minimatch": "10.2.5",
"npm-package-arg": "13.0.2",
"semver": "^7.6.3",
"venice-ai-sdk-provider": "2.0.1",
"venice-ai-sdk-provider": "2.0.2",
"xdg-basedir": "5.1.0",
"zod": "catalog:",
},
@@ -253,7 +254,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@zip.js/zip.js": "2.7.62",
"drizzle-orm": "catalog:",
@@ -308,7 +309,7 @@
},
"packages/effect-drizzle-sqlite": {
"name": "@opencode-ai/effect-drizzle-sqlite",
"version": "1.15.5",
"version": "1.15.10",
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:",
@@ -322,7 +323,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -352,7 +353,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -368,7 +369,7 @@
},
"packages/http-recorder": {
"name": "@opencode-ai/http-recorder",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@effect/platform-node": "catalog:",
"effect": "catalog:",
@@ -381,7 +382,7 @@
},
"packages/llm": {
"name": "@opencode-ai/llm",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@smithy/eventstream-codec": "4.2.14",
"@smithy/util-utf8": "4.2.2",
@@ -399,7 +400,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.15.6",
"version": "1.15.10",
"bin": {
"opencode": "./bin/opencode",
},
@@ -408,15 +409,15 @@
"@actions/github": "6.0.1",
"@agentclientprotocol/sdk": "0.21.0",
"@ai-sdk/alibaba": "1.0.17",
"@ai-sdk/amazon-bedrock": "4.0.96",
"@ai-sdk/amazon-bedrock": "4.0.107",
"@ai-sdk/anthropic": "3.0.71",
"@ai-sdk/azure": "3.0.49",
"@ai-sdk/cerebras": "2.0.41",
"@ai-sdk/cohere": "3.0.27",
"@ai-sdk/deepinfra": "2.0.41",
"@ai-sdk/gateway": "3.0.104",
"@ai-sdk/google": "3.0.63",
"@ai-sdk/google-vertex": "4.0.112",
"@ai-sdk/google": "3.0.75",
"@ai-sdk/google-vertex": "4.0.131",
"@ai-sdk/groq": "3.0.31",
"@ai-sdk/mistral": "3.0.27",
"@ai-sdk/openai": "3.0.53",
@@ -469,7 +470,7 @@
"drizzle-orm": "catalog:",
"effect": "catalog:",
"fuzzysort": "3.1.0",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"gray-matter": "4.0.3",
@@ -493,7 +494,7 @@
"tree-sitter-powershell": "0.25.10",
"turndown": "7.2.0",
"ulid": "catalog:",
"venice-ai-sdk-provider": "2.0.1",
"venice-ai-sdk-provider": "2.0.2",
"vscode-jsonrpc": "8.2.1",
"web-tree-sitter": "0.25.10",
"which": "6.0.1",
@@ -537,7 +538,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"effect": "catalog:",
@@ -575,7 +576,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -590,7 +591,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -625,7 +626,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -674,7 +675,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -809,7 +810,7 @@
"@ai-sdk/alibaba": ["@ai-sdk/alibaba@1.0.17", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZbE+U5bWz2JBc5DERLowx5+TKbjGBE93LqKZAWvuEn7HOSQMraxFMZuc0ST335QZJAyfBOzh7m1mPQ+y7EaaoA=="],
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.96", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.71", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Mc4Ias2jRMD1jOB6xWtKNPdhECeuCZyIlbr9EAGfBnyBt++sS13ziZh9qv9TdyMCAZJ7xoQcpbchoRJcKwPdpA=="],
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.107", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.78", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8nT08pGPy25rleJNk56ep00UHK6kCtCmu+ZNqVVSSPDieADlIZqcaN1iRXAFBoCH0Fb9F6C2EjFDaySdsargfQ=="],
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rwLi/Rsuj2pYniQXIrvClHvXDzgM4UQHHnvHTWEF14efnlKclG/1ghpNC+adsRujAbCTr6gRsSbDE2vEqriV7g=="],
@@ -831,9 +832,9 @@
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.104", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA=="],
"@ai-sdk/google": ["@ai-sdk/google@3.0.63", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RfOZWVMYSPu2sPRfGajrauWAZ9BSaRopSn+AszkKWQ1MFj8nhaXvCqRHB5pBQUaHTfZKagvOmMpNfa/s3gPLgQ=="],
"@ai-sdk/google": ["@ai-sdk/google@3.0.75", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XAm31ftiOrzlb8NjDzT7kw0xw+4lmgFdGFn1QKM73nXFFKyN1kWLESBV75UGNfjXP8X1YJ0YydnMVqO0jaPghw=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.112", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.71", "@ai-sdk/google": "3.0.64", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cSfHCkM+9ZrFtQWIN1WlV93JPD+isGSdFxKj7u1L9m2aLVZajlXdcE41GL9hMt7ld7bZYE4NnZ+4VLxBAHE+Eg=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.131", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.78", "@ai-sdk/google": "3.0.75", "@ai-sdk/openai-compatible": "2.0.47", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Oj1X8p0rVgvEoR5OOSxWi6XgzJ3QDlE/n30MZVtpKkCiToYYDyvlvVDGXz3IqhMyUev2JhlcuUk1brScKT01kA=="],
"@ai-sdk/groq": ["@ai-sdk/groq@3.0.31", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XbbugpnFmXGu2TlXiq8KUJskP6/VVbuFcnFIGDzDIB/Chg6XHsNnqrTF80Zxkh0Pd3+NvbM+2Uqrtsndk6bDAg=="],
@@ -907,6 +908,8 @@
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Ne3Yk/bgQPVebAkv7W+RfhiwTRSbfER9BtbhOa2w/+dIr902LrJf6vrZlxiqaJbGj2ALx8M+ZK1YIHVxSwu9A=="],
"@aws-sdk/client-lambda": ["@aws-sdk/client-lambda@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-ryEYNVdilyWkKsOs/7Xy/l7+qjtSz4sll8NpcWD6AtONxjG/5OMaAhxxDkQb4iBoNMKnISxsARzQAp/Wa8pXIg=="],
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.933.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-node": "3.933.0", "@aws-sdk/middleware-bucket-endpoint": "3.930.0", "@aws-sdk/middleware-expect-continue": "3.930.0", "@aws-sdk/middleware-flexible-checksums": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-location-constraint": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-sdk-s3": "3.932.0", "@aws-sdk/middleware-ssec": "3.930.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/signature-v4-multi-region": "3.932.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-KxwZvdxdCeWK6o8mpnb+kk7Kgb8V+8AjTwSXUWH1UAD85B0tjdo1cSfE5zoR5fWGol4Ml5RLez12a6LPhsoTqA=="],
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zwGLSiK48z3PzKpQiDMKP85+fpIrPMF1qQOQW9OW7BGj5AuBZIisT2O4VzIgYJeh+t47MLU7VgBQL7muc+MJDg=="],
@@ -977,6 +980,8 @@
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
"@aws/durable-execution-sdk-js": ["@aws/durable-execution-sdk-js@1.0.2", "", { "dependencies": { "@aws-sdk/client-lambda": "^3.943.0" } }, "sha512-KIYBVqV9gArkWdPEDYOjJqLlO+NecmCXOXadNBlOspJTmqztwuiyb6aZc468bYWSGuL4Me/gyTIK97ubkwFNCw=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
"@azure-rest/core-client": ["@azure-rest/core-client@2.6.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-iuFKDm8XPzNxPfRjhyU5/xKZmcRDzSuEghXDHHk4MjBV/wFL34GmYVBZnn9wmuoLBeS1qAw9ceMdaeJBPcB1QQ=="],
@@ -1463,6 +1468,8 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
@@ -2171,7 +2178,7 @@
"@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="],
"@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-/j2igE0xyNaHhj6kMfcUQn5rAVSTLbAX+CDEBm25hSNBmNiHLu2lM7Usj2kJJ5j36D67bE8wR1hBNA8hjtvsQA=="],
"@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.3", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-oNwLE6E6lxJAWrc8QXuwM0k2oU1BnANnkChwMw82aK1j3+mWGJkG1IFe5gCwbV+afYmjI76t9JJV3md/8tLw+g=="],
"@solid-primitives/scroll": ["@solid-primitives/scroll@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-Ejq/Z7zKo/6eIEFr1bFLzXFxiGBCMLuqCM8QB8urr3YdPzjSETFLzYRWUyRiDWaBQN0F7k0SY6S7ig5nWOP7vg=="],
@@ -2613,8 +2620,6 @@
"avvio": ["avvio@9.2.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
"aws-sdk": ["aws-sdk@2.1692.0", "", { "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", "xml2js": "0.6.2" } }, "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
@@ -2699,7 +2704,7 @@
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="],
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
@@ -3295,7 +3300,7 @@
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.6.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-jUxYnKA4XQaPc3wxACDZ8bPDXO0Mzx7cZaBDxbT2uGgLqtGZmSi+9tVNIg7louSS+s/ioVra3SoUz3iOFVhKPA=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.7.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-J7apROAmDwDJcLPcbQ9YQiPhyFN1Vvt/f3ugsePaUnzh3sedfHCHGETgG0Mm0dJCB2rC7DYwmV/sgxLQcQ/sjw=="],
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
@@ -3437,7 +3442,7 @@
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
"ieee754": ["ieee754@1.1.13", "", {}, "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
@@ -3569,7 +3574,7 @@
"is64bit": ["is64bit@2.0.0", "", { "dependencies": { "system-architecture": "^0.1.0" } }, "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
@@ -3587,8 +3592,6 @@
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jmespath": ["jmespath@0.16.0", "", {}, "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="],
"jose": ["jose@6.0.11", "", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
"js-base64": ["js-base64@3.7.7", "", {}, "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="],
@@ -4075,8 +4078,6 @@
"opencode-poe-auth": ["opencode-poe-auth@0.0.1", "", { "dependencies": { "open": "^10.0.0", "poe-oauth": "*" }, "peerDependencies": { "@opencode-ai/plugin": "*" } }, "sha512-cXqTlS6AXHzo1oBdosnxbT47ZJEZ9WXn050X8Re6wZ1vaNnTpB/l2fMQt90evT7RBK0fB8UjXQUDMKyd7bbiqg=="],
"opencontrol": ["opencontrol@0.0.6", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.6.1", "@tsconfig/bun": "1.0.7", "hono": "4.7.4", "zod": "3.24.2", "zod-to-json-schema": "3.24.3" }, "bin": { "opencontrol": "bin/index.mjs" } }, "sha512-QeCrpOK5D15QV8kjnGVeD/BHFLwcVr+sn4T6KKmP0WAMs2pww56e4h+eOGHb5iPOufUQXbdbBKi6WV2kk7tefQ=="],
"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=="],
"opentui-spinner": ["opentui-spinner@0.0.6", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.1.49", "@opentui/react": "^0.1.49", "@opentui/solid": "^0.1.49", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-xupLOeVQEAXEvVJCvHkfX6fChDWmJIPHe5jyUrVb8+n4XVTX8mBNhitFfB9v2ZbkC1H2UwPab/ElePHoW37NcA=="],
@@ -4273,7 +4274,7 @@
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
"punycode": ["punycode@1.3.2", "", {}, "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="],
@@ -4283,8 +4284,6 @@
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
"querystring": ["querystring@0.2.0", "", {}, "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
@@ -4469,7 +4468,7 @@
"sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="],
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
@@ -4597,23 +4596,23 @@
"ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
"sst": ["sst@3.18.10", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.18.10", "sst-darwin-x64": "3.18.10", "sst-linux-arm64": "3.18.10", "sst-linux-x64": "3.18.10", "sst-linux-x86": "3.18.10", "sst-win32-arm64": "3.18.10", "sst-win32-x64": "3.18.10", "sst-win32-x86": "3.18.10" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-SY+ldeJ9K5E9q+DhjXA3e2W3BEOzBwkE3IyLSD71uA3/5nRhUAST31iOWEpW36LbIvSQ9uOVDFcebztoLJ8s7w=="],
"sst": ["sst@4.13.1", "", { "dependencies": { "@aws/durable-execution-sdk-js": "1.0.2", "aws4fetch": "1.0.18", "jose": "5.2.3", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "4.13.1", "sst-darwin-x64": "4.13.1", "sst-linux-arm64": "4.13.1", "sst-linux-x64": "4.13.1", "sst-linux-x86": "4.13.1", "sst-win32-arm64": "4.13.1", "sst-win32-x64": "4.13.1", "sst-win32-x86": "4.13.1" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-FKzhWuqV5NPhFWH7b+Ktf5+Mvox6DCMc8iOrjaS3mkjnnmyEcOZq7HNx2/ZlIcBwgOZE9sCPR3ot1zXtXkXzZg=="],
"sst-darwin-arm64": ["sst-darwin-arm64@3.18.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3MwIpMZhhdZKDqLp9ZQNlwkWix5+q+N0PWstuTomYwgZOxCCe6u9IIsoIszSk+GAJJN/jvGZyLiXKeV4iiQvw=="],
"sst-darwin-arm64": ["sst-darwin-arm64@4.13.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gGwfgZGOsulU6iGeZJtxP5BpSZFsRUy1dG/yTqJa++n1P1APWXZC64u2qRHm6rdwLAjEeWaCQ9k7ouKgn0zh5Q=="],
"sst-darwin-x64": ["sst-darwin-x64@3.18.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-nQ0jMKkPOa+kj6Ygz8+kYhBua/vgNTLkd+4r8NSmk7v+Zs78lKnx3T//kEzS0yik6Q6QwGfokwrTcA1Jii2xSw=="],
"sst-darwin-x64": ["sst-darwin-x64@4.13.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-RcKLI2oAY+Wk5fWUF0gJFKvwMSpid3qw9L3OcfgFL2ymZH652ybLpQPBxtoJ95FdyknWIMnYf6ZK9C2jnH5eaw=="],
"sst-linux-arm64": ["sst-linux-arm64@3.18.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-mj9VNj3SvLS+HaXx2PhCX0aTA7CwJNoM6JhRc0s/zCilqchcvqDjbhpYBJO4brEPv6aOaaa7T3WvIQqtYauK4Q=="],
"sst-linux-arm64": ["sst-linux-arm64@4.13.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Lp/8BWhH8KZnb50xPd8A6bkZ7QqCH3Dar9l16TPwuAmPxxRUlkmg42h+ACZ9n02vSJl8y/WLnV4JB6SgJpRFVg=="],
"sst-linux-x64": ["sst-linux-x64@3.18.10", "", { "os": "linux", "cpu": "x64" }, "sha512-7iy1Eq2eqnT9Ag/8OVgC04vRjV7AAQyf/BvzLc+6Sz+GvRiKA8VEuPnbXNYQF+NIvEqsawfcd7MknSTtImpsvQ=="],
"sst-linux-x64": ["sst-linux-x64@4.13.1", "", { "os": "linux", "cpu": "x64" }, "sha512-f4eHOsApoP+CpUzLUGzfoTBYsqqr6nl/M0gjYsVNi1T+6v92lc84AhQ4DoDZx033QggyxkqRezj8jOkjkKxa7w=="],
"sst-linux-x86": ["sst-linux-x86@3.18.10", "", { "os": "linux", "cpu": "none" }, "sha512-77qZSuPZeQ5bdRCiq1pQEdY8EcGNHboKrx4P2yFid2FBDKJsXxOXtIxJdloyx+ljBn0+nxl/g040QBmXxdc9tA=="],
"sst-linux-x86": ["sst-linux-x86@4.13.1", "", { "os": "linux", "cpu": "none" }, "sha512-5OHdtPa0GmozDpBPdsD9Tv+SGnpjJSoTXptgK59ivQg3UYNWBBotidvEyUJs1MU9B/3+I+Fmw32zsF2O+ZtPgg=="],
"sst-win32-arm64": ["sst-win32-arm64@3.18.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aY+FhMxvYs8crlrKALpLn/kKmud8YQj6LkMHsrOAAIJhfNyxhCja2vrYQaY+bcqdsS5W2LMVcS2hyaMqKXZKcg=="],
"sst-win32-arm64": ["sst-win32-arm64@4.13.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-qAUuCQj6wDdZ84a9W4FhnRTHYlEnYRHGYFqO3cmMImbnAQEoKSQR1XuWqt0Ev4RT3kU4Lz/fgvJjGkR7WUstLg=="],
"sst-win32-x64": ["sst-win32-x64@3.18.10", "", { "os": "win32", "cpu": "x64" }, "sha512-rY+yJXOpG+P5xXnaQRpCvBK2zwwLhjzpYidGkp6F+cGgiVdh2Wre/CIQNRaVHr20ncj8lLe/RsHWa9QCNM48jg=="],
"sst-win32-x64": ["sst-win32-x64@4.13.1", "", { "os": "win32", "cpu": "x64" }, "sha512-r+83NMwpe4MLAvkVoOxi1KNs6nIqilFucsTr+VN1PgknsiVBY1emITjSWS2jDJJMuH8xJXL5xJzF/l5rcKWErg=="],
"sst-win32-x86": ["sst-win32-x86@3.18.10", "", { "os": "win32", "cpu": "none" }, "sha512-pq8SmV0pIjBFMY6DraUZ4akyTxHnfjIKCRbBLdMxFUZK8TzA1NK2YdjRt1AwrgXRYGRyctrz/mt4WyO0SMOVQQ=="],
"sst-win32-x86": ["sst-win32-x86@4.13.1", "", { "os": "win32", "cpu": "none" }, "sha512-YPxBVdac/MsrzwlC6pF0NrrvMcmfdBLYjv7MbzHc5jNh1FQ1WPh6bdWQqgv0KD9EQTNLLEkej0beydgUvcCWJg=="],
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
@@ -4893,8 +4892,6 @@
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"url": ["url@0.10.3", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
"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=="],
@@ -4903,8 +4900,6 @@
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
@@ -4917,7 +4912,7 @@
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
"venice-ai-sdk-provider": ["venice-ai-sdk-provider@2.0.1", "", { "dependencies": { "@ai-sdk/openai-compatible": "^2.0.37", "@ai-sdk/provider": "^3.0.8", "@ai-sdk/provider-utils": "^4.0.21" }, "peerDependencies": { "ai": "^6.0.90" } }, "sha512-6SxA8a4MoA6Q/c+D3q7My0Hfog76enN3n0MXhwosM+tso66rXBEGeBRD/0lravRDVzL2Q1w5QJPc86rAVJtfXg=="],
"venice-ai-sdk-provider": ["venice-ai-sdk-provider@2.0.2", "", { "dependencies": { "@ai-sdk/openai-compatible": "^2.0.47", "@ai-sdk/provider": "^3.0.10", "@ai-sdk/provider-utils": "^4.0.27" }, "peerDependencies": { "ai": "^6.0.90" } }, "sha512-aoa05nI3BTK5aGbjBflq+Gfln2AHAkwNbWuGGvCzUIsOfp5Y3iPD4O4PUGDAEiWVJWbjpPn0KfDa0H/HebwsaA=="],
"verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="],
@@ -5031,6 +5026,8 @@
"xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
@@ -5093,9 +5090,13 @@
"@ai-sdk/alibaba/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.71", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bUWOzrzR0gJKJO/PLGMR4uH2dqEgqGhrsCV+sSpk4KtOEnUQlfjZI/F7BFlqSvVpFbjdgYRRLysAeEZpJ6S1lg=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.78", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-0OY12G20cUt6iU6htpEA1491Oz++NVxZxlmWGX4B7rSbeZ5pnDmOu6YtW9BKzdZlNx5Gn23i6WMxyZFoMKNcgA=="],
"@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.13", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.0", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-vYahwBAtRaAcFbOmE9aLr12z7RiHYDSLcnogSdxfm7kKfsNa3wH+NU5r7vTeB5rKvLsWyPjVX8iH94brP7umiQ=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
"@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.14", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw=="],
"@ai-sdk/amazon-bedrock/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
@@ -5111,11 +5112,17 @@
"@ai-sdk/fireworks/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.71", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bUWOzrzR0gJKJO/PLGMR4uH2dqEgqGhrsCV+sSpk4KtOEnUQlfjZI/F7BFlqSvVpFbjdgYRRLysAeEZpJ6S1lg=="],
"@ai-sdk/google/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
"@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CbR82EgGPNrj/6q0HtclwuCqe0/pDShyv3nWDP/A9DroujzWXnLMlUJVrgPOsg4b40zQCwwVs2XSKCxvt/4QaA=="],
"@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
"@ai-sdk/google-vertex/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.78", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-0OY12G20cUt6iU6htpEA1491Oz++NVxZxlmWGX4B7rSbeZ5pnDmOu6YtW9BKzdZlNx5Gn23i6WMxyZFoMKNcgA=="],
"@ai-sdk/google-vertex/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Enm5UlL0zUCrW3792opk5h7hRWxZOZzDe6eQYVFqX9LUOGGCe1h8MZWAGim765nwzgnjlpeYOsuzZmLtRsTPlg=="],
"@ai-sdk/google-vertex/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
"@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
@@ -5193,6 +5200,20 @@
"@aws-sdk/client-cognito-identity/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/client-lambda/@aws-sdk/core": ["@aws-sdk/core@3.974.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws-sdk/xml-builder": "^3.972.24", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.42", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.37", "@aws-sdk/credential-provider-http": "^3.972.39", "@aws-sdk/credential-provider-ini": "^3.972.41", "@aws-sdk/credential-provider-process": "^3.972.37", "@aws-sdk/credential-provider-sso": "^3.972.41", "@aws-sdk/credential-provider-web-identity": "^3.972.41", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/credential-provider-imds": "^4.3.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ=="],
"@aws-sdk/client-lambda/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="],
"@aws-sdk/client-lambda/@smithy/core": ["@smithy/core@3.24.3", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg=="],
"@aws-sdk/client-lambda/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A=="],
"@aws-sdk/client-lambda/@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="],
"@aws-sdk/client-lambda/@smithy/types": ["@smithy/types@4.14.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg=="],
"@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/client-sts/@aws-sdk/core": ["@aws-sdk/core@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/core": "^3.2.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/signature-v4": "^5.0.2", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA=="],
@@ -5657,28 +5678,18 @@
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"aws-sdk/events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="],
"aws-sdk/uuid": ["uuid@8.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw=="],
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"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=="],
"bl/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"body-parser/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
"buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"builder-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"builder-util-runtime/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"c12/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
@@ -5735,8 +5746,6 @@
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
@@ -5839,22 +5848,14 @@
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"opencode/@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-/j2igE0xyNaHhj6kMfcUQn5rAVSTLbAX+CDEBm25hSNBmNiHLu2lM7Usj2kJJ5j36D67bE8wR1hBNA8hjtvsQA=="],
"opencode/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
"opencode-gitlab-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"opencode-poe-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
"opencontrol/zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
@@ -5901,8 +5902,6 @@
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"readdir-glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
"restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
@@ -5913,10 +5912,6 @@
"router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
@@ -5929,8 +5924,6 @@
"shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
"sitemap/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
@@ -5973,9 +5966,11 @@
"unused-filename/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
"uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Enm5UlL0zUCrW3792opk5h7hRWxZOZzDe6eQYVFqX9LUOGGCe1h8MZWAGim765nwzgnjlpeYOsuzZmLtRsTPlg=="],
"venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"venice-ai-sdk-provider/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
"venice-ai-sdk-provider/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
"vite-plugin-icons-spritesheet/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
@@ -5993,8 +5988,6 @@
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
"which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
@@ -6003,8 +5996,6 @@
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="],
"yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
@@ -6025,6 +6016,12 @@
"@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
"@ai-sdk/amazon-bedrock/@smithy/eventstream-codec/@smithy/types": ["@smithy/types@4.14.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg=="],
"@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@ai-sdk/azure/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
@@ -6035,6 +6032,14 @@
"@ai-sdk/deepinfra/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@ai-sdk/google-vertex/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@ai-sdk/google-vertex/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
"@ai-sdk/google/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@ai-sdk/google/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
"@ai-sdk/groq/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@ai-sdk/mistral/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
@@ -6069,6 +6074,36 @@
"@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.6", "", { "dependencies": { "@aws-sdk/types": "^3.973.7", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-endpoints": "^3.3.4", "tslib": "^2.6.2" } }, "sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.24", "", { "dependencies": { "@nodable/entities": "2.1.0", "@smithy/types": "^4.14.1", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.37", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.39", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-env": "^3.972.37", "@aws-sdk/credential-provider-http": "^3.972.39", "@aws-sdk/credential-provider-login": "^3.972.41", "@aws-sdk/credential-provider-process": "^3.972.37", "@aws-sdk/credential-provider-sso": "^3.972.41", "@aws-sdk/credential-provider-web-identity": "^3.972.41", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/credential-provider-imds": "^4.3.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.37", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@aws-sdk/types/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@smithy/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@smithy/fetch-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@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=="],
@@ -6239,8 +6274,6 @@
"@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@azure/core-http/xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@azure/identity/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
@@ -6553,14 +6586,10 @@
"babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
"crc/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="],
@@ -6603,6 +6632,8 @@
"lazystream/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
@@ -6617,18 +6648,6 @@
"opencode-poe-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
"opencontrol/@modelcontextprotocol/sdk/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=="],
"opencontrol/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
"opencontrol/@modelcontextprotocol/sdk/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
"opencontrol/@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
@@ -6641,8 +6660,6 @@
"pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
"restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
@@ -6669,6 +6686,10 @@
"unplugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
"venice-ai-sdk-provider/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"venice-ai-sdk-provider/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
"vitest/@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
@@ -6761,6 +6782,18 @@
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.7.3", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.7", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
"@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=="],
@@ -6957,30 +6990,6 @@
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"opencontrol/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
"opencontrol/@modelcontextprotocol/sdk/express/content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
"opencontrol/@modelcontextprotocol/sdk/express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"opencontrol/@modelcontextprotocol/sdk/express/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
"opencontrol/@modelcontextprotocol/sdk/express/finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
"opencontrol/@modelcontextprotocol/sdk/express/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
"opencontrol/@modelcontextprotocol/sdk/express/merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
"opencontrol/@modelcontextprotocol/sdk/express/send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
"opencontrol/@modelcontextprotocol/sdk/express/serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
"opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
@@ -7003,6 +7012,16 @@
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/fast-xml-builder": ["fast-xml-builder@1.2.0", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
@@ -7077,8 +7096,6 @@
"js-beautify/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"opencontrol/@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
@@ -7087,6 +7104,12 @@
"tw-to-css/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
+11 -11
View File
@@ -4,7 +4,7 @@ import { domain } from "./stage"
const description = "Managed by SST (Don't edit in Honeycomb UI)"
const alertsDisabled = $app.stage !== "production"
const webhookRecipient = new honeycomb.WebhookRecipient("DiscordAlerts", {
const webhookRecipient = new honeycombio.WebhookRecipient("DiscordAlerts", {
name: $app.stage === "production" ? "Discord Alerts" : `Discord Alerts (${$app.stage})`,
url: `https://${domain}/honeycomb/webhook`,
secret: SECRET.HoneycombWebhookSecret.result,
@@ -67,7 +67,7 @@ IF(
)`,
})
return honeycomb.getQuerySpecificationOutput({
return honeycombio.getQuerySpecificationOutput({
breakdowns: ["model"],
calculatedFields: [failedHttpStatus],
calculations: [
@@ -99,7 +99,7 @@ const providerHttpErrorsQuery = () => {
expression: `IF(GT($llm.error.code, "400"), 1, 0)`,
})
return honeycomb.getQuerySpecificationOutput({
return honeycombio.getQuerySpecificationOutput({
breakdowns: ["provider"],
calculatedFields: [successHttpStatus, failedProviderHttpStatus],
calculations: [
@@ -140,7 +140,7 @@ const modelLowTpsQuery = (product: "go" | "zen") => {
{ column: "tps.output", op: "exists" },
]
return honeycomb.getQuerySpecificationOutput({
return honeycombio.getQuerySpecificationOutput({
breakdowns: ["model"],
calculations: [
{ op: "COUNT", name: "TOTAL", filterCombination: "AND", filters },
@@ -157,7 +157,7 @@ const modelLowTpsQuery = (product: "go" | "zen") => {
}).json
}
new honeycomb.Trigger("IncreasedModelHttpErrorsGo", {
new honeycombio.Trigger("IncreasedModelHttpErrorsGo", {
name: "Increased Model HTTP Errors [Go]",
description,
disabled: alertsDisabled,
@@ -177,7 +177,7 @@ new honeycomb.Trigger("IncreasedModelHttpErrorsGo", {
],
})
new honeycomb.Trigger("IncreasedModelHttpErrorsZen", {
new honeycombio.Trigger("IncreasedModelHttpErrorsZen", {
name: "Increased Model HTTP Errors [Zen]",
description,
disabled: alertsDisabled,
@@ -197,7 +197,7 @@ new honeycomb.Trigger("IncreasedModelHttpErrorsZen", {
],
})
new honeycomb.Trigger("LowModelTpsGo", {
new honeycombio.Trigger("LowModelTpsGo", {
name: "Low Model TPS [Go]",
description,
disabled: alertsDisabled,
@@ -217,7 +217,7 @@ new honeycomb.Trigger("LowModelTpsGo", {
],
})
new honeycomb.Trigger("LowModelTpsZen", {
new honeycombio.Trigger("LowModelTpsZen", {
name: "Low Model TPS [Zen]",
description,
disabled: alertsDisabled,
@@ -237,7 +237,7 @@ new honeycomb.Trigger("LowModelTpsZen", {
],
})
new honeycomb.Trigger("IncreasedProviderHttpErrors", {
new honeycombio.Trigger("IncreasedProviderHttpErrors", {
name: "Increased Provider HTTP Errors",
description,
disabled: alertsDisabled,
@@ -257,11 +257,11 @@ new honeycomb.Trigger("IncreasedProviderHttpErrors", {
],
})
new honeycomb.Trigger("IncreasedFreeTierRequests", {
new honeycombio.Trigger("IncreasedFreeTierRequests", {
name: "Increased Free Tier Requests",
description,
disabled: alertsDisabled,
queryJson: honeycomb.getQuerySpecificationOutput({
queryJson: honeycombio.getQuerySpecificationOutput({
calculations: [{ op: "COUNT" }],
filters: [
{ column: "event_type", op: "=", value: "completions" },
+10 -1
View File
@@ -3,6 +3,7 @@
stdenv,
bun,
nodejs,
darwin,
electron_41,
makeWrapper,
writableTmpDirAsHomeHook,
@@ -14,7 +15,12 @@ let
in
stdenv.mkDerivation (finalAttrs: {
pname = "opencode-desktop";
inherit (opencode) version src node_modules;
inherit (opencode)
version
src
node_modules
patches
;
nativeBuildInputs = [
bun
@@ -23,6 +29,9 @@ stdenv.mkDerivation (finalAttrs: {
writableTmpDirAsHomeHook
] ++ lib.optionals stdenv.hostPlatform.isLinux [
autoPatchelfHook
] ++ lib.optionals stdenv.hostPlatform.isDarwin [
# Ad-hoc sign the .app: --config.mac.identity=null below skips signing.
darwin.autoSignDarwinBinariesHook
];
buildInputs = lib.optionals stdenv.hostPlatform.isLinux [
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-hIarzU3QNIvkwpfnearfsGaBMCSdovkOWAuvX+EBQI8=",
"aarch64-linux": "sha256-qu+86RNsBZhqzE3CEyg4XHD/3Czc/BqHhY3O1vyogtY=",
"aarch64-darwin": "sha256-7sXZdPh/R4fQBBMq36YdpQE6G2ELMZd08BqMhHbhwv4=",
"x86_64-darwin": "sha256-g5QWpX33R143sNR1G9m7UsSA3jygFyJjT7Kjyjo6sCc="
"x86_64-linux": "sha256-szOnLhAI4d3O4NaQvCRBufklmtRU8AR8p5LyFxjlTXo=",
"aarch64-linux": "sha256-mfyL4Pswl6HeObPg+TMK1r7RjvLCMgDWYrFnfHkXGj4=",
"aarch64-darwin": "sha256-XMK5IFCIxlSwH21Nqya8ZkAQ4Skp6GghukIl7NMp+so=",
"x86_64-darwin": "sha256-W7nLMO6qhR32442yMebEJJgaZRNpqTbZ8BJhdqMK3S4="
}
}
+1 -1
View File
@@ -98,7 +98,7 @@
"oxlint-tsgolint": "0.21.0",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "3.18.10",
"sst": "4.13.1",
"turbo": "2.8.13"
},
"dependencies": {
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.15.6",
"version": "1.15.10",
"description": "",
"type": "module",
"exports": {
@@ -42,10 +42,10 @@
},
"dependencies": {
"@kobalte/core": "catalog:",
"@sentry/solid": "catalog:",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/core": "workspace:*",
"@sentry/solid": "catalog:",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3",
"@solid-primitives/audio": "1.4.2",
@@ -54,6 +54,7 @@
"@solid-primitives/i18n": "2.2.1",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.5",
"@solid-primitives/scheduled": "1.5.3",
"@solid-primitives/scroll": "2.1.3",
"@solid-primitives/storage": "catalog:",
"@solid-primitives/timer": "1.4.4",
+9 -14
View File
@@ -48,22 +48,17 @@ import { ErrorPage } from "./pages/error"
import { useCheckServerHealth } from "./utils/server-health"
const HomeRoute = lazy(() => import("@/pages/home"))
const loadSession = () => import("@/pages/session")
const Session = lazy(loadSession)
const Loading = () => <div class="size-full" />
const Session = lazy(() => import("@/pages/session"))
if (typeof location === "object" && /\/session(?:\/|$)/.test(location.pathname)) {
void loadSession()
}
const SessionRoute = () => (
<SessionProviders>
<Session />
</SessionProviders>
const SessionRoute = Object.assign(
() => (
<SessionProviders>
<Session />
</SessionProviders>
),
{ preload: Session.preload },
)
const SessionIndexRoute = () => <Navigate href="session" />
function UiI18nBridge(props: ParentProps) {
const language = useLanguage()
return <I18nProvider value={{ locale: language.intl, t: language.t }}>{props.children}</I18nProvider>
@@ -317,7 +312,7 @@ export function AppInterface(props: {
>
<Route path="/" component={HomeRoute} />
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={SessionIndexRoute} />
<Route path="/" component={() => <Navigate href="session" />} />
<Route path="/session/:id?" component={SessionRoute} />
</Route>
</Dynamic>
@@ -41,9 +41,7 @@ export function DialogConnectProvider(props: { provider: string }) {
})
const provider = createMemo(
() =>
providers.all().find((x) => x.id === props.provider) ??
globalSync.data.provider.all.find((x) => x.id === props.provider)!,
() => providers.all().get(props.provider) ?? globalSync.data.provider.all.get(props.provider)!,
)
const fallback = createMemo<ProviderAuthMethod[]>(() => [
{
@@ -526,7 +524,7 @@ export function DialogConnectProvider(props: { provider: string }) {
const code = createMemo(() => {
const instructions = store.authorization?.instructions
if (instructions?.includes(":")) {
return instructions.split(":")[1]?.trim()
return instructions.split(":").pop()?.trim()
}
return instructions
})
@@ -106,7 +106,7 @@ export function DialogCustomProvider(props: Props) {
form,
t: language.t,
disabledProviders: globalSync.data.config.disabled_providers ?? [],
existingProviderIDs: new Set(globalSync.data.provider.all.map((p) => p.id)),
existingProviderIDs: new Set(globalSync.data.provider.all.keys()),
})
batch(() => {
setForm("err", output.err)
@@ -91,7 +91,7 @@ export const DialogSelectModelUnpaid: Component<{ model?: ModelState }> = (props
<div class="w-full">
<List
class="w-full px-0"
key={(x) => x?.id}
key={(p) => p.id}
items={providers.popular}
activeIcon="plus-small"
sortBy={(a, b) => {
@@ -35,7 +35,7 @@ export const DialogSelectProvider: Component = () => {
key={(x) => x?.id}
items={() => {
language.locale()
return [{ id: CUSTOM_ID, name: customLabel() }, ...providers.all()]
return [{ id: CUSTOM_ID, name: customLabel() }, ...providers.all().values()]
}}
filterKeys={["id", "name"]}
groupBy={(x) => (popularProviders.includes(x.id) ? popularGroup() : otherGroup())}
@@ -52,7 +52,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
}),
)
const metrics = createMemo(() => getSessionContextMetrics(messages(), providers.all()))
const metrics = createMemo(() => getSessionContextMetrics(messages(), [...providers.all().values()]))
const context = createMemo(() => metrics().context)
const cost = createMemo(() => {
return usd().format(metrics().totalCost)
@@ -132,7 +132,7 @@ export function SessionContextTab() {
}),
)
const metrics = createMemo(() => getSessionContextMetrics(messages(), providers.all()))
const metrics = createMemo(() => getSessionContextMetrics(messages(), [...providers.all().values()]))
const ctx = createMemo(() => metrics().context)
const formatter = createMemo(() => createSessionContextFormatter(language.intl()))
@@ -193,6 +193,12 @@ export const SettingsGeneral: Component = () => {
{ initialValue: null as DisplayBackend | null },
)
const [pinchZoom, { mutate: setPinchZoom }] = createResource(
() => (desktop() && platform.getPinchZoomEnabled ? true : false),
() => Promise.resolve(platform.getPinchZoomEnabled?.() ?? false).catch(() => false),
{ initialValue: false },
)
onMount(() => {
void theme.loadThemes()
})
@@ -239,6 +245,13 @@ export const SettingsGeneral: Component = () => {
})
}
const onPinchZoomChange = (checked: boolean) => {
setPinchZoom(checked)
const update = platform.setPinchZoomEnabled?.(checked)
if (!update) return
void update.catch(() => setPinchZoom(!checked))
}
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
{ value: "system", label: language.t("theme.scheme.system") },
{ value: "light", label: language.t("theme.scheme.light") },
@@ -729,6 +742,45 @@ export const SettingsGeneral: Component = () => {
</div>
)
const DisplaySection = () => (
<Show when={desktop()}>
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
<SettingsList>
<SettingsRow
title={language.t("settings.general.row.pinchZoom.title")}
description={language.t("settings.general.row.pinchZoom.description")}
>
<div data-action="settings-pinch-zoom">
<Switch checked={pinchZoom.latest} onChange={onPinchZoomChange} />
</div>
</SettingsRow>
<Show when={linux()}>
<SettingsRow
title={
<div class="flex items-center gap-2">
<span>{language.t("settings.general.row.wayland.title")}</span>
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<Icon name="help" size="small" />
</span>
</Tooltip>
</div>
}
description={language.t("settings.general.row.wayland.description")}
>
<div data-action="settings-wayland">
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</div>
</SettingsRow>
</Show>
</SettingsList>
</div>
</Show>
)
console.log(import.meta.env)
return (
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
@@ -749,31 +801,7 @@ export const SettingsGeneral: Component = () => {
<UpdatesSection />
<Show when={linux()}>
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
<SettingsList>
<SettingsRow
title={
<div class="flex items-center gap-2">
<span>{language.t("settings.general.row.wayland.title")}</span>
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<Icon name="help" size="small" />
</span>
</Tooltip>
</div>
}
description={language.t("settings.general.row.wayland.description")}
>
<div data-action="settings-wayland">
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</div>
</SettingsRow>
</SettingsList>
</div>
</Show>
<DisplaySection />
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
<AdvancedSection />
+114 -15
View File
@@ -22,6 +22,9 @@ import { iife } from "@opencode-ai/core/util/iife"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { Avatar as AvatarV2 } from "@opencode-ai/ui/v2/components/avatar-v2.jsx"
import { displayName, getProjectAvatarSource, projectForSession } from "@/pages/layout/helpers"
import { makeEventListener } from "@solid-primitives/event-listener"
import { StatusPopover } from "./status-popover"
import { SDKProvider } from "@/context/sdk"
type TauriDesktopWindow = {
startDragging?: () => Promise<void>
@@ -222,19 +225,13 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
const navigate = useNavigate()
const homeMatch = useMatch(() => "/")
const openNewSession = () => {
if (params.dir) {
navigate(`/${params.dir}/session`)
return
}
const newSessionHref = () => {
if (params.dir) return `/${params.dir}/session`
const project = layout.projects.list()[0]
if (!project) {
navigate("/")
return
}
if (!project) return "/"
navigate(`/${base64Encode(project.worktree)}/session`)
return `/${base64Encode(project.worktree)}/session`
}
type Tab = { dir: string; sessionId: string; href: string }
@@ -298,6 +295,98 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
() => new Map(projects().flatMap((project) => (project.id ? [[project.id, project] as const] : []))),
)
const currentSessionTab = () => {
if (!params.dir || !params.id) return
const href = makeSessionHref(params.dir, params.id)
return tabsStore.find((tab) => tab.href === href)
}
const closeCurrentSessionTab = () => {
const tab = currentSessionTab()
if (!tab) return false
tabsStoreActions.removeTab(tab.href)
return true
}
const closeNewSessionTab = () => {
if (!(params.dir && !params.id)) return false
const last = tabsStore[tabsStore.length - 1]
if (last) navigate(last.href)
else navigate("/")
return true
}
makeEventListener(
document,
"keydown",
(event) => {
if (!event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return
if (event.key.toLowerCase() !== "w") return
if (!(closeCurrentSessionTab() || closeNewSessionTab())) return
event.preventDefault()
event.stopPropagation()
},
{ capture: true },
)
command.register(() => {
const commands = [
{
id: `tab.prev`,
category: "tab",
title: "",
keybind: `mod+option+ArrowLeft`,
hidden: true,
onSelect: () => {
let index = tabsStore.findIndex((tab) => tab.href === currentSessionTab()?.href)
if (index === -1) return
index -= 1
if (index === -1) index = tabsStore.length - 1
const next = tabsStore[index]
if (next) navigate(next.href)
},
},
{
id: `tab.next`,
category: "tab",
title: "",
keybind: `mod+option+ArrowRight`,
hidden: true,
onSelect: () => {
let index = tabsStore.findIndex((tab) => tab.href === currentSessionTab()?.href)
if (index === -1) return
index += 1
if (index === tabsStore.length) index = 0
const next = tabsStore[index]
if (next) navigate(next.href)
},
},
...Array.from({ length: 9 }, (_, i) => {
const index = i
const number = index + 1
return {
id: `tab.${number}`,
category: "tab",
title: "",
keybind: `mod+${number}`,
disabled: layout.projects.list().length <= index,
hidden: true,
onSelect: () => {
const tab = tabsStore[index]
if (tab) navigate(tab.href)
},
}
}),
]
return commands
})
const tabsEnriched = iife(() => {
const base = mapArray(
() => tabsStore,
@@ -362,7 +451,8 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
size="large"
class="shrink-0"
icon={<IconV2 name="plus" />}
onClick={openNewSession}
as="a"
href={newSessionHref()}
aria-label={language.t("command.session.new")}
/>
}
@@ -375,6 +465,15 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
</Show>
<div class="min-w-0 flex-1" />
</div>
<Show when={currentSessionTab()?.dir} keyed>
{(dir) => (
<SDKProvider directory={dir}>
<Tooltip placement="bottom" value={language.t("status.popover.trigger")}>
<StatusPopover />
</Tooltip>
</SDKProvider>
)}
</Show>
<TitlebarUpdatePill update={props.update} />
<Show when={windows() && !electronWindows()}>
<div data-tauri-decorum-tb class="flex flex-row" />
@@ -578,15 +677,15 @@ function TabNavItem(props: {
const isActive = () => !!match()
return (
<div
class="group relative flex h-7 min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] pl-1.5 pr-8 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
class="group relative flex h-7 min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] pl-1.5 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
data-active={isActive()}
>
<a
href={props.href}
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium leading-none tracking-[-0.04px] text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
>
<ProjectTabAvatar project={props.project} directory={props.directory} />
<span class="truncate">{props.title}</span>
<span class="text-clip">{props.title}</span>
</a>
<div class="absolute right-0 inset-y-0 flex flex-row items-center pr-1 py-1 w-8 pl-2">
@@ -624,7 +723,7 @@ function ProjectTabAvatar(props: { project?: LocalProject; directory: string })
function NewSessionTabItem(props: { href: string; title: string; onClose: () => void }) {
return (
<div class="group relative flex h-7 w-[135px] min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]">
<div class="group relative flex h-7 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]">
<a
href={props.href}
aria-current="page"
+55
View File
@@ -232,6 +232,9 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
throwOnError: true,
})
const dirSyncContexts = new Map<string, ReturnType<typeof createDirSdkContext>>()
const dirSdkContextRefCounts = new Map<string, number>()
return {
url: currentServer.http.url,
client: sdk,
@@ -249,6 +252,58 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
...opts,
})
},
createDirSyncContext: (directory: string) => {
onCleanup(() => {
dirSdkContextRefCounts.set(directory, (dirSdkContextRefCounts.get(directory) ?? 0) - 1)
if (dirSdkContextRefCounts.get(directory) === 0) {
dirSyncContexts.delete(directory)
dirSdkContextRefCounts.delete(directory)
}
})
const cached = dirSyncContexts.get(directory)
if (cached) {
dirSdkContextRefCounts.set(directory, (dirSdkContextRefCounts.get(directory) ?? 0) + 1)
return cached
}
const ctx = createDirSdkContext(directory)
dirSyncContexts.set(directory, ctx)
dirSdkContextRefCounts.set(directory, 1)
return ctx
},
}
},
})
type SDKEventMap = {
[key in Event["type"]]: Extract<Event, { type: key }>
}
function createDirSdkContext(directory: string) {
const globalSDK = useGlobalSDK()
const client = globalSDK.createClient({
directory,
throwOnError: true,
})
const emitter = createGlobalEmitter<SDKEventMap>()
const unsub = globalSDK.event.on(directory, (event) => {
emitter.emit(event.type, event)
})
onCleanup(unsub)
return {
directory,
client,
event: emitter,
get url() {
return globalSDK.url
},
createClient(opts: Parameters<typeof globalSDK.createClient>[0]) {
return globalSDK.createClient(opts)
},
}
}
+4 -11
View File
@@ -1,12 +1,4 @@
import type {
Config,
OpencodeClient,
Path,
Project,
ProviderAuthResponse,
ProviderListResponse,
Todo,
} from "@opencode-ai/sdk/v2/client"
import type { Config, OpencodeClient, Path, Project, ProviderAuthResponse, Todo } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/core/util/path"
import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
@@ -37,6 +29,7 @@ import { createRefreshQueue } from "./global-sync/queue"
import { directoryKey } from "./global-sync/utils"
import { PathKey } from "@/utils/path-key"
import { createDirSyncContext } from "./directory-sync"
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
type GlobalStore = {
ready: boolean
@@ -46,7 +39,7 @@ type GlobalStore = {
session_todo: {
[sessionID: string]: Todo[]
}
provider: ProviderListResponse
provider: NormalizedProviderListResponse
provider_auth: ProviderAuthResponse
config: Config
reload: undefined | "pending" | "complete"
@@ -121,7 +114,7 @@ function createGlobalSync() {
return pathQuery.data ?? EMPTY
},
get provider() {
const EMPTY = { all: [], connected: [], default: {} }
const EMPTY = { all: new Map(), connected: [], default: {} }
if (providerQuery.isLoading) return EMPTY
return providerQuery.data ?? EMPTY
},
@@ -0,0 +1,78 @@
import { describe, expect, test } from "bun:test"
import { createStore } from "solid-js/store"
import { QueryClient } from "@tanstack/solid-query"
import type { Config, OpencodeClient, Project } from "@opencode-ai/sdk/v2/client"
import type { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
import { bootstrapDirectory } from "./bootstrap"
import type { State, VcsCache } from "./types"
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
describe("bootstrapDirectory", () => {
test("marks a loading directory partial during bootstrap and complete after success", async () => {
const [store, setStore] = createStore<State>({
status: "loading",
agent: [],
command: [],
project: "",
projectMeta: undefined,
icon: undefined,
provider_ready: true,
provider,
config: {},
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
session: [],
sessionTotal: 0,
session_status: {},
session_working(id: string) {
return this.session_status[id]?.type !== "idle"
},
session_diff: {},
todo: {},
permission: {},
question: {},
mcp_ready: true,
mcp: {},
lsp_ready: true,
lsp: [],
vcs: undefined,
limit: 5,
message: {},
part: {},
part_text_accum_delta: {},
})
await bootstrapDirectory({
directory: "/project",
global: {
config: {} satisfies Config,
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
project: [{ id: "project", worktree: "/project" } as Project],
provider,
},
sdk: {
app: { agents: async () => ({ data: [{ name: "build", mode: "primary" }] }) },
config: { get: async () => ({ data: {} }) },
session: { status: async () => ({ data: {} }) },
vcs: { get: async () => ({ data: undefined }) },
command: { list: async () => ({ data: [] }) },
permission: { list: async () => ({ data: [] }) },
question: { list: async () => ({ data: [] }) },
mcp: { status: async () => ({ data: {} }) },
provider: { list: async () => ({ data: { all: [], connected: [], default: {} } }) },
} as unknown as OpencodeClient,
store,
setStore,
vcsCache: { setStore() {} } as unknown as VcsCache,
loadSessions() {},
translate: (key) => key,
queryClient: new QueryClient(),
})
expect(store.status).toBe("partial")
await new Promise((resolve) => setTimeout(resolve, 80))
expect(store.status).toBe("complete")
})
})
@@ -5,7 +5,6 @@ import type {
PermissionRequest,
Project,
ProviderAuthResponse,
ProviderListResponse,
QuestionRequest,
Session,
Todo,
@@ -20,6 +19,7 @@ import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
import { formatServerError } from "@/utils/server-errors"
import { QueryClient, queryOptions } from "@tanstack/solid-query"
import { loadMcpQuery } from "../global-sync"
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
type GlobalStore = {
ready: boolean
@@ -28,7 +28,7 @@ type GlobalStore = {
session_todo: {
[sessionID: string]: Todo[]
}
provider: ProviderListResponse
provider: NormalizedProviderListResponse
provider_auth: ProviderAuthResponse
config: Config
reload: undefined | "pending" | "complete"
@@ -208,7 +208,7 @@ export async function bootstrapDirectory(input: {
config: Config
path: Path
project: Project[]
provider: ProviderListResponse
provider: NormalizedProviderListResponse
}
queryClient: QueryClient
}) {
@@ -1,10 +1,63 @@
import { describe, expect, test } from "bun:test"
import { createRoot, getOwner } from "solid-js"
import { beforeAll, describe, expect, mock, test } from "bun:test"
import { createRoot, getOwner, type Owner } from "solid-js"
import { createStore } from "solid-js/store"
import type { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
import type { State } from "./types"
import { createChildStoreManager } from "./child-store"
import type { QueryOptionsApi } from "../global-sync"
let createChildStoreManager: typeof import("./child-store").createChildStoreManager
const child = () => createStore({} as State)
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
const queryOptionsApi = {
globalConfig: () => ({ queryKey: ["globalConfig"], queryFn: async () => ({}) }),
projects: () => ({ queryKey: ["projects"], queryFn: async () => [] }),
providers: (directory: string | null) => ({ queryKey: [directory, "providers"], queryFn: async () => provider }),
path: (directory: string | null) => ({
queryKey: [directory, "path"],
queryFn: async () => ({
state: "",
config: "",
worktree: "",
directory: directory ?? "",
home: "",
}),
}),
agents: (directory: string) => ({ queryKey: [directory, "agents"], queryFn: async () => [] }),
mcp: (directory: string) => ({ queryKey: [directory, "mcp"], queryFn: async () => ({}) }),
lsp: (directory: string) => ({ queryKey: [directory, "lsp"], queryFn: async () => [] }),
sessions: (directory: string) => ({ queryKey: [directory, "loadSessions"] as const }),
} as unknown as QueryOptionsApi
function createOwner(callback: (owner: Owner) => void) {
return createRoot((dispose) => {
const owner = getOwner()
if (!owner) throw new Error("owner required")
callback(owner)
return dispose
})
}
beforeAll(async () => {
mock.module("@/utils/persist", () => ({
Persist: {
workspace: (...parts: string[]) => parts.join(":"),
},
persisted: (_target: string, store: unknown[]) => [store[0], store[1], null, () => true],
}))
mock.module("@tanstack/solid-query", () => ({
useQueries: () => [
{ isLoading: false, data: { state: "", config: "", worktree: "", directory: "", home: "" } },
{ isLoading: false, data: {} },
{ isLoading: false, data: [] },
{ isLoading: false, data: provider },
],
}))
createChildStoreManager = (await import("./child-store")).createChildStoreManager
})
describe("createChildStoreManager", () => {
test("does not evict the active directory during mark", () => {
@@ -22,8 +75,8 @@ describe("createChildStoreManager", () => {
onBootstrap() {},
onDispose() {},
translate: (key) => key,
queryOptions: {} as any,
global: { provider: null! },
queryOptions: queryOptionsApi,
global: { provider },
})
Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {
@@ -37,4 +90,35 @@ describe("createChildStoreManager", () => {
expect(manager.children[directory]).toBeDefined()
})
test("starts new child stores as loading and bootstraps them on first access", () => {
const bootstraps: string[] = []
let manager: ReturnType<typeof createChildStoreManager> | undefined
const dispose = createOwner((owner) => {
manager = createChildStoreManager({
owner,
isBooting: () => false,
isLoadingSessions: () => false,
onBootstrap(directory) {
bootstraps.push(directory)
},
onDispose() {},
translate: (key) => key,
queryOptions: queryOptionsApi,
global: { provider },
})
})
try {
if (!manager) throw new Error("manager required")
const [store] = manager.child("/project")
expect(store.status).toBe("loading")
expect(bootstraps).toEqual(["/project"])
} finally {
dispose()
}
})
})
@@ -1,7 +1,7 @@
import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
import { Persist, persisted } from "@/utils/persist"
import type { ProviderListResponse, VcsInfo } from "@opencode-ai/sdk/v2/client"
import type { VcsInfo } from "@opencode-ai/sdk/v2/client"
import {
DIR_IDLE_TTL_MS,
MAX_DIR_STORES,
@@ -17,6 +17,7 @@ import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
import { useQueries } from "@tanstack/solid-query"
import { QueryOptionsApi } from "../global-sync"
import { directoryKey, type DirectoryKey } from "./utils"
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
export function createChildStoreManager(input: {
owner: Owner
@@ -27,7 +28,7 @@ export function createChildStoreManager(input: {
translate: (key: string, vars?: Record<string, string | number>) => string
queryOptions: QueryOptionsApi
global: {
provider: ProviderListResponse
provider: NormalizedProviderListResponse
}
}) {
const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
@@ -190,10 +191,9 @@ export function createChildStoreManager(input: {
return !providerQuery.isLoading
},
get provider() {
const EMPTY = { all: [], connected: [], default: {} }
const EMPTY = { all: new Map(), connected: [], default: {} }
if (providerQuery.isLoading) return EMPTY
if (providerQuery.data?.all.length === 0 && input.global.provider.all.length > 0)
return input.global.provider
if (providerQuery.data?.all.size === 0 && input.global.provider.all.size > 0) return input.global.provider
return providerQuery.data ?? EMPTY
},
config: {},
@@ -8,7 +8,6 @@ import type {
Part,
Path,
PermissionRequest,
ProviderListResponse,
QuestionRequest,
Session,
SessionStatus,
@@ -16,6 +15,7 @@ import type {
Todo,
VcsInfo,
} from "@opencode-ai/sdk/v2/client"
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
import type { Accessor } from "solid-js"
import type { SetStoreFunction, Store } from "solid-js/store"
@@ -38,7 +38,7 @@ export type State = {
projectMeta: ProjectMeta | undefined
icon: string | undefined
provider_ready: boolean
provider: ProviderListResponse
provider: NormalizedProviderListResponse
config: Config
path: Path
session: Session[]
+16 -5
View File
@@ -1,4 +1,5 @@
import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
@@ -17,13 +18,23 @@ export function normalizeAgentList(input: unknown): Agent[] {
return Object.values(input).filter(isAgent)
}
export function normalizeProviderList(input: ProviderListResponse): ProviderListResponse {
export function normalizeProviderList(input: ProviderListResponse): NormalizedProviderListResponse {
return {
...input,
all: input.all.map((provider) => ({
...provider,
models: Object.fromEntries(Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated")),
})),
all: new Map(
input.all.map(
(provider) =>
[
provider.id,
{
...provider,
models: Object.fromEntries(
Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
),
},
] as const,
),
),
}
}
+1 -1
View File
@@ -90,7 +90,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
})
const validModel = (model: ModelKey) => {
const provider = providers.all().find((item) => item.id === model.providerID)
const provider = providers.all().get(model.providerID)
return !!provider?.models[model.modelID] && connected().has(model.providerID)
}
+6
View File
@@ -93,6 +93,12 @@ export type Platform = {
/** Webview zoom level (desktop only) */
webviewZoom?: Accessor<number>
/** Get whether native pinch/Ctrl-scroll zoom gestures are enabled (desktop only) */
getPinchZoomEnabled?(): Promise<boolean> | boolean
/** Allow native pinch/Ctrl-scroll zoom gestures (desktop only) */
setPinchZoomEnabled?(enabled: boolean): Promise<void> | void
/** Run a desktop-only menu action from the app chrome */
runDesktopMenuAction?(action: DesktopMenuAction): Promise<void> | void
+2 -40
View File
@@ -1,49 +1,11 @@
import type { Event } from "@opencode-ai/sdk/v2/client"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { type Accessor, createEffect, createMemo, onCleanup } from "solid-js"
import { useGlobalSDK } from "./global-sdk"
type SDKEventMap = {
[key in Event["type"]]: Extract<Event, { type: key }>
}
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
name: "SDK",
init: (props: { directory: Accessor<string> }) => {
init: (props: { directory: string }) => {
const globalSDK = useGlobalSDK()
const directory = createMemo(props.directory)
const client = createMemo(() =>
globalSDK.createClient({
directory: directory(),
throwOnError: true,
}),
)
const emitter = createGlobalEmitter<SDKEventMap>()
createEffect(() => {
const unsub = globalSDK.event.on(directory(), (event) => {
emitter.emit(event.type, event)
})
onCleanup(unsub)
})
return {
get directory() {
return directory()
},
get client() {
return client()
},
event: emitter,
get url() {
return globalSDK.url
},
createClient(opts: Parameters<typeof globalSDK.createClient>[0]) {
return globalSDK.createClient(opts)
},
}
return globalSDK.createDirSyncContext(props.directory)
},
})
+5 -55
View File
@@ -1,5 +1,4 @@
import { Binary } from "@opencode-ai/core/util/binary"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSync } from "./global-sync"
import { useSDK } from "./sdk"
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
@@ -10,26 +9,8 @@ function sortParts(parts: Part[]) {
return parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id))
}
function runInflight(map: Map<string, Promise<void>>, key: string, task: () => Promise<void>) {
const pending = map.get(key)
if (pending) return pending
const promise = task().finally(() => {
map.delete(key)
})
map.set(key, promise)
return promise
}
const keyFor = (directory: string, id: string) => `${directory}\n${id}`
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
function merge<T extends { id: string }>(a: readonly T[], b: readonly T[]) {
const map = new Map(a.map((item) => [item.id, item] as const))
for (const item of b) map.set(item.id, item)
return [...map.values()].sort((x, y) => cmp(x.id, y.id))
}
type OptimisticStore = {
message: Record<string, Message[] | undefined>
part: Record<string, Part[] | undefined>
@@ -127,40 +108,9 @@ export function applyOptimisticRemove(draft: OptimisticStore, input: OptimisticR
delete draft.part[input.messageID]
}
function setOptimisticAdd(setStore: (...args: unknown[]) => void, input: OptimisticAddInput) {
setStore("message", input.sessionID, (messages: Message[] | undefined) => {
if (!messages) return [input.message]
const result = Binary.search(messages, input.message.id, (m) => m.id)
const next = [...messages]
next.splice(result.index, 0, input.message)
return next
})
setStore("part", input.message.id, sortParts(input.parts))
export const useSync = () => {
const globalSync = useGlobalSync()
const sdk = useSDK()
return globalSync.createDirSyncContext(sdk.directory)
}
function setOptimisticRemove(setStore: (...args: unknown[]) => void, input: OptimisticRemoveInput) {
setStore("message", input.sessionID, (messages: Message[] | undefined) => {
if (!messages) return messages
const result = Binary.search(messages, input.messageID, (m) => m.id)
if (!result.found) return messages
const next = [...messages]
next.splice(result.index, 1)
return next
})
setStore("part", (part: Record<string, Part[] | undefined>) => {
if (!(input.messageID in part)) return part
const next = { ...part }
delete next[input.messageID]
return next
})
}
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
name: "Sync",
init: () => {
const globalSync = useGlobalSync()
const sdk = useSDK()
return globalSync.createDirSyncContext(sdk.directory)
},
})
+22 -5
View File
@@ -1,6 +1,7 @@
import { useGlobalSync } from "@/context/global-sync"
import { decode64 } from "@/utils/base64"
import { useParams } from "@solidjs/router"
import { Iterable, pipe } from "effect"
import { createMemo } from "solid-js"
export const popularProviders = [
@@ -29,16 +30,32 @@ export function useProviders() {
return {
all: () => providers().all,
default: () => providers().default,
popular: () => providers().all.filter((p) => popularProviderSet.has(p.id)),
popular: () =>
pipe(
providers().all,
Iterable.map(([, p]) => p),
Iterable.filter((p) => popularProviderSet.has(p.id)),
(v) => Array.from(v),
),
connected: () => {
const connected = new Set(providers().connected)
return providers().all.filter((p) => connected.has(p.id))
return pipe(
providers().all,
Iterable.map(([, p]) => p),
Iterable.filter((p) => connected.has(p.id)),
(v) => Array.from(v),
)
},
paid: () => {
const connected = new Set(providers().connected)
return providers().all.filter(
(p) => connected.has(p.id) && (p.id !== "opencode" || Object.values(p.models).some((m) => m.cost?.input)),
)
return [
...Iterable.filter(
providers().all,
([id]) =>
connected.has(id) &&
(id !== "opencode" || Object.values(providers().all.get(id)?.models ?? {}).some((m) => m.cost?.input)),
),
]
},
}
}
+2
View File
@@ -784,6 +784,8 @@ export const dict = {
"settings.general.row.showSessionProgressBar.title": "Show session progress bar",
"settings.general.row.showSessionProgressBar.description":
"Display the animated progress bar at the top of the session when the agent is working",
"settings.general.row.pinchZoom.title": "Pinch to zoom",
"settings.general.row.pinchZoom.description": "Allow trackpad pinch and Ctrl-scroll gestures to zoom",
"settings.general.row.wayland.title": "Use native Wayland",
"settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",
+3 -5
View File
@@ -6,7 +6,7 @@ import { createEffect, createMemo, createResource, type ParentProps, Show } from
import { useLanguage } from "@/context/language"
import { LocalProvider } from "@/context/local"
import { SDKProvider } from "@/context/sdk"
import { SyncProvider, useSync } from "@/context/sync"
import { useSync } from "@/context/sync"
import { decode64 } from "@/utils/base64"
import { Schema } from "effect"
@@ -81,10 +81,8 @@ export default function Layout(props: ParentProps) {
return (
<Show when={resolved()} keyed>
{(resolved) => (
<SDKProvider directory={() => resolved}>
<SyncProvider>
<DirectoryDataProvider directory={resolved}>{props.children}</DirectoryDataProvider>
</SyncProvider>
<SDKProvider directory={resolved}>
<DirectoryDataProvider directory={resolved}>{props.children}</DirectoryDataProvider>
</SDKProvider>
)}
</Show>
+57 -181
View File
@@ -1,5 +1,5 @@
import type { Session } from "@opencode-ai/sdk/v2/client"
import { createMemo, createSignal, For, Match, Show, Switch } from "solid-js"
import { createMemo, For, Match, Show, Switch } from "solid-js"
import { createStore } from "solid-js/store"
import { useQuery } from "@tanstack/solid-query"
import { Button } from "@opencode-ai/ui/button"
@@ -18,7 +18,6 @@ import { DateTime } from "luxon"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
import { DialogSelectServer } from "@/components/dialog-select-server"
import { DialogSelectModel } from "@/components/dialog-select-model"
import { useServer } from "@/context/server"
import { useGlobalSync } from "@/context/global-sync"
import { useLanguage } from "@/context/language"
@@ -467,11 +466,6 @@ function LegacyHome() {
const navigate = useNavigate()
const server = useServer()
const language = useLanguage()
const [promptText, setPromptText] = createSignal("")
const [selectedAgent, setSelectedAgent] = createSignal("frontend-specialist")
const [showProjectsDropdown, setShowProjectsDropdown] = createSignal(false)
const homedir = createMemo(() => sync.data.path.home)
const recent = createMemo(() => {
return sync.data.project
@@ -480,8 +474,6 @@ function LegacyHome() {
.slice(0, 5)
})
const currentProject = createMemo(() => recent()[0]?.worktree)
const serverDotClass = createMemo(() => {
const healthy = server.healthy()
if (healthy === true) return "bg-icon-success-base"
@@ -520,185 +512,69 @@ function LegacyHome() {
}
}
function handleModelSelect() {
dialog.show(() => <DialogSelectModel />)
}
function toggleAgent() {
const agents = ["frontend-specialist", "build", "general"]
const nextIndex = (agents.indexOf(selectedAgent()) + 1) % agents.length
setSelectedAgent(agents[nextIndex])
}
function handleSubmit() {
const projectToOpen = currentProject()
if (projectToOpen) {
openProject(projectToOpen)
} else {
chooseProject()
}
}
const activeModelName = createMemo(() => {
const model = sync.data.config.model
if (!model) return "GPT-5.7 Pro"
const parts = model.split("/")
return parts[parts.length - 1]
})
return (
<div class="mx-auto mt-24 w-full max-w-2xl px-6 flex flex-col items-center">
<div class="flex flex-col items-center gap-3 mb-10">
<div onClick={chooseProject} class="cursor-pointer hover:opacity-25 transition-opacity duration-200">
<Logo class="w-48 opacity-15" />
</div>
<Button
size="normal"
variant="ghost"
class="text-12-regular text-text-weak px-3"
onClick={() => dialog.show(() => <DialogSelectServer />)}
>
<div
classList={{
"size-1.5 rounded-full mr-2": true,
[serverDotClass()]: true,
}}
/>
{server.name}
</Button>
</div>
<div class="mx-auto mt-55 w-full md:w-auto px-4">
<Logo class="md:w-xl opacity-12" />
<Button
size="large"
variant="ghost"
class="mt-4 mx-auto text-14-regular text-text-weak"
onClick={() => dialog.show(() => <DialogSelectServer />)}
>
<div
classList={{
"size-2 rounded-full": true,
[serverDotClass()]: true,
}}
/>
{server.name}
</Button>
<Switch>
<Match when={recent().length > 0}>
<div class="w-full flex flex-col items-center gap-6">
<div class="text-20-medium text-text-strong text-center">{language.t("session.new.title")}</div>
<div class="w-full bg-surface-base border border-border-base rounded-xl p-4 flex flex-col gap-3 shadow-md relative">
<textarea
class="bg-transparent border-none outline-none text-14-regular text-text-base placeholder-text-weak w-full resize-none h-20 focus:outline-none"
placeholder="Ask anything, / for commands, @ for context..."
value={promptText()}
onInput={(e) => setPromptText(e.currentTarget.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
handleSubmit()
}
}}
/>
<div class="flex flex-wrap items-center gap-2 pt-3 border-t border-border-weak-base">
<Button
size="small"
variant="ghost"
class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
onClick={toggleAgent}
>
<Icon name="sliders" size="small" class="shrink-0" />
<span>Agent: {selectedAgent()}</span>
</Button>
<Button
size="small"
variant="ghost"
class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
onClick={handleModelSelect}
>
<Icon name="brain" size="small" class="shrink-0" />
<span>Model: {activeModelName()}</span>
</Button>
<div class="relative">
<Button
size="small"
variant="ghost"
class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
onClick={() => setShowProjectsDropdown(!showProjectsDropdown())}
>
<Icon name="folder" size="small" class="shrink-0" />
<span>Project: {currentProject() ? getFilename(currentProject()) : "Select Project"}</span>
</Button>
<Show when={showProjectsDropdown()}>
<div class="absolute left-0 mt-1 w-64 bg-surface-raised-base border border-border-base rounded-lg p-2 shadow-lg z-50 flex flex-col gap-1">
<div class="text-10-semibold text-text-weak px-2 py-1 uppercase tracking-wider">
{language.t("home.recentProjects")}
</div>
<For each={recent()}>
{(project) => (
<button
class="text-12-mono text-left px-2 py-1.5 hover:bg-surface-raised-base-hover rounded flex items-center justify-between w-full"
onClick={() => {
openProject(project.worktree)
setShowProjectsDropdown(false)
}}
>
<span class="truncate">{getFilename(project.worktree)}</span>
<span class="text-10-regular text-text-weak shrink-0 pl-2">
{DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
</span>
</button>
)}
</For>
<div class="border-t border-border-weak-base my-1" />
<button
class="text-12-medium text-text-strong text-left px-2 py-1.5 hover:bg-surface-raised-base-hover rounded flex items-center gap-2 w-full"
onClick={() => {
setShowProjectsDropdown(false)
chooseProject()
}}
>
<Icon name="folder-add-left" size="small" />
{language.t("command.project.open")}
</button>
</div>
</Show>
</div>
<Button
size="small"
variant="ghost"
class="text-12-medium text-text-weak flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base border border-border-weak-base rounded-md cursor-default pointer-events-none"
>
<Icon name="branch" size="small" class="shrink-0" />
<span>Branch: dev</span>
</Button>
</div>
<Match when={sync.data.project.length > 0}>
<div class="mt-20 w-full flex flex-col gap-4">
<div class="flex gap-2 items-center justify-between pl-3">
<div class="text-14-medium text-text-strong">{language.t("home.recentProjects")}</div>
<Button icon="folder-add-left" size="normal" class="pl-2 pr-3" onClick={chooseProject}>
{language.t("command.project.open")}
</Button>
</div>
<ul class="flex flex-col gap-2">
<For each={recent()}>
{(project) => (
<Button
size="large"
variant="ghost"
class="text-14-mono text-left justify-between px-3"
onClick={() => openProject(project.worktree)}
>
{project.worktree.replace(homedir(), "~")}
<div class="text-14-regular text-text-weak">
{DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
</div>
</Button>
)}
</For>
</ul>
</div>
</Match>
<Match when={!sync.ready}>
<div class="mt-30 mx-auto flex flex-col items-center gap-3">
<div class="text-12-regular text-text-weak">{language.t("common.loading")}</div>
<Button class="px-3" onClick={chooseProject}>
{language.t("command.project.open")}
</Button>
</div>
</Match>
<Match when={true}>
<div class="w-full flex flex-col items-center gap-6">
<div class="text-20-medium text-text-strong text-center">{language.t("home.empty.title")}</div>
<div class="w-full bg-surface-base border border-border-base rounded-xl p-4 flex flex-col gap-3 shadow-md">
<div class="text-14-regular text-text-weak w-full min-h-[4rem] cursor-pointer" onClick={chooseProject}>
Ask anything, / for commands, @ for context...
</div>
<div class="flex flex-wrap items-center gap-2 pt-3 border-t border-border-weak-base">
<Button
size="small"
variant="ghost"
class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
onClick={chooseProject}
>
<Icon name="folder" size="small" class="shrink-0" />
<span>Open project</span>
</Button>
<Button
size="small"
variant="ghost"
class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
onClick={handleModelSelect}
>
<Icon name="brain" size="small" class="shrink-0" />
<span>Model: {activeModelName()}</span>
</Button>
</div>
<div class="mt-30 mx-auto flex flex-col items-center gap-3">
<Icon name="folder-add-left" size="large" />
<div class="flex flex-col gap-1 items-center justify-center">
<div class="text-14-medium text-text-strong">{language.t("home.empty.title")}</div>
<div class="text-12-regular text-text-weak">{language.t("home.empty.description")}</div>
</div>
<Button class="px-3 mt-1" onClick={chooseProject}>
{language.t("command.project.open")}
</Button>
</div>
</Match>
</Switch>
+20 -18
View File
@@ -89,7 +89,7 @@ import {
import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project"
import { SidebarContent } from "./layout/sidebar-shell"
const USE_HOME_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
const USE_NEW_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
export default function Layout(props: ParentProps) {
const [store, setStore, , ready] = persisted(
@@ -154,7 +154,7 @@ export default function Layout(props: ParentProps) {
const currentDir = createMemo(() => route().dir)
const [state, setState] = createStore({
autoselect: !initialDirectory && !USE_HOME_DESIGN,
autoselect: !initialDirectory && !USE_NEW_DESIGN,
busyWorkspaces: {} as Record<string, boolean>,
hoverProject: undefined as string | undefined,
scrollSessionKey: undefined as string | undefined,
@@ -1028,19 +1028,6 @@ export default function Layout(props: ParentProps) {
keybind: "mod+alt+arrowdown",
onSelect: () => navigateProjectByOffset(1),
},
...Array.from({ length: 9 }, (_, i) => {
const index = i
const number = index + 1
return {
id: `project.${number}`,
category: language.t("command.category.project"),
title: `Open Project {number}`,
keybind: `mod+${number}`,
disabled: layout.projects.list().length <= index,
hidden: true,
onSelect: () => navigateToProjectIndex(index),
}
}),
{
id: "provider.connect",
title: language.t("command.provider.connect"),
@@ -1155,6 +1142,21 @@ export default function Layout(props: ParentProps) {
},
]
if (!USE_NEW_DESIGN)
Array.from({ length: 9 }, (_, i) => {
const index = i
const number = index + 1
commands.push({
id: `project.${number}`,
category: language.t("command.category.project"),
title: `Open Project {number}`,
keybind: `mod+${number}`,
disabled: layout.projects.list().length <= index,
hidden: true,
onSelect: () => navigateToProjectIndex(index),
})
})
for (const [id] of availableThemeEntries()) {
commands.push({
id: `theme.set.${id}`,
@@ -1819,7 +1821,7 @@ export default function Layout(props: ParentProps) {
createEffect(() => {
document.documentElement.style.setProperty(
"--dialog-left-margin",
USE_HOME_DESIGN ? "0px" : `${layout.sidebar.opened() ? layout.sidebar.width() : 48}px`,
USE_NEW_DESIGN ? "0px" : `${layout.sidebar.opened() ? layout.sidebar.width() : 48}px`,
)
})
@@ -2303,7 +2305,7 @@ export default function Layout(props: ParentProps) {
<div
class="shrink-0 px-3 py-3"
classList={{
hidden: store.gettingStartedDismissed || !(providers.all().length > 0 && providers.paid().length === 0),
hidden: store.gettingStartedDismissed || !(providers.all().size > 0 && providers.paid().length === 0),
}}
>
<div class="rounded-xl bg-background-base shadow-xs-border-base" data-component="getting-started">
@@ -2361,7 +2363,7 @@ export default function Layout(props: ParentProps) {
/>
)
if (USE_HOME_DESIGN) {
if (USE_NEW_DESIGN) {
return (
<div class="relative bg-v2-background-bg-deep flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
{autoselecting() ?? ""}
+9 -5
View File
@@ -18,6 +18,7 @@ import {
import { makeEventListener } from "@solid-primitives/event-listener"
import { createMediaQuery } from "@solid-primitives/media"
import { createResizeObserver } from "@solid-primitives/resize-observer"
import { debounce } from "@solid-primitives/scheduled"
import { useLocal } from "@/context/local"
import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file"
import { createStore } from "solid-js/store"
@@ -58,6 +59,7 @@ import { SessionSidePanel } from "@/pages/session/session-side-panel"
import { TerminalPanel } from "@/pages/session/terminal-panel"
import { useSessionCommands } from "@/pages/session/use-session-commands"
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
import { shouldUseV2NewSessionPage } from "@/pages/session/new-session-layout"
import { Identifier } from "@/utils/id"
import { diffs as list } from "@/utils/diffs"
import { Persist, persisted } from "@/utils/persist"
@@ -262,8 +264,10 @@ export default function Page() {
const isDesktop = createMediaQuery("(min-width: 768px)")
const size = createSizing()
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
const desktopFileTreeOpen = createMemo(() => isDesktop() && layout.fileTree.opened())
const isV2NewSessionPage = () =>
shouldUseV2NewSessionPage({ channel: import.meta.env.VITE_OPENCODE_CHANNEL, sessionID: params.id })
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened() && !isV2NewSessionPage())
const desktopFileTreeOpen = createMemo(() => isDesktop() && layout.fileTree.opened() && !isV2NewSessionPage())
const desktopSidePanelOpen = createMemo(() => desktopReviewOpen() || desktopFileTreeOpen())
const sessionPanelWidth = createMemo(() => {
if (!desktopSidePanelOpen()) return "100%"
@@ -477,7 +481,7 @@ export default function Page() {
: skipToken,
}
})
const refreshVcs = () => void queryClient.invalidateQueries({ queryKey: vcsKey() })
const refreshVcs = debounce(() => void queryClient.invalidateQueries({ queryKey: vcsKey() }), 100)
const reviewDiffs = () => {
if (store.changes === "git" || store.changes === "branch")
// avoids suspense
@@ -1733,12 +1737,12 @@ export default function Page() {
</Tabs>
</Show>
{/* Session panel */}
<div
classList={{
"@container relative shrink-0 flex flex-col min-h-0 h-full bg-background-stronger flex-1 md:flex-none": true,
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!size.active() && !ui.reviewSnap,
"transition-[width]": !isV2NewSessionPage(),
}}
style={{
width: sessionPanelWidth(),
@@ -0,0 +1,14 @@
import { describe, expect, test } from "bun:test"
import { shouldUseV2NewSessionPage } from "./new-session-layout"
describe("shouldUseV2NewSessionPage", () => {
test("keeps prod session pages on the legacy layout", () => {
expect(shouldUseV2NewSessionPage({ channel: "prod", sessionID: "ses_123" })).toBe(false)
expect(shouldUseV2NewSessionPage({ channel: "prod" })).toBe(false)
})
test("uses the v2 layout only for non-prod new-session pages", () => {
expect(shouldUseV2NewSessionPage({ channel: "dev" })).toBe(true)
expect(shouldUseV2NewSessionPage({ channel: "dev", sessionID: "ses_123" })).toBe(false)
})
})
@@ -0,0 +1,3 @@
export function shouldUseV2NewSessionPage(input: { channel?: "dev" | "beta" | "prod"; sessionID?: string }) {
return input.channel !== "prod" && !input.sessionID
}
@@ -55,7 +55,7 @@ export function SessionSidePanel(props: {
const language = useLanguage()
const command = useCommand()
const dialog = useDialog()
const { sessionKey, tabs, view } = useSessionLayout()
const { sessionKey, tabs, view, params } = useSessionLayout()
const isDesktop = createMediaQuery("(min-width: 768px)")
const shown = createMemo(
@@ -207,7 +207,7 @@ export function SessionSidePanel(props: {
})
return (
<Show when={isDesktop()}>
<Show when={isDesktop() && !(import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" && !params.id)}>
<aside
id="review-panel"
aria-label={language.t("session.panel.reviewAndFiles")}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.15.6",
"version": "1.15.10",
"type": "module",
"license": "MIT",
"scripts": {
+2 -2
View File
@@ -348,8 +348,8 @@ export const dict = {
"Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
"zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
"zen.api.error.modelNotSupported": "Model {{model}} not supported",
"zen.api.error.modelFormatNotSupported": "Model {{model}} not supported for format {{format}}",
"zen.api.error.modelNotSupported": "Model {{model}} is not supported",
"zen.api.error.modelFormatNotSupported": "Model {{model}} is not supported for format {{format}}",
"zen.api.error.noProviderAvailable": "No provider available",
"zen.api.error.providerNotSupported": "Provider {{provider}} not supported",
"zen.api.error.missingApiKey": "Missing API key.",
@@ -10,9 +10,12 @@ export function createRateLimiter(modelId: string, rateLimit: number | undefined
const dict = i18n(localeFromRequest(request))
const limits = Subscription.getFreeLimits()
const headersExist = Object.entries(limits.checkHeaders).every(
([name, value]) => request.headers.get(name)?.toLowerCase().includes(value) ?? false,
)
// temporarily disable check headers
//const headersExist = Object.entries(limits.checkHeaders).every(
// ([name, value]) => request.headers.get(name)?.toLowerCase().includes(value) ?? false,
//)
//const dailyLimit = !headersExist ? limits.dailyRequestsFallback : (rateLimit ?? limits.dailyRequests)
const headersExist = true
const dailyLimit = !headersExist ? limits.dailyRequestsFallback : (rateLimit ?? limits.dailyRequests)
const isDefaultModel = headersExist && !rateLimit
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.15.6",
"version": "1.15.10",
"private": true,
"type": "module",
"license": "MIT",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.15.6",
"version": "1.15.10",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.15.6",
"version": "1.15.10",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -5,6 +5,11 @@ export const Resource = new Proxy(
{},
{
get(_target, prop: string) {
if (`SST_RESOURCE_${prop}` in env) {
// @ts-expect-error
const value = env[`SST_RESOURCE_${prop}`]
return typeof value === "string" ? JSON.parse(value) : value
}
if (prop in env) {
// @ts-expect-error
const value = env[prop]
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
+6 -6
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.10",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
@@ -27,15 +27,15 @@
},
"dependencies": {
"@ai-sdk/alibaba": "1.0.17",
"@ai-sdk/amazon-bedrock": "4.0.96",
"@ai-sdk/amazon-bedrock": "4.0.107",
"@ai-sdk/anthropic": "3.0.71",
"@ai-sdk/azure": "3.0.49",
"@ai-sdk/cerebras": "2.0.41",
"@ai-sdk/cohere": "3.0.27",
"@ai-sdk/deepinfra": "2.0.41",
"@ai-sdk/gateway": "3.0.104",
"@ai-sdk/google": "3.0.63",
"@ai-sdk/google-vertex": "4.0.112",
"@ai-sdk/google": "3.0.75",
"@ai-sdk/google-vertex": "4.0.131",
"@ai-sdk/groq": "3.0.31",
"@ai-sdk/mistral": "3.0.27",
"@ai-sdk/openai": "3.0.53",
@@ -59,7 +59,7 @@
"ai-gateway-provider": "3.1.2",
"cross-spawn": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"immer": "11.1.4",
@@ -67,7 +67,7 @@
"minimatch": "10.2.5",
"npm-package-arg": "13.0.2",
"semver": "^7.6.3",
"venice-ai-sdk-provider": "2.0.1",
"venice-ai-sdk-provider": "2.0.2",
"xdg-basedir": "5.1.0",
"zod": "catalog:"
},
@@ -15,6 +15,7 @@ type ServiceUse<Identifier, Shape> = {
}
export const serviceUse = <Identifier, Shape>(tag: Context.Service<Identifier, Shape>) => {
const cache = new Map<string, (...args: unknown[]) => Effect.Effect<unknown, unknown, unknown>>()
// This is the only dynamic boundary: TypeScript knows the accessor shape,
// but Proxy property names are runtime values.
const access = new Proxy(
@@ -22,7 +23,9 @@ export const serviceUse = <Identifier, Shape>(tag: Context.Service<Identifier, S
{
get: (_, key) => {
if (typeof key !== "string") return undefined
return (...args: unknown[]) =>
const cached = cache.get(key)
if (cached) return cached
const accessor = (...args: unknown[]) =>
tag.use((service) => {
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- Proxy keys are checked at runtime.
const method = service[key as keyof Shape]
@@ -30,6 +33,8 @@ export const serviceUse = <Identifier, Shape>(tag: Context.Service<Identifier, S
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- ServiceUse exposes only Effect-returning methods.
return (method as (...args: unknown[]) => Effect.Effect<unknown, unknown, unknown>)(...args)
})
cache.set(key, accessor)
return accessor
},
},
)
+4 -1
View File
@@ -3,9 +3,10 @@ import { dirname, join, relative, resolve as pathResolve } from "path"
import { realpathSync } from "fs"
import * as NFS from "fs/promises"
import { lookup } from "mime-types"
import { Effect, FileSystem, Layer, Schema, Context } from "effect"
import { Context, Effect, FileSystem, Layer, Schema } from "effect"
import type { PlatformError } from "effect/PlatformError"
import { Glob } from "./util/glob"
import { serviceUse } from "./effect/service-use"
export namespace AppFileSystem {
export class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {
@@ -39,6 +40,8 @@ export namespace AppFileSystem {
export class Service extends Context.Service<Service, Interface>()("@opencode/FileSystem") {}
export const use = serviceUse(Service)
export const layer = Layer.effect(
Service,
Effect.gen(function* () {
+114
View File
@@ -0,0 +1,114 @@
export * as Git from "./git"
import path from "path"
import { Context, Effect, Layer } from "effect"
import { ChildProcess } from "effect/unstable/process"
import { AbsolutePath } from "./schema"
import { AppFileSystem } from "./filesystem"
import { AppProcess } from "./process"
export interface Repo {
/**
* The root directory of the working tree that contains the input path.
*
* For `/home/me/app/src/file.ts` in a normal clone, this is `/home/me/app`.
* For `/home/me/app-feature/src/file.ts` in a linked worktree, this is
* `/home/me/app-feature`.
*/
readonly directory: AbsolutePath
/**
* The shared Git storage directory used by this repo and any linked worktrees.
*
* For a normal clone at `/home/me/app`, this is usually `/home/me/app/.git`.
* For a linked worktree at `/home/me/app-feature` whose main checkout is
* `/home/me/app`, this is usually `/home/me/app/.git`.
*/
readonly store: AbsolutePath
}
export interface Interface {
readonly find: (input: AbsolutePath) => Effect.Effect<Repo | undefined>
readonly remote: (repo: Repo, name?: string) => Effect.Effect<string | undefined>
readonly roots: (repo: Repo) => Effect.Effect<string[]>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/GitV2") {}
export const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
const proc = yield* AppProcess.Service
const find = Effect.fn("Git.find")(function* (input: AbsolutePath) {
const dotgit = yield* fs.up({ targets: [".git"], start: input }).pipe(
Effect.map((matches) => matches[0]),
Effect.catch(() => Effect.succeed(undefined)),
)
if (!dotgit) return undefined
const cwd = path.dirname(dotgit)
const git = run(cwd, proc)
const topLevel = yield* git(["rev-parse", "--show-toplevel"])
const commonDir = yield* git(["rev-parse", "--git-common-dir"])
if (commonDir.exitCode !== 0) return undefined
return {
directory: AbsolutePath.make(topLevel.exitCode === 0 ? resolvePath(cwd, topLevel.text) : cwd),
store: AbsolutePath.make(resolvePath(cwd, commonDir.text)),
} satisfies Repo
})
const remote = Effect.fn("Git.remote")(function* (repo: Repo, name = "origin") {
const result = yield* run(repo.directory, proc)(["remote", "get-url", name])
if (result.exitCode !== 0) return undefined
return result.text.trim() || undefined
})
const roots = Effect.fn("Git.roots")(function* (repo: Repo) {
const result = yield* run(repo.directory, proc)(["rev-list", "--max-parents=0", "HEAD"])
if (result.exitCode !== 0) return []
return result.text
.split("\n")
.map((item) => item.trim())
.filter(Boolean)
.toSorted()
})
return Service.of({ find, remote, roots })
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(AppProcess.defaultLayer),
)
interface Result {
readonly exitCode: number
readonly text: string
}
function run(cwd: string, proc: AppProcess.Interface) {
return (args: string[]) =>
proc
.run(
ChildProcess.make("git", args, {
cwd,
extendEnv: true,
stdin: "ignore",
}),
)
.pipe(
Effect.map((result) => ({ exitCode: result.exitCode, text: result.stdout.toString("utf8") }) satisfies Result),
Effect.catch(() => Effect.succeed({ exitCode: 1, text: "" } satisfies Result)),
)
}
function resolvePath(cwd: string, value: string) {
const trimmed = value.replace(/[\r\n]+$/, "")
if (!trimmed) return cwd
const normalized = AppFileSystem.windowsPath(trimmed)
if (path.isAbsolute(normalized)) return path.normalize(normalized)
return path.resolve(cwd, normalized)
}
+1 -1
View File
@@ -1,6 +1,6 @@
export * as PluginBoot from "./boot"
import { Context, Deferred, Effect, Layer, Scope } from "effect"
import { Context, Deferred, Effect, Layer } from "effect"
import { AccountV2 } from "../account"
import { AgentV2 } from "../agent"
import { Catalog } from "../catalog"
@@ -25,7 +25,8 @@ function resolveLocation(options: Record<string, any>) {
}
function vertexEndpoint(location: string) {
return location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`
if (location === "global") return "aiplatform.googleapis.com"
return `${location}-aiplatform.googleapis.com`
}
function replaceVertexVars(value: string, project: string | undefined, location: string) {
@@ -131,16 +132,25 @@ export const GoogleVertexAnthropicPlugin = PluginV2.define({
"aisdk.sdk": Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/google-vertex/anthropic") return
const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex/anthropic"))
const project =
typeof evt.options.project === "string"
? evt.options.project
: (process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCP_PROJECT ?? process.env.GCLOUD_PROJECT)
const location =
typeof evt.options.location === "string"
? evt.options.location
: (process.env.GOOGLE_CLOUD_LOCATION ?? process.env.VERTEX_LOCATION ?? "global")
evt.sdk = mod.createVertexAnthropic({
...evt.options,
project:
typeof evt.options.project === "string"
? evt.options.project
: (process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCP_PROJECT ?? process.env.GCLOUD_PROJECT),
location:
typeof evt.options.location === "string"
? evt.options.location
: (process.env.GOOGLE_CLOUD_LOCATION ?? process.env.VERTEX_LOCATION ?? "global"),
project,
location,
// Continental multi-regions (eu, us) require Regional Endpoint Platform
// domains; the default {region}-aiplatform.googleapis.com does not resolve.
...((location === "eu" || location === "us") && project && !evt.options.baseURL
? {
baseURL: `https://aiplatform.${location}.rep.googleapis.com/v1/projects/${project}/locations/${location}/publishers/anthropic/models`,
}
: {}),
})
}),
"aisdk.language": Effect.fn(function* (evt) {
@@ -1,6 +1,5 @@
import { Effect } from "effect"
import { PluginV2 } from "../../plugin"
import { ProviderV2 } from "../../provider"
export const VercelPlugin = PluginV2.define({
id: PluginV2.ID.make("vercel"),
+129
View File
@@ -0,0 +1,129 @@
export * as Project from "./project"
import { Context, Effect, Layer, Schema } from "effect"
import path from "path"
import { AbsolutePath, withStatics } from "./schema"
import { AppFileSystem } from "./filesystem"
import { Git } from "./git"
import { Hash } from "./util/hash"
export const ID = Schema.String.pipe(
Schema.brand("Project.ID"),
withStatics((schema) => ({
global: schema.make("global"),
})),
)
export type ID = typeof ID.Type
export const Vcs = Schema.Union([
Schema.Struct({
type: Schema.Literal("git"),
store: AbsolutePath,
}),
])
export type Vcs = typeof Vcs.Type
export class Info extends Schema.Class<Info>("Project.Info")({
id: ID,
vcs: Schema.optional(Vcs),
}) {}
export interface Interface {
readonly resolve: (input: AbsolutePath) => Effect.Effect<
{
previous?: ID
id: ID
directory: AbsolutePath
vcs?: Vcs
},
never
>
/**
* Temporary bridge method for writing the resolved project ID to the repo-local cache.
*
* This exists while the old opencode project service and this core project
* service work together: core resolves the ID, while the old service still owns
* database migration and persistence. The old service should call this after it
* finishes migrating from `resolve().previous` to `resolve().id`; once project
* persistence moves into core, this separate bridge method can go away.
*/
readonly commit: (input: { store: AbsolutePath; id: ID }) => Effect.Effect<void>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/ProjectV2") {}
export const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
const git = yield* Git.Service
const cached = Effect.fnUntraced(function* (dir: string) {
return yield* fs.readFileString(path.join(dir, "opencode")).pipe(
Effect.map((value) => value.trim()),
Effect.map((value) => (value ? ID.make(value) : undefined)),
Effect.catch(() => Effect.succeed(undefined)),
)
})
const remote = Effect.fnUntraced(function* (repo: Git.Repo) {
const origin = yield* git.remote(repo)
if (!origin) return undefined
const normalized = url(origin)
if (!normalized) return undefined
return ID.make(Hash.fast(`git-remote:${normalized}`))
})
function url(input: string) {
const value = input.trim()
if (!value) return undefined
try {
const parsed = new URL(value)
if (parsed.protocol === "file:") return undefined
return parts(parsed.hostname, parsed.pathname)
} catch {
const scp = value.match(/^([^@/:]+@)?([^/:]+):(.+)$/)
if (scp) return parts(scp[2], scp[3])
return undefined
}
}
function parts(host: string, name: string) {
const pathname = name
.replace(/^\/+/, "")
.replace(/\.git\/?$/, "")
.replace(/\/+$/, "")
if (!host || !pathname) return undefined
return `${host.toLowerCase()}/${pathname}`
}
const root = Effect.fnUntraced(function* (repo: Git.Repo) {
const root = (yield* git.roots(repo))[0]
return root ? ID.make(root) : undefined
})
const resolve = Effect.fn("Project.resolve")(function* (input: AbsolutePath) {
const repo = yield* git.find(input)
if (!repo) return { id: ID.global, directory: input, vcs: undefined }
const previous = yield* cached(repo.store)
const id = previous ?? (yield* root(repo))
return {
previous,
id: id ?? ID.global,
directory: repo.directory,
vcs: { type: "git" as const, store: repo.store },
}
})
const commit = Effect.fn("Project.commit")(function* (input: { store: AbsolutePath; id: ID }) {
yield* fs.writeFileString(path.join(input.store, "opencode"), input.id).pipe(Effect.ignore)
})
return Service.of({ resolve, commit })
}),
)
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer), Layer.provide(Git.defaultLayer))
+6
View File
@@ -1,5 +1,11 @@
import { Option, Schema, SchemaGetter } from "effect"
export const AbsolutePath = Schema.String.pipe(Schema.brand("AbsolutePath"))
export type AbsolutePath = typeof AbsolutePath.Type
export const RelativePath = Schema.String.pipe(Schema.brand("RelativePath"))
export type RelativePath = typeof RelativePath.Type
/**
* Integer greater than zero.
*/
+1
View File
@@ -65,5 +65,6 @@ export abstract class NamedError extends Error {
public static readonly Unknown = NamedError.create("UnknownError", {
message: Schema.String,
ref: Schema.optional(Schema.String),
})
}
@@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
import { PluginV2 } from "@opencode-ai/core/plugin"
import { CerebrasPlugin } from "@opencode-ai/core/plugin/provider/cerebras"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { it, model, provider } from "./provider-helper"
import { it, model } from "./provider-helper"
const cerebrasOptions: Record<string, unknown>[] = []
@@ -2,7 +2,7 @@ import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { PluginV2 } from "@opencode-ai/core/plugin"
import { GoogleVertexAnthropicPlugin } from "@opencode-ai/core/plugin/provider/google-vertex"
import { GoogleVertexAnthropicPlugin, GoogleVertexPlugin } from "@opencode-ai/core/plugin/provider/google-vertex"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { fakeSelectorSdk, it, model, withEnv } from "./provider-helper"
@@ -109,6 +109,73 @@ describe("GoogleVertexAnthropicPlugin", () => {
),
)
it.effect("creates SDKs for google-vertex Anthropic models with multi-region endpoints", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
yield* plugin.add(GoogleVertexAnthropicPlugin)
const result = yield* plugin.trigger(
"aisdk.sdk",
{
model: model("google-vertex", "claude-sonnet-4-5"),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex", project: "project", location: "eu" },
},
{},
)
expect(result.sdk.languageModel("claude-sonnet-4-5").config.baseURL).toBe(
"https://aiplatform.eu.rep.googleapis.com/v1/projects/project/locations/eu/publishers/anthropic/models",
)
}),
)
it.effect("keeps configured baseURL for google-vertex Anthropic models", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
yield* plugin.add(GoogleVertexAnthropicPlugin)
const result = yield* plugin.trigger(
"aisdk.sdk",
{
model: model("google-vertex", "claude-sonnet-4-5"),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex", project: "project", location: "eu", baseURL: "https://proxy.example/v1" },
},
{},
)
expect(result.sdk.languageModel("claude-sonnet-4-5").config.baseURL).toBe("https://proxy.example/v1")
}),
)
it.effect("selects google-vertex Anthropic language models through V2 plugins", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
yield* plugin.add(GoogleVertexPlugin)
yield* plugin.add(GoogleVertexAnthropicPlugin)
const sdkResult = yield* plugin.trigger(
"aisdk.sdk",
{
model: model("google-vertex", " claude-sonnet-4-5 "),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex", project: "project", location: "us" },
},
{},
)
const languageResult = yield* plugin.trigger(
"aisdk.language",
{
model: model("google-vertex", " claude-sonnet-4-5 "),
sdk: sdkResult.sdk,
options: {},
},
{},
)
const language = languageResult.language as unknown as { config: { baseURL: string }; modelId: string }
expect(language.config.baseURL).toBe(
"https://aiplatform.us.rep.googleapis.com/v1/projects/project/locations/us/publishers/anthropic/models",
)
expect(language.modelId).toBe("claude-sonnet-4-5")
}),
)
it.effect("trims model IDs before selecting language models", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
@@ -160,6 +160,32 @@ describe("GoogleVertexPlugin", () => {
),
)
it.effect("keeps OpenAI-compatible Vertex endpoint templates regional for eu", () =>
Effect.gen(function* () {
const plugin = yield* PluginV2.Service
const catalog = yield* Catalog.Service
yield* plugin.add(GoogleVertexPlugin)
const load = yield* catalog.loader()
yield* load((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
provider.endpoint = {
type: "aisdk",
package: "@ai-sdk/openai-compatible",
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
}
provider.options.aisdk.provider.project = "config-project"
provider.options.aisdk.provider.location = "eu"
}),
)
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
expect(provider.endpoint).toEqual({
type: "aisdk",
package: "@ai-sdk/openai-compatible",
url: "https://eu-aiplatform.googleapis.com/v1/projects/config-project/locations/eu",
})
}),
)
it.effect("defaults location to us-central1 when only project is configured", () =>
withEnv(
{
+192
View File
@@ -0,0 +1,192 @@
import { describe, expect } from "bun:test"
import { $ } from "bun"
import fs from "fs/promises"
import path from "path"
import { Effect } from "effect"
import { Project } from "@opencode-ai/core/project"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { tmpdir } from "./fixture/tmpdir"
import { testEffect } from "./lib/effect"
const it = testEffect(Project.defaultLayer)
function abs(value: string) {
return AbsolutePath.make(value)
}
function real(value: string) {
return Effect.promise(() => fs.realpath(value)).pipe(Effect.map((value) => AbsolutePath.make(value)))
}
async function initRepo(dir: string, opts?: { commit?: boolean; remote?: string }) {
await $`git init`.cwd(dir).quiet()
await $`git config core.fsmonitor false`.cwd(dir).quiet()
await $`git config commit.gpgsign false`.cwd(dir).quiet()
await $`git config user.email test@opencode.test`.cwd(dir).quiet()
await $`git config user.name Test`.cwd(dir).quiet()
if (opts?.commit) await $`git commit --allow-empty -m root`.cwd(dir).quiet()
if (opts?.remote) await $`git remote add origin ${opts.remote}`.cwd(dir).quiet()
}
async function rootCommit(dir: string) {
return (await $`git rev-list --max-parents=0 HEAD`.cwd(dir).text()).trim()
}
describe("ProjectV2.resolve", () => {
it.live("returns global for non-git directory", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
const project = yield* Project.Service
const result = yield* project.resolve(abs(tmp.path))
expect(result.id).toBe(Project.ID.make("global"))
expect(path.resolve(result.directory)).toBe(path.resolve(tmp.path))
expect(result.previous).toBeUndefined()
expect(result.vcs).toBeUndefined()
}),
)
it.live("returns git global for repo with no commits and no remote", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path))
const project = yield* Project.Service
const result = yield* project.resolve(abs(tmp.path))
expect(result.id).toBe(Project.ID.make("global"))
expect(result.directory).toBe(yield* real(tmp.path))
expect(result.previous).toBeUndefined()
expect(result.vcs?.type).toBe("git")
}),
)
it.live("falls back to root commit when origin is missing", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true }))
const project = yield* Project.Service
const result = yield* project.resolve(abs(tmp.path))
expect(result.id).toBe(Project.ID.make(yield* Effect.promise(() => rootCommit(tmp.path))))
expect(result.directory).toBe(yield* real(tmp.path))
expect(result.previous).toBeUndefined()
expect(result.vcs?.type).toBe("git")
}),
)
it.live("uses root commit when origin exists", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:Acme/App.git" }))
const project = yield* Project.Service
const result = yield* project.resolve(abs(tmp.path))
expect(result.id).toBe(Project.ID.make(yield* Effect.promise(() => rootCommit(tmp.path))))
expect(result.directory).toBe(yield* real(tmp.path))
expect(result.vcs?.type).toBe("git")
}),
)
it.live("uses root commit when local remote exists", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: `file://${tmp.path}` }))
const project = yield* Project.Service
const result = yield* project.resolve(abs(tmp.path))
expect(result.id).toBe(Project.ID.make(yield* Effect.promise(() => rootCommit(tmp.path))))
}),
)
it.live("prefers previous cached id over origin", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
yield* Effect.promise(() => Bun.write(path.join(tmp.path, ".git", "opencode"), "old-id"))
const project = yield* Project.Service
const result = yield* project.resolve(abs(tmp.path))
expect(result.previous).toBe(Project.ID.make("old-id"))
expect(result.id).toBe(Project.ID.make("old-id"))
}),
)
it.live("does not write the cache while resolving", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
const project = yield* Project.Service
yield* project.resolve(abs(tmp.path))
expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, ".git", "opencode")).exists())).toBe(false)
}),
)
it.live("resolves from nested directories to repo root", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true }))
yield* Effect.promise(() => fs.mkdir(path.join(tmp.path, "a", "b"), { recursive: true }))
const project = yield* Project.Service
const result = yield* project.resolve(abs(path.join(tmp.path, "a", "b")))
expect(result.directory).toBe(yield* real(tmp.path))
}),
)
it.live("linked worktree returns opened worktree directory and previous from common dir", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
const worktree = `${tmp.path}-worktree`
yield* Effect.addFinalizer(() =>
Effect.promise(() => $`rm -rf ${worktree}`.quiet().nothrow()).pipe(Effect.ignore),
)
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
yield* Effect.promise(() => Bun.write(path.join(tmp.path, ".git", "opencode"), "old-id"))
yield* Effect.promise(() => $`git worktree add ${worktree} -b test-${Date.now()}`.cwd(tmp.path).quiet())
const project = yield* Project.Service
const result = yield* project.resolve(abs(worktree))
expect(result.directory).toBe(yield* real(worktree))
expect(result.previous).toBe(Project.ID.make("old-id"))
expect(result.id).toBe(Project.ID.make("old-id"))
expect(result.vcs?.type).toBe("git")
}),
)
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.15.6",
"version": "1.15.10",
"type": "module",
"license": "MIT",
"homepage": "https://opencode.ai",
+1
View File
@@ -7,4 +7,5 @@ export const CHANNEL: Channel = raw === "dev" || raw === "beta" || raw === "prod
export const SETTINGS_STORE = "opencode.settings"
export const DEFAULT_SERVER_URL_KEY = "defaultServerUrl"
export const WSL_ENABLED_KEY = "wslEnabled"
export const PINCH_ZOOM_ENABLED_KEY = "pinchZoomEnabled"
export const UPDATER_ENABLED = app.isPackaged && CHANNEL !== "dev"
+5 -1
View File
@@ -14,7 +14,7 @@ import type {
} from "../preload/types"
import { runDesktopMenuAction } from "./desktop-menu-actions"
import { getStore } from "./store"
import { setTitlebar, updateTitlebar } from "./windows"
import { getPinchZoomEnabled, setPinchZoomEnabled, setTitlebar, updateTitlebar } from "./windows"
const pickerFilters = (ext?: string[]) => {
if (!ext || ext.length === 0) return undefined
@@ -202,6 +202,10 @@ export function registerIpcHandlers(deps: Deps) {
if (!win) return
updateTitlebar(win)
})
ipcMain.handle("get-pinch-zoom-enabled", () => getPinchZoomEnabled())
ipcMain.handle("set-pinch-zoom-enabled", (_event: IpcMainInvokeEvent, enabled: boolean) => {
setPinchZoomEnabled(enabled)
})
ipcMain.handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => {
const win = BrowserWindow.fromWebContents(event.sender)
if (!win) return
+53 -5
View File
@@ -1,9 +1,14 @@
import windowState from "electron-window-state"
import { resolveThemeVariant } from "@opencode-ai/ui/theme/resolve"
import type { DesktopTheme } from "@opencode-ai/ui/theme/types"
import oc2ThemeJson from "../../../ui/src/theme/themes/oc-2.json"
import { app, BrowserWindow, dialog, net, nativeImage, nativeTheme, protocol } from "electron"
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
import { fileURLToPath, pathToFileURL } from "node:url"
import type { TitlebarTheme } from "../preload/types"
import { PINCH_ZOOM_ENABLED_KEY } from "./constants"
import { exportDebugLogs, write as writeLog } from "./logging"
import { getStore } from "./store"
import { createUnresponsiveSampler } from "./unresponsive"
const root = dirname(fileURLToPath(import.meta.url))
@@ -13,6 +18,11 @@ const rendererHost = "renderer"
const clipboardWritePermission = "clipboard-sanitized-write"
const notificationPermission = "notifications"
const rendererPermissions = new Set([clipboardWritePermission, notificationPermission])
const oc2Theme = oc2ThemeJson as DesktopTheme
const oc2Background = {
light: resolveThemeVariant(oc2Theme.light, false)["background-base"],
dark: resolveThemeVariant(oc2Theme.dark, true)["background-base"],
}
const documentPolicyHeader = "Document-Policy"
const jsCallStacksDocumentPolicy = "include-js-call-stacks-in-crash-reports"
@@ -33,7 +43,10 @@ let relaunchHandler = () => {
app.exit(0)
}
const titlebarThemes = new WeakMap<BrowserWindow, Partial<TitlebarTheme>>()
const pinchZoomEnabled = new WeakMap<BrowserWindow, boolean>()
const titlebarHeight = 40
const maxZoomLevel = 10
const minZoomLevel = 0.2
export function setRelaunchHandler(handler: () => void) {
relaunchHandler = handler
@@ -41,6 +54,7 @@ export function setRelaunchHandler(handler: () => void) {
export function setBackgroundColor(color: string) {
backgroundColor = color
BrowserWindow.getAllWindows().forEach((win) => win.setBackgroundColor(color))
}
export function getBackgroundColor(): string | undefined {
@@ -60,6 +74,10 @@ function tone() {
return nativeTheme.shouldUseDarkColors ? "dark" : "light"
}
function defaultBackgroundColor() {
return oc2Background[tone()]
}
function overlay(theme: Partial<TitlebarTheme> = {}, zoom = 1) {
const mode = theme.mode ?? tone()
return {
@@ -79,6 +97,20 @@ export function updateTitlebar(win: BrowserWindow) {
win.setTitleBarOverlay(overlay(titlebarThemes.get(win), win.webContents.getZoomFactor()))
}
export function setPinchZoomEnabled(enabled: boolean) {
getStore().set(PINCH_ZOOM_ENABLED_KEY, enabled)
for (const win of BrowserWindow.getAllWindows()) {
pinchZoomEnabled.set(win, enabled)
win.webContents.send("pinch-zoom-enabled-changed", enabled)
if (!enabled && win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1)
updateZoom(win)
}
}
export function getPinchZoomEnabled() {
return getStore().get(PINCH_ZOOM_ENABLED_KEY) === true
}
export function setDockIcon() {
if (process.platform !== "darwin") return
const icon = nativeImage.createFromPath(join(iconsDir(), "dock.png"))
@@ -101,7 +133,7 @@ export function createMainWindow() {
autoHideMenuBar: true,
title: "OpenCode",
icon: iconPath(),
backgroundColor,
backgroundColor: backgroundColor ?? defaultBackgroundColor(),
...(process.platform === "darwin"
? {
titleBarStyle: "hidden" as const,
@@ -159,7 +191,7 @@ export function createLoadingWindow() {
show: true,
autoHideMenuBar: true,
icon: iconPath(),
backgroundColor,
backgroundColor: backgroundColor ?? defaultBackgroundColor(),
...(process.platform === "darwin" ? { titleBarStyle: "hidden" as const } : {}),
...(process.platform === "win32"
? {
@@ -392,13 +424,29 @@ function isRendererUrl(value?: string, html = false) {
}
function wireZoom(win: BrowserWindow) {
pinchZoomEnabled.set(win, getPinchZoomEnabled())
win.webContents.setZoomFactor(1)
win.webContents.on("zoom-changed", () => {
win.webContents.setZoomFactor(1)
updateTitlebar(win)
win.webContents.on("zoom-changed", (event, zoomDirection) => {
event.preventDefault()
if (pinchZoomEnabled.get(win)) {
win.webContents.setZoomFactor(clampZoom(win.webContents.getZoomFactor() + (zoomDirection === "in" ? 0.2 : -0.2)))
updateZoom(win)
return
}
if (win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1)
updateZoom(win)
})
}
function clampZoom(value: number) {
return Math.min(Math.max(value, minZoomLevel), maxZoomLevel)
}
function updateZoom(win: BrowserWindow) {
updateTitlebar(win)
win.webContents.send("zoom-factor-changed", win.webContents.getZoomFactor())
}
function upsertKeyValue(obj: Record<string, any>, keyToChange: string, value: any) {
const keyToChangeLower = keyToChange.toLowerCase()
for (const key of Object.keys(obj)) {
+12
View File
@@ -60,6 +60,18 @@ const api: ElectronAPI = {
relaunch: () => ipcRenderer.send("relaunch"),
getZoomFactor: () => ipcRenderer.invoke("get-zoom-factor"),
setZoomFactor: (factor) => ipcRenderer.invoke("set-zoom-factor", factor),
getPinchZoomEnabled: () => ipcRenderer.invoke("get-pinch-zoom-enabled"),
setPinchZoomEnabled: (enabled) => ipcRenderer.invoke("set-pinch-zoom-enabled", enabled),
onPinchZoomEnabledChanged: (cb) => {
const handler = (_: unknown, enabled: boolean) => cb(enabled)
ipcRenderer.on("pinch-zoom-enabled-changed", handler)
return () => ipcRenderer.removeListener("pinch-zoom-enabled-changed", handler)
},
onZoomFactorChanged: (cb) => {
const handler = (_: unknown, factor: number) => cb(factor)
ipcRenderer.on("zoom-factor-changed", handler)
return () => ipcRenderer.removeListener("zoom-factor-changed", handler)
},
setTitlebar: (theme) => ipcRenderer.invoke("set-titlebar", theme),
runDesktopMenuAction: (action) => ipcRenderer.invoke("run-desktop-menu-action", action),
loadingWindowComplete: () => ipcRenderer.send("loading-window-complete"),
+4
View File
@@ -79,6 +79,10 @@ export type ElectronAPI = {
relaunch: () => void
getZoomFactor: () => Promise<number>
setZoomFactor: (factor: number) => Promise<void>
getPinchZoomEnabled: () => Promise<boolean>
setPinchZoomEnabled: (enabled: boolean) => Promise<void>
onPinchZoomEnabledChanged: (cb: (enabled: boolean) => void) => () => void
onZoomFactorChanged: (cb: (factor: number) => void) => () => void
setTitlebar: (theme: TitlebarTheme) => Promise<void>
runDesktopMenuAction: (action: DesktopMenuAction) => Promise<void>
loadingWindowComplete: () => void
+5 -1
View File
@@ -21,7 +21,7 @@ import { createEffect, createResource, onCleanup, onMount, Show } from "solid-js
import { render } from "solid-js/web"
import pkg from "../../package.json"
import { initI18n, t } from "./i18n"
import { resetZoom, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
import { resetZoom, setPinchZoomEnabled, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
import "./styles.css"
import { useTheme } from "@opencode-ai/ui/theme"
@@ -274,6 +274,10 @@ const createPlatform = (): Platform => {
webviewZoom,
getPinchZoomEnabled: () => window.api.getPinchZoomEnabled(),
setPinchZoomEnabled,
runDesktopMenuAction,
checkAppExists: async (appName: string) => {
+80 -1
View File
@@ -13,9 +13,21 @@ const OS_NAME = (() => {
const [webviewZoom, setWebviewZoom] = createSignal(1)
let requestedZoom = 1
let pinchZoomEnabled = false
let wheelPinch = undefined as
| {
active: boolean
startZoom: number
totalDelta: number
timeout: ReturnType<typeof setTimeout> | undefined
}
| undefined
const MAX_ZOOM_LEVEL = 10
const MIN_ZOOM_LEVEL = 0.2
const WHEEL_PINCH_THRESHOLD = 20
const WHEEL_PINCH_STEP = 0.2
const WHEEL_PINCH_END_DELAY = 160
const clamp = (value: number) => Math.min(Math.max(value, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL)
@@ -33,10 +45,77 @@ const applyZoom = (next: number) => {
})
}
window.api.onZoomFactorChanged((factor) => {
requestedZoom = clamp(factor)
setWebviewZoom(requestedZoom)
})
void window.api.getPinchZoomEnabled().then((enabled) => {
pinchZoomEnabled = enabled
})
window.api.onPinchZoomEnabledChanged((enabled) => {
pinchZoomEnabled = enabled
resetWheelPinch()
})
const setPinchZoomEnabled = (enabled: boolean) => {
pinchZoomEnabled = enabled
resetWheelPinch()
return window.api.setPinchZoomEnabled(enabled)
}
const resetZoom = () => applyZoom(1)
const zoomIn = () => applyZoom(clamp(requestedZoom + 0.2))
const zoomOut = () => applyZoom(clamp(requestedZoom - 0.2))
const resetWheelPinch = () => {
clearTimeout(wheelPinch?.timeout)
wheelPinch = undefined
}
const normalizeWheelDelta = (event: WheelEvent) => {
if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) return event.deltaY * 16
if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) return event.deltaY * window.innerHeight
return event.deltaY
}
const updateWheelPinch = (event: WheelEvent) => {
wheelPinch ??= {
active: false,
startZoom: requestedZoom,
totalDelta: 0,
timeout: undefined,
}
clearTimeout(wheelPinch.timeout)
wheelPinch.timeout = setTimeout(resetWheelPinch, WHEEL_PINCH_END_DELAY)
wheelPinch.totalDelta += normalizeWheelDelta(event)
if (!wheelPinch.active && Math.abs(wheelPinch.totalDelta) < WHEEL_PINCH_THRESHOLD) return
if (!wheelPinch.active) {
wheelPinch.active = true
wheelPinch.startZoom = requestedZoom
wheelPinch.totalDelta = 0
return
}
wheelPinch.active = true
applyZoom(clamp(wheelPinch.startZoom - (wheelPinch.totalDelta / WHEEL_PINCH_THRESHOLD) * WHEEL_PINCH_STEP))
}
window.addEventListener(
"wheel",
(event) => {
if (!pinchZoomEnabled) return
if (!event.ctrlKey) return
event.preventDefault()
updateWheelPinch(event)
},
{ passive: false },
)
window.addEventListener("keydown", (event) => {
if (!(OS_NAME === "macos" ? event.metaKey : event.ctrlKey)) return
@@ -56,4 +135,4 @@ window.addEventListener("keydown", (event) => {
}
})
export { webviewZoom, resetZoom, zoomIn, zoomOut }
export { webviewZoom, resetZoom, setPinchZoomEnabled, zoomIn, zoomOut }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.5",
"version": "1.15.10",
"name": "@opencode-ai/effect-drizzle-sqlite",
"type": "module",
"license": "MIT",
+10
View File
@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.15.6",
"version": "1.15.10",
"private": true,
"type": "module",
"license": "MIT",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+6 -6
View File
@@ -1,7 +1,7 @@
id = "opencode"
name = "OpenCode"
description = "The open source coding agent."
version = "1.15.6"
version = "1.15.10"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/anomalyco/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-darwin-arm64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.10/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-darwin-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.10/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-linux-arm64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.10/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-linux-x64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.10/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-windows-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.10/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.15.6",
"version": "1.15.10",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.10",
"name": "@opencode-ai/http-recorder",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.10",
"name": "@opencode-ai/llm",
"type": "module",
"license": "MIT",
-1
View File
@@ -14,7 +14,6 @@ import {
ToolChoice,
ToolDefinition,
type ContentPart,
ToolCallPart,
ToolResultPart,
} from "./schema"
import { make as makeTool, type ToolSchema } from "./tool"
@@ -10,9 +10,11 @@ import {
type CacheHint,
type FinishReason,
type LLMRequest,
type MediaPart,
type ProviderMetadata,
type ToolCallPart,
type ToolDefinition,
type ToolResultContentPart,
type ToolResultPart,
} from "../schema"
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared"
@@ -39,6 +41,17 @@ const AnthropicTextBlock = Schema.Struct({
})
type AnthropicTextBlock = Schema.Schema.Type<typeof AnthropicTextBlock>
const AnthropicImageBlock = Schema.Struct({
type: Schema.tag("image"),
source: Schema.Struct({
type: Schema.tag("base64"),
media_type: Schema.String,
data: Schema.String,
}),
cache_control: Schema.optional(AnthropicCacheControl),
})
type AnthropicImageBlock = Schema.Schema.Type<typeof AnthropicImageBlock>
const AnthropicThinkingBlock = Schema.Struct({
type: Schema.tag("thinking"),
thinking: Schema.String,
@@ -84,15 +97,24 @@ const AnthropicServerToolResultBlock = Schema.Struct({
})
type AnthropicServerToolResultBlock = Schema.Schema.Type<typeof AnthropicServerToolResultBlock>
// Anthropic accepts either a plain string or an ordered array of text/image
// blocks inside `tool_result.content`. The array form is required when a tool
// returns image bytes (screenshot, image search, etc.) so they can be passed
// to the model as proper image inputs instead of being JSON-stringified into
// the prompt — which silently inflates context by megabytes and can push the
// conversation over the model's token limit.
const AnthropicToolResultContent = Schema.Union([AnthropicTextBlock, AnthropicImageBlock])
const AnthropicToolResultBlock = Schema.Struct({
type: Schema.tag("tool_result"),
tool_use_id: Schema.String,
content: Schema.String,
content: Schema.Union([Schema.String, Schema.Array(AnthropicToolResultContent)]),
is_error: Schema.optional(Schema.Boolean),
cache_control: Schema.optional(AnthropicCacheControl),
})
const AnthropicUserBlock = Schema.Union([AnthropicTextBlock, AnthropicToolResultBlock])
const AnthropicUserBlock = Schema.Union([AnthropicTextBlock, AnthropicImageBlock, AnthropicToolResultBlock])
type AnthropicUserBlock = Schema.Schema.Type<typeof AnthropicUserBlock>
const AnthropicAssistantBlock = Schema.Union([
AnthropicTextBlock,
AnthropicThinkingBlock,
@@ -184,7 +206,13 @@ const AnthropicEvent = Schema.Struct({
content_block: Schema.optional(AnthropicStreamBlock),
delta: Schema.optional(AnthropicStreamDelta),
usage: Schema.optional(AnthropicUsage),
error: Schema.optional(Schema.Struct({ type: Schema.String, message: Schema.String })),
// `type` and `message` are both required per Anthropic's spec, but
// OpenAI-compatible proxies and gateway translations occasionally drop one
// or the other; mark them optional so a partial payload still parses and
// the parser can fall back to whichever field is populated.
error: Schema.optional(
Schema.Struct({ type: Schema.optional(Schema.String), message: Schema.optional(Schema.String) }),
),
})
type AnthropicEvent = Schema.Schema.Type<typeof AnthropicEvent>
@@ -272,6 +300,46 @@ const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult
return { type: wireType, tool_use_id: part.id, content: part.result.value } satisfies AnthropicServerToolResultBlock
})
const lowerImage = Effect.fn("AnthropicMessages.lowerImage")(function* (part: MediaPart) {
if (!part.mediaType.startsWith("image/"))
return yield* invalid(`Anthropic Messages user media content only supports images`)
return {
type: "image" as const,
source: {
type: "base64" as const,
media_type: part.mediaType,
data: ProviderShared.mediaBase64(part),
},
} satisfies AnthropicImageBlock
})
// Tool results may carry structured text/images. Keep media as provider-native
// content instead of JSON-stringifying base64 into a prompt string.
const lowerToolResultContentItem = Effect.fn("AnthropicMessages.lowerToolResultContentItem")(function* (
item: ToolResultContentPart,
) {
if (item.type === "text") return { type: "text" as const, text: item.text } satisfies AnthropicTextBlock
if (item.mediaType.startsWith("image/"))
return {
type: "image" as const,
source: {
type: "base64" as const,
media_type: item.mediaType,
data: ProviderShared.mediaBase64(item),
},
} satisfies AnthropicImageBlock
return yield* invalid(`Anthropic Messages tool-result media content only supports images, got ${item.mediaType}`)
})
const lowerToolResultContent = Effect.fn("AnthropicMessages.lowerToolResultContent")(function* (part: ToolResultPart) {
// Text / json / error results stay as a string for backward compatibility
// with existing cassettes and provider expectations.
if (part.result.type !== "content") return ProviderShared.toolResultText(part)
// Preserve the narrowed array element type when compiled through a consumer package.
const content: ReadonlyArray<ToolResultContentPart> = part.result.value
return yield* Effect.forEach(content, lowerToolResultContentItem)
})
const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
request: LLMRequest,
breakpoints: Cache.Breakpoints,
@@ -280,11 +348,17 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
for (const message of request.messages) {
if (message.role === "user") {
const content: AnthropicTextBlock[] = []
const content: AnthropicUserBlock[] = []
for (const part of message.content) {
if (!ProviderShared.supportsContent(part, ["text"]))
return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text"])
content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) })
if (part.type === "text") {
content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) })
continue
}
if (part.type === "media") {
content.push(yield* lowerImage(part))
continue
}
return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text", "media"])
}
messages.push({ role: "user", content })
continue
@@ -328,7 +402,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
content.push({
type: "tool_result",
tool_use_id: part.id,
content: ProviderShared.toolResultText(part),
content: yield* lowerToolResultContent(part),
is_error: part.result.type === "error" ? true : undefined,
cache_control: cacheControl(breakpoints, part.cache),
})
@@ -635,9 +709,18 @@ const onMessageDelta = (state: ParserState, event: AnthropicEvent): StepResult =
return [{ ...state, lifecycle, usage }, events]
}
// Prefix `error.type` so overloads, rate limits, and quota errors are visible
// even when the provider message is generic or empty.
const providerErrorMessage = (event: AnthropicEvent): string => {
const type = event.error?.type
const message = event.error?.message
if (type && message) return `${type}: ${message}`
return message || type || "Anthropic Messages stream error"
}
const onError = (state: ParserState, event: AnthropicEvent): StepResult => [
state,
[LLMEvent.providerError({ message: event.error?.message ?? "Anthropic Messages stream error" })],
[LLMEvent.providerError({ message: providerErrorMessage(event) })],
]
const step = (state: ParserState, event: AnthropicEvent) => {
+347 -30
View File
@@ -6,14 +6,16 @@ import { HttpTransport, WebSocketTransport } from "../route/transport"
import { Protocol } from "../route/protocol"
import {
LLMEvent,
type MediaPart,
Usage,
type FinishReason,
type LLMRequest,
type ProviderMetadata,
type ReasoningPart,
type TextPart,
type ToolCallPart,
type ToolDefinition,
type ToolResultContentPart,
type ToolResultPart,
} from "../schema"
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared"
import { OpenAIOptions } from "./utils/openai-options"
@@ -43,10 +45,39 @@ const OpenAIResponsesOutputText = Schema.Struct({
text: Schema.String,
})
const OpenAIResponsesReasoningSummaryText = Schema.Struct({
type: Schema.tag("summary_text"),
text: Schema.String,
})
const OpenAIResponsesReasoningItem = Schema.Struct({
type: Schema.tag("reasoning"),
id: Schema.String,
summary: Schema.Array(OpenAIResponsesReasoningSummaryText),
encrypted_content: optionalNull(Schema.String),
})
const OpenAIResponsesItemReference = Schema.Struct({
type: Schema.tag("item_reference"),
id: Schema.String,
})
// `function_call_output.output` accepts either a plain string or an ordered
// array of content items so tools can return images in addition to text.
// https://platform.openai.com/docs/api-reference/responses/object
const OpenAIResponsesFunctionCallOutputContent = Schema.Union([OpenAIResponsesInputText, OpenAIResponsesInputImage])
const OpenAIResponsesFunctionCallOutput = Schema.Union([
Schema.String,
Schema.Array(OpenAIResponsesFunctionCallOutputContent),
])
const OpenAIResponsesInputItem = Schema.Union([
Schema.Struct({ role: Schema.tag("system"), content: Schema.String }),
Schema.Struct({ role: Schema.tag("user"), content: Schema.Array(OpenAIResponsesInputContent) }),
Schema.Struct({ role: Schema.tag("assistant"), content: Schema.Array(OpenAIResponsesOutputText) }),
OpenAIResponsesReasoningItem,
OpenAIResponsesItemReference,
Schema.Struct({
type: Schema.tag("function_call"),
call_id: Schema.String,
@@ -56,11 +87,20 @@ const OpenAIResponsesInputItem = Schema.Union([
Schema.Struct({
type: Schema.tag("function_call_output"),
call_id: Schema.String,
output: Schema.String,
output: OpenAIResponsesFunctionCallOutput,
}),
])
type OpenAIResponsesInputItem = Schema.Schema.Type<typeof OpenAIResponsesInputItem>
// Mutable counterpart of the schema reasoning item so `lowerMessages` can fold
// multiple streamed summary parts into the same item before flushing.
type OpenAIResponsesReasoningInput = {
type: "reasoning"
id: string
summary: Array<{ type: "summary_text"; text: string }>
encrypted_content?: string | null
}
const OpenAIResponsesTool = Schema.Struct({
type: Schema.tag("function"),
name: Schema.String,
@@ -87,7 +127,7 @@ const OpenAIResponsesCoreFields = {
tool_choice: Schema.optional(OpenAIResponsesToolChoice),
store: Schema.optional(Schema.Boolean),
prompt_cache_key: Schema.optional(Schema.String),
include: optionalArray(Schema.Literal("reasoning.encrypted_content")),
include: optionalArray(OpenAIOptions.OpenAIResponseIncludable),
reasoning: Schema.optional(
Schema.Struct({
effort: Schema.optional(OpenAIOptions.OpenAIReasoningEffort),
@@ -149,13 +189,26 @@ const OpenAIResponsesStreamItem = Schema.Struct({
server_label: Schema.optional(Schema.String),
output: Schema.optional(Schema.Unknown),
error: Schema.optional(Schema.Unknown),
encrypted_content: optionalNull(Schema.String),
})
type OpenAIResponsesStreamItem = Schema.Schema.Type<typeof OpenAIResponsesStreamItem>
// OpenAI Responses surfaces provider failures in two related shapes. The
// streaming `error` event carries the details at the top level
// (`{ type: "error", code, message, param, sequence_number }`), while
// `response.failed` carries them under `response.error`. We capture both so
// the parser can surface a useful provider-error message in either path.
const OpenAIResponsesErrorPayload = Schema.Struct({
code: optionalNull(Schema.String),
message: optionalNull(Schema.String),
param: optionalNull(Schema.String),
})
const OpenAIResponsesEvent = Schema.Struct({
type: Schema.String,
delta: Schema.optional(Schema.String),
item_id: Schema.optional(Schema.String),
summary_index: Schema.optional(Schema.Number),
item: Schema.optional(OpenAIResponsesStreamItem),
response: Schema.optional(
Schema.StructWithRest(
@@ -164,12 +217,14 @@ const OpenAIResponsesEvent = Schema.Struct({
service_tier: optionalNull(Schema.String),
incomplete_details: optionalNull(Schema.Struct({ reason: Schema.String })),
usage: optionalNull(OpenAIResponsesUsage),
error: optionalNull(OpenAIResponsesErrorPayload),
}),
[Schema.Record(Schema.String, Schema.Unknown)],
),
),
code: Schema.optional(Schema.String),
message: Schema.optional(Schema.String),
param: Schema.optional(Schema.String),
})
type OpenAIResponsesEvent = Schema.Schema.Type<typeof OpenAIResponsesEvent>
@@ -177,6 +232,18 @@ interface ParserState {
readonly tools: ToolStream.State<string>
readonly hasFunctionCall: boolean
readonly lifecycle: Lifecycle.State
readonly reasoningItems: Readonly<Record<string, ReasoningStreamItem>>
readonly store: boolean | undefined
}
type ReasoningSummaryStatus = "active" | "can-conclude" | "concluded"
interface ReasoningStreamItem {
readonly encryptedContent: string | null | undefined
// Keyed by OpenAI's numeric `summary_index`. JS object keys coerce to
// strings, but typing the map as `Record<number, ...>` documents intent
// and matches the wire field.
readonly summaryParts: Readonly<Record<number, ReasoningSummaryStatus>>
}
const invalid = ProviderShared.invalidRequest
@@ -206,26 +273,61 @@ const lowerToolCall = (part: ToolCallPart): OpenAIResponsesInputItem => ({
arguments: ProviderShared.encodeJson(part.input),
})
const imageUrl = (part: MediaPart) =>
typeof part.data === "string" && part.data.startsWith("data:")
? part.data
: `data:${part.mediaType};base64,${ProviderShared.mediaBytes(part)}`
const lowerReasoning = (part: ReasoningPart): OpenAIResponsesReasoningInput | undefined => {
const openai = part.providerMetadata?.openai
if (!ProviderShared.isRecord(openai) || typeof openai.itemId !== "string" || openai.itemId.length === 0)
return undefined
const encryptedContent =
typeof openai.reasoningEncryptedContent === "string"
? openai.reasoningEncryptedContent
: openai.reasoningEncryptedContent === null
? null
: undefined
return {
type: "reasoning",
id: openai.itemId,
summary: part.text.length > 0 ? [{ type: "summary_text", text: part.text }] : [],
encrypted_content: encryptedContent,
}
}
const lowerUserContent = Effect.fn("OpenAIResponses.lowerUserContent")(function* (
part: LLMRequest["messages"][number]["content"][number],
) {
if (part.type === "text") return { type: "input_text" as const, text: part.text }
if (part.type === "media" && part.mediaType.startsWith("image/")) {
return { type: "input_image" as const, image_url: imageUrl(part) }
return { type: "input_image" as const, image_url: ProviderShared.mediaDataUrl(part) }
}
if (part.type === "media") return yield* invalid("OpenAI Responses user media content only supports images")
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "user", ["text", "media"])
})
// Tool results may carry structured text/images. Keep media as provider-native
// content instead of JSON-stringifying base64 into a prompt string.
const lowerToolResultContentItem = Effect.fn("OpenAIResponses.lowerToolResultContentItem")(function* (
item: ToolResultContentPart,
) {
if (item.type === "text") return { type: "input_text" as const, text: item.text }
if (item.mediaType.startsWith("image/"))
return {
type: "input_image" as const,
image_url: ProviderShared.mediaDataUrl(item),
}
return yield* invalid(`OpenAI Responses tool-result media content only supports images, got ${item.mediaType}`)
})
const lowerToolResultOutput = Effect.fn("OpenAIResponses.lowerToolResultOutput")(function* (part: ToolResultPart) {
// Text/json/error results are encoded as a plain string for backward
// compatibility with existing cassettes and provider expectations.
if (part.result.type !== "content") return ProviderShared.toolResultText(part)
return yield* Effect.forEach(part.result.value, lowerToolResultContentItem)
})
const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (request: LLMRequest) {
const system: OpenAIResponsesInputItem[] =
request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }]
const input: OpenAIResponsesInputItem[] = [...system]
const store = OpenAIOptions.store(request)
for (const message of request.messages) {
if (message.role === "user") {
@@ -235,31 +337,72 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
if (message.role === "assistant") {
const content: TextPart[] = []
const reasoningItems: Record<string, OpenAIResponsesReasoningInput> = {}
const reasoningReferences = new Set<string>()
const flushText = () => {
if (content.length === 0) return
input.push({ role: "assistant", content: content.map((part) => ({ type: "output_text", text: part.text })) })
content.splice(0, content.length)
}
for (const part of message.content) {
if (!ProviderShared.supportsContent(part, ["text", "tool-call"]))
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "assistant", ["text", "tool-call"])
if (part.type === "text") {
content.push(part)
continue
}
if (part.type === "reasoning") {
flushText()
const reasoning = lowerReasoning(part)
if (!reasoning) continue
if (store !== false && reasoning.id) {
if (!reasoningReferences.has(reasoning.id)) input.push({ type: "item_reference", id: reasoning.id })
reasoningReferences.add(reasoning.id)
continue
}
const existing = reasoningItems[reasoning.id]
if (existing) {
existing.summary.push(...reasoning.summary)
if (typeof reasoning.encrypted_content === "string")
existing.encrypted_content = reasoning.encrypted_content
continue
}
reasoningItems[reasoning.id] = reasoning
input.push(reasoning)
continue
}
if (part.type === "tool-call") {
flushText()
input.push(lowerToolCall(part))
continue
}
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "assistant", [
"text",
"reasoning",
"tool-call",
])
}
if (content.length > 0)
input.push({ role: "assistant", content: content.map((part) => ({ type: "output_text", text: part.text })) })
flushText()
continue
}
for (const part of message.content) {
if (!ProviderShared.supportsContent(part, ["tool-result"]))
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "tool", ["tool-result"])
input.push({ type: "function_call_output", call_id: part.id, output: ProviderShared.toolResultText(part) })
input.push({
type: "function_call_output",
call_id: part.id,
output: yield* lowerToolResultOutput(part),
})
}
}
return input
// With store:false, OpenAI only accepts previous reasoning items when the
// complete item has encrypted state. Summary blocks for one item may carry
// that state only on the last block, so filter after they have been joined.
return store === false
? input.filter(
(item) => !("type" in item) || item.type !== "reasoning" || typeof item.encrypted_content === "string",
)
: input
})
const lowerOptions = Effect.fn("OpenAIResponses.lowerOptions")(function* (request: LLMRequest) {
@@ -269,14 +412,14 @@ const lowerOptions = Effect.fn("OpenAIResponses.lowerOptions")(function* (reques
if (effort && !OpenAIOptions.isReasoningEffort(effort))
return yield* invalid(`OpenAI Responses does not support reasoning effort ${effort}`)
const summary = OpenAIOptions.reasoningSummary(request)
const encryptedState = OpenAIOptions.encryptedReasoning(request)
const include = OpenAIOptions.include(request)
const verbosity = OpenAIOptions.textVerbosity(request)
const instructions = OpenAIOptions.instructions(request)
return {
...(instructions ? { instructions } : {}),
...(store !== undefined ? { store } : {}),
...(promptCacheKey ? { prompt_cache_key: promptCacheKey } : {}),
...(encryptedState ? { include: ["reasoning.encrypted_content"] as const } : {}),
...(include ? { include } : {}),
...(effort || summary ? { reasoning: { effort, summary } } : {}),
...(verbosity ? { text: { verbosity } } : {}),
}
@@ -367,6 +510,11 @@ const isHostedToolItem = (
): item is OpenAIResponsesStreamItem & { type: HostedToolType; id: string } =>
item.type in HOSTED_TOOLS && typeof item.id === "string" && item.id.length > 0
const isReasoningItem = (
item: OpenAIResponsesStreamItem,
): item is OpenAIResponsesStreamItem & { type: "reasoning"; id: string } =>
item.type === "reasoning" && typeof item.id === "string" && item.id.length > 0
// Round-trip the full item as the structured result so consumers can extract
// outputs / sources / status without re-decoding.
const hostedToolResult = (item: OpenAIResponsesStreamItem) => {
@@ -419,28 +567,51 @@ const onOutputTextDelta = (state: ParserState, event: OpenAIResponsesEvent): Ste
const onReasoningDelta = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
if (!event.delta) return [state, NO_EVENTS]
const events: LLMEvent[] = []
const itemID = event.item_id ?? "reasoning-0"
const id =
event.summary_index !== undefined || state.reasoningItems[itemID] ? `${itemID}:${event.summary_index ?? 0}` : itemID
return [
{
...state,
lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, event.item_id ?? "reasoning-0", event.delta),
lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, id, event.delta),
},
events,
]
}
const onReasoningDone = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
const events: LLMEvent[] = []
return [
{
...state,
lifecycle: Lifecycle.reasoningEnd(state.lifecycle, events, event.item_id ?? "reasoning-0"),
},
events,
]
}
const onReasoningDone = (state: ParserState, _event: OpenAIResponsesEvent): StepResult => [state, NO_EVENTS]
const reasoningMetadata = (item: OpenAIResponsesStreamItem & { id: string }) =>
openaiMetadata({ itemId: item.id, reasoningEncryptedContent: item.encrypted_content ?? null })
// OpenAI Responses streams reasoning items in a stable order:
// `output_item.added` (reasoning) →
// `reasoning_summary_part.added` (index=0) →
// `reasoning_summary_text.delta` →
// `reasoning_summary_part.done` (index=0) →
// (repeat for index>0) →
// `output_item.done` (reasoning).
// The handlers below rely on this ordering: `onOutputItemAdded` seeds the
// per-item entry, `onReasoningSummaryPartAdded` for `summary_index === 0`
// short-circuits when the entry already exists, and higher-index handlers
// fold against the same entry. Behaviour for out-of-order events is
// best-effort, not guaranteed.
const onOutputItemAdded = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
const item = event.item
if (item && isReasoningItem(item)) {
const events: LLMEvent[] = []
return [
{
...state,
lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `${item.id}:0`, reasoningMetadata(item)),
reasoningItems: {
...state.reasoningItems,
[item.id]: { encryptedContent: item.encrypted_content, summaryParts: { 0: "active" } },
},
},
events,
]
}
if (item?.type !== "function_call" || !item.id) return [state, NO_EVENTS]
const providerMetadata = openaiMetadata({ itemId: item.id })
const events: LLMEvent[] = []
@@ -461,6 +632,103 @@ const onOutputItemAdded = (state: ParserState, event: OpenAIResponsesEvent): Ste
]
}
const onReasoningSummaryPartAdded = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
if (!event.item_id || event.summary_index === undefined) return [state, NO_EVENTS]
const item = state.reasoningItems[event.item_id] ?? { encryptedContent: undefined, summaryParts: {} }
if (event.summary_index === 0) {
if (state.reasoningItems[event.item_id]) return [state, NO_EVENTS]
const events: LLMEvent[] = []
return [
{
...state,
lifecycle: Lifecycle.reasoningStart(
state.lifecycle,
events,
`${event.item_id}:0`,
openaiMetadata({ itemId: event.item_id, reasoningEncryptedContent: null }),
),
reasoningItems: {
...state.reasoningItems,
[event.item_id]: { ...item, summaryParts: { 0: "active" } },
},
},
events,
]
}
const events: LLMEvent[] = []
const closed = Object.entries(item.summaryParts)
.filter((entry) => entry[1] === "can-conclude")
.reduce(
(lifecycle, entry) =>
Lifecycle.reasoningEnd(
lifecycle,
events,
`${event.item_id}:${entry[0]}`,
openaiMetadata({ itemId: event.item_id }),
),
state.lifecycle,
)
return [
{
...state,
lifecycle: Lifecycle.reasoningStart(
closed,
events,
`${event.item_id}:${event.summary_index}`,
openaiMetadata({ itemId: event.item_id, reasoningEncryptedContent: item.encryptedContent ?? null }),
),
reasoningItems: {
...state.reasoningItems,
[event.item_id]: {
...item,
summaryParts: {
...Object.fromEntries(
Object.entries(item.summaryParts).map((entry) =>
entry[1] === "can-conclude" ? [entry[0], "concluded" as const] : entry,
),
),
[event.summary_index]: "active",
},
},
},
},
events,
]
}
const onReasoningSummaryPartDone = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
if (!event.item_id || event.summary_index === undefined) return [state, NO_EVENTS]
const item = state.reasoningItems[event.item_id]
if (!item) return [state, NO_EVENTS]
const events: LLMEvent[] = []
return [
{
...state,
lifecycle:
state.store !== false
? Lifecycle.reasoningEnd(
state.lifecycle,
events,
`${event.item_id}:${event.summary_index}`,
openaiMetadata({ itemId: event.item_id }),
)
: state.lifecycle,
reasoningItems: {
...state.reasoningItems,
[event.item_id]: {
...item,
summaryParts: {
...item.summaryParts,
[event.summary_index]: state.store !== false ? "concluded" : "can-conclude",
},
},
},
},
events,
]
}
const onFunctionCallArgumentsDelta = Effect.fn("OpenAIResponses.onFunctionCallArgumentsDelta")(function* (
state: ParserState,
event: OpenAIResponsesEvent,
@@ -518,6 +786,32 @@ const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function*
return [{ ...state, lifecycle }, events] satisfies StepResult
}
if (isReasoningItem(item)) {
const events: LLMEvent[] = []
const providerMetadata = reasoningMetadata(item)
const reasoningItem = state.reasoningItems[item.id]
if (reasoningItem) {
const lifecycle = Object.entries(reasoningItem.summaryParts)
.filter((entry) => entry[1] === "active" || entry[1] === "can-conclude")
.reduce(
(lifecycle, entry) => Lifecycle.reasoningEnd(lifecycle, events, `${item.id}:${entry[0]}`, providerMetadata),
state.lifecycle,
)
const { [item.id]: _removed, ...reasoningItems } = state.reasoningItems
return [{ ...state, lifecycle, reasoningItems }, events] satisfies StepResult
}
if (!state.lifecycle.reasoning.has(item.id)) {
const lifecycle = Lifecycle.stepStart(state.lifecycle, events)
events.push(LLMEvent.reasoningStart({ id: item.id, providerMetadata }))
events.push(LLMEvent.reasoningEnd({ id: item.id, providerMetadata }))
return [{ ...state, lifecycle }, events] satisfies StepResult
}
return [
{ ...state, lifecycle: Lifecycle.reasoningEnd(state.lifecycle, events, item.id, providerMetadata) },
events,
] satisfies StepResult
}
return [state, NO_EVENTS] satisfies StepResult
})
@@ -537,14 +831,27 @@ const onResponseFinish = (state: ParserState, event: OpenAIResponsesEvent): Step
return [{ ...state, lifecycle }, events]
}
// Build a single human-readable message from whatever the provider supplied.
// When both code and message are present, prefix the code so consumers see
// the failure mode (e.g. `rate_limit_exceeded: Slow down`) instead of just
// the bare message — production rate limits and context-length failures used
// to be indistinguishable from generic stream drops.
const providerErrorMessage = (event: OpenAIResponsesEvent, fallback: string): string => {
const nested = event.response?.error ?? undefined
const message = event.message || nested?.message || undefined
const code = event.code || nested?.code || undefined
if (message && code) return `${code}: ${message}`
return message || code || fallback
}
const onResponseFailed = (state: ParserState, event: OpenAIResponsesEvent): StepResult => [
state,
[LLMEvent.providerError({ message: event.message ?? event.code ?? "OpenAI Responses response failed" })],
[LLMEvent.providerError({ message: providerErrorMessage(event, "OpenAI Responses response failed") })],
]
const onError = (state: ParserState, event: OpenAIResponsesEvent): StepResult => [
state,
[LLMEvent.providerError({ message: event.message ?? event.code ?? "OpenAI Responses stream error" })],
[LLMEvent.providerError({ message: providerErrorMessage(event, "OpenAI Responses stream error") })],
]
const step = (state: ParserState, event: OpenAIResponsesEvent) => {
@@ -561,6 +868,10 @@ const step = (state: ParserState, event: OpenAIResponsesEvent) => {
event.type === "response.reasoning_summary_text.done"
)
return Effect.succeed(onReasoningDone(state, event))
if (event.type === "response.reasoning_summary_part.added")
return Effect.succeed(onReasoningSummaryPartAdded(state, event))
if (event.type === "response.reasoning_summary_part.done")
return Effect.succeed(onReasoningSummaryPartDone(state, event))
if (event.type === "response.output_item.added") return Effect.succeed(onOutputItemAdded(state, event))
if (event.type === "response.function_call_arguments.delta") return onFunctionCallArgumentsDelta(state, event)
if (event.type === "response.output_item.done") return onOutputItemDone(state, event)
@@ -587,7 +898,13 @@ export const protocol = Protocol.make({
},
stream: {
event: Protocol.jsonEvent(OpenAIResponsesEvent),
initial: () => ({ hasFunctionCall: false, tools: ToolStream.empty<string>(), lifecycle: Lifecycle.initial() }),
initial: (request) => ({
hasFunctionCall: false,
tools: ToolStream.empty<string>(),
lifecycle: Lifecycle.initial(),
reasoningItems: {},
store: OpenAIOptions.store(request),
}),
step,
terminal: (event) => TERMINAL_TYPES.has(event.type),
},
+11 -1
View File
@@ -80,7 +80,7 @@ export const subtractTokens = (total: number | undefined, subtrahend: number | u
*/
export const sumTokens = (...values: ReadonlyArray<number | undefined>): number | undefined => {
if (values.every((value) => value === undefined)) return undefined
return values.reduce<number>((acc, value) => acc + (value ?? 0), 0)
return values.reduce((acc: number, value) => acc + (value ?? 0), 0)
}
export const eventError = (route: string, message: string, raw?: string) =>
@@ -122,6 +122,16 @@ export const parseToolInput = (route: string, name: string, raw: string) =>
export const mediaBytes = (part: MediaPart) =>
typeof part.data === "string" ? part.data : Buffer.from(part.data).toString("base64")
export const mediaBase64 = (part: MediaPart) => {
if (typeof part.data !== "string" || !part.data.startsWith("data:")) return mediaBytes(part)
return part.data.slice(part.data.indexOf(",") + 1)
}
export const mediaDataUrl = (part: MediaPart) =>
typeof part.data === "string" && part.data.startsWith("data:")
? part.data
: `data:${part.mediaType};base64,${mediaBytes(part)}`
export const trimBaseUrl = (value: string) => value.replace(/\/+$/, "")
export const toolResultText = (part: ToolResultPart) => {
+14 -6
View File
@@ -24,16 +24,24 @@ export const textDelta = (state: State, events: LLMEvent[], id: string, text: st
return { ...stepped, text: new Set([...stepped.text, id]) }
}
export const reasoningDelta = (state: State, events: LLMEvent[], id: string, text: string): State => {
export const reasoningStart = (
state: State,
events: LLMEvent[],
id: string,
providerMetadata?: ProviderMetadata,
): State => {
if (state.reasoning.has(id)) return state
const stepped = stepStart(state, events)
if (stepped.reasoning.has(id)) {
events.push(LLMEvent.reasoningDelta({ id, text }))
return stepped
}
events.push(LLMEvent.reasoningStart({ id }), LLMEvent.reasoningDelta({ id, text }))
events.push(LLMEvent.reasoningStart({ id, providerMetadata }))
return { ...stepped, reasoning: new Set([...stepped.reasoning, id]) }
}
export const reasoningDelta = (state: State, events: LLMEvent[], id: string, text: string): State => {
const started = reasoningStart(state, events, id)
events.push(LLMEvent.reasoningDelta({ id, text }))
return started
}
export const reasoningEnd = (
state: State,
events: LLMEvent[],
@@ -7,12 +7,28 @@ export const OpenAIReasoningEfforts = ReasoningEfforts.filter(
)
export type OpenAIReasoningEffort = (typeof OpenAIReasoningEfforts)[number]
// Mirrors OpenAI's `ResponseIncludable` union from the official SDK. Keep this
// in lockstep with `openai-node/src/resources/responses/responses.ts`.
export const OpenAIResponseIncludables = [
"file_search_call.results",
"web_search_call.results",
"web_search_call.action.sources",
"message.input_image.image_url",
"computer_call_output.output.image_url",
"code_interpreter_call.outputs",
"reasoning.encrypted_content",
"message.output_text.logprobs",
] as const
export type OpenAIResponseIncludable = (typeof OpenAIResponseIncludables)[number]
const REASONING_EFFORTS = new Set<string>(ReasoningEfforts)
const OPENAI_REASONING_EFFORTS = new Set<string>(OpenAIReasoningEfforts)
const TEXT_VERBOSITY = new Set<string>(["low", "medium", "high"])
const INCLUDABLES = new Set<string>(OpenAIResponseIncludables)
export const OpenAIReasoningEffort = Schema.Literals(OpenAIReasoningEfforts)
export const OpenAITextVerbosity = TextVerbosity
export const OpenAIResponseIncludable = Schema.Literals(OpenAIResponseIncludables)
const isAnyReasoningEffort = (effort: unknown): effort is ReasoningEffort =>
typeof effort === "string" && REASONING_EFFORTS.has(effort)
@@ -35,12 +51,20 @@ export const reasoningEffort = (request: LLMRequest): ReasoningEffort | undefine
return isAnyReasoningEffort(value) ? value : undefined
}
export const reasoningSummary = (request: LLMRequest): "auto" | undefined => {
return options(request)?.reasoningSummary === "auto" ? "auto" : undefined
}
export const reasoningSummary = (request: LLMRequest): "auto" | undefined =>
options(request)?.reasoningSummary === "auto" ? "auto" : undefined
export const encryptedReasoning = (request: LLMRequest) =>
options(request)?.includeEncryptedReasoning === true ? true : undefined
// Resolve the OpenAI Responses `include` field. Filters out unknown
// includable values defensively so a typo in upstream config drops the
// invalid entry instead of poisoning the wire body. An empty array (either
// passed directly or produced by filtering) is treated as "no include" and
// returns undefined so the request body omits the field entirely.
export const include = (request: LLMRequest): ReadonlyArray<OpenAIResponseIncludable> | undefined => {
const value = options(request)?.include
if (!Array.isArray(value)) return undefined
const filtered = value.filter((entry): entry is OpenAIResponseIncludable => INCLUDABLES.has(entry))
return filtered.length > 0 ? filtered : undefined
}
export const promptCacheKey = (request: LLMRequest) => {
const value = options(request)?.promptCacheKey
+14 -2
View File
@@ -1,5 +1,8 @@
import type { ProviderOptions, ReasoningEffort, TextVerbosity } from "../schema"
import { mergeProviderOptions } from "../schema"
import type { OpenAIResponseIncludable } from "../protocols/utils/openai-options"
export type { OpenAIResponseIncludable } from "../protocols/utils/openai-options"
export interface OpenAIOptionsInput {
readonly [key: string]: unknown
@@ -7,7 +10,10 @@ export interface OpenAIOptionsInput {
readonly promptCacheKey?: string
readonly reasoningEffort?: ReasoningEffort
readonly reasoningSummary?: "auto"
readonly includeEncryptedReasoning?: boolean
// OpenAI Responses `include` wire field. Mirrors the official SDK's
// `ResponseIncludable[]` union exactly so AI SDK callers and direct
// native-SDK callers share one shape and no translation is required.
readonly include?: ReadonlyArray<OpenAIResponseIncludable>
readonly textVerbosity?: TextVerbosity
}
@@ -25,7 +31,7 @@ const openAIProviderOptions = (options: OpenAIOptionsInput | undefined): Provide
promptCacheKey: options?.promptCacheKey,
reasoningEffort: options?.reasoningEffort,
reasoningSummary: options?.reasoningSummary,
includeEncryptedReasoning: options?.includeEncryptedReasoning,
include: options?.include,
textVerbosity: options?.textVerbosity,
}),
)
@@ -42,6 +48,12 @@ export const gpt5DefaultOptions = (
return openAIProviderOptions({
reasoningEffort: "medium",
reasoningSummary: "auto",
// GPT-5 reasoning models are configured stateless (`store: false`) by
// `openAIDefaultOptions` below, so the only way a follow-up turn can
// carry reasoning state is via the encrypted reasoning include. Without
// this, callers using the default model facade get reasoning summaries
// they cannot replay statelessly.
include: ["reasoning.encrypted_content"],
textVerbosity:
options.textVerbosity === true && id.includes("gpt-5.") && !id.includes("codex") && !id.includes("-chat")
? "low"
+1 -1
View File
@@ -5,7 +5,7 @@ import * as OpenAIChat from "../protocols/openai-chat"
import * as OpenAIResponses from "../protocols/openai-responses"
import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options"
export type { OpenAIOptionsInput } from "./openai-options"
export type { OpenAIOptionsInput, OpenAIResponseIncludable } from "./openai-options"
export const id = ProviderID.make("openai")
+1 -1
View File
@@ -283,7 +283,7 @@ function makeFromTransport<Body, Prepared, Frame, Event, State>(
)
return events.pipe(
Stream.mapAccumEffect(
protocol.stream.initial,
() => protocol.stream.initial(request),
protocol.stream.step,
protocol.stream.onHalt ? { onHalt: protocol.stream.onHalt } : undefined,
),
+2 -2
View File
@@ -52,8 +52,8 @@ export interface ProtocolBody<Body> {
export interface ProtocolStream<Frame, Event, State> {
/** Schema for one decoded streaming event, decoded from a transport frame. */
readonly event: Schema.Codec<Event, Frame>
/** Initial parser state. Called once per response. */
readonly initial: () => State
/** Initial parser state. Called once per response with the resolved request. */
readonly initial: (request: LLMRequest) => State
/** Translate one event into emitted `LLMEvent`s plus the next state. */
readonly step: (state: State, event: Event) => Effect.Effect<readonly [State, ReadonlyArray<LLMEvent>], LLMError>
/** Optional request-completion signal for transports that do not end naturally. */
@@ -1,7 +1,6 @@
import { Cause, Context, Effect, Layer, Queue, Stream } from "effect"
import { Headers } from "effect/unstable/http"
import { Auth } from "../auth"
import { LLMError, TransportReason, type LLMRequest } from "../../schema"
import { LLMError, TransportReason } from "../../schema"
import * as HttpTransport from "./http"
import type { Transport } from "./index"
+104
View File
@@ -0,0 +1,104 @@
import { LLM, Message, ToolCallPart, ToolDefinition, ToolResultPart, type ContentPart, type Model } from "../src"
export const basicContinuation = ["system", "user-text", "assistant-text", "user-follow-up"] as const
export const toolContinuation = ["tool-call", "tool-result"] as const
export const reasoningContinuation = ["assistant-reasoning", "encrypted-reasoning"] as const
export const mediaContinuation = ["user-image"] as const
export const maximalContinuation = [
...basicContinuation,
...toolContinuation,
...reasoningContinuation,
...mediaContinuation,
] as const
export type ContinuationFeature = (typeof maximalContinuation)[number]
export const nativeOpenAIResponsesContinuation = [
...basicContinuation,
...toolContinuation,
"encrypted-reasoning",
...mediaContinuation,
] as const satisfies ReadonlyArray<ContinuationFeature>
export const nativeAnthropicMessagesContinuation = [
...basicContinuation,
...toolContinuation,
"assistant-reasoning",
...mediaContinuation,
] as const satisfies ReadonlyArray<ContinuationFeature>
export const continuationTool = ToolDefinition.make({
name: "get_weather",
description: "Get current weather for a city.",
inputSchema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
additionalProperties: false,
},
})
export function continuationRequest(input: {
readonly id: string
readonly model: Model
readonly features: ReadonlyArray<ContinuationFeature>
readonly image?: string
}) {
const features = new Set(input.features)
const messages = []
const firstUser: ContentPart[] = []
const firstAssistant: ContentPart[] = []
if (features.has("user-text")) firstUser.push({ type: "text", text: "What is shown here?" })
if (features.has("user-image"))
firstUser.push({ type: "media", mediaType: "image/png", data: input.image ?? "AAECAw==" })
if (firstUser.length > 0) messages.push(Message.user(firstUser))
if (features.has("assistant-reasoning"))
firstAssistant.push({
type: "reasoning",
text: "I inspected the previous turn.",
providerMetadata: { anthropic: { signature: "sig_continuation_1" } },
})
if (features.has("encrypted-reasoning"))
firstAssistant.push({
type: "reasoning",
text: "I inspected the previous turn.",
providerMetadata: {
openai: {
itemId: "rs_continuation_1",
reasoningEncryptedContent: "encrypted-continuation-state",
},
},
})
if (features.has("assistant-text")) firstAssistant.push({ type: "text", text: "It shows a small test image." })
if (firstAssistant.length > 0) messages.push(Message.assistant(firstAssistant))
if (features.has("tool-call")) {
messages.push(Message.user("Check the weather in Paris before continuing."))
messages.push(
Message.assistant([ToolCallPart.make({ id: "call_weather_1", name: "get_weather", input: { city: "Paris" } })]),
)
}
if (features.has("tool-result")) {
messages.push(
Message.tool(ToolResultPart.make({ id: "call_weather_1", name: "get_weather", result: { temperature: 22 } })),
)
if (features.has("assistant-text")) messages.push(Message.assistant("Paris is 22 degrees."))
}
if (features.has("user-follow-up"))
messages.push(Message.user("Continue from this conversation in one short sentence."))
return LLM.request({
id: input.id,
model: input.model,
system: features.has("system") ? "You are concise. Continue from the provided history." : undefined,
messages,
tools: features.has("tool-call") ? [continuationTool] : [],
cache: "none",
providerOptions: features.has("encrypted-reasoning")
? { openai: { store: false, include: ["reasoning.encrypted_content"], reasoningSummary: "auto" } }
: undefined,
generation: { maxTokens: 80, temperature: 0 },
})
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
View File
@@ -1,4 +1,3 @@
import { Stream } from "effect"
import { LLMClient } from "../../src/route"
import type { Tools } from "../../src/tool"
import type { RunOptions } from "../../src/tool-runtime"
@@ -1,10 +1,12 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { HttpClientRequest } from "effect/unstable/http"
import { CacheHint, LLM, LLMError, Message, ToolCallPart, Usage } from "../../src"
import { Auth, LLMClient } from "../../src/route"
import * as AnthropicMessages from "../../src/protocols/anthropic-messages"
import { continuationRequest, nativeAnthropicMessagesContinuation } from "../continuation-scenarios"
import { it } from "../lib/effect"
import { fixedResponse } from "../lib/http"
import { dynamicResponse, fixedResponse } from "../lib/http"
import { sseEvents } from "../lib/sse"
const model = AnthropicMessages.route
@@ -22,6 +24,19 @@ const request = LLM.request({
generation: { maxTokens: 20, temperature: 0 },
})
type AnthropicToolResult = Extract<
AnthropicMessages.AnthropicMessagesBody["messages"][number]["content"][number],
{ readonly type: "tool_result" }
>
const expectToolResult = (body: AnthropicMessages.AnthropicMessagesBody): AnthropicToolResult => {
const result = body.messages
.flatMap((message) => (message.role === "user" ? message.content : []))
.find((block): block is AnthropicToolResult => block.type === "tool_result")
expect(result).toBeDefined()
return result!
}
describe("Anthropic Messages route", () => {
it.effect("prepares Anthropic Messages target", () =>
Effect.gen(function* () {
@@ -40,7 +55,7 @@ describe("Anthropic Messages route", () => {
it.effect("prepares tool call and tool result messages", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
const prepared = yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({
id: "req_tool_result",
model,
@@ -69,6 +84,131 @@ describe("Anthropic Messages route", () => {
}),
)
// Regression: screenshot/read tool results must stay structured so base64
// image data is not JSON-stringified into `tool_result.content`.
it.effect("lowers image tool-result content as structured image blocks", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({
id: "req_tool_result_image",
model,
messages: [
Message.user("Show me the screenshot."),
Message.assistant([ToolCallPart.make({ id: "call_1", name: "read", input: { filePath: "shot.png" } })]),
Message.tool({
id: "call_1",
name: "read",
resultType: "content",
result: [
{ type: "text", text: "Image read successfully" },
{ type: "media", mediaType: "image/png", data: "AAECAw==" },
],
}),
],
cache: "none",
}),
)
expect(expectToolResult(prepared.body).content).toEqual([
{ type: "text", text: "Image read successfully" },
{ type: "image", source: { type: "base64", media_type: "image/png", data: "AAECAw==" } },
])
}),
)
it.effect("lowers single-image tool-result content as a structured image block", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({
id: "req_tool_result_image_only",
model,
messages: [
Message.assistant([ToolCallPart.make({ id: "call_1", name: "screenshot", input: {} })]),
Message.tool({
id: "call_1",
name: "screenshot",
resultType: "content",
result: [{ type: "media", mediaType: "image/jpeg", data: "/9j/AA==" }],
}),
],
cache: "none",
}),
)
expect(expectToolResult(prepared.body).content).toEqual([
{ type: "image", source: { type: "base64", media_type: "image/jpeg", data: "/9j/AA==" } },
])
}),
)
it.effect("rejects non-image media in tool-result content with a clear error", () =>
Effect.gen(function* () {
const error = yield* LLMClient.prepare(
LLM.request({
id: "req_tool_result_unsupported_media",
model,
messages: [
Message.assistant([ToolCallPart.make({ id: "call_1", name: "fetch", input: {} })]),
Message.tool({
id: "call_1",
name: "fetch",
resultType: "content",
result: [{ type: "media", mediaType: "audio/mpeg", data: "AAECAw==" }],
}),
],
cache: "none",
}),
).pipe(Effect.flip)
expect(error.message).toContain("Anthropic Messages")
expect(error.message).toContain("audio/mpeg")
}),
)
it.effect("prepares the composed native continuation request", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
continuationRequest({
id: "req_native_continuation_anthropic",
model,
features: nativeAnthropicMessagesContinuation,
}),
)
expect(prepared.body).toMatchObject({
system: [{ type: "text", text: "You are concise. Continue from the provided history." }],
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is shown here?" },
{ type: "image", source: { type: "base64", media_type: "image/png", data: "AAECAw==" } },
],
},
{
role: "assistant",
content: [
{ type: "thinking", thinking: "I inspected the previous turn.", signature: "sig_continuation_1" },
{ type: "text", text: "It shows a small test image." },
],
},
{ role: "user", content: [{ type: "text", text: "Check the weather in Paris before continuing." }] },
{
role: "assistant",
content: [{ type: "tool_use", id: "call_weather_1", name: "get_weather", input: { city: "Paris" } }],
},
{
role: "user",
content: [{ type: "tool_result", tool_use_id: "call_weather_1", content: '{"temperature":22}' }],
},
{ role: "assistant", content: [{ type: "text", text: "Paris is 22 degrees." }] },
{ role: "user", content: [{ type: "text", text: "Continue from this conversation in one short sentence." }] },
],
})
expect(prepared.body.tools).toEqual([expect.objectContaining({ name: "get_weather" })])
}),
)
it.effect("lowers preserved Anthropic reasoning signature metadata", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
@@ -197,7 +337,29 @@ describe("Anthropic Messages route", () => {
),
)
expect(response.events).toEqual([{ type: "provider-error", message: "Overloaded" }])
// Prefix the error type so consumers can distinguish overloads, rate
// limits, and quota errors without parsing the message string.
expect(response.events).toEqual([{ type: "provider-error", message: "overloaded_error: Overloaded" }])
}),
)
it.effect("falls back to error type when no message is present", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(fixedResponse(sseEvents({ type: "error", error: { type: "overloaded_error", message: "" } }))),
)
expect(response.events).toEqual([{ type: "provider-error", message: "overloaded_error" }])
}),
)
it.effect("falls back to a stable default when error payload is absent", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(fixedResponse(sseEvents({ type: "error" }))),
)
expect(response.events).toEqual([{ type: "provider-error", message: "Anthropic Messages stream error" }])
}),
)
@@ -392,17 +554,51 @@ describe("Anthropic Messages route", () => {
}),
)
it.effect("rejects unsupported user media content", () =>
it.effect("continues a conversation with user image content", () =>
Effect.gen(function* () {
const error = yield* LLMClient.prepare(
const response = yield* LLMClient.generate(
LLM.request({
id: "req_media",
model,
messages: [Message.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })],
messages: [
Message.user([
{ type: "text", text: "What is in this image?" },
{ type: "media", mediaType: "image/png", data: "AAECAw==" },
]),
],
}),
).pipe(Effect.flip)
).pipe(
Effect.provide(
dynamicResponse((input) =>
Effect.gen(function* () {
const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(yield* Effect.promise(() => web.json())).toMatchObject({
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is in this image?" },
{ type: "image", source: { type: "base64", media_type: "image/png", data: "AAECAw==" } },
],
},
],
})
return input.respond(
sseEvents(
{ type: "content_block_start", index: 0, content_block: { type: "text", text: "" } },
{ type: "content_block_delta", index: 0, delta: { type: "text_delta", text: "An image." } },
{ type: "content_block_stop", index: 0 },
{ type: "message_delta", delta: { stop_reason: "end_turn" }, usage: { output_tokens: 3 } },
{ type: "message_stop" },
),
{ headers: { "content-type": "text/event-stream" } },
)
}),
),
),
)
expect(error.message).toContain("Anthropic Messages user messages only support text content for now")
expect(response.text).toBe("An image.")
}),
)
@@ -5,7 +5,6 @@ import { Effect } from "effect"
import { CacheHint, LLM, Message, ToolCallPart, ToolChoice } from "../../src"
import { LLMClient } from "../../src/route"
import { AmazonBedrock } from "../../src/providers"
import * as BedrockConverse from "../../src/protocols/bedrock-converse"
import { it } from "../lib/effect"
import { fixedResponse } from "../lib/http"
import {
@@ -84,8 +84,10 @@ describeRecordedGoldenScenarios([
scenarios: [
{ id: "text", temperature: false },
{ id: "reasoning", temperature: false },
{ id: "reasoning-continuation", temperature: false },
{ id: "tool-call", temperature: false },
{ id: "tool-loop", temperature: false },
{ id: "image-tool-result", temperature: false, maxTokens: 40 },
],
},
{
@@ -111,7 +113,10 @@ describeRecordedGoldenScenarios([
requires: ["ANTHROPIC_API_KEY"],
tags: ["flagship"],
options: { redactor: Redactor.defaults({ requestHeaders: { allow: ["content-type", "anthropic-version"] } }) },
scenarios: [{ id: "tool-loop", temperature: false }],
scenarios: [
{ id: "tool-loop", temperature: false },
{ id: "image-tool-result", temperature: false, maxTokens: 40 },
],
},
{
name: "Gemini 2.5 Flash",
@@ -7,6 +7,7 @@ import * as Azure from "../../src/providers/azure"
import * as OpenAI from "../../src/providers/openai"
import * as OpenAIResponses from "../../src/protocols/openai-responses"
import * as ProviderShared from "../../src/protocols/shared"
import { continuationRequest, nativeOpenAIResponsesContinuation } from "../continuation-scenarios"
import { it } from "../lib/effect"
import { dynamicResponse, fixedResponse } from "../lib/http"
import { sseEvents } from "../lib/sse"
@@ -25,6 +26,19 @@ const request = LLM.request({
const configEnv = (env: Record<string, string>) => Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env })))
type OpenAIToolOutput = Extract<
OpenAIResponses.OpenAIResponsesBody["input"][number],
{ readonly type: "function_call_output" }
>
const expectToolOutput = (body: OpenAIResponses.OpenAIResponsesBody): OpenAIToolOutput => {
const output = body.input.find(
(item): item is OpenAIToolOutput => "type" in item && item.type === "function_call_output",
)
expect(output).toBeDefined()
return output!
}
describe("OpenAI Responses route", () => {
it.effect("prepares OpenAI Responses target", () =>
Effect.gen(function* () {
@@ -247,6 +261,127 @@ describe("OpenAI Responses route", () => {
}),
)
// Regression: screenshot/read tool results must stay structured so base64
// image data is not JSON-stringified into `function_call_output.output`.
it.effect("lowers image tool-result content as structured input_image items", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
id: "req_tool_result_image",
model,
messages: [
Message.user("Show me the screenshot."),
Message.assistant([ToolCallPart.make({ id: "call_1", name: "read", input: { filePath: "shot.png" } })]),
Message.tool({
id: "call_1",
name: "read",
resultType: "content",
result: [
{ type: "text", text: "Image read successfully" },
{ type: "media", mediaType: "image/png", data: "AAECAw==" },
],
}),
],
}),
)
expect(expectToolOutput(prepared.body).output).toEqual([
{ type: "input_text", text: "Image read successfully" },
{ type: "input_image", image_url: "data:image/png;base64,AAECAw==" },
])
}),
)
it.effect("lowers single-image tool-result content as structured input_image array", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
id: "req_tool_result_image_only",
model,
messages: [
Message.assistant([ToolCallPart.make({ id: "call_1", name: "screenshot", input: {} })]),
Message.tool({
id: "call_1",
name: "screenshot",
resultType: "content",
result: [{ type: "media", mediaType: "image/png", data: "AAECAw==" }],
}),
],
}),
)
expect(expectToolOutput(prepared.body).output).toEqual([
{ type: "input_image", image_url: "data:image/png;base64,AAECAw==" },
])
}),
)
it.effect("rejects non-image media in tool-result content with a clear error", () =>
Effect.gen(function* () {
const error = yield* LLMClient.prepare(
LLM.request({
id: "req_tool_result_unsupported_media",
model,
messages: [
Message.assistant([ToolCallPart.make({ id: "call_1", name: "fetch", input: {} })]),
Message.tool({
id: "call_1",
name: "fetch",
resultType: "content",
result: [{ type: "media", mediaType: "audio/mpeg", data: "AAECAw==" }],
}),
],
}),
).pipe(Effect.flip)
expect(error.message).toContain("OpenAI Responses")
expect(error.message).toContain("audio/mpeg")
}),
)
it.effect("prepares the composed native continuation request", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
continuationRequest({
id: "req_native_continuation_openai",
model,
features: nativeOpenAIResponsesContinuation,
}),
)
expect(prepared.body).toMatchObject({
input: [
{ role: "system", content: "You are concise. Continue from the provided history." },
{
role: "user",
content: [
{ type: "input_text", text: "What is shown here?" },
{ type: "input_image", image_url: "data:image/png;base64,AAECAw==" },
],
},
{
type: "reasoning",
id: "rs_continuation_1",
encrypted_content: "encrypted-continuation-state",
summary: [{ type: "summary_text", text: "I inspected the previous turn." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "It shows a small test image." }] },
{ role: "user", content: [{ type: "input_text", text: "Check the weather in Paris before continuing." }] },
{ type: "function_call", call_id: "call_weather_1", name: "get_weather", arguments: '{"city":"Paris"}' },
{ type: "function_call_output", call_id: "call_weather_1", output: '{"temperature":22}' },
{ role: "assistant", content: [{ type: "output_text", text: "Paris is 22 degrees." }] },
{
role: "user",
content: [{ type: "input_text", text: "Continue from this conversation in one short sentence." }],
},
],
include: ["reasoning.encrypted_content"],
store: false,
})
expect(prepared.body.tools).toEqual([expect.objectContaining({ type: "function", name: "get_weather" })])
}),
)
it.effect("maps OpenAI provider options to Responses options", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
@@ -258,7 +393,7 @@ describe("OpenAI Responses route", () => {
promptCacheKey: "session_123",
reasoningEffort: "high",
reasoningSummary: "auto",
includeEncryptedReasoning: true,
include: ["reasoning.encrypted_content"],
},
},
}),
@@ -272,6 +407,108 @@ describe("OpenAI Responses route", () => {
}),
)
it.effect("accepts the full ResponseIncludable union", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
model,
prompt: "hi",
providerOptions: {
openai: {
include: ["reasoning.encrypted_content", "code_interpreter_call.outputs", "web_search_call.results"],
},
},
}),
)
expect(prepared.body.include).toEqual([
"reasoning.encrypted_content",
"code_interpreter_call.outputs",
"web_search_call.results",
])
}),
)
it.effect("filters unknown includable values out of the include array", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
model,
prompt: "hi",
// The user passed one invalid entry alongside a valid one. Keep the
// valid one so the request still succeeds rather than failing on a
// typo from upstream config.
providerOptions: { openai: { include: ["reasoning.encrypted_content", "bogus.thing"] } },
}),
)
expect(prepared.body.include).toEqual(["reasoning.encrypted_content"])
}),
)
it.effect("treats an explicit empty include as no include at all", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({ model, prompt: "hi", providerOptions: { openai: { include: [] } } }),
)
expect(prepared.body.include).toBeUndefined()
}),
)
it.effect("treats an all-invalid include as no include at all", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({ model, prompt: "hi", providerOptions: { openai: { include: ["bogus.thing"] } } }),
)
expect(prepared.body.include).toBeUndefined()
}),
)
it.effect("omits include when no include is set", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({ model, prompt: "hi", providerOptions: { openai: { store: false } } }),
)
expect(prepared.body.include).toBeUndefined()
}),
)
it.effect("requests encrypted reasoning by default for GPT-5 reasoning models", () =>
Effect.gen(function* () {
// The native OpenAI facade configures GPT-5 stateless (store: false) with
// reasoningSummary: "auto" by default. Without `include`, a follow-up
// turn cannot replay reasoning state, so the facade also opts into
// `reasoning.encrypted_content` automatically.
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
model: OpenAI.configure({ baseURL: "https://api.openai.test/v1/", apiKey: "test" }).responses("gpt-5.2"),
prompt: "hi",
}),
)
expect(prepared.body.store).toBe(false)
expect(prepared.body.include).toEqual(["reasoning.encrypted_content"])
expect(prepared.body.reasoning).toEqual({ effort: "medium", summary: "auto" })
}),
)
it.effect("lets callers opt out of the GPT-5 default include", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
model: OpenAI.configure({ baseURL: "https://api.openai.test/v1/", apiKey: "test" }).responses("gpt-5.2"),
prompt: "hi",
providerOptions: { openai: { include: [] } },
}),
)
expect(prepared.body.include).toBeUndefined()
}),
)
it.effect("request OpenAI provider options override route defaults", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
@@ -380,6 +617,322 @@ describe("OpenAI Responses route", () => {
}),
)
it.effect("preserves encrypted reasoning metadata for continuation", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(
fixedResponse(
sseEvents(
{ type: "response.reasoning_summary_text.delta", item_id: "rs_1", delta: "thinking" },
{
type: "response.output_item.done",
item: {
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [{ type: "summary_text", text: "thinking" }],
},
},
{ type: "response.completed", response: { id: "resp_1" } },
),
),
),
)
expect(response.events).toContainEqual(
expect.objectContaining({
type: "reasoning-end",
id: "rs_1",
providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: "encrypted-state" } },
}),
)
}),
)
it.effect("streams each reasoning summary part as a separate block", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(
LLM.updateRequest(request, { providerOptions: { openai: { store: false } } }),
).pipe(
Effect.provide(
fixedResponse(
sseEvents(
{
type: "response.output_item.added",
item: { type: "reasoning", id: "rs_1", encrypted_content: null },
},
{ type: "response.reasoning_summary_part.added", item_id: "rs_1", summary_index: 0 },
{ type: "response.reasoning_summary_text.delta", item_id: "rs_1", summary_index: 0, delta: "First" },
{ type: "response.reasoning_summary_part.done", item_id: "rs_1", summary_index: 0 },
{ type: "response.reasoning_summary_part.added", item_id: "rs_1", summary_index: 1 },
{ type: "response.reasoning_summary_text.delta", item_id: "rs_1", summary_index: 1, delta: "Second" },
{ type: "response.reasoning_summary_part.done", item_id: "rs_1", summary_index: 1 },
{
type: "response.output_item.done",
item: { type: "reasoning", id: "rs_1", encrypted_content: "encrypted-state" },
},
{ type: "response.completed", response: { id: "resp_1" } },
),
),
),
)
expect(response.reasoning).toBe("FirstSecond")
expect(response.events).toMatchObject([
{ type: "step-start", index: 0 },
{
type: "reasoning-start",
id: "rs_1:0",
providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: null } },
},
{ type: "reasoning-delta", id: "rs_1:0", text: "First" },
{ type: "reasoning-end", id: "rs_1:0", providerMetadata: { openai: { itemId: "rs_1" } } },
{
type: "reasoning-start",
id: "rs_1:1",
providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: null } },
},
{ type: "reasoning-delta", id: "rs_1:1", text: "Second" },
{
type: "reasoning-end",
id: "rs_1:1",
providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: "encrypted-state" } },
},
{ type: "step-finish", index: 0, reason: "stop" },
{ type: "finish", reason: "stop" },
])
}),
)
it.effect("closes reasoning summary parts when storage is not disabled", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(
fixedResponse(
sseEvents(
{
type: "response.output_item.added",
item: { type: "reasoning", id: "rs_1", encrypted_content: null },
},
{ type: "response.reasoning_summary_part.added", item_id: "rs_1", summary_index: 0 },
{ type: "response.reasoning_summary_text.delta", item_id: "rs_1", summary_index: 0, delta: "First" },
{ type: "response.reasoning_summary_part.done", item_id: "rs_1", summary_index: 0 },
{ type: "response.reasoning_summary_part.added", item_id: "rs_1", summary_index: 1 },
{ type: "response.reasoning_summary_text.delta", item_id: "rs_1", summary_index: 1, delta: "Second" },
{ type: "response.reasoning_summary_part.done", item_id: "rs_1", summary_index: 1 },
{
type: "response.output_item.done",
item: { type: "reasoning", id: "rs_1", encrypted_content: null },
},
{ type: "response.completed", response: { id: "resp_1" } },
),
),
),
)
expect(response.events.filter((event) => event.type === "reasoning-end")).toEqual([
{ type: "reasoning-end", id: "rs_1:0", providerMetadata: { openai: { itemId: "rs_1" } } },
{ type: "reasoning-end", id: "rs_1:1", providerMetadata: { openai: { itemId: "rs_1" } } },
])
}),
)
it.effect("continues a stateless reasoning conversation", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(
LLM.request({
id: "req_reasoning_continue",
model,
messages: [
Message.user("What changed?"),
Message.assistant([
{
type: "reasoning",
text: "Checked the previous diff.",
providerMetadata: {
openai: {
itemId: "rs_1",
reasoningEncryptedContent: "encrypted-state",
},
},
},
{ type: "text", text: "The parser changed." },
]),
Message.user("Summarize it."),
],
providerOptions: { openai: { store: false } },
}),
).pipe(
Effect.provide(
dynamicResponse((input) =>
Effect.gen(function* () {
const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(yield* Effect.promise(() => web.json())).toMatchObject({
input: [
{ role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [{ type: "summary_text", text: "Checked the previous diff." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "The parser changed." }] },
{ role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
],
})
return input.respond(
sseEvents(
{ type: "response.output_text.delta", item_id: "msg_1", delta: "Parser now round-trips reasoning." },
{ type: "response.completed", response: { id: "resp_1" } },
),
{ headers: { "content-type": "text/event-stream" } },
)
}),
),
),
)
expect(response.text).toBe("Parser now round-trips reasoning.")
}),
)
it.effect("preserves assistant content order around reasoning items", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
id: "req_reasoning_order",
model,
messages: [
Message.assistant([
{ type: "text", text: "Before." },
{
type: "reasoning",
text: "Checked order.",
providerMetadata: {
openai: {
itemId: "rs_1",
reasoningEncryptedContent: "encrypted-state",
},
},
},
{ type: "text", text: "After." },
]),
],
providerOptions: { openai: { store: false } },
}),
)
expect(prepared.body.input).toEqual([
{ role: "assistant", content: [{ type: "output_text", text: "Before." }] },
{
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [{ type: "summary_text", text: "Checked order." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "After." }] },
])
}),
)
it.effect("references stored reasoning items by id", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
model,
messages: [
Message.assistant([
{
type: "reasoning",
text: "Checked the previous diff.",
providerMetadata: { openai: { itemId: "rs_1" } },
},
]),
],
providerOptions: { openai: { store: true } },
}),
)
expect(prepared.body.input).toEqual([{ type: "item_reference", id: "rs_1" }])
}),
)
it.effect("joins streamed summary blocks into one continuation reasoning item", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
id: "req_multi_summary_continuation",
model,
messages: [
Message.assistant([
{
type: "reasoning",
text: "First",
providerMetadata: { openai: { itemId: "rs_1" } },
},
{
type: "reasoning",
text: "Second",
providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: "encrypted-state" } },
},
]),
],
providerOptions: { openai: { store: false } },
}),
)
expect(prepared.body.input).toEqual([
{
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [
{ type: "summary_text", text: "First" },
{ type: "summary_text", text: "Second" },
],
},
])
}),
)
it.effect("skips non-persisted reasoning ids without encrypted state", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
LLM.request({
id: "req_reasoning_without_encrypted_state",
model,
messages: [
Message.user("What changed?"),
Message.assistant([
{
type: "reasoning",
text: "Checked the previous diff.",
providerMetadata: {
openai: {
itemId: "rs_1",
reasoningEncryptedContent: null,
},
},
},
{ type: "text", text: "The parser changed." },
]),
Message.user("Summarize it."),
],
providerOptions: { openai: { store: false } },
}),
)
expect(prepared.body).toMatchObject({
input: [
{ role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{ role: "assistant", content: [{ type: "output_text", text: "The parser changed." }] },
{ role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
],
store: false,
})
}),
)
it.effect("assembles streamed function call input", () =>
Effect.gen(function* () {
const body = sseEvents(
@@ -576,7 +1129,11 @@ describe("OpenAI Responses route", () => {
Effect.provide(fixedResponse(sseEvents({ type: "error", code: "rate_limit_exceeded", message: "Slow down" }))),
)
expect(response.events).toEqual([{ type: "provider-error", message: "Slow down" }])
// Prefix the code so consumers see the failure mode, not just the
// sometimes-generic provider message. The bare message alone meant
// production errors like rate limits were indistinguishable from
// unrelated stream failures.
expect(response.events).toEqual([{ type: "provider-error", message: "rate_limit_exceeded: Slow down" }])
}),
)
@@ -590,6 +1147,99 @@ describe("OpenAI Responses route", () => {
}),
)
it.effect("falls back to error code when message is empty", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(fixedResponse(sseEvents({ type: "error", code: "internal_error", message: "" }))),
)
expect(response.events).toEqual([{ type: "provider-error", message: "internal_error" }])
}),
)
// Regression: `response.failed` carries the failure details under
// `response.error`, not at the top level. The previous handler only
// checked top-level `message`/`code` and so always emitted the bare
// "OpenAI Responses response failed" string, hiding the real cause.
it.effect("surfaces response.failed details from response.error", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(
fixedResponse(
sseEvents({
type: "response.failed",
response: {
id: "resp_failed_1",
error: { code: "server_error", message: "Upstream model unavailable" },
},
}),
),
),
)
expect(response.events).toEqual([{ type: "provider-error", message: "server_error: Upstream model unavailable" }])
}),
)
it.effect("surfaces response.failed code when no nested message is present", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(
fixedResponse(
sseEvents({
type: "response.failed",
response: { id: "resp_failed_2", error: { code: "invalid_prompt" } },
}),
),
),
)
expect(response.events).toEqual([{ type: "provider-error", message: "invalid_prompt" }])
}),
)
it.effect("surfaces error event details even when they arrive nested under response.error", () =>
Effect.gen(function* () {
// Some OpenAI-compatible proxies and older SDK versions wrap the
// top-level error fields into a nested `response.error` payload
// when they bubble up an HTTP error as an SSE `error` event. Honour
// both shapes so the user still sees the underlying cause instead
// of the catch-all string.
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(
fixedResponse(
sseEvents({
type: "error",
response: { error: { code: "context_length_exceeded", message: "prompt too long" } },
}),
),
),
)
expect(response.events).toEqual([{ type: "provider-error", message: "context_length_exceeded: prompt too long" }])
}),
)
it.effect("falls back to a stable default when both error and response are absent", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(fixedResponse(sseEvents({ type: "error" }))),
)
expect(response.events).toEqual([{ type: "provider-error", message: "OpenAI Responses stream error" }])
}),
)
it.effect("falls back to a stable default when response.failed has no error payload", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(fixedResponse(sseEvents({ type: "response.failed", response: { id: "resp_failed_3" } }))),
)
expect(response.events).toEqual([{ type: "provider-error", message: "OpenAI Responses response failed" }])
}),
)
it.effect("fails HTTP provider errors before stream parsing", () =>
Effect.gen(function* () {
const error = yield* LLMClient.generate(request).pipe(
+2 -10
View File
@@ -2,7 +2,7 @@ import type { HttpRecorder } from "@opencode-ai/http-recorder"
import { describe } from "bun:test"
import { Effect } from "effect"
import type { Model } from "../src"
import { goldenScenarioTags, runGoldenScenario, type GoldenScenarioID } from "./recorded-scenarios"
import { goldenScenarioTags, goldenScenarioTitle, runGoldenScenario, type GoldenScenarioID } from "./recorded-scenarios"
import { recordedTests } from "./recorded-test"
import { kebab } from "./recorded-utils"
@@ -35,14 +35,6 @@ type TargetInput = {
const scenarioInput = (input: ScenarioInput) => (typeof input === "string" ? { id: input } : input)
const scenarioTitle = (id: GoldenScenarioID) => {
if (id === "text") return "streams text"
if (id === "tool-call") return "streams tool call"
if (id === "reasoning") return "uses reasoning"
if (id === "image") return "reads image text"
return "drives a tool loop"
}
const defaultPrefix = (target: TargetInput) => {
if (target.prefix) return target.prefix
const transport = target.transport === "websocket" ? "-websocket" : ""
@@ -77,7 +69,7 @@ const runTarget = (target: TargetInput) => {
describe(`${target.name} recorded`, () => {
target.scenarios.forEach((raw) => {
const input = scenarioInput(raw)
const name = input.name ?? scenarioTitle(input.id)
const name = input.name ?? goldenScenarioTitle(input.id)
recorded.effect.with(
name,
{

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