diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6647e9fe..1ec299cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ importers: version: 1.18.2(ws@8.18.3) '@modelcontextprotocol/sdk': specifier: ^1.10.2 - version: 1.24.3(zod@4.2.0) + version: 1.25.0(hono@4.11.1)(zod@4.2.1) '@octokit/rest': specifier: ^21.1.1 version: 21.1.1 @@ -244,13 +244,13 @@ importers: version: 10.1.1 openai: specifier: ^6.10.0 - version: 6.10.0(ws@8.18.3)(zod@4.2.0) + version: 6.15.0(ws@8.18.3)(zod@4.2.1) pdfjs-dist: specifier: ^5.3.31 version: 5.4.449 posthog-js: specifier: ^1.236.6 - version: 1.306.1 + version: 1.306.2 react: specifier: ^18.3.1 version: 18.3.1 @@ -473,7 +473,7 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.31.0 - version: 8.49.0(eslint@9.39.2)(typescript@5.9.3) + version: 8.50.0(eslint@9.39.2)(typescript@5.9.3) vite: specifier: ^5.4.18 version: 5.4.21(@types/node@22.19.3) @@ -1663,6 +1663,12 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + '@hono/node-server@1.19.7': + resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1885,8 +1891,8 @@ packages: '@mixmark-io/domino@2.2.0': resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} - '@modelcontextprotocol/sdk@1.24.3': - resolution: {integrity: sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==} + '@modelcontextprotocol/sdk@1.25.0': + resolution: {integrity: sha512-z0Zhn/LmQ3yz91dEfd5QgS7DpSjA4pk+3z2++zKgn5L6iDFM9QapsVoAQSbKLvlrFsZk9+ru6yHHWNq2lCYJKQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -2872,113 +2878,113 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.53.4': - resolution: {integrity: sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ==} + '@rollup/rollup-android-arm-eabi@4.53.5': + resolution: {integrity: sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.53.4': - resolution: {integrity: sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng==} + '@rollup/rollup-android-arm64@4.53.5': + resolution: {integrity: sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.4': - resolution: {integrity: sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q==} + '@rollup/rollup-darwin-arm64@4.53.5': + resolution: {integrity: sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.4': - resolution: {integrity: sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw==} + '@rollup/rollup-darwin-x64@4.53.5': + resolution: {integrity: sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.53.4': - resolution: {integrity: sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ==} + '@rollup/rollup-freebsd-arm64@4.53.5': + resolution: {integrity: sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.4': - resolution: {integrity: sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw==} + '@rollup/rollup-freebsd-x64@4.53.5': + resolution: {integrity: sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.53.4': - resolution: {integrity: sha512-UPMMNeC4LXW7ZSHxeP3Edv09aLsFUMaD1TSVW6n1CWMECnUIJMFFB7+XC2lZTdPtvB36tYC0cJWc86mzSsaviw==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': + resolution: {integrity: sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.53.4': - resolution: {integrity: sha512-H8uwlV0otHs5Q7WAMSoyvjV9DJPiy5nJ/xnHolY0QptLPjaSsuX7tw+SPIfiYH6cnVx3fe4EWFafo6gH6ekZKA==} + '@rollup/rollup-linux-arm-musleabihf@4.53.5': + resolution: {integrity: sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.53.4': - resolution: {integrity: sha512-BLRwSRwICXz0TXkbIbqJ1ibK+/dSBpTJqDClF61GWIrxTXZWQE78ROeIhgl5MjVs4B4gSLPCFeD4xML9vbzvCQ==} + '@rollup/rollup-linux-arm64-gnu@4.53.5': + resolution: {integrity: sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.53.4': - resolution: {integrity: sha512-6bySEjOTbmVcPJAywjpGLckK793A0TJWSbIa0sVwtVGfe/Nz6gOWHOwkshUIAp9j7wg2WKcA4Snu7Y1nUZyQew==} + '@rollup/rollup-linux-arm64-musl@4.53.5': + resolution: {integrity: sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.53.4': - resolution: {integrity: sha512-U0ow3bXYJZ5MIbchVusxEycBw7bO6C2u5UvD31i5IMTrnt2p4Fh4ZbHSdc/31TScIJQYHwxbj05BpevB3201ug==} + '@rollup/rollup-linux-loong64-gnu@4.53.5': + resolution: {integrity: sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.53.4': - resolution: {integrity: sha512-iujDk07ZNwGLVn0YIWM80SFN039bHZHCdCCuX9nyx3Jsa2d9V/0Y32F+YadzwbvDxhSeVo9zefkoPnXEImnM5w==} + '@rollup/rollup-linux-ppc64-gnu@4.53.5': + resolution: {integrity: sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.53.4': - resolution: {integrity: sha512-MUtAktiOUSu+AXBpx1fkuG/Bi5rhlorGs3lw5QeJ2X3ziEGAq7vFNdWVde6XGaVqi0LGSvugwjoxSNJfHFTC0g==} + '@rollup/rollup-linux-riscv64-gnu@4.53.5': + resolution: {integrity: sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.53.4': - resolution: {integrity: sha512-btm35eAbDfPtcFEgaXCI5l3c2WXyzwiE8pArhd66SDtoLWmgK5/M7CUxmUglkwtniPzwvWioBKKl6IXLbPf2sQ==} + '@rollup/rollup-linux-riscv64-musl@4.53.5': + resolution: {integrity: sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.53.4': - resolution: {integrity: sha512-uJlhKE9ccUTCUlK+HUz/80cVtx2RayadC5ldDrrDUFaJK0SNb8/cCmC9RhBhIWuZ71Nqj4Uoa9+xljKWRogdhA==} + '@rollup/rollup-linux-s390x-gnu@4.53.5': + resolution: {integrity: sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.53.4': - resolution: {integrity: sha512-jjEMkzvASQBbzzlzf4os7nzSBd/cvPrpqXCUOqoeCh1dQ4BP3RZCJk8XBeik4MUln3m+8LeTJcY54C/u8wb3DQ==} + '@rollup/rollup-linux-x64-gnu@4.53.5': + resolution: {integrity: sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.53.4': - resolution: {integrity: sha512-lu90KG06NNH19shC5rBPkrh6mrTpq5kviFylPBXQVpdEu0yzb0mDgyxLr6XdcGdBIQTH/UAhDJnL+APZTBu1aQ==} + '@rollup/rollup-linux-x64-musl@4.53.5': + resolution: {integrity: sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.53.4': - resolution: {integrity: sha512-dFDcmLwsUzhAm/dn0+dMOQZoONVYBtgik0VuY/d5IJUUb787L3Ko/ibvTvddqhb3RaB7vFEozYevHN4ox22R/w==} + '@rollup/rollup-openharmony-arm64@4.53.5': + resolution: {integrity: sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.53.4': - resolution: {integrity: sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w==} + '@rollup/rollup-win32-arm64-msvc@4.53.5': + resolution: {integrity: sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.4': - resolution: {integrity: sha512-JGbeF2/FDU0x2OLySw/jgvkwWUo05BSiJK0dtuI4LyuXbz3wKiC1xHhLB1Tqm5VU6ZZDmAorj45r/IgWNWku5g==} + '@rollup/rollup-win32-ia32-msvc@4.53.5': + resolution: {integrity: sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.4': - resolution: {integrity: sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g==} + '@rollup/rollup-win32-x64-gnu@4.53.5': + resolution: {integrity: sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.4': - resolution: {integrity: sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA==} + '@rollup/rollup-win32-x64-msvc@4.53.5': + resolution: {integrity: sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==} cpu: [x64] os: [win32] @@ -3493,11 +3499,11 @@ packages: typescript: optional: true - '@typescript-eslint/eslint-plugin@8.49.0': - resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.49.0 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' @@ -3533,15 +3539,15 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.49.0': - resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.49.0': - resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -3550,12 +3556,12 @@ packages: resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/scope-manager@8.49.0': - resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.49.0': - resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -3570,8 +3576,8 @@ packages: typescript: optional: true - '@typescript-eslint/type-utils@8.49.0': - resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==} + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3581,8 +3587,8 @@ packages: resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/types@8.49.0': - resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@2.34.0': @@ -3603,8 +3609,8 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.49.0': - resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -3615,8 +3621,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@typescript-eslint/utils@8.49.0': - resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3626,8 +3632,8 @@ packages: resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/visitor-keys@8.49.0': - resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -5641,10 +5647,6 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -6799,6 +6801,10 @@ packages: hold-event@1.1.2: resolution: {integrity: sha512-Bx0A6OBY70cs23orUWk0DuBAAeJjEbmyg8Gnye9+M8+XeWy2CcmRyfiJhTnQQz9s25r9SYjici3URy176MFs5A==} + hono@4.11.1: + resolution: {integrity: sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==} + engines: {node: '>=16.9.0'} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -6857,10 +6863,6 @@ packages: resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} engines: {node: '>= 0.6'} - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -7743,6 +7745,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -8813,8 +8818,8 @@ packages: zod: optional: true - openai@6.10.0: - resolution: {integrity: sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==} + openai@6.15.0: + resolution: {integrity: sha512-F1Lvs5BoVvmZtzkUEVyh8mDQPPFolq4F+xdsx/DO8Hee8YF3IGAlZqUIsF+DVGhqf4aU0a3bTghsxB6OIsRy1g==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -9504,8 +9509,8 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.306.1: - resolution: {integrity: sha512-wO7bliv/5tlAlfoKCUzwkGXZVNexk0dHigMf9tNp0q1rzs62wThogREY7Tz7h/iWKYiuXy1RumtVlTmHuBXa1w==} + posthog-js@1.306.2: + resolution: {integrity: sha512-zBEjDvUs5RNTWbyHVjbL16Oigm2buFG2PQRRMC46hfL2HSh+8//zMD5gV3etxkUhjjTltKubK3mkqONMldw9Yg==} potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} @@ -9715,7 +9720,6 @@ packages: resolution: {integrity: sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==} peerDependencies: react: ^16.14.0 - bundledDependencies: false react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} @@ -9874,7 +9878,6 @@ packages: react@16.14.0: resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==} engines: {node: '>=0.10.0'} - bundledDependencies: false react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} @@ -10159,8 +10162,8 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rollup@4.53.4: - resolution: {integrity: sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg==} + rollup@4.53.5: + resolution: {integrity: sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -10297,16 +10300,12 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} - send@0.19.1: - resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==} - engines: {node: '>= 0.8.0'} - - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} serialize-javascript@2.1.2: @@ -10319,12 +10318,12 @@ packages: resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} engines: {node: '>= 0.8.0'} - serve-static@1.16.2: - resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} set-blocking@2.0.0: @@ -10554,9 +10553,6 @@ packages: sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} - peerDependenciesMeta: - node-gyp: - optional: true sshpk@1.18.0: resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} @@ -10606,10 +10602,6 @@ packages: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -10848,8 +10840,8 @@ packages: deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. hasBin: true - swr@2.3.7: - resolution: {integrity: sha512-ZEquQ82QvalqTxhBVv/DlAg2mbmUjF4UgpPg9wwk4ufb9rQnZXh1iKyyKBqV6bQGu1Ie7L1QwSYO07qFIa1p+g==} + swr@2.3.8: + resolution: {integrity: sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==} peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -11207,8 +11199,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.49.0: - resolution: {integrity: sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==} + typescript-eslint@8.50.0: + resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -11913,8 +11905,8 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.2.0: - resolution: {integrity: sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==} + zod@4.2.1: + resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} @@ -11988,7 +11980,7 @@ snapshots: '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) react: 18.3.1 - swr: 2.3.7(react@18.3.1) + swr: 2.3.8(react@18.3.1) throttleit: 2.1.0 zod: 3.25.76 @@ -14354,6 +14346,10 @@ snapshots: transitivePeerDependencies: - '@types/react' + '@hono/node-server@1.19.7(hono@4.11.1)': + dependencies: + hono: 4.11.1 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -14746,14 +14742,14 @@ snapshots: '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) tslib: 2.8.1 - '@langchain/core@0.3.79(openai@6.10.0)': + '@langchain/core@0.3.79(openai@6.15.0)': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.87(openai@6.10.0) + langsmith: 0.3.87(openai@6.15.0) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -14768,7 +14764,7 @@ snapshots: '@langchain/openai@0.6.16(@langchain/core@0.3.79)(ws@8.18.3)': dependencies: - '@langchain/core': 0.3.79(openai@6.10.0) + '@langchain/core': 0.3.79(openai@6.15.0) js-tiktoken: 1.0.21 openai: 5.12.2(ws@8.18.3)(zod@3.25.76) zod: 3.25.76 @@ -14777,7 +14773,7 @@ snapshots: '@langchain/textsplitters@0.1.0(@langchain/core@0.3.79)': dependencies: - '@langchain/core': 0.3.79(openai@6.10.0) + '@langchain/core': 0.3.79(openai@6.15.0) js-tiktoken: 1.0.21 '@mediapipe/tasks-vision@0.10.17': {} @@ -14795,8 +14791,9 @@ snapshots: '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/sdk@1.24.3(zod@4.2.0)': + '@modelcontextprotocol/sdk@1.25.0(hono@4.11.1)(zod@4.2.1)': dependencies: + '@hono/node-server': 1.19.7(hono@4.11.1) ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 @@ -14807,11 +14804,13 @@ snapshots: express: 5.2.1 express-rate-limit: 7.5.1(express@5.2.1) jose: 6.1.3 + json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.2.0 - zod-to-json-schema: 3.25.0(zod@4.2.0) + zod: 4.2.1 + zod-to-json-schema: 3.25.0(zod@4.2.1) transitivePeerDependencies: + - hono - supports-color '@monogrid/gainmap-js@3.4.0(three@0.180.0)': @@ -14971,7 +14970,7 @@ snapshots: '@posthog/ai@3.3.2(cheerio@1.1.2)(react@18.3.1)(ws@8.18.3)': dependencies: '@anthropic-ai/sdk': 0.36.3 - '@langchain/core': 0.3.79(openai@6.10.0) + '@langchain/core': 0.3.79(openai@6.15.0) ai: 4.3.19(react@18.3.1)(zod@3.25.76) langchain: 0.3.36(@langchain/core@0.3.79)(cheerio@1.1.2)(openai@4.104.0)(ws@8.18.3) openai: 4.104.0(ws@8.18.3)(zod@3.25.76) @@ -15794,70 +15793,70 @@ snapshots: estree-walker: 2.0.2 picomatch: 4.0.3 - '@rollup/rollup-android-arm-eabi@4.53.4': + '@rollup/rollup-android-arm-eabi@4.53.5': optional: true - '@rollup/rollup-android-arm64@4.53.4': + '@rollup/rollup-android-arm64@4.53.5': optional: true - '@rollup/rollup-darwin-arm64@4.53.4': + '@rollup/rollup-darwin-arm64@4.53.5': optional: true - '@rollup/rollup-darwin-x64@4.53.4': + '@rollup/rollup-darwin-x64@4.53.5': optional: true - '@rollup/rollup-freebsd-arm64@4.53.4': + '@rollup/rollup-freebsd-arm64@4.53.5': optional: true - '@rollup/rollup-freebsd-x64@4.53.4': + '@rollup/rollup-freebsd-x64@4.53.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.4': + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.4': + '@rollup/rollup-linux-arm-musleabihf@4.53.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.4': + '@rollup/rollup-linux-arm64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.4': + '@rollup/rollup-linux-arm64-musl@4.53.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.4': + '@rollup/rollup-linux-loong64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.4': + '@rollup/rollup-linux-ppc64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.4': + '@rollup/rollup-linux-riscv64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.4': + '@rollup/rollup-linux-riscv64-musl@4.53.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.4': + '@rollup/rollup-linux-s390x-gnu@4.53.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.4': + '@rollup/rollup-linux-x64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-x64-musl@4.53.4': + '@rollup/rollup-linux-x64-musl@4.53.5': optional: true - '@rollup/rollup-openharmony-arm64@4.53.4': + '@rollup/rollup-openharmony-arm64@4.53.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.53.4': + '@rollup/rollup-win32-arm64-msvc@4.53.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.53.4': + '@rollup/rollup-win32-ia32-msvc@4.53.5': optional: true - '@rollup/rollup-win32-x64-gnu@4.53.4': + '@rollup/rollup-win32-x64-gnu@4.53.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.53.4': + '@rollup/rollup-win32-x64-msvc@4.53.5': optional: true '@rtsao/scc@1.1.0': {} @@ -15961,7 +15960,7 @@ snapshots: '@tanstack/eslint-plugin-query@5.91.2(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 transitivePeerDependencies: - supports-color @@ -16405,14 +16404,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0)(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0)(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.49.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 @@ -16462,22 +16461,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.49.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3(supports-color@6.1.0) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 debug: 4.4.3(supports-color@6.1.0) typescript: 5.9.3 transitivePeerDependencies: @@ -16488,12 +16487,12 @@ snapshots: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - '@typescript-eslint/scope-manager@8.49.0': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -16508,11 +16507,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.49.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3(supports-color@6.1.0) eslint: 9.39.2 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -16522,7 +16521,7 @@ snapshots: '@typescript-eslint/types@5.62.0': {} - '@typescript-eslint/types@8.49.0': {} + '@typescript-eslint/types@8.50.0': {} '@typescript-eslint/typescript-estree@2.34.0(typescript@5.9.3)': dependencies: @@ -16550,12 +16549,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3(supports-color@6.1.0) minimatch: 9.0.5 semver: 7.7.3 @@ -16580,12 +16579,12 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@8.49.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: @@ -16596,9 +16595,9 @@ snapshots: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.49.0': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -19097,8 +19096,6 @@ snapshots: emojis-list@3.0.0: {} - encodeurl@1.0.2: {} - encodeurl@2.0.0: {} encoding-sniffer@0.2.1: @@ -19784,8 +19781,8 @@ snapshots: qs: 6.14.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.19.1(supports-color@6.1.0) - serve-static: 1.16.2(supports-color@6.1.0) + send: 0.19.2(supports-color@6.1.0) + serve-static: 1.16.3(supports-color@6.1.0) setprototypeof: 1.2.0 statuses: 2.0.2 type-is: 1.6.18 @@ -19819,8 +19816,8 @@ snapshots: qs: 6.14.0 range-parser: 1.2.1 router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 statuses: 2.0.2 type-is: 2.0.1 vary: 1.1.2 @@ -20649,6 +20646,8 @@ snapshots: hold-event@1.1.2: {} + hono@4.11.1: {} + hosted-git-info@2.8.9: {} hpack.js@2.1.6: @@ -20722,14 +20721,6 @@ snapshots: setprototypeof: 1.1.0 statuses: 1.5.0 - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -22061,6 +22052,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -22164,7 +22157,7 @@ snapshots: langchain@0.3.36(@langchain/core@0.3.79)(cheerio@1.1.2)(openai@4.104.0)(ws@8.18.3): dependencies: - '@langchain/core': 0.3.79(openai@6.10.0) + '@langchain/core': 0.3.79(openai@6.15.0) '@langchain/openai': 0.6.16(@langchain/core@0.3.79)(ws@8.18.3) '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79) cheerio: 1.1.2 @@ -22194,12 +22187,12 @@ snapshots: semver: 7.7.3 uuid: 10.0.0 - langsmith@0.3.87(openai@6.10.0): + langsmith@0.3.87(openai@6.15.0): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 console-table-printer: 2.15.0 - openai: 6.10.0(ws@8.18.3)(zod@4.2.0) + openai: 6.15.0(ws@8.18.3)(zod@4.2.1) p-queue: 6.6.2 semver: 7.7.3 uuid: 10.0.0 @@ -23418,10 +23411,10 @@ snapshots: ws: 8.18.3 zod: 3.25.76 - openai@6.10.0(ws@8.18.3)(zod@4.2.0): + openai@6.15.0(ws@8.18.3)(zod@4.2.1): dependencies: ws: 8.18.3 - zod: 4.2.0 + zod: 4.2.1 openapi-types@12.1.3: {} @@ -24217,7 +24210,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.306.1: + posthog-js@1.306.2: dependencies: '@posthog/core': 1.7.1 core-js: 3.47.0 @@ -25094,32 +25087,32 @@ snapshots: robust-predicates@3.0.2: {} - rollup@4.53.4: + rollup@4.53.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.4 - '@rollup/rollup-android-arm64': 4.53.4 - '@rollup/rollup-darwin-arm64': 4.53.4 - '@rollup/rollup-darwin-x64': 4.53.4 - '@rollup/rollup-freebsd-arm64': 4.53.4 - '@rollup/rollup-freebsd-x64': 4.53.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.4 - '@rollup/rollup-linux-arm-musleabihf': 4.53.4 - '@rollup/rollup-linux-arm64-gnu': 4.53.4 - '@rollup/rollup-linux-arm64-musl': 4.53.4 - '@rollup/rollup-linux-loong64-gnu': 4.53.4 - '@rollup/rollup-linux-ppc64-gnu': 4.53.4 - '@rollup/rollup-linux-riscv64-gnu': 4.53.4 - '@rollup/rollup-linux-riscv64-musl': 4.53.4 - '@rollup/rollup-linux-s390x-gnu': 4.53.4 - '@rollup/rollup-linux-x64-gnu': 4.53.4 - '@rollup/rollup-linux-x64-musl': 4.53.4 - '@rollup/rollup-openharmony-arm64': 4.53.4 - '@rollup/rollup-win32-arm64-msvc': 4.53.4 - '@rollup/rollup-win32-ia32-msvc': 4.53.4 - '@rollup/rollup-win32-x64-gnu': 4.53.4 - '@rollup/rollup-win32-x64-msvc': 4.53.4 + '@rollup/rollup-android-arm-eabi': 4.53.5 + '@rollup/rollup-android-arm64': 4.53.5 + '@rollup/rollup-darwin-arm64': 4.53.5 + '@rollup/rollup-darwin-x64': 4.53.5 + '@rollup/rollup-freebsd-arm64': 4.53.5 + '@rollup/rollup-freebsd-x64': 4.53.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.5 + '@rollup/rollup-linux-arm-musleabihf': 4.53.5 + '@rollup/rollup-linux-arm64-gnu': 4.53.5 + '@rollup/rollup-linux-arm64-musl': 4.53.5 + '@rollup/rollup-linux-loong64-gnu': 4.53.5 + '@rollup/rollup-linux-ppc64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-musl': 4.53.5 + '@rollup/rollup-linux-s390x-gnu': 4.53.5 + '@rollup/rollup-linux-x64-gnu': 4.53.5 + '@rollup/rollup-linux-x64-musl': 4.53.5 + '@rollup/rollup-openharmony-arm64': 4.53.5 + '@rollup/rollup-win32-arm64-msvc': 4.53.5 + '@rollup/rollup-win32-ia32-msvc': 4.53.5 + '@rollup/rollup-win32-x64-gnu': 4.53.5 + '@rollup/rollup-win32-x64-msvc': 4.53.5 fsevents: 2.3.3 router@2.2.0: @@ -25270,25 +25263,7 @@ snapshots: semver@7.7.3: {} - send@0.19.0(supports-color@6.1.0): - dependencies: - debug: 2.6.9(supports-color@6.1.0) - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - - send@0.19.1(supports-color@6.1.0): + send@0.19.2(supports-color@6.1.0): dependencies: debug: 2.6.9(supports-color@6.1.0) depd: 2.0.0 @@ -25297,16 +25272,16 @@ snapshots: escape-html: 1.0.3 etag: 1.8.1 fresh: 0.5.2 - http-errors: 2.0.0 + http-errors: 2.0.1 mime: 1.6.0 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 - statuses: 2.0.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color - send@1.2.0: + send@1.2.1: dependencies: debug: 4.4.3(supports-color@6.1.0) encodeurl: 2.0.0 @@ -25340,21 +25315,21 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.16.2(supports-color@6.1.0): + serve-static@1.16.3(supports-color@6.1.0): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.19.0(supports-color@6.1.0) + send: 0.19.2(supports-color@6.1.0) transitivePeerDependencies: - supports-color - serve-static@2.2.0: + serve-static@2.2.1: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.0 + send: 1.2.1 transitivePeerDependencies: - supports-color @@ -25694,8 +25669,6 @@ snapshots: statuses@1.5.0: {} - statuses@2.0.1: {} - statuses@2.0.2: {} std-env@3.10.0: {} @@ -25987,7 +25960,7 @@ snapshots: unquote: 1.1.1 util.promisify: 1.0.1 - swr@2.3.7(react@18.3.1): + swr@2.3.8(react@18.3.1): dependencies: dequal: 2.0.3 react: 18.3.1 @@ -26392,12 +26365,12 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.49.0(eslint@9.39.2)(typescript@5.9.3): + typescript-eslint@8.50.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0)(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/parser': 8.49.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0)(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: @@ -26716,7 +26689,7 @@ snapshots: '@types/node': 22.19.3 esbuild: 0.21.5 postcss: 8.5.6 - rollup: 4.53.4 + rollup: 4.53.5 optionalDependencies: fsevents: 2.3.3 @@ -27224,13 +27197,13 @@ snapshots: dependencies: zod: 3.25.76 - zod-to-json-schema@3.25.0(zod@4.2.0): + zod-to-json-schema@3.25.0(zod@4.2.1): dependencies: - zod: 4.2.0 + zod: 4.2.1 zod@3.25.76: {} - zod@4.2.0: {} + zod@4.2.1: {} zustand@4.5.7(@types/react@18.3.27)(immer@10.2.0)(react@18.3.1): dependencies: diff --git a/src-tauri/src/command.rs b/src-tauri/src/command.rs index 5af603f6..45e69ab8 100644 --- a/src-tauri/src/command.rs +++ b/src-tauri/src/command.rs @@ -761,12 +761,201 @@ pub async fn write_file_async(path: String, content: Option>, source_pat #[tauri::command] pub fn get_file_metadata(path: String) -> Result { use std::fs; - + let metadata = fs::metadata(&path).map_err(|e| e.to_string())?; - + Ok(serde_json::json!({ "size": metadata.len(), "isFile": metadata.is_file(), "isDirectory": metadata.is_dir() })) } + +#[tauri::command] +pub async fn check_claude_code_available() -> Result { + use std::process::Command; + + let result = tauri::async_runtime::spawn_blocking(|| { + let which_output = Command::new("which") + .arg("claude") + .output(); + + let cli_exists = match which_output { + Ok(output) => output.status.success(), + Err(_) => false, + }; + + if !cli_exists { + return serde_json::json!({ + "available": false, + "version": null, + "authenticated": false + }); + } + + let version_output = Command::new("claude") + .arg("--version") + .output(); + + let version = match version_output { + Ok(output) if output.status.success() => { + Some(String::from_utf8_lossy(&output.stdout).trim().to_string()) + } + _ => None, + }; + + serde_json::json!({ + "available": true, + "version": version, + "authenticated": true + }) + }).await.map_err(|e| format!("Failed to check Claude Code availability: {}", e))?; + + Ok(result) +} + +/// Stream a response from Claude Code CLI +/// This spawns the claude CLI process and emits events as responses stream in +#[tauri::command] +pub async fn stream_claude_code_response( + app_handle: AppHandle, + request_id: String, + prompt: String, + system_prompt: Option, + model: Option, + disable_project_context: Option, +) -> Result<(), String> { + use std::io::{BufRead, BufReader}; + use std::process::{Command, Stdio}; + + let mut cmd = Command::new("claude"); + cmd.arg("-p") // Print mode (non-interactive) + .arg("--output-format") + .arg("stream-json") + .arg("--verbose") + .arg("--permission-mode") + .arg("bypassPermissions"); // Auto-approve for chat use + + // If we want to disable project context, only load user settings + // but keep tools enabled so we can see tool calls in the UI + let disable_context = disable_project_context.unwrap_or(true); + if disable_context { + // Only load user settings, not project-specific ones + cmd.arg("--setting-sources").arg("user"); + // Run from home directory to avoid picking up any project context + if let Ok(home) = std::env::var("HOME") { + cmd.current_dir(home); + } + } + + if let Some(m) = model { + cmd.arg("--model").arg(m); + } + + // Add system prompt - use append to keep Claude Code's base capabilities + // but add our general assistant instructions + if let Some(sp) = system_prompt { + if !sp.is_empty() { + cmd.arg("--append-system-prompt").arg(sp); + } + } else if disable_context { + // Default prompt for general assistant mode + cmd.arg("--append-system-prompt") + .arg("You are a helpful general assistant. Answer questions directly and helpfully. Do not reference any specific project or codebase context."); + } + + cmd.arg(&prompt); + + cmd.stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let mut child = cmd.spawn().map_err(|e| format!("Failed to spawn claude CLI: {}", e))?; + + let stdout = child.stdout.take().ok_or("Failed to capture stdout")?; + let stderr = child.stderr.take().ok_or("Failed to capture stderr")?; + let request_id_clone = request_id.clone(); + let app_handle_clone = app_handle.clone(); + + // Spawn a thread to read stdout and emit final result only (no streaming to avoid freeze) + std::thread::spawn(move || { + let reader = BufReader::new(stdout); + let mut last_assistant_line: Option = None; + + // Read all lines, keep only the last assistant message + for line in reader.lines() { + if let Ok(json_line) = line { + if json_line.contains("\"type\":\"assistant\"") { + last_assistant_line = Some(json_line); + } + } + } + + // Emit once at the end with the final content + if let Some(final_line) = last_assistant_line { + let _ = app_handle_clone.emit( + &format!("claude-code-stream-{}", request_id_clone), + serde_json::json!({ + "type": "data", + "data": final_line + }) + ); + } + }); + + let request_id_clone2 = request_id.clone(); + let app_handle_clone2 = app_handle.clone(); + + std::thread::spawn(move || { + let reader = BufReader::new(stderr); + let mut stderr_content = String::new(); + + for line in reader.lines() { + if let Ok(l) = line { + stderr_content.push_str(&l); + stderr_content.push('\n'); + } + } + + if !stderr_content.is_empty() { + let _ = app_handle_clone2.emit( + &format!("claude-code-stream-{}", request_id_clone2), + serde_json::json!({ + "type": "stderr", + "data": stderr_content + }) + ); + } + }); + + // Wait for the process to complete in a regular thread (not spawn_blocking) + // to avoid potential thread pool issues + let request_id_clone3 = request_id.clone(); + let app_handle_clone3 = app_handle.clone(); + + std::thread::spawn(move || { + match child.wait() { + Ok(status) => { + // Small delay to ensure stdout thread finishes emitting before done + std::thread::sleep(std::time::Duration::from_millis(50)); + let _ = app_handle_clone3.emit( + &format!("claude-code-stream-{}", request_id_clone3), + serde_json::json!({ + "type": "done", + "exitCode": status.code() + }) + ); + } + Err(e) => { + let _ = app_handle_clone3.emit( + &format!("claude-code-stream-{}", request_id_clone3), + serde_json::json!({ + "type": "error", + "error": format!("Process error: {}", e) + }) + ); + } + } + }); + + Ok(()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index b843c3d2..f72f0b49 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -429,6 +429,8 @@ pub fn run() { command::get_instance_name, command::write_file_async, command::get_file_metadata, + command::check_claude_code_available, + command::stream_claude_code_response, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/migrations.rs b/src-tauri/src/migrations.rs index 87622999..cd73e924 100644 --- a/src-tauri/src/migrations.rs +++ b/src-tauri/src/migrations.rs @@ -2554,5 +2554,24 @@ You have full access to bash commands on the user''''s computer. If you write a UPDATE projects SET total_cost_usd = 0.0 WHERE total_cost_usd IS NULL; "#, }, + Migration { + version: 139, + description: "add claude code provider models for using claude code subscription", + kind: MigrationKind::Up, + sql: r#" + -- Add Claude models via Claude Code CLI + -- These use the local Claude Code CLI and subscription instead of an API key + INSERT OR REPLACE INTO models (id, display_name, is_enabled, supported_attachment_types) VALUES + ('claude-code::claude-sonnet-4-5-20250929', 'Claude Sonnet 4.5 (via Claude Code)', 1, '["text", "webpage", "image", "pdf"]'), + ('claude-code::claude-opus-4-20250514', 'Claude Opus 4 (via Claude Code)', 1, '["text", "webpage", "image", "pdf"]'), + ('claude-code::claude-sonnet-4-20250514', 'Claude Sonnet 4 (via Claude Code)', 1, '["text", "webpage", "image", "pdf"]'); + + -- Add model configs for each variant + INSERT OR REPLACE INTO model_configs (author, id, model_id, display_name, system_prompt, is_default, new_until) VALUES + ('system', 'claude-code::claude-sonnet-4-5-20250929', 'claude-code::claude-sonnet-4-5-20250929', 'Claude Sonnet 4.5 (via Claude Code)', '', 0, '2026-01-15 00:00:00'), + ('system', 'claude-code::claude-opus-4-20250514', 'claude-code::claude-opus-4-20250514', 'Claude Opus 4 (via Claude Code)', '', 0, '2026-01-15 00:00:00'), + ('system', 'claude-code::claude-sonnet-4-20250514', 'claude-code::claude-sonnet-4-20250514', 'Claude Sonnet 4 (via Claude Code)', '', 0, '2026-01-15 00:00:00'); + "#, + }, ]; -} +} \ No newline at end of file diff --git a/src/core/chorus/ModelProviders/ProviderClaudeCode.ts b/src/core/chorus/ModelProviders/ProviderClaudeCode.ts new file mode 100644 index 00000000..d3955f0c --- /dev/null +++ b/src/core/chorus/ModelProviders/ProviderClaudeCode.ts @@ -0,0 +1,281 @@ +import { IProvider } from "./IProvider"; +import { + LLMMessage, + StreamResponseParams, + llmMessageToString, + readTextAttachment, + readWebpageAttachment, +} from "../Models"; +import { invoke } from "@tauri-apps/api/core"; +import { listen, UnlistenFn } from "@tauri-apps/api/event"; + +// Types for Claude Code stream-json output +interface ClaudeCodeSystemInit { + type: "system"; + subtype: "init"; + session_id: string; + model: string; + tools: string[]; +} + +interface ClaudeCodeAssistantMessage { + type: "assistant"; + message: { + content: Array<{ type: "text"; text: string } | { type: string }>; + }; + session_id: string; +} + +interface ClaudeCodeResult { + type: "result"; + subtype: "success" | "error"; + result?: string; + error?: string; + session_id: string; +} + +type ClaudeCodeStreamMessage = + | ClaudeCodeSystemInit + | ClaudeCodeAssistantMessage + | ClaudeCodeResult; + +interface TauriStreamEvent { + type: "data" | "error" | "stderr" | "done"; + data?: string; + error?: string; + exitCode?: number; +} + +/** + * Check if Claude Code CLI is available and authenticated + */ +export async function checkClaudeCodeAvailable(): Promise<{ + available: boolean; + version: string | null; + authenticated: boolean; +}> { + try { + const result = await invoke<{ + available: boolean; + version: string | null; + authenticated: boolean; + }>("check_claude_code_available"); + return result; + } catch { + return { available: false, version: null, authenticated: false }; + } +} + +export class ProviderClaudeCode implements IProvider { + async streamResponse({ + llmConversation, + modelConfig, + onChunk, + onComplete, + onError, + }: StreamResponseParams): Promise { + const requestId = `${Date.now()}-${Math.random().toString(36).substring(2)}`; + const prompt = await this.formatConversationAsPrompt(llmConversation); + + // Extract model name from model ID like "claude-code::claude-sonnet-4-5-20250929" + const modelName = modelConfig.modelId.split("::")[1]; + const model = modelName === "default" ? undefined : modelName; + + let unlisten: UnlistenFn | undefined; + let stderrContent = ""; + let hasReceivedContent = false; + + // Track what text we've already emitted to compute deltas + // (Claude Code sends complete messages, not deltas) + let lastEmittedText = ""; + + let resolveStream: () => void; + let rejectStream: (error: Error) => void; + const streamingComplete = new Promise((resolve, reject) => { + resolveStream = resolve; + rejectStream = reject; + }); + + try { + unlisten = await listen( + `claude-code-stream-${requestId}`, + (event) => { + const payload = event.payload; + + if (payload.type === "data" && payload.data) { + try { + const message = JSON.parse( + payload.data, + ) as ClaudeCodeStreamMessage; + + // Handle error results + if ( + message.type === "result" && + message.subtype === "error" + ) { + rejectStream( + new Error( + message.error || + "Claude Code returned an error", + ), + ); + return; + } + + // Process assistant messages - extract text and emit deltas + if ( + message.type === "assistant" && + message.message?.content + ) { + const textParts: string[] = []; + for (const block of message.message.content) { + if ( + block.type === "text" && + "text" in block + ) { + textParts.push(block.text); + } + } + + if (textParts.length > 0) { + hasReceivedContent = true; + const fullText = textParts.join(""); + + // Compute delta - only emit NEW content + if (fullText.startsWith(lastEmittedText)) { + const delta = fullText.slice( + lastEmittedText.length, + ); + if (delta) { + onChunk(delta); + lastEmittedText = fullText; + } + } else if (fullText) { + // Text changed completely (rare) - emit full text + onChunk(fullText); + lastEmittedText = fullText; + } + } + } + } catch { + // Ignore parse errors for non-JSON lines + } + } else if (payload.type === "error") { + rejectStream( + new Error(payload.error || "Unknown error"), + ); + } else if (payload.type === "stderr" && payload.data) { + stderrContent += payload.data; + } else if (payload.type === "done") { + if ( + payload.exitCode !== undefined && + payload.exitCode !== 0 + ) { + if (stderrContent && !hasReceivedContent) { + rejectStream( + new Error( + stderrContent.trim() || + `Claude Code CLI exited with code ${payload.exitCode}`, + ), + ); + } else if (!hasReceivedContent) { + rejectStream( + new Error( + `Claude Code CLI exited with code ${payload.exitCode}`, + ), + ); + } else { + resolveStream(); + } + } else { + resolveStream(); + } + } + }, + ); + + await invoke("stream_claude_code_response", { + requestId, + prompt, + systemPrompt: modelConfig.systemPrompt || undefined, + model, + disableProjectContext: true, + }); + + await streamingComplete; + void onComplete(); + } catch (error) { + onError( + error instanceof Error ? error.message : "Unknown error occurred", + ); + } finally { + if (unlisten) { + unlisten(); + } + } + } + + private async formatConversationAsPrompt( + messages: LLMMessage[], + ): Promise { + // For single message, just return the content + if (messages.length === 1 && messages[0].role === "user") { + return await this.formatSingleMessage(messages[0]); + } + + // For multi-turn conversations, format as a transcript + const parts: string[] = []; + + for (const message of messages) { + const formatted = await this.formatMessageForTranscript(message); + parts.push(formatted); + } + + return parts.join("\n\n"); + } + + private async formatSingleMessage(message: LLMMessage): Promise { + if (message.role !== "user") { + return llmMessageToString(message); + } + + let content = message.content; + + // Append attachment contents + // Note: The Claude Code CLI in print mode doesn't support direct image/PDF input. + // Images would require either keeping tools enabled (to use Read tool on files) + // or using the stream-json input format with base64 data. + // For now, we only support text and webpage attachments which can be inlined. + for (const attachment of message.attachments) { + if (attachment.type === "text") { + const textContent = await readTextAttachment(attachment); + content += `\n\n[Attachment: ${attachment.originalName}]\n${textContent}`; + } else if (attachment.type === "webpage") { + const webContent = await readWebpageAttachment(attachment); + content += `\n\n[Webpage: ${attachment.originalName}]\n${webContent}`; + } else if ( + attachment.type === "image" || + attachment.type === "pdf" + ) { + // Images and PDFs are not supported in CLI print mode without tools + content += `\n\n[Attachment: ${attachment.originalName}] (Note: ${attachment.type} attachments are not supported with Claude Code in general assistant mode)`; + } + } + + return content; + } + + private async formatMessageForTranscript( + message: LLMMessage, + ): Promise { + if (message.role === "user") { + const content = await this.formatSingleMessage(message); + return `Human: ${content}`; + } else if (message.role === "assistant") { + return `Assistant: ${message.content}`; + } else if (message.role === "tool_results") { + return `Tool Results: ${llmMessageToString(message)}`; + } + return ""; + } +} diff --git a/src/core/chorus/Models.ts b/src/core/chorus/Models.ts index 1563d354..b49ab5d3 100644 --- a/src/core/chorus/Models.ts +++ b/src/core/chorus/Models.ts @@ -17,6 +17,7 @@ import { ollamaClient } from "./OllamaClient"; import { ProviderOllama } from "./ModelProviders/ProviderOllama"; import { ProviderLMStudio } from "./ModelProviders/ProviderLMStudio"; import { ProviderGrok } from "./ModelProviders/ProviderGrok"; +import { ProviderClaudeCode } from "./ModelProviders/ProviderClaudeCode"; import posthog from "posthog-js"; import { UserTool, UserToolCall, UserToolResult } from "./Toolsets"; import { Attachment } from "./api/AttachmentsAPI"; @@ -242,7 +243,8 @@ export type ProviderName = | "ollama" | "lmstudio" | "grok" - | "meta"; + | "meta" + | "claude-code"; /** * Returns a human readable label for the provider @@ -296,6 +298,8 @@ function getProvider(providerName: string): IProvider { return new ProviderLMStudio(); case "grok": return new ProviderGrok(); + case "claude-code": + return new ProviderClaudeCode(); default: throw new Error(`Unknown provider: ${providerName}`); } @@ -613,6 +617,7 @@ const CONTEXT_LIMIT_PATTERNS: Record = { lmstudio: "context window", // best guess perplexity: "context window", // best guess ollama: "context window", // best guess + "claude-code": "prompt is too long", // same as anthropic }; /** diff --git a/src/core/chorus/api/MessageAPI.ts b/src/core/chorus/api/MessageAPI.ts index 013cd067..03af71b2 100644 --- a/src/core/chorus/api/MessageAPI.ts +++ b/src/core/chorus/api/MessageAPI.ts @@ -1023,7 +1023,6 @@ export function useStreamMessagePart() { const chat = await queryClient.ensureQueryData( chatQueries.detail(chatId), ); - console.log(chat); const project = await queryClient.ensureQueryData( projectQueries.detail(chat.projectId), ); diff --git a/src/core/chorus/api/ModelsAPI.ts b/src/core/chorus/api/ModelsAPI.ts index 2a30a09c..20e16f2c 100644 --- a/src/core/chorus/api/ModelsAPI.ts +++ b/src/core/chorus/api/ModelsAPI.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import * as Models from "../Models"; import { db } from "../DB"; import { ModelConfig } from "../Models"; +import { checkClaudeCodeAvailable } from "../ModelProviders/ProviderClaudeCode"; import { getApiKeys } from "./AppMetadataAPI"; // all @@ -442,3 +443,11 @@ export function useCreateModelConfig() { }, }); } + +export function useClaudeCodeAvailable() { + return useQuery({ + queryKey: ["claudeCodeAvailable"] as const, + queryFn: checkClaudeCodeAvailable, + staleTime: 30000, + }); +} diff --git a/src/core/utilities/ProxyUtils.ts b/src/core/utilities/ProxyUtils.ts index 7f3a48eb..725c9bd4 100644 --- a/src/core/utilities/ProxyUtils.ts +++ b/src/core/utilities/ProxyUtils.ts @@ -55,8 +55,13 @@ export function canProceedWithProvider( ): CanProceedResult { const apiKeyField = PROVIDER_TO_API_KEY[providerKey]; - // Local models (ollama, lmstudio) don't require API keys - if (providerKey === "ollama" || providerKey === "lmstudio") { + // Local models (ollama, lmstudio) and claude-code don't require API keys + // claude-code uses the local Claude Code CLI authentication + if ( + providerKey === "ollama" || + providerKey === "lmstudio" || + providerKey === "claude-code" + ) { return { canProceed: true }; } diff --git a/src/ui/components/ChatInput.tsx b/src/ui/components/ChatInput.tsx index 1892f80c..2a6d6993 100644 --- a/src/ui/components/ChatInput.tsx +++ b/src/ui/components/ChatInput.tsx @@ -3,7 +3,7 @@ import React from "react"; import { useAppContext } from "@ui/hooks/useAppContext"; import AutoExpandingTextarea from "./AutoExpandingTextarea"; import { AttachmentAddPill, AttachmentDropArea } from "./AttachmentsViews"; -import { AttachmentType } from "@core/chorus/Models"; +import { AttachmentType, getProviderName } from "@core/chorus/Models"; import { MANAGE_MODELS_COMPARE_DIALOG_ID, ManageModelsBox, @@ -92,6 +92,15 @@ export function ChatInput({ ModelsAPI.useSelectedModelConfigsCompare(); const modelConfigs = ModelsAPI.useModelConfigs(); const appMetadata = useWaitForAppMetadata(); + + // Hide toolsets when all selected models are Claude Code (which has its own built-in tools) + const allModelsAreClaudeCode = useMemo(() => { + const selectedConfigs = selectedModelConfigsCompare.data ?? []; + if (selectedConfigs.length === 0) return false; + return selectedConfigs.every( + (config) => getProviderName(config.modelId) === "claude-code", + ); + }, [selectedModelConfigsCompare.data]); const cautiousEnter = appMetadata["cautious_enter"] === "true"; const { draft, setDraft } = DraftAPI.useAutoSyncMessageDraft(chatId); @@ -624,7 +633,7 @@ export function ChatInput({ showShortcut={false} /> )} - {!isReply && } + {!isReply && !allModelsAreClaudeCode && }
diff --git a/src/ui/components/ManageModelsBox.tsx b/src/ui/components/ManageModelsBox.tsx index cbf1dc27..8012c7d0 100644 --- a/src/ui/components/ManageModelsBox.tsx +++ b/src/ui/components/ManageModelsBox.tsx @@ -156,6 +156,7 @@ function ModelGroup({ emptyState, onAddApiKey, groupId, + claudeCodeAvailable, showCost, }: { heading: React.ReactNode; @@ -167,6 +168,7 @@ function ModelGroup({ emptyState?: React.ReactNode; onAddApiKey: () => void; groupId?: string; + claudeCodeAvailable?: boolean; showCost: boolean; }) { const { data: apiKeys } = AppMetadataAPI.useApiKeys(); @@ -181,6 +183,11 @@ function ModelGroup({ return false; } + // Claude Code requires the CLI to be installed + if (provider === "claude-code") { + return !claudeCodeAvailable; + } + // If user has API key for this provider, allow it if ( apiKeys && @@ -196,7 +203,7 @@ function ModelGroup({ // No API key for this provider - model is not allowed return true; }, - [apiKeys], + [apiKeys, claudeCodeAvailable], ); return ( @@ -250,19 +257,26 @@ function ModelGroup({
{isModelNotAllowed(m) ? ( - + getProviderName(m.modelId) === + "claude-code" ? ( + + Install Claude Code CLI + + ) : ( + + ) ) : ( <>

@@ -340,6 +354,9 @@ export function ManageModelsBox({ const modelConfigs = ModelsAPI.useModelConfigs(); const showOpenRouter = AppMetadataAPI.useShowOpenRouter(); const setShowOpenRouterMutation = AppMetadataAPI.useSetShowOpenRouter(); + const claudeCodeAvailableQuery = ModelsAPI.useClaudeCodeAvailable(); + const claudeCodeAvailable = + claudeCodeAvailableQuery.data?.available ?? false; const settings = useSettings(); const showCost = settings?.showCost ?? false; @@ -498,6 +515,11 @@ export function ManageModelsBox({ (m) => getProviderName(m.modelId) === "openrouter", ); + // Claude Code models use the local Claude Code CLI subscription + const claudeCodeModels = systemModels.filter( + (m) => getProviderName(m.modelId) === "claude-code", + ); + // Direct provider models grouped by provider const directProviders = [ "anthropic", @@ -523,6 +545,7 @@ export function ManageModelsBox({ custom: filterBySearch(userModels, searchTerms), local: filterBySearch(localModels, searchTerms), openrouter: filterBySearch(openrouterModels, searchTerms), + claudeCode: filterBySearch(claudeCodeModels, searchTerms), directByProvider, }; }, [modelConfigs.data, searchQuery]); @@ -743,6 +766,25 @@ export function ManageModelsBox({ /> )} + {/* Claude Code Models - uses local Claude Code subscription */} + {modelGroups.claudeCode.length > 0 && ( + + Claude Code Subscription +

+ } + models={modelGroups.claudeCode} + checkedModelConfigIds={checkedModelConfigIds} + mode={mode} + onToggleModelConfig={handleToggleModelConfig} + onAddApiKey={handleAddApiKey} + groupId="claude-code" + claudeCodeAvailable={claudeCodeAvailable} + showCost={showCost} + /> + )} + {/* Direct Provider Models (Anthropic, OpenAI, Google, etc.) */} {modelGroups.directByProvider.anthropic.length > 0 && ( ; }; +const ToolCall = ({ + children, + name, + "data-input": dataInput, +}: { + children?: React.ReactNode; + name?: string; + "data-input"?: string; +}) => { + // Decode base64 content if present, otherwise use children + let content = ""; + if (dataInput) { + try { + content = atob(dataInput); + } catch (e) { + console.error("[ToolCall] Failed to decode data-input:", e); + content = extractTextFromChildren(children); + } + } else { + content = extractTextFromChildren(children); + } + + return ; +}; + export const Img = ({ src, alt }: { src?: string; alt?: string }) => { if (!src) return null; return ; }; +const ToolCallGroupWrapper = ({ children }: { children?: React.ReactNode }) => { + return {children}; +}; + const components = { code: Code, sup: Sup, @@ -149,6 +180,8 @@ const components = { th: Th, td: Td, think: Think, + "tool-call": ToolCall, + "tool-call-group": ToolCallGroupWrapper, img: Img, }; @@ -241,7 +274,20 @@ export const MessageMarkdown = ({ text }: { text: string }) => { const isComplete = _match.endsWith("</think>"); return `\n${content}\n\n\n`; }, - ); + ) + // Decode tool-call tags so they can be rendered + .replace( + /<tool-call\s+name="([^&]+)"\s+data-input="([^&]+)"><\/tool-call>/g, + (_match, name, dataInput) => { + return ``; + }, + ) + // Group consecutive tool-call tags together + .replace(/(]*><\/tool-call>\s*)+/g, (match) => { + return `\n\n${match}\n\n\n`; + }); + + const finalProcessedText = processedMainText; const sourceReferences = sourcesSection?.split("\n").reduce( @@ -256,7 +302,7 @@ export const MessageMarkdown = ({ text }: { text: string }) => { ) || {}; // Process citation links in the text - const processedText = processedMainText.replace( + const processedText = finalProcessedText.replace( /\[(\d+)\]/g, (match, num: string) => { const url = sourceReferences[num]; diff --git a/src/ui/components/renderers/ToolCallBlock.tsx b/src/ui/components/renderers/ToolCallBlock.tsx new file mode 100644 index 00000000..eb299c27 --- /dev/null +++ b/src/ui/components/renderers/ToolCallBlock.tsx @@ -0,0 +1,63 @@ +import { ChevronDown as ChevronDownIcon, WrenchIcon } from "lucide-react"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@ui/components/ui/collapsible"; +import { useState, useMemo } from "react"; +import { CodeBlock } from "./CodeBlock"; +import { extractToolSummary } from "./toolCallHelpers"; + +export const ToolCallBlock = ({ + toolName, + content, +}: { + toolName: string; + content: string; +}) => { + // Always start collapsed for cleaner UX + const [isOpen, setIsOpen] = useState(false); + + // Memoize the tool summary extraction + const { summary, displayContent, isJSON } = useMemo( + () => extractToolSummary(content), + [content], + ); + + return ( + ) => { + e.stopPropagation(); // prevent message from selecting + }} + > + +
+ + + {toolName} + + {summary && ( + <> + · + + {summary} + + + )} +
+ +
+ +
+ +
+
+
+ ); +}; diff --git a/src/ui/components/renderers/ToolCallGroup.tsx b/src/ui/components/renderers/ToolCallGroup.tsx new file mode 100644 index 00000000..6956e1a4 --- /dev/null +++ b/src/ui/components/renderers/ToolCallGroup.tsx @@ -0,0 +1,204 @@ +import { ChevronDown as ChevronDownIcon, WrenchIcon } from "lucide-react"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@ui/components/ui/collapsible"; +import { useState, useMemo } from "react"; +import React from "react"; +import { CodeBlock } from "./CodeBlock"; +import { extractToolSummary } from "./toolCallHelpers"; + +interface ToolCall { + name: string; + content: string; +} + +interface ToolCallElementProps { + name?: string; + "data-input"?: string; + children?: React.ReactNode; +} + +function isToolCallElement( + child: unknown, +): child is React.ReactElement { + return ( + child !== null && + typeof child === "object" && + "props" in child && + typeof (child as React.ReactElement).props === "object" + ); +} + +function extractTextFromChildren(children: React.ReactNode): string { + return React.Children.toArray(children) + .map((child) => { + if (typeof child === "string") return child; + if (typeof child === "number") return String(child); + if (child && typeof child === "object" && "props" in child) { + const props = child.props as { children?: React.ReactNode }; + return extractTextFromChildren(props.children); + } + return ""; + }) + .join(""); +} + +export const ToolCallGroup = ({ children }: { children?: React.ReactNode }) => { + const [isOpen, setIsOpen] = useState(false); + + // Memoize the expensive parsing operation based on children + const toolCalls = useMemo(() => { + const calls: ToolCall[] = []; + + // Extract tool call data from children + if (children) { + const childArray = Array.isArray(children) ? children : [children]; + + // First, try to extract from properly parsed ToolCall components + childArray.forEach((child) => { + if ( + isToolCallElement(child) && + child.props.name && + child.props["data-input"] + ) { + try { + const content = atob(child.props["data-input"]); + calls.push({ + name: child.props.name, + content: content, + }); + } catch (e) { + console.error( + "[ToolCallGroup] Failed to decode tool call:", + e, + ); + } + } + }); + + // This handles cases where react-markdown doesn't parse all tags correctly + if ( + calls.length === 0 || + childArray.some((child) => typeof child === "string") + ) { + const htmlContent = extractTextFromChildren(children); + + // Extract tool calls from HTML string + const toolCallRegex = + /<\/tool-call>/g; + let match; + + while ((match = toolCallRegex.exec(htmlContent)) !== null) { + const [, name, dataInput] = match; + try { + const content = atob(dataInput); + calls.push({ + name, + content, + }); + } catch (e) { + console.error( + "[ToolCallGroup] Failed to decode tool call from HTML:", + e, + ); + } + } + } + } + + return calls; + }, [children]); + + // Memoize the summary calculation + const summary = useMemo(() => { + const toolCounts = toolCalls.reduce( + (acc, tool) => { + acc[tool.name] = (acc[tool.name] || 0) + 1; + return acc; + }, + {} as Record, + ); + + return Object.entries(toolCounts) + .map(([name, count]) => (count > 1 ? `${name} (${count}×)` : name)) + .join(", "); + }, [toolCalls]); + + return ( + ) => { + e.stopPropagation(); + }} + > + +
+ + + {toolCalls.length} tool + {toolCalls.length !== 1 ? "s" : ""} + + · + {summary} +
+ +
+ +
+ {toolCalls.map((tool, idx) => ( + + ))} +
+
+
+ ); +}; + +function ToolCallItem({ name, content }: { name: string; content: string }) { + const [isOpen, setIsOpen] = useState(false); + + // Memoize the tool summary extraction + const { summary, displayContent, isJSON } = useMemo( + () => extractToolSummary(content), + [content], + ); + + return ( + + + + {name} + + {summary && ( + <> + · + + {summary} + + + )} + + + +
+ +
+
+
+ ); +} diff --git a/src/ui/components/renderers/toolCallHelpers.ts b/src/ui/components/renderers/toolCallHelpers.ts new file mode 100644 index 00000000..254511c5 --- /dev/null +++ b/src/ui/components/renderers/toolCallHelpers.ts @@ -0,0 +1,119 @@ +interface ToolCallParsedContent { + description?: string; + prompt?: string; + command?: string; + file_path?: string; + path?: string; + pattern?: string; +} + +function isToolCallParsedContent( + value: unknown, +): value is ToolCallParsedContent { + return typeof value === "object" && value !== null; +} + +function getString(value: unknown): string | undefined { + return typeof value === "string" ? value : undefined; +} + +/** + * Extracts a human-readable summary from tool call content + * @param content - Raw content string (usually JSON) + * @returns Object with summary text, formatted content, and whether it's JSON + */ +export function extractToolSummary(content: string): { + summary: string; + displayContent: string; + isJSON: boolean; +} { + try { + const parsed: unknown = JSON.parse(content); + const displayContent = JSON.stringify(parsed, null, 2); + + if (!isToolCallParsedContent(parsed)) { + return { + summary: "", + displayContent, + isJSON: true, + }; + } + + // Extract summary from common fields + // Show full description (usually concise and meaningful) + const description = getString(parsed.description); + if (description) { + return { + summary: description, + displayContent, + isJSON: true, + }; + } + + // Truncate prompts as they can be very long + const prompt = getString(parsed.prompt); + if (prompt) { + const truncated = + prompt.length > 60 ? prompt.substring(0, 60) + "..." : prompt; + return { + summary: truncated, + displayContent, + isJSON: true, + }; + } + + // Show full command, file_path, path, pattern (usually short) + const command = getString(parsed.command); + if (command) { + return { + summary: command, + displayContent, + isJSON: true, + }; + } + + const filePath = getString(parsed.file_path); + if (filePath) { + return { + summary: filePath, + displayContent, + isJSON: true, + }; + } + + const path = getString(parsed.path); + if (path) { + return { + summary: path, + displayContent, + isJSON: true, + }; + } + + const pattern = getString(parsed.pattern); + if (pattern) { + return { + summary: pattern, + displayContent, + isJSON: true, + }; + } + + // No recognized field, return empty summary + return { + summary: "", + displayContent, + isJSON: true, + }; + } catch { + // Not JSON, use content as-is with truncation for summary + const summary = + content.length > 60 ? content.substring(0, 60) + "..." : content; + + return { + summary, + displayContent: content, + isJSON: false, + }; + } +} diff --git a/src/ui/components/ui/provider-logo.tsx b/src/ui/components/ui/provider-logo.tsx index 49d1b342..70a663fe 100644 --- a/src/ui/components/ui/provider-logo.tsx +++ b/src/ui/components/ui/provider-logo.tsx @@ -77,6 +77,9 @@ export function ProviderLogo({ className="w-4 h-4 invert dark:invert-0" /> ); + case "claude-code": + // Claude Code uses Anthropic's Claude, so use the Anthropic logo + return ; default: { // @ts-expect-error: creating unused variable to provide exhaustiveness check const _unused: never = provider;