diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b8f0da07..cdae8ab49 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,8 +19,8 @@ Take a look at recent git history to understand what usually lands. If you are unsure if a PR would be accepted, feel free to ask a maintainer or look for issues with either of the following labels: -- `help wanted` -- `bug` +- [`help wanted`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted) +- [`bug`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) > [!NOTE] > PRs that ignore these guardrails will likely be closed. diff --git a/bun.lock b/bun.lock index 7b21002e7..92e8142be 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -149,7 +149,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -165,7 +165,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "0.15.8", + "version": "0.15.10", "bin": { "opencode": "./bin/opencode", }, @@ -187,6 +187,7 @@ "@parcel/watcher": "2.5.1", "@solid-primitives/event-bus": "1.1.2", "@standard-schema/spec": "1.0.0", + "@zed-industries/agent-client-protocol": "0.4.5", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", "chokidar": "4.0.3", @@ -238,7 +239,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -258,7 +259,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "0.15.8", + "version": "0.15.10", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -269,7 +270,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -282,7 +283,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@kobalte/core": "catalog:", "@solidjs/meta": "catalog:", @@ -303,7 +304,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", @@ -896,11 +897,11 @@ "@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="], - "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.0", "", { "dependencies": { "@octokit/types": "^15.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-YuAlyjR8o5QoRSOvMHxSJzPtogkNMgeMv2mpccrvdUGeC3MKyfi/hS+KiFwyH/iRKIKyx+eIMsDjbt3p9r2GYA=="], + "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="], "@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="], - "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.0", "", { "dependencies": { "@octokit/types": "^15.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-nCsyiKoGRnhH5LkH8hJEZb9swpqOcsW+VXv1QoyUNQXJeVODG4+xM6UICEqyqe9XFr6LkL8BIiFCPev8zMDXPw=="], + "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="], "@octokit/request": ["@octokit/request@10.0.5", "", { "dependencies": { "@octokit/endpoint": "^11.0.1", "@octokit/request-error": "^7.0.1", "@octokit/types": "^15.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-TXnouHIYLtgDhKo+N6mXATnDBkV05VwbR0TtMWpgTHIoQdRQfCSzmy/LGqR1AbRMbijq/EckC/E3/ZNcU92NaQ=="], @@ -1106,49 +1107,49 @@ "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.4", "", { "os": "android", "cpu": "arm" }, "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.4", "", { "os": "android", "cpu": "arm64" }, "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.4", "", { "os": "none", "cpu": "arm64" }, "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="], "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], @@ -1470,7 +1471,9 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], - "@webgpu/types": ["@webgpu/types@0.1.65", "", {}, "sha512-cYrHab4d6wuVvDW5tdsfI6/o6vcLMDe6w2Citd1oS51Xxu2ycLCnVo4fqwujfKWijrZMInTJIKcXxteoy21nVA=="], + "@webgpu/types": ["@webgpu/types@0.1.66", "", {}, "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA=="], + + "@zed-industries/agent-client-protocol": ["@zed-industries/agent-client-protocol@0.4.5", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-OwHKfu5AiKul/GSJilHg36+kOSV9eOl2eYnjR6tenAQDdraSnt22WFk0SDFTeuAANz1Gxkv+DTf7Hq32rvX7FQ=="], "@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="], @@ -1596,13 +1599,13 @@ "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], - "bare-url": ["bare-url@2.3.0", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-c+RCqMSZbkz97Mw1LWR0gcOqwK82oyYKfLoHJ8k13ybi1+I80ffdDzUy0TdAburdrR/kI0/VuN8YgEnJqX+Nyw=="], + "bare-url": ["bare-url@2.3.1", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-v2yl0TnaZTdEnelkKtXZGnotiV6qATBlnNuUMrHl6v9Lmmrh9mw9RYyImPU7/4RahumSwQS1k2oKXcRfXcbjJw=="], "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.8.17", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-j5zJcx6golJYTG6c05LUZ3Z8Gi+M62zRT/ycz4Xq4iCOdpcxwg7ngEYD4KA0eWZC7U17qh/Smq8bYbACJ0ipBA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w=="], "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="], @@ -2776,7 +2779,7 @@ "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], - "package-manager-detector": ["package-manager-detector@1.4.1", "", {}, "sha512-dSMiVLBEA4XaNJ0PRb4N5cV/SEP4BWrWZKBmfF+OUm2pQTiZ6DDkKeWaltwu3JRhLoy59ayIkJ00cx9K9CaYTg=="], + "package-manager-detector": ["package-manager-detector@1.5.0", "", {}, "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw=="], "pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="], @@ -3008,7 +3011,7 @@ "reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="], - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], "resolve-cwd": ["resolve-cwd@3.0.0", "", { "dependencies": { "resolve-from": "^5.0.0" } }, "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg=="], @@ -3030,7 +3033,7 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rollup": ["rollup@4.52.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.4", "@rollup/rollup-android-arm64": "4.52.4", "@rollup/rollup-darwin-arm64": "4.52.4", "@rollup/rollup-darwin-x64": "4.52.4", "@rollup/rollup-freebsd-arm64": "4.52.4", "@rollup/rollup-freebsd-x64": "4.52.4", "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", "@rollup/rollup-linux-arm-musleabihf": "4.52.4", "@rollup/rollup-linux-arm64-gnu": "4.52.4", "@rollup/rollup-linux-arm64-musl": "4.52.4", "@rollup/rollup-linux-loong64-gnu": "4.52.4", "@rollup/rollup-linux-ppc64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-musl": "4.52.4", "@rollup/rollup-linux-s390x-gnu": "4.52.4", "@rollup/rollup-linux-x64-gnu": "4.52.4", "@rollup/rollup-linux-x64-musl": "4.52.4", "@rollup/rollup-openharmony-arm64": "4.52.4", "@rollup/rollup-win32-arm64-msvc": "4.52.4", "@rollup/rollup-win32-ia32-msvc": "4.52.4", "@rollup/rollup-win32-x64-gnu": "4.52.4", "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ=="], + "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="], "rollup-plugin-visualizer": ["rollup-plugin-visualizer@6.0.5", "", { "dependencies": { "open": "^8.0.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rolldown": "1.x || ^1.0.0-beta", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg=="], @@ -3120,7 +3123,7 @@ "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - "sitemap": ["sitemap@8.0.0", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.2.4" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A=="], + "sitemap": ["sitemap@8.0.1", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-4Y8ynSMFAy/DadeAeio8Kx4zfC8/0VcKi7TH0I1SazvBcrU2fpJaGoeWsX1FMRaHoe3VGMA53DqVoLErZrtG9Q=="], "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], @@ -3568,7 +3571,7 @@ "@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="], - "@astrojs/cloudflare/vite": ["vite@6.4.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw=="], + "@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], @@ -3576,7 +3579,7 @@ "@astrojs/sitemap/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@astrojs/solid-js/vite": ["vite@6.4.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw=="], + "@astrojs/solid-js/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], @@ -3658,27 +3661,27 @@ "@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@octokit/auth-oauth-app/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/auth-oauth-app/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/auth-oauth-device/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/auth-oauth-device/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/auth-oauth-user/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/auth-oauth-user/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/core/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/core/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/endpoint/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/endpoint/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/graphql/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/graphql/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/oauth-methods/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/oauth-methods/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/request/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/request/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], - "@octokit/request-error/@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="], + "@octokit/request-error/@octokit/types": ["@octokit/types@15.0.1", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q=="], "@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], @@ -3744,6 +3747,8 @@ "@tanstack/router-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@types/serve-static/@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="], "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -3756,6 +3761,8 @@ "@vinxi/server-components/magicast": ["magicast@0.2.11", "", { "dependencies": { "@babel/parser": "^7.22.16", "@babel/types": "^7.22.17", "recast": "^0.23.4" } }, "sha512-6saXbRDA1HMkqbsvHOU6HBjCVgZT460qheRkLhJQHWAbhXoWESI3Kn/dGGXyKs15FFKR85jsUqFx2sMK0wy/5g=="], + "@zed-industries/agent-client-protocol/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g=="], @@ -3774,7 +3781,7 @@ "astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "astro/vite": ["vite@6.4.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw=="], + "astro/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -4042,7 +4049,9 @@ "uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "vinxi/vite": ["vite@6.4.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw=="], + "utif2/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + + "vinxi/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "vinxi/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -4322,6 +4331,14 @@ "axios/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "babel-plugin-module-resolver/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="], + + "babel-plugin-module-resolver/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="], + + "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/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -4432,8 +4449,6 @@ "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], - "parse-bmfont-xml/xml2js/sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], - "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=="], "prebuild-install/tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], @@ -4618,6 +4633,10 @@ "axios/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "babel-plugin-module-resolver/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "esbuild-plugin-copy/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "giget/tar/minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], diff --git a/infra/app.ts b/infra/app.ts index 151879208..7215995ba 100644 --- a/infra/app.ts +++ b/infra/app.ts @@ -3,6 +3,7 @@ import { domain } from "./stage" const GITHUB_APP_ID = new sst.Secret("GITHUB_APP_ID") const GITHUB_APP_PRIVATE_KEY = new sst.Secret("GITHUB_APP_PRIVATE_KEY") export const EMAILOCTOPUS_API_KEY = new sst.Secret("EMAILOCTOPUS_API_KEY") +const ADMIN_SECRET = new sst.Secret("ADMIN_SECRET") const bucket = new sst.cloudflare.Bucket("Bucket") export const api = new sst.cloudflare.Worker("Api", { @@ -12,7 +13,7 @@ export const api = new sst.cloudflare.Worker("Api", { WEB_DOMAIN: domain, }, url: true, - link: [bucket, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY], + link: [bucket, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, ADMIN_SECRET], transform: { worker: (args) => { args.logpush = true diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 6ddb62183..8fdd90b77 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "0.15.8" + "version": "0.15.10" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 723e56dd4..508aee5cb 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "0.15.8", + "version": "0.15.10", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 8d6b4db72..6d5ef7181 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "0.15.8", + "version": "0.15.10", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index ab2308c82..5fbc12051 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "0.15.8", + "version": "0.15.10", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 389910241..a6df47e13 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "0.15.8", + "version": "0.15.10", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index 9c39484ca..81e418c96 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "0.15.8", + "version": "0.15.10", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/function/src/api.ts b/packages/function/src/api.ts index f94e87a96..6f00dae9a 100644 --- a/packages/function/src/api.ts +++ b/packages/function/src/api.ts @@ -137,7 +137,11 @@ export default new Hono<{ Bindings: Env }>() return c.json({}) }) .post("/share_delete_admin", async (c) => { - const id = c.env.SYNC_SERVER.idFromName("oVF8Rsiv") + const body = await c.req.json<{ sessionShortName: string; adminSecret: string }>() + const sessionShortName = body.sessionShortName + const adminSecret = body.adminSecret + if (adminSecret !== Resource.ADMIN_SECRET.value) throw new Error("Invalid admin secret") + const id = c.env.SYNC_SERVER.idFromName(sessionShortName) const stub = c.env.SYNC_SERVER.get(id) await stub.clear() return c.json({}) diff --git a/packages/opencode/package.json b/packages/opencode/package.json index c6f690ab8..3fa4766e4 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.15.8", + "version": "0.15.10", "name": "opencode", "type": "module", "private": true, @@ -54,6 +54,7 @@ "@parcel/watcher": "2.5.1", "@solid-primitives/event-bus": "1.1.2", "@standard-schema/spec": "1.0.0", + "@zed-industries/agent-client-protocol": "0.4.5", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", "chokidar": "4.0.3", diff --git a/packages/opencode/src/acp/README.md b/packages/opencode/src/acp/README.md new file mode 100644 index 000000000..5e51af13c --- /dev/null +++ b/packages/opencode/src/acp/README.md @@ -0,0 +1,164 @@ +# ACP (Agent Client Protocol) Implementation + +This directory contains a clean, protocol-compliant implementation of the [Agent Client Protocol](https://agentclientprotocol.com/) for opencode. + +## Architecture + +The implementation follows a clean separation of concerns: + +### Core Components + +- **`agent.ts`** - Implements the `Agent` interface from `@zed-industries/agent-client-protocol` + - Handles initialization and capability negotiation + - Manages session lifecycle (`session/new`, `session/load`) + - Processes prompts and returns responses + - Properly implements ACP protocol v1 + +- **`client.ts`** - Implements the `Client` interface for client-side capabilities + - File operations (`readTextFile`, `writeTextFile`) + - Permission requests (auto-approves for now) + - Terminal support (stub implementation) + +- **`session.ts`** - Session state management + - Creates and tracks ACP sessions + - Maps ACP sessions to internal opencode sessions + - Maintains working directory context + - Handles MCP server configurations + +- **`server.ts`** - ACP server startup and lifecycle + - Sets up JSON-RPC over stdio using the official library + - Manages graceful shutdown on SIGTERM/SIGINT + - Provides Instance context for the agent + +- **`types.ts`** - Type definitions for internal use + +## Usage + +### Command Line + +```bash +# Start the ACP server in the current directory +opencode acp + +# Start in a specific directory +opencode acp --cwd /path/to/project +``` + +### Programmatic + +```typescript +import { ACPServer } from "./acp/server" + +await ACPServer.start() +``` + +### Integration with Zed + +Add to your Zed configuration (`~/.config/zed/settings.json`): + +```json +{ + "agent_servers": { + "OpenCode": { + "command": "opencode", + "args": ["acp"] + } + } +} +``` + +## Protocol Compliance + +This implementation follows the ACP specification v1: + +✅ **Initialization** + +- Proper `initialize` request/response with protocol version negotiation +- Capability advertisement (`agentCapabilities`) +- Authentication support (stub) + +✅ **Session Management** + +- `session/new` - Create new conversation sessions +- `session/load` - Resume existing sessions (basic support) +- Working directory context (`cwd`) +- MCP server configuration support + +✅ **Prompting** + +- `session/prompt` - Process user messages +- Content block handling (text, resources) +- Response with stop reasons + +✅ **Client Capabilities** + +- File read/write operations +- Permission requests +- Terminal support (stub for future) + +## Current Limitations + +### Not Yet Implemented + +1. **Streaming Responses** - Currently returns complete responses instead of streaming via `session/update` notifications +2. **Tool Call Reporting** - Doesn't report tool execution progress +3. **Session Modes** - No mode switching support yet +4. **Authentication** - No actual auth implementation +5. **Terminal Support** - Placeholder only +6. **Session Persistence** - `session/load` doesn't restore actual conversation history + +### Future Enhancements + +- **Real-time Streaming**: Implement `session/update` notifications for progressive responses +- **Tool Call Visibility**: Report tool executions as they happen +- **Session Persistence**: Save and restore full conversation history +- **Mode Support**: Implement different operational modes (ask, code, etc.) +- **Enhanced Permissions**: More sophisticated permission handling +- **Terminal Integration**: Full terminal support via opencode's bash tool + +## Testing + +```bash +# Run ACP tests +bun test test/acp.test.ts + +# Test manually with stdio +echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1}}' | opencode acp +``` + +## Design Decisions + +### Why the Official Library? + +We use `@zed-industries/agent-client-protocol` instead of implementing JSON-RPC ourselves because: + +- Ensures protocol compliance +- Handles edge cases and future protocol versions +- Reduces maintenance burden +- Works with other ACP clients automatically + +### Clean Architecture + +Each component has a single responsibility: + +- **Agent** = Protocol interface +- **Client** = Client-side operations +- **Session** = State management +- **Server** = Lifecycle and I/O + +This makes the codebase maintainable and testable. + +### Mapping to OpenCode + +ACP sessions map cleanly to opencode's internal session model: + +- ACP `session/new` → creates internal Session +- ACP `session/prompt` → uses SessionPrompt.prompt() +- Working directory context preserved per-session +- Tool execution uses existing ToolRegistry + +## References + +- [ACP Specification](https://agentclientprotocol.com/) +- [TypeScript Library](https://github.com/zed-industries/agent-client-protocol/tree/main/typescript) +- [Protocol Examples](https://github.com/zed-industries/agent-client-protocol/tree/main/typescript/examples) diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts new file mode 100644 index 000000000..d0981b056 --- /dev/null +++ b/packages/opencode/src/acp/agent.ts @@ -0,0 +1,141 @@ +import type { + Agent, + AgentSideConnection, + AuthenticateRequest, + AuthenticateResponse, + CancelNotification, + InitializeRequest, + InitializeResponse, + LoadSessionRequest, + LoadSessionResponse, + NewSessionRequest, + NewSessionResponse, + PromptRequest, + PromptResponse, +} from "@zed-industries/agent-client-protocol" +import { Log } from "../util/log" +import { ACPSessionManager } from "./session" +import type { ACPConfig } from "./types" +import { Provider } from "../provider/provider" +import { SessionPrompt } from "../session/prompt" +import { Identifier } from "../id/id" + +export class OpenCodeAgent implements Agent { + private log = Log.create({ service: "acp-agent" }) + private sessionManager = new ACPSessionManager() + private connection: AgentSideConnection + private config: ACPConfig + + constructor(connection: AgentSideConnection, config: ACPConfig = {}) { + this.connection = connection + this.config = config + } + + async initialize(params: InitializeRequest): Promise { + this.log.info("initialize", { protocolVersion: params.protocolVersion }) + + return { + protocolVersion: 1, + agentCapabilities: { + loadSession: false, + }, + _meta: { + opencode: { + version: await import("../installation").then((m) => m.Installation.VERSION), + }, + }, + } + } + + async authenticate(params: AuthenticateRequest): Promise { + this.log.info("authenticate", { methodId: params.methodId }) + throw new Error("Authentication not yet implemented") + } + + async newSession(params: NewSessionRequest): Promise { + this.log.info("newSession", { cwd: params.cwd, mcpServers: params.mcpServers.length }) + + const session = await this.sessionManager.create(params.cwd, params.mcpServers) + + return { + sessionId: session.id, + _meta: {}, + } + } + + async loadSession(params: LoadSessionRequest): Promise { + this.log.info("loadSession", { sessionId: params.sessionId, cwd: params.cwd }) + + await this.sessionManager.load(params.sessionId, params.cwd, params.mcpServers) + + return { + _meta: {}, + } + } + + async prompt(params: PromptRequest): Promise { + this.log.info("prompt", { + sessionId: params.sessionId, + promptLength: params.prompt.length, + }) + + const acpSession = this.sessionManager.get(params.sessionId) + if (!acpSession) { + throw new Error(`Session not found: ${params.sessionId}`) + } + + const model = this.config.defaultModel || (await Provider.defaultModel()) + + const parts = params.prompt.map((content) => { + if (content.type === "text") { + return { + type: "text" as const, + text: content.text, + } + } + if (content.type === "resource") { + const resource = content.resource + let text = "" + if ("text" in resource && typeof resource.text === "string") { + text = resource.text + } + return { + type: "text" as const, + text, + } + } + return { + type: "text" as const, + text: JSON.stringify(content), + } + }) + + await SessionPrompt.prompt({ + sessionID: acpSession.openCodeSessionId, + messageID: Identifier.ascending("message"), + model: { + providerID: model.providerID, + modelID: model.modelID, + }, + parts, + acpConnection: { + connection: this.connection, + sessionId: params.sessionId, + }, + }) + + this.log.debug("prompt response completed") + + // Streaming notifications are now handled during prompt execution + // No need to send final text chunk here + + return { + stopReason: "end_turn", + _meta: {}, + } + } + + async cancel(params: CancelNotification): Promise { + this.log.info("cancel", { sessionId: params.sessionId }) + } +} diff --git a/packages/opencode/src/acp/client.ts b/packages/opencode/src/acp/client.ts new file mode 100644 index 000000000..50d599916 --- /dev/null +++ b/packages/opencode/src/acp/client.ts @@ -0,0 +1,85 @@ +import type { + Client, + CreateTerminalRequest, + CreateTerminalResponse, + KillTerminalCommandRequest, + KillTerminalResponse, + ReadTextFileRequest, + ReadTextFileResponse, + ReleaseTerminalRequest, + ReleaseTerminalResponse, + RequestPermissionRequest, + RequestPermissionResponse, + SessionNotification, + TerminalOutputRequest, + TerminalOutputResponse, + WaitForTerminalExitRequest, + WaitForTerminalExitResponse, + WriteTextFileRequest, + WriteTextFileResponse, +} from "@zed-industries/agent-client-protocol" +import { Log } from "../util/log" + +export class ACPClient implements Client { + private log = Log.create({ service: "acp-client" }) + + async requestPermission(params: RequestPermissionRequest): Promise { + this.log.debug("requestPermission", params) + const firstOption = params.options[0] + if (!firstOption) { + return { outcome: { outcome: "cancelled" } } + } + return { + outcome: { + outcome: "selected", + optionId: firstOption.optionId, + }, + } + } + + async sessionUpdate(params: SessionNotification): Promise { + this.log.debug("sessionUpdate", { sessionId: params.sessionId }) + } + + async writeTextFile(params: WriteTextFileRequest): Promise { + this.log.debug("writeTextFile", { path: params.path }) + await Bun.write(params.path, params.content) + return { _meta: {} } + } + + async readTextFile(params: ReadTextFileRequest): Promise { + this.log.debug("readTextFile", { path: params.path }) + const file = Bun.file(params.path) + const exists = await file.exists() + if (!exists) { + throw new Error(`File not found: ${params.path}`) + } + const content = await file.text() + return { content, _meta: {} } + } + + async createTerminal(params: CreateTerminalRequest): Promise { + this.log.debug("createTerminal", params) + throw new Error("Terminal support not yet implemented") + } + + async terminalOutput(params: TerminalOutputRequest): Promise { + this.log.debug("terminalOutput", params) + throw new Error("Terminal support not yet implemented") + } + + async releaseTerminal(params: ReleaseTerminalRequest): Promise { + this.log.debug("releaseTerminal", params) + throw new Error("Terminal support not yet implemented") + } + + async waitForTerminalExit(params: WaitForTerminalExitRequest): Promise { + this.log.debug("waitForTerminalExit", params) + throw new Error("Terminal support not yet implemented") + } + + async killTerminal(params: KillTerminalCommandRequest): Promise { + this.log.debug("killTerminal", params) + throw new Error("Terminal support not yet implemented") + } +} diff --git a/packages/opencode/src/acp/server.ts b/packages/opencode/src/acp/server.ts new file mode 100644 index 000000000..667a09873 --- /dev/null +++ b/packages/opencode/src/acp/server.ts @@ -0,0 +1,53 @@ +import { AgentSideConnection, ndJsonStream } from "@zed-industries/agent-client-protocol" +import { Log } from "../util/log" +import { Instance } from "../project/instance" +import { OpenCodeAgent } from "./agent" + +export namespace ACPServer { + const log = Log.create({ service: "acp-server" }) + + export async function start() { + await Instance.provide({ + directory: process.cwd(), + fn: async () => { + log.info("starting ACP server", { cwd: process.cwd() }) + + const stdout = new WritableStream({ + write(chunk) { + process.stdout.write(chunk) + }, + }) + + const stdin = new ReadableStream({ + start(controller) { + process.stdin.on("data", (chunk) => { + controller.enqueue(new Uint8Array(chunk)) + }) + process.stdin.on("end", () => { + controller.close() + }) + }, + }) + + const stream = ndJsonStream(stdout, stdin) + + new AgentSideConnection((conn) => { + return new OpenCodeAgent(conn) + }, stream) + + await new Promise((resolve) => { + process.on("SIGTERM", () => { + log.info("received SIGTERM") + resolve() + }) + process.on("SIGINT", () => { + log.info("received SIGINT") + resolve() + }) + }) + + log.info("ACP server stopped") + }, + }) + } +} diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts new file mode 100644 index 000000000..01d079bcf --- /dev/null +++ b/packages/opencode/src/acp/session.ts @@ -0,0 +1,60 @@ +import type { McpServer } from "@zed-industries/agent-client-protocol" +import { Identifier } from "../id/id" +import { Session } from "../session" +import type { ACPSessionState } from "./types" + +export class ACPSessionManager { + private sessions = new Map() + + async create(cwd: string, mcpServers: McpServer[]): Promise { + const sessionId = `acp_${Identifier.ascending("session")}` + const openCodeSession = await Session.create({ title: `ACP Session ${sessionId}` }) + + const state: ACPSessionState = { + id: sessionId, + cwd, + mcpServers, + openCodeSessionId: openCodeSession.id, + createdAt: new Date(), + } + + this.sessions.set(sessionId, state) + return state + } + + get(sessionId: string): ACPSessionState | undefined { + return this.sessions.get(sessionId) + } + + async remove(sessionId: string): Promise { + const state = this.sessions.get(sessionId) + if (!state) return + + await Session.remove(state.openCodeSessionId).catch(() => {}) + this.sessions.delete(sessionId) + } + + has(sessionId: string): boolean { + return this.sessions.has(sessionId) + } + + async load(sessionId: string, cwd: string, mcpServers: McpServer[]): Promise { + const existing = this.sessions.get(sessionId) + if (existing) { + return existing + } + + const openCodeSession = await Session.create({ title: `ACP Session ${sessionId} (loaded)` }) + + const state: ACPSessionState = { + id: sessionId, + cwd, + mcpServers, + openCodeSessionId: openCodeSession.id, + createdAt: new Date(), + } + + this.sessions.set(sessionId, state) + return state + } +} diff --git a/packages/opencode/src/acp/types.ts b/packages/opencode/src/acp/types.ts new file mode 100644 index 000000000..f10066a8b --- /dev/null +++ b/packages/opencode/src/acp/types.ts @@ -0,0 +1,16 @@ +import type { McpServer } from "@zed-industries/agent-client-protocol" + +export interface ACPSessionState { + id: string + cwd: string + mcpServers: McpServer[] + openCodeSessionId: string + createdAt: Date +} + +export interface ACPConfig { + defaultModel?: { + providerID: string + modelID: string + } +} diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 252c0bd6b..26a5a472a 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -176,6 +176,16 @@ export namespace Agent { } function mergeAgentPermissions(basePermission: any, overridePermission: any): Agent.Info["permission"] { + if (typeof basePermission.bash === "string") { + basePermission.bash = { + "*": basePermission.bash, + } + } + if (typeof overridePermission.bash === "string") { + overridePermission.bash = { + "*": overridePermission.bash, + } + } const merged = mergeDeep(basePermission ?? {}, overridePermission ?? {}) as any let mergedBash if (merged.bash) { @@ -183,12 +193,10 @@ function mergeAgentPermissions(basePermission: any, overridePermission: any): Ag mergedBash = { "*": merged.bash, } - } - // if granular permissions are provided, default to "ask" - if (typeof merged.bash === "object") { + } else if (typeof merged.bash === "object") { mergedBash = mergeDeep( { - "*": "ask", + "*": "allow", }, merged.bash, ) diff --git a/packages/opencode/src/cli/cmd/acp.ts b/packages/opencode/src/cli/cmd/acp.ts new file mode 100644 index 000000000..4ae5dc839 --- /dev/null +++ b/packages/opencode/src/cli/cmd/acp.ts @@ -0,0 +1,21 @@ +import type { CommandModule } from "yargs" +import { ACPServer } from "../../acp/server" + +export const AcpCommand: CommandModule = { + command: "acp", + describe: "Start ACP (Agent Client Protocol) server", + builder: (yargs) => { + return yargs.option("cwd", { + describe: "working directory", + type: "string", + default: process.cwd(), + }) + }, + handler: async (opts) => { + if (opts["cwd"] && typeof opts["cwd"] === "string") { + process.chdir(opts["cwd"]) + } + + await ACPServer.start() + }, +} diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 95c0f465e..a1437a15b 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -17,9 +17,14 @@ import { StatsCommand } from "./cli/cmd/stats" import { McpCommand } from "./cli/cmd/mcp" import { GithubCommand } from "./cli/cmd/github" import { ExportCommand } from "./cli/cmd/export" +<<<<<<< HEAD import { AttachCommand } from "./cli/cmd/tui/attach" import { TuiThreadCommand } from "./cli/cmd/tui/thread" import { TuiSpawnCommand } from "./cli/cmd/tui/spawn" +======= +import { AttachCommand } from "./cli/cmd/attach" +import { AcpCommand } from "./cli/cmd/acp" +>>>>>>> dev const cancel = new AbortController() @@ -68,6 +73,7 @@ const cli = yargs(hideBin(process.argv)) }) }) .usage("\n" + UI.logo()) + .command(AcpCommand) .command(McpCommand) .command(TuiThreadCommand) .command(TuiSpawnCommand) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 537bddefc..9f8739686 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -36,6 +36,7 @@ import { MCP } from "../mcp" import { Storage } from "../storage/storage" import type { ContentfulStatusCode } from "hono/utils/http-status" import { TuiEvent } from "@/cli/cmd/tui/event" +import { Snapshot } from "@/snapshot" const ERRORS = { 400: { @@ -611,6 +612,44 @@ export namespace Server { return c.json(session) }, ) + .get( + "/session/:id/diff", + describeRoute({ + description: "Get the diff that resulted from this user message", + operationId: "session.diff", + responses: { + 200: { + description: "Successfully retrieved diff", + content: { + "application/json": { + schema: resolver(Snapshot.FileDiff.array()), + }, + }, + }, + }, + }), + validator( + "param", + z.object({ + id: Session.diff.schema.shape.sessionID, + }), + ), + validator( + "query", + z.object({ + messageID: Session.diff.schema.shape.messageID, + }), + ), + async (c) => { + const query = c.req.valid("query") + const params = c.req.valid("param") + const result = await Session.diff({ + sessionID: params.id, + messageID: query.messageID, + }) + return c.json(result) + }, + ) .delete( "/session/:id/share", describeRoute({ @@ -737,7 +776,10 @@ export namespace Server { ), async (c) => { const params = c.req.valid("param") - const message = await Session.getMessage({ sessionID: params.id, messageID: params.messageID }) + const message = await Session.getMessage({ + sessionID: params.id, + messageID: params.messageID, + }) return c.json(message) }, ) @@ -871,7 +913,10 @@ export namespace Server { async (c) => { const id = c.req.valid("param").id log.info("revert", c.req.valid("json")) - const session = await SessionRevert.revert({ sessionID: id, ...c.req.valid("json") }) + const session = await SessionRevert.revert({ + sessionID: id, + ...c.req.valid("json"), + }) return c.json(session) }, ) @@ -932,7 +977,11 @@ export namespace Server { const params = c.req.valid("param") const id = params.id const permissionID = params.permissionID - Permission.respond({ sessionID: id, permissionID, response: c.req.valid("json").response }) + Permission.respond({ + sessionID: id, + permissionID, + response: c.req.valid("json").response, + }) return c.json(true) }, ) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 521dcfe72..9e7f3db13 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -18,6 +18,7 @@ import { Project } from "../project/project" import { Instance } from "../project/instance" import { SessionPrompt } from "./prompt" import { fn } from "@/util/fn" +import { Snapshot } from "@/snapshot" export namespace Session { const log = Log.create({ service: "session" }) @@ -146,7 +147,12 @@ export namespace Session { }) }) - export async function createNext(input: { id?: string; title?: string; parentID?: string; directory: string }) { + export async function createNext(input: { + id?: string + title?: string + parentID?: string + directory: string + }) { const result: Info = { id: Identifier.descending("session", input.id), version: Installation.VERSION, @@ -366,7 +372,9 @@ export namespace Session { .add(new Decimal(tokens.input).mul(input.model.cost?.input ?? 0).div(1_000_000)) .add(new Decimal(tokens.output).mul(input.model.cost?.output ?? 0).div(1_000_000)) .add(new Decimal(tokens.cache.read).mul(input.model.cost?.cache_read ?? 0).div(1_000_000)) - .add(new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000)) + .add( + new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000), + ) .toNumber(), tokens, } @@ -405,4 +413,47 @@ export namespace Session { await Project.setInitialized(Instance.project.id) }, ) + + export const diff = fn( + z.object({ + sessionID: Identifier.schema("session"), + messageID: Identifier.schema("message").optional(), + }), + async (input) => { + const all = await messages(input.sessionID) + const index = !input.messageID ? 0 : all.findIndex((x) => x.info.id === input.messageID) + if (index === -1) return [] + + let from: string | undefined + let to: string | undefined + + // scan assistant messages to find earliest from and latest to + // snapshot + for (let i = index + 1; i < all.length; i++) { + const item = all[i] + + // if messageID is provided, stop at the next user message + if (input.messageID && item.info.role === "user") break + + if (!from) { + for (const part of item.parts) { + if (part.type === "step-start" && part.snapshot) { + from = part.snapshot + break + } + } + } + + for (const part of item.parts) { + if (part.type === "step-finish" && part.snapshot) { + to = part.snapshot + break + } + } + } + + if (from && to) return Snapshot.diffFull(from, to) + return [] + }, + ) } diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 70eefbfc4..313cb41ab 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -130,6 +130,7 @@ export namespace MessageV2 { export const StepStartPart = PartBase.extend({ type: z.literal("step-start"), + snapshot: z.string().optional(), }).meta({ ref: "StepStartPart", }) @@ -137,6 +138,7 @@ export namespace MessageV2 { export const StepFinishPart = PartBase.extend({ type: z.literal("step-finish"), + snapshot: z.string().optional(), cost: z.number(), tokens: z.object({ input: z.number(), diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 745aa4b2b..c7147bcf5 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -95,6 +95,16 @@ export namespace SessionPrompt { agent: z.string().optional(), system: z.string().optional(), tools: z.record(z.string(), z.boolean()).optional(), + /** + * ACP (Agent Client Protocol) connection details for streaming responses. + * When provided, enables real-time streaming and tool execution visibility. + */ + acpConnection: z + .object({ + connection: z.any(), // AgentSideConnection - using any to avoid circular deps + sessionId: z.string(), // ACP session ID (different from opencode sessionID) + }) + .optional(), parts: z.array( z.discriminatedUnion("type", [ MessageV2.TextPart.omit({ @@ -173,6 +183,7 @@ export namespace SessionPrompt { agent: agent.name, system, abort: abort.signal, + acpConnection: input.acpConnection, }) const tools = await resolveTools({ @@ -820,6 +831,60 @@ export namespace SessionPrompt { return input.messages } + /** + * Maps tool names to ACP tool kinds for consistent categorization. + * - read: Tools that read data (read, glob, grep, list, webfetch, docs) + * - edit: Tools that modify state (edit, write, bash) + * - other: All other tools (MCP tools, task, todowrite, etc.) + */ + function determineToolKind(toolName: string): "read" | "edit" | "other" { + const readTools = [ + "read", + "glob", + "grep", + "list", + "webfetch", + "context7_resolve_library_id", + "context7_get_library_docs", + ] + const editTools = ["edit", "write", "bash"] + + if (readTools.includes(toolName.toLowerCase())) return "read" + if (editTools.includes(toolName.toLowerCase())) return "edit" + return "other" + } + + /** + * Extracts file/directory locations from tool inputs for ACP notifications. + * Returns array of {path} objects that ACP clients can use for navigation. + * + * Examples: + * - read({filePath: "/foo/bar.ts"}) -> [{path: "/foo/bar.ts"}] + * - glob({pattern: "*.ts", path: "/src"}) -> [{path: "/src"}] + * - bash({command: "ls"}) -> [] (no file references) + */ + function extractLocations(toolName: string, input: Record): { path: string }[] { + try { + switch (toolName.toLowerCase()) { + case "read": + case "edit": + case "write": + return input["filePath"] ? [{ path: input["filePath"] }] : [] + case "glob": + case "grep": + return input["path"] ? [{ path: input["path"] }] : [] + case "bash": + return [] + case "list": + return input["path"] ? [{ path: input["path"] }] : [] + default: + return [] + } + } catch { + return [] + } + } + export type Processor = Awaited> async function createProcessor(input: { sessionID: string @@ -828,6 +893,10 @@ export namespace SessionPrompt { system: string[] agent: string abort: AbortSignal + acpConnection?: { + connection: any + sessionId: string + } }) { const toolcalls: Record = {} let snapshot: string | undefined @@ -957,6 +1026,26 @@ export namespace SessionPrompt { }, }) toolcalls[value.id] = part as MessageV2.ToolPart + + // Notify ACP client of pending tool call + if (input.acpConnection) { + await input.acpConnection.connection + .sessionUpdate({ + sessionId: input.acpConnection.sessionId, + update: { + sessionUpdate: "tool_call", + toolCallId: value.id, + title: value.toolName, + kind: determineToolKind(value.toolName), + status: "pending", + locations: [], // Will be populated when we have input + rawInput: {}, + }, + }) + .catch((err: Error) => { + log.error("failed to send tool pending to ACP", { error: err }) + }) + } break case "tool-input-delta": @@ -981,6 +1070,24 @@ export namespace SessionPrompt { metadata: value.providerMetadata, }) toolcalls[value.toolCallId] = part as MessageV2.ToolPart + + // Notify ACP client that tool is running + if (input.acpConnection) { + await input.acpConnection.connection + .sessionUpdate({ + sessionId: input.acpConnection.sessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: value.toolCallId, + status: "in_progress", + locations: extractLocations(value.toolName, value.input), + rawInput: value.input, + }, + }) + .catch((err: Error) => { + log.error("failed to send tool in_progress to ACP", { error: err }) + }) + } } break } @@ -1002,6 +1109,33 @@ export namespace SessionPrompt { attachments: value.output.attachments, }, }) + + // Notify ACP client that tool completed + if (input.acpConnection) { + await input.acpConnection.connection + .sessionUpdate({ + sessionId: input.acpConnection.sessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: value.toolCallId, + status: "completed", + content: [ + { + type: "content", + content: { + type: "text", + text: value.output.output, + }, + }, + ], + rawOutput: value.output, + }, + }) + .catch((err: Error) => { + log.error("failed to send tool completed to ACP", { error: err }) + }) + } + delete toolcalls[value.toolCallId] } break @@ -1023,6 +1157,35 @@ export namespace SessionPrompt { }, }, }) + + // Notify ACP client of tool error + if (input.acpConnection) { + await input.acpConnection.connection + .sessionUpdate({ + sessionId: input.acpConnection.sessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: value.toolCallId, + status: "failed", + content: [ + { + type: "content", + content: { + type: "text", + text: `Error: ${(value.error as any).toString()}`, + }, + }, + ], + rawOutput: { + error: (value.error as any).toString(), + }, + }, + }) + .catch((err: Error) => { + log.error("failed to send tool error to ACP", { error: err }) + }) + } + if (value.error instanceof Permission.RejectedError) { blocked = true } @@ -1035,6 +1198,13 @@ export namespace SessionPrompt { case "start-step": snapshot = await Snapshot.track() + await Session.updatePart({ + id: Identifier.ascending("part"), + messageID: assistantMsg.id, + sessionID: assistantMsg.sessionID, + snapshot, + type: "step-start", + }) break case "finish-step": @@ -1045,6 +1215,15 @@ export namespace SessionPrompt { }) assistantMsg.cost += usage.cost assistantMsg.tokens = usage.tokens + await Session.updatePart({ + id: Identifier.ascending("part"), + snapshot: await Snapshot.track(), + messageID: assistantMsg.id, + sessionID: assistantMsg.sessionID, + type: "step-finish", + tokens: usage.tokens, + cost: usage.cost, + }) await Session.updateMessage(assistantMsg) if (snapshot) { const patch = await Snapshot.patch(snapshot) @@ -1081,6 +1260,25 @@ export namespace SessionPrompt { currentText.text += value.text if (value.providerMetadata) currentText.metadata = value.providerMetadata if (currentText.text) await Session.updatePart(currentText) + + // Send streaming chunk to ACP client + if (input.acpConnection && value.text) { + await input.acpConnection.connection + .sessionUpdate({ + sessionId: input.acpConnection.sessionId, + update: { + sessionUpdate: "agent_message_chunk", + content: { + type: "text", + text: value.text, + }, + }, + }) + .catch((err: Error) => { + log.error("failed to send text delta to ACP", { error: err }) + // Don't fail the whole request if ACP notification fails + }) + } } break diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 6a94fd269..d973315e2 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -26,8 +26,15 @@ export namespace Snapshot { .nothrow() log.info("initialized") } - await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const hash = await $`git --git-dir ${git} write-tree`.quiet().cwd(Instance.directory).nothrow().text() + await $`git --git-dir ${git} add .` + .quiet() + .cwd(Instance.directory) + .nothrow() + const hash = await $`git --git-dir ${git} write-tree` + .quiet() + .cwd(Instance.directory) + .nothrow() + .text() log.info("tracking", { hash, cwd: Instance.directory, git }) return hash.trim() } @@ -40,8 +47,14 @@ export namespace Snapshot { export async function patch(hash: string): Promise { const git = gitdir() - await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .`.quiet().cwd(Instance.directory).nothrow() + await $`git --git-dir ${git} add .` + .quiet() + .cwd(Instance.directory) + .nothrow() + const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .` + .quiet() + .cwd(Instance.directory) + .nothrow() // If git diff fails, return empty patch if (result.exitCode !== 0) { @@ -63,10 +76,11 @@ export namespace Snapshot { export async function restore(snapshot: string) { log.info("restore", { commit: snapshot }) const git = gitdir() - const result = await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f` - .quiet() - .cwd(Instance.worktree) - .nothrow() + const result = + await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (result.exitCode !== 0) { log.error("failed to restore snapshot", { @@ -85,18 +99,22 @@ export namespace Snapshot { for (const file of item.files) { if (files.has(file)) continue log.info("reverting", { file, hash: item.hash }) - const result = await $`git --git-dir=${git} checkout ${item.hash} -- ${file}` - .quiet() - .cwd(Instance.worktree) - .nothrow() - if (result.exitCode !== 0) { - const relativePath = path.relative(Instance.worktree, file) - const checkTree = await $`git --git-dir=${git} ls-tree ${item.hash} -- ${relativePath}` + const result = + await $`git --git-dir=${git} checkout ${item.hash} -- ${file}` .quiet() .cwd(Instance.worktree) .nothrow() + if (result.exitCode !== 0) { + const relativePath = path.relative(Instance.worktree, file) + const checkTree = + await $`git --git-dir=${git} ls-tree ${item.hash} -- ${relativePath}` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (checkTree.exitCode === 0 && checkTree.text().trim()) { - log.info("file existed in snapshot but checkout failed, keeping", { file }) + log.info("file existed in snapshot but checkout failed, keeping", { + file, + }) } else { log.info("file did not exist in snapshot, deleting", { file }) const absolutePath = path.join(Instance.worktree, file) @@ -110,8 +128,14 @@ export namespace Snapshot { export async function diff(hash: string) { const git = gitdir() - await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const result = await $`git --git-dir=${git} diff ${hash} -- .`.quiet().cwd(Instance.worktree).nothrow() + await $`git --git-dir ${git} add .` + .quiet() + .cwd(Instance.directory) + .nothrow() + const result = await $`git --git-dir=${git} diff ${hash} -- .` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (result.exitCode !== 0) { log.warn("failed to get diff", { @@ -126,6 +150,45 @@ export namespace Snapshot { return result.text().trim() } + export const FileDiff = z + .object({ + file: z.string(), + left: z.string(), + right: z.string(), + }) + .meta({ + ref: "FileDiff", + }) + export type FileDiff = z.infer + export async function diffFull( + from: string, + to: string, + ): Promise { + const git = gitdir() + const result: FileDiff[] = [] + for await (const line of $`git --git-dir=${git} diff --name-only ${from} ${to} -- .` + .quiet() + .cwd(Instance.directory) + .nothrow() + .lines()) { + if (!line) continue + const left = await $`git --git-dir=${git} show ${from}:${line}` + .quiet() + .nothrow() + .text() + const right = await $`git --git-dir=${git} show ${to}:${line}` + .quiet() + .nothrow() + .text() + result.push({ + file: line, + left, + right, + }) + } + return result + } + function gitdir() { const project = Instance.project return path.join(Global.Path.data, "snapshot", project.id) diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts index 53e1ce5fe..1172e1e6a 100644 --- a/packages/opencode/src/tool/write.ts +++ b/packages/opencode/src/tool/write.ts @@ -10,8 +10,6 @@ import { FileTime } from "../file/time" import { Filesystem } from "../util/filesystem" import { Instance } from "../project/instance" import { Agent } from "../agent/agent" -import { createTwoFilesPatch } from "diff" -import { trimDiff } from "./edit" export const WriteTool = Tool.define("write", { description: DESCRIPTION, @@ -29,13 +27,6 @@ export const WriteTool = Tool.define("write", { const exists = await file.exists() if (exists) await FileTime.assert(ctx.sessionID, filepath) - let oldContent = "" - let diff = "" - - if (exists) { - oldContent = await file.text() - } - const agent = await Agent.get(ctx.agent) if (agent.permission.edit === "ask") await Permission.ask({ @@ -57,9 +48,6 @@ export const WriteTool = Tool.define("write", { }) FileTime.read(ctx.sessionID, filepath) - // Generate diff for the write operation - diff = trimDiff(createTwoFilesPatch(filepath, filepath, oldContent, params.content)) - let output = "" await LSP.touchFile(filepath, true) const diagnostics = await LSP.diagnostics() diff --git a/packages/opencode/test/acp.test.ts b/packages/opencode/test/acp.test.ts new file mode 100644 index 000000000..b4ed5d2d1 --- /dev/null +++ b/packages/opencode/test/acp.test.ts @@ -0,0 +1,109 @@ +import { describe, expect, test } from "bun:test" +import { spawn } from "child_process" + +describe("ACP Server", () => { + test("initialize and shutdown", async () => { + const proc = spawn("bun", ["run", "--conditions=development", "src/index.ts", "acp"], { + cwd: process.cwd(), + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env, OPENCODE: "1" }, + }) + + const encoder = new TextEncoder() + const decoder = new TextDecoder() + + let initResponse: any = null + + proc.stdout.on("data", (chunk: Buffer) => { + const lines = decoder.decode(chunk).split("\n") + for (const line of lines) { + const trimmed = line.trim() + if (!trimmed) continue + + try { + const msg = JSON.parse(trimmed) + if (msg.id === 1) initResponse = msg + } catch (e) {} + } + }) + + proc.stdin.write( + encoder.encode( + JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { protocolVersion: 1 }, + }) + "\n", + ), + ) + + await new Promise((resolve) => setTimeout(resolve, 500)) + + expect(initResponse).toBeTruthy() + expect(initResponse.result.protocolVersion).toBe(1) + expect(initResponse.result.agentCapabilities).toBeTruthy() + + proc.kill() + }, 10000) + + test("create session", async () => { + const proc = spawn("bun", ["run", "--conditions=development", "src/index.ts", "acp"], { + cwd: process.cwd(), + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env, OPENCODE: "1" }, + }) + + const encoder = new TextEncoder() + const decoder = new TextDecoder() + + let sessionResponse: any = null + + proc.stdout.on("data", (chunk: Buffer) => { + const lines = decoder.decode(chunk).split("\n") + for (const line of lines) { + const trimmed = line.trim() + if (!trimmed) continue + + try { + const msg = JSON.parse(trimmed) + if (msg.id === 2) sessionResponse = msg + } catch (e) {} + } + }) + + proc.stdin.write( + encoder.encode( + JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { protocolVersion: 1 }, + }) + "\n", + ), + ) + + await new Promise((resolve) => setTimeout(resolve, 500)) + + proc.stdin.write( + encoder.encode( + JSON.stringify({ + jsonrpc: "2.0", + id: 2, + method: "session/new", + params: { + cwd: process.cwd(), + mcpServers: [], + }, + }) + "\n", + ), + ) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + + expect(sessionResponse).toBeTruthy() + expect(sessionResponse.result.sessionId).toBeTruthy() + + proc.kill() + }, 10000) +}) diff --git a/packages/opencode/test/snapshot/snapshot.test.ts b/packages/opencode/test/snapshot/snapshot.test.ts index 96cbb6fbc..c67fe1d59 100644 --- a/packages/opencode/test/snapshot/snapshot.test.ts +++ b/packages/opencode/test/snapshot/snapshot.test.ts @@ -33,7 +33,7 @@ test("tracks deleted files correctly", async () => { await $`rm ${tmp.path}/a.txt`.quiet() - expect((await Snapshot.patch(before!)).files).toContain(`a.txt`) + expect((await Snapshot.patch(before!)).files).toContain(`${tmp.path}/a.txt`) }, }) }) @@ -144,7 +144,7 @@ test("symlink handling", async () => { await $`ln -s ${tmp.path}/a.txt ${tmp.path}/link.txt`.quiet() - expect((await Snapshot.patch(before!)).files).toContain(`link.txt`) + expect((await Snapshot.patch(before!)).files).toContain(`${tmp.path}/link.txt`) }, }) }) @@ -159,7 +159,7 @@ test("large file handling", async () => { await Bun.write(`${tmp.path}/large.txt`, "x".repeat(1024 * 1024)) - expect((await Snapshot.patch(before!)).files).toContain(`large.txt`) + expect((await Snapshot.patch(before!)).files).toContain(`${tmp.path}/large.txt`) }, }) }) @@ -584,3 +584,60 @@ test("revert preserves file that existed in snapshot when deleted then recreated }, }) }) + +test("diffFull function", async () => { + await using tmp = await bootstrap() + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const before = await Snapshot.track() + expect(before).toBeTruthy() + + await Bun.write(`${tmp.path}/new.txt`, "new content") + await Bun.write(`${tmp.path}/b.txt`, "modified content") + + const after = await Snapshot.track() + expect(after).toBeTruthy() + + const diffs = await Snapshot.diffFull(before!, after!) + expect(diffs.length).toBe(2) + + const newFileDiff = diffs.find((d) => d.file === "new.txt") + expect(newFileDiff).toBeDefined() + expect(newFileDiff!.left).toBe("") + expect(newFileDiff!.right).toBe("new content") + + const modifiedFileDiff = diffs.find((d) => d.file === "b.txt") + expect(modifiedFileDiff).toBeDefined() + expect(modifiedFileDiff!.left).toBe(tmp.extra.bContent) + expect(modifiedFileDiff!.right).toBe("modified content") + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const before = await Snapshot.track() + expect(before).toBeTruthy() + + await Bun.write(`${tmp.path}/added.txt`, "added content") + await $`rm ${tmp.path}/a.txt`.quiet() + + const after = await Snapshot.track() + expect(after).toBeTruthy() + + const diffs = await Snapshot.diffFull(before!, after!) + expect(diffs.length).toBe(2) + + const addedFileDiff = diffs.find((d) => d.file === "added.txt") + expect(addedFileDiff).toBeDefined() + expect(addedFileDiff!.left).toBe("") + expect(addedFileDiff!.right).toBe("added content") + + const removedFileDiff = diffs.find((d) => d.file === "a.txt") + expect(removedFileDiff).toBeDefined() + expect(removedFileDiff!.left).toBe(tmp.extra.aContent) + expect(removedFileDiff!.right).toBe("") + }, + }) +}) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f3d16bdd1..c7960112f 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.15.8", + "version": "0.15.10", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index c0ea43bf8..1e8a38115 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.15.8", + "version": "0.15.10", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index 9fd73d38d..095c0ee5d 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -53,6 +53,8 @@ import type { SessionShareData, SessionShareResponses, SessionShareErrors, + SessionDiffData, + SessionDiffResponses, SessionSummarizeData, SessionSummarizeResponses, SessionSummarizeErrors, @@ -392,6 +394,16 @@ class Session extends _HeyApiClient { }) } + /** + * Get the diff that resulted from this user message + */ + public diff(options: Options) { + return (options.client ?? this._client).get({ + url: "/session/{id}/diff", + ...options, + }) + } + /** * Summarize the session */ diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 4cdefab65..1aefc46ae 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -595,6 +595,12 @@ export type Todo = { id: string } +export type FileDiff = { + file: string + left: string + right: string +} + export type UserMessage = { id: string sessionID: string @@ -817,6 +823,7 @@ export type StepStartPart = { sessionID: string messageID: string type: "step-start" + snapshot?: string } export type StepFinishPart = { @@ -824,6 +831,7 @@ export type StepFinishPart = { sessionID: string messageID: string type: "step-finish" + snapshot?: string cost: number tokens: { input: number @@ -1821,6 +1829,27 @@ export type SessionShareResponses = { export type SessionShareResponse = SessionShareResponses[keyof SessionShareResponses] +export type SessionDiffData = { + body?: never + path: { + id: string + } + query?: { + directory?: string + messageID?: string + } + url: "/session/{id}/diff" +} + +export type SessionDiffResponses = { + /** + * Successfully retrieved diff + */ + 200: Array +} + +export type SessionDiffResponse = SessionDiffResponses[keyof SessionDiffResponses] + export type SessionSummarizeData = { body?: { providerID: string @@ -1911,6 +1940,10 @@ export type SessionPromptData = { tools?: { [key: string]: boolean } + acpConnection?: { + connection: unknown + sessionId: string + } parts: Array } path: { diff --git a/packages/slack/package.json b/packages/slack/package.json index 99f45a718..0da1175fb 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "0.15.8", + "version": "0.15.10", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index c1d5532d9..576db85ae 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "0.15.8", + "version": "0.15.10", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 05dea16cf..eb260f13f 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "0.15.8", + "version": "0.15.10", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index c526ae2ba..d477908ca 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.15.8", + "version": "0.15.10", "publisher": "sst-dev", "repository": { "type": "git",