diff --git a/package-lock.json b/package-lock.json
index b0292d26c..8821e145e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,7 @@
"devDependencies": {
"concurrently": "^9.2.1",
"get-port-cli": "^3.0.0",
+ "knip": "^5.82.1",
"patch-package": "^8.0.1",
"typescript": "^5.9.3"
}
@@ -1848,9 +1849,9 @@
}
},
"node_modules/@emnapi/core": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz",
- "integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -6340,6 +6341,306 @@
"node": ">=8.0.0"
}
},
+ "node_modules/@oxc-resolver/binding-android-arm-eabi": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.16.4.tgz",
+ "integrity": "sha512-6XUHilmj8D6Ggus+sTBp64x/DUQ7LgC/dvTDdUOt4iMQnDdSep6N1mnvVLIiG+qM5tRnNHravNzBJnUlYwRQoA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-android-arm64": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.16.4.tgz",
+ "integrity": "sha512-5ODwd1F5mdkm6JIg1CNny9yxIrCzrkKpxmqas7Alw23vE0Ot8D4ykqNBW5Z/nIZkXVEo5VDmnm0sMBBIANcpeQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-darwin-arm64": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.16.4.tgz",
+ "integrity": "sha512-egwvDK9DMU4Q8F4BG74/n4E22pQ0lT5ukOVB6VXkTj0iG2fnyoStHoFaBnmDseLNRA4r61Mxxz8k940CIaJMDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-darwin-x64": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.16.4.tgz",
+ "integrity": "sha512-HMkODYrAG4HaFNCpaYzSQFkxeiz2wzl+smXwxeORIQVEo1WAgUrWbvYT/0RNJg/A8z2aGMGK5KWTUr2nX5GiMw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-freebsd-x64": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.16.4.tgz",
+ "integrity": "sha512-mkcKhIdSlUqnndD928WAVVFMEr1D5EwHOBGHadypW0PkM0h4pn89ZacQvU7Qs/Z2qquzvbyw8m4Mq3jOYI+4Dw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.16.4.tgz",
+ "integrity": "sha512-ZJvzbmXI/cILQVcJL9S2Fp7GLAIY4Yr6mpGb+k6LKLUSEq85yhG+rJ9eWCqgULVIf2BFps/NlmPTa7B7oj8jhQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.16.4.tgz",
+ "integrity": "sha512-iZUB0W52uB10gBUDAi79eTnzqp1ralikCAjfq7CdokItwZUVJXclNYANnzXmtc0Xr0ox+YsDsG2jGcj875SatA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-arm64-gnu": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.16.4.tgz",
+ "integrity": "sha512-qNQk0H6q1CnwS9cnvyjk9a+JN8BTbxK7K15Bb5hYfJcKTG1hfloQf6egndKauYOO0wu9ldCMPBrEP1FNIQEhaA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-arm64-musl": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.16.4.tgz",
+ "integrity": "sha512-wEXSaEaYxGGoVSbw0i2etjDDWcqErKr8xSkTdwATP798efsZmodUAcLYJhN0Nd4W35Oq6qAvFGHpKwFrrhpTrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.16.4.tgz",
+ "integrity": "sha512-CUFOlpb07DVOFLoYiaTfbSBRPIhNgwc/MtlYeg3p6GJJw+kEm/vzc9lohPSjzF2MLPB5hzsJdk+L/GjrTT3UPw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.16.4.tgz",
+ "integrity": "sha512-d8It4AH8cN9ReK1hW6ZO4x3rMT0hB2LYH0RNidGogV9xtnjLRU+Y3MrCeClLyOSGCibmweJJAjnwB7AQ31GEhg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-riscv64-musl": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.16.4.tgz",
+ "integrity": "sha512-d09dOww9iKyEHSxuOQ/Iu2aYswl0j7ExBcyy14D6lJ5ijQSP9FXcJYJsJ3yvzboO/PDEFjvRuF41f8O1skiPVg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-s390x-gnu": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.16.4.tgz",
+ "integrity": "sha512-lhjyGmUzTWHduZF3MkdUSEPMRIdExnhsqv8u1upX3A15epVn6YVwv4msFQPJl1x1wszkACPeDHGOtzHsITXGdw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-x64-gnu": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.16.4.tgz",
+ "integrity": "sha512-ZtqqiI5rzlrYBm/IMMDIg3zvvVj4WO/90Dg/zX+iA8lWaLN7K5nroXb17MQ4WhI5RqlEAgrnYDXW+hok1D9Kaw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-linux-x64-musl": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.16.4.tgz",
+ "integrity": "sha512-LM424h7aaKcMlqHnQWgTzO+GRNLyjcNnMpqm8SygEtFRVW693XS+XGXYvjORlmJtsyjo84ej1FMb3U2HE5eyjg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-openharmony-arm64": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.16.4.tgz",
+ "integrity": "sha512-8w8U6A5DDWTBv3OUxSD9fNk37liZuEC5jnAc9wQRv9DeYKAXvuUtBfT09aIZ58swaci0q1WS48/CoMVEO6jdCA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-wasm32-wasi": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.16.4.tgz",
+ "integrity": "sha512-hnjb0mDVQOon6NdfNJ1EmNquonJUjoYkp7UyasjxVa4iiMcApziHP4czzzme6WZbp+vzakhVv2Yi5ACTon3Zlw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
+ "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@oxc-resolver/binding-win32-arm64-msvc": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.16.4.tgz",
+ "integrity": "sha512-+i0XtNfSP7cfnh1T8FMrMm4HxTeh0jxKP/VQCLWbjdUxaAQ4damho4gN9lF5dl0tZahtdszXLUboBFNloSJNOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-win32-ia32-msvc": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.16.4.tgz",
+ "integrity": "sha512-ePW1islJrv3lPnef/iWwrjrSpRH8kLlftdKf2auQNWvYLx6F0xvcnv9d+r/upnVuttoQY9amLnWJf+JnCRksTw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@oxc-resolver/binding-win32-x64-msvc": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.16.4.tgz",
+ "integrity": "sha512-qnjQhjHI4TDL3hkidZyEmQRK43w2NHl6TP5Rnt/0XxYuLdEgx/1yzShhYidyqWzdnhGhSPTM/WVP2mK66XLegA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@paseo/app": {
"resolved": "packages/app",
"link": true
@@ -14834,6 +15135,16 @@
"asap": "~2.0.3"
}
},
+ "node_modules/fd-package-json": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz",
+ "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "walk-up-path": "^4.0.0"
+ }
+ },
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -15123,6 +15434,22 @@
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
"license": "MIT"
},
+ "node_modules/formatly": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz",
+ "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fd-package-json": "^2.0.0"
+ },
+ "bin": {
+ "formatly": "bin/index.mjs"
+ },
+ "engines": {
+ "node": ">=18.3.0"
+ }
+ },
"node_modules/formdata-node": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
@@ -17208,6 +17535,124 @@
"node": ">=6"
}
},
+ "node_modules/knip": {
+ "version": "5.82.1",
+ "resolved": "https://registry.npmjs.org/knip/-/knip-5.82.1.tgz",
+ "integrity": "sha512-1nQk+5AcnkqL40kGQXfouzAEXkTR+eSrgo/8m1d0BMei4eAzFwghoXC4gOKbACgBiCof7hE8wkBVDsEvznf85w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/webpro"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/knip"
+ }
+ ],
+ "license": "ISC",
+ "dependencies": {
+ "@nodelib/fs.walk": "^1.2.3",
+ "fast-glob": "^3.3.3",
+ "formatly": "^0.3.0",
+ "jiti": "^2.6.0",
+ "js-yaml": "^4.1.1",
+ "minimist": "^1.2.8",
+ "oxc-resolver": "^11.15.0",
+ "picocolors": "^1.1.1",
+ "picomatch": "^4.0.1",
+ "smol-toml": "^1.5.2",
+ "strip-json-comments": "5.0.3",
+ "zod": "^4.1.11"
+ },
+ "bin": {
+ "knip": "bin/knip.js",
+ "knip-bun": "bin/knip-bun.js"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18",
+ "typescript": ">=5.0.4 <7"
+ }
+ },
+ "node_modules/knip/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/knip/node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/knip/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/knip/node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/knip/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/knip/node_modules/strip-json-comments": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
+ "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lan-network": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz",
@@ -19226,6 +19671,38 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/oxc-resolver": {
+ "version": "11.16.4",
+ "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.16.4.tgz",
+ "integrity": "sha512-nvJr3orFz1wNaBA4neRw7CAn0SsjgVaEw1UHpgO/lzVW12w+nsFnvU/S6vVX3kYyFaZdxZheTExi/fa8R8PrZA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ },
+ "optionalDependencies": {
+ "@oxc-resolver/binding-android-arm-eabi": "11.16.4",
+ "@oxc-resolver/binding-android-arm64": "11.16.4",
+ "@oxc-resolver/binding-darwin-arm64": "11.16.4",
+ "@oxc-resolver/binding-darwin-x64": "11.16.4",
+ "@oxc-resolver/binding-freebsd-x64": "11.16.4",
+ "@oxc-resolver/binding-linux-arm-gnueabihf": "11.16.4",
+ "@oxc-resolver/binding-linux-arm-musleabihf": "11.16.4",
+ "@oxc-resolver/binding-linux-arm64-gnu": "11.16.4",
+ "@oxc-resolver/binding-linux-arm64-musl": "11.16.4",
+ "@oxc-resolver/binding-linux-ppc64-gnu": "11.16.4",
+ "@oxc-resolver/binding-linux-riscv64-gnu": "11.16.4",
+ "@oxc-resolver/binding-linux-riscv64-musl": "11.16.4",
+ "@oxc-resolver/binding-linux-s390x-gnu": "11.16.4",
+ "@oxc-resolver/binding-linux-x64-gnu": "11.16.4",
+ "@oxc-resolver/binding-linux-x64-musl": "11.16.4",
+ "@oxc-resolver/binding-openharmony-arm64": "11.16.4",
+ "@oxc-resolver/binding-wasm32-wasi": "11.16.4",
+ "@oxc-resolver/binding-win32-arm64-msvc": "11.16.4",
+ "@oxc-resolver/binding-win32-ia32-msvc": "11.16.4",
+ "@oxc-resolver/binding-win32-x64-msvc": "11.16.4"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -22504,6 +22981,19 @@
"node": ">=8.0.0"
}
},
+ "node_modules/smol-toml": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz",
+ "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
"node_modules/sonic-boom": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
@@ -24520,6 +25010,16 @@
"integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==",
"license": "MIT"
},
+ "node_modules/walk-up-path": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz",
+ "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
"node_modules/walker": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
diff --git a/package.json b/package.json
index 0f9cf3601..91765d94a 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"devDependencies": {
"concurrently": "^9.2.1",
"get-port-cli": "^3.0.0",
+ "knip": "^5.82.1",
"patch-package": "^8.0.1",
"typescript": "^5.9.3"
},
diff --git a/packages/app/app.config.js b/packages/app/app.config.js
index 4f7492a8a..734d11027 100644
--- a/packages/app/app.config.js
+++ b/packages/app/app.config.js
@@ -1,8 +1,6 @@
// App name and package ID are controlled by Gradle product flavors (dev/production)
// See plugins/with-android-product-flavors.js for flavor configuration
-const withAndroidProductFlavors = require("./plugins/with-android-product-flavors");
-
export default {
expo: {
name: "Paseo",
@@ -91,7 +89,7 @@ export default {
},
},
],
- withAndroidProductFlavors,
+ "./plugins/with-android-product-flavors",
],
experiments: {
typedRoutes: true,
diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx
index c32aa04fb..7aab73946 100644
--- a/packages/app/src/app/_layout.tsx
+++ b/packages/app/src/app/_layout.tsx
@@ -347,7 +347,6 @@ export default function RootLayout() {
>
-
diff --git a/packages/app/src/app/orchestrator.tsx b/packages/app/src/app/orchestrator.tsx
deleted file mode 100644
index 505e78104..000000000
--- a/packages/app/src/app/orchestrator.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { View } from "react-native";
-import { useMemo, useRef, useState } from "react";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
-import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
-import ReanimatedAnimated, { useAnimatedStyle } from "react-native-reanimated";
-import { StyleSheet, useUnistyles } from "react-native-unistyles";
-import { BackHeader } from "@/components/headers/back-header";
-import { OrchestratorMessagesView } from "@/components/orchestrator-messages-view";
-import { useSessionDirectory } from "@/hooks/use-session-directory";
-import type { ScrollView } from "react-native";
-import type { Artifact } from "@/components/artifact-drawer";
-import type { MessageEntry } from "@/contexts/session-context";
-
-export default function OrchestratorScreen() {
- const { theme } = useUnistyles();
- const insets = useSafeAreaInsets();
- const sessionDirectory = useSessionDirectory();
- const scrollViewRef = useRef(null);
- const [currentArtifact, setCurrentArtifact] = useState(null);
- const aggregatedMessages = useMemo(() => {
- const merged: MessageEntry[] = [];
- sessionDirectory.forEach((session) => {
- if (!session) {
- return;
- }
- merged.push(...session.messages);
- });
- merged.sort((left, right) => left.timestamp - right.timestamp);
- return merged;
- }, [sessionDirectory]);
-
- const streamingAssistantMessage = useMemo(() => {
- for (const session of sessionDirectory.values()) {
- if (session?.currentAssistantMessage) {
- return session.currentAssistantMessage;
- }
- }
- return "";
- }, [sessionDirectory]);
-
- // Keyboard animation
- const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
- const animatedKeyboardStyle = useAnimatedStyle(() => {
- "worklet";
- const absoluteHeight = Math.abs(keyboardHeight.value);
- const padding = Math.max(0, absoluteHeight - insets.bottom);
- return {
- paddingBottom: padding,
- };
- });
-
- function handleArtifactClick(artifactId: string) {
- // TODO: Implement artifact drawer
- console.log("[Orchestrator] Artifact clicked:", artifactId);
- }
-
- return (
-
- {/* Header */}
-
-
- {/* Content Area with Keyboard Animation */}
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create((theme) => ({
- container: {
- flex: 1,
- backgroundColor: theme.colors.surface0,
- },
- content: {
- flex: 1,
- },
-}));
diff --git a/packages/app/src/components/agent-stream-view.tsx b/packages/app/src/components/agent-stream-view.tsx
index c7136e166..f2c4565ec 100644
--- a/packages/app/src/components/agent-stream-view.tsx
+++ b/packages/app/src/components/agent-stream-view.tsx
@@ -34,7 +34,6 @@ import {
UserMessage,
ActivityLog,
ToolCall,
- AgentThoughtMessage,
TodoListCard,
TurnCopyButton,
MessageOuterSpacingProvider,
@@ -49,7 +48,6 @@ import type { DaemonClientV2 } from "@server/client/daemon-client-v2";
import { parseToolCallDisplay } from "@/utils/tool-call-parsers";
import { ToolCallDetailsContent } from "./tool-call-details";
import { ToolCallSheetProvider } from "./tool-call-sheet";
-import { ThinkingSheetProvider } from "./thinking-sheet";
import { createMarkdownStyles } from "@/styles/markdown-styles";
import { MAX_CONTENT_WIDTH } from "@/constants/layout";
import { isPerfLoggingEnabled, measurePayload, perfLog } from "@/utils/perf";
@@ -274,9 +272,10 @@ export function AgentStreamView({
const isLastInSequence =
nextItem?.kind !== "tool_call" && nextItem?.kind !== "thought";
return (
-
);
@@ -533,8 +532,7 @@ export function AgentStreamView({
return (
-
-
+
)}
-
);
}
diff --git a/packages/app/src/components/agent-thought-content.tsx b/packages/app/src/components/agent-thought-content.tsx
deleted file mode 100644
index 1515b548d..000000000
--- a/packages/app/src/components/agent-thought-content.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { memo, useMemo } from "react";
-import { Text, View } from "react-native";
-import Markdown from "react-native-markdown-display";
-import { StyleSheet, useUnistyles } from "react-native-unistyles";
-import { createCompactMarkdownStyles } from "@/styles/markdown-styles";
-
-export const AgentThoughtContent = memo(function AgentThoughtContent({
- message,
-}: {
- message: string;
-}) {
- const { theme } = useUnistyles();
- const markdownContent = useMemo(() => message?.trim() ?? "", [message]);
- const markdownStyles = useMemo(
- () => createCompactMarkdownStyles(theme),
- [theme]
- );
-
- const markdownRules = useMemo(() => {
- return {
- text: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
-
- {node.content}
-
- ),
- textgroup: (node: any, children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
-
- {children}
-
- ),
- code_block: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
-
- {node.content}
-
- ),
- fence: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
-
- {node.content}
-
- ),
- code_inline: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
-
- {node.content}
-
- ),
- bullet_list: (node: any, children: any[], _parent: any, styles: any) => (
-
- {children}
-
- ),
- ordered_list: (node: any, children: any[], _parent: any, styles: any) => (
-
- {children}
-
- ),
- list_item: (node: any, children: any[], parent: any, styles: any) => {
- const isOrdered = parent?.type === "ordered_list";
- const index = parent?.children?.indexOf(node) ?? 0;
- const bullet = isOrdered ? `${index + 1}.` : "•";
- const iconStyle = isOrdered ? styles.ordered_list_icon : styles.bullet_list_icon;
- const contentStyle = isOrdered
- ? styles.ordered_list_content
- : styles.bullet_list_content;
-
- return (
-
- {bullet}
-
- {children}
-
-
- );
- },
- };
- }, []);
-
- if (!markdownContent) {
- return No captured thinking;
- }
-
- return (
-
- {markdownContent}
-
- );
-});
-
-const styles = StyleSheet.create((theme) => ({
- emptyText: {
- color: theme.colors.foregroundMuted,
- fontSize: theme.fontSize.xs,
- fontStyle: "italic" as const,
- },
-}));
-
diff --git a/packages/app/src/components/conversation-selector.tsx b/packages/app/src/components/conversation-selector.tsx
deleted file mode 100644
index f52e850f3..000000000
--- a/packages/app/src/components/conversation-selector.tsx
+++ /dev/null
@@ -1,437 +0,0 @@
-import { useState, useEffect, useCallback } from "react";
-import {
- Modal,
- View,
- Text,
- Pressable,
- ScrollView,
- Alert,
- KeyboardAvoidingView,
- Platform,
-} from "react-native";
-import { SafeAreaView } from "react-native-safe-area-context";
-import { MessageSquare, X, Plus, Trash2 } from "lucide-react-native";
-import AsyncStorage from "@react-native-async-storage/async-storage";
-import { StyleSheet } from "react-native-unistyles";
-import type { DaemonClientV2 } from "@server/client/daemon-client-v2";
-
-const STORAGE_KEY = "@paseo:voice-conversation-id";
-
-interface Conversation {
- id: string;
- lastUpdated: string;
- messageCount: number;
-}
-
-interface ConversationSelectorProps {
- currentVoiceConversationId: string | null;
- onSelectVoiceConversation: (voiceConversationId: string | null) => void;
- client: DaemonClientV2 | null;
-}
-
-export function ConversationSelector({
- currentVoiceConversationId,
- onSelectVoiceConversation,
- client,
-}: ConversationSelectorProps) {
- const [conversations, setConversations] = useState([]);
- const [isOpen, setIsOpen] = useState(false);
- const [isLoading, setIsLoading] = useState(false);
-
- const fetchConversations = useCallback(async () => {
- setIsLoading(true);
-
- if (!client) {
- setIsLoading(false);
- Alert.alert("Error", "Daemon unavailable.");
- return;
- }
-
- try {
- console.log("[ConversationSelector] Fetching conversations");
- const response = await client.listVoiceConversations();
- setConversations(response.conversations);
- } catch (error) {
- console.error(
- "[ConversationSelector] Failed to list conversations:",
- error
- );
- Alert.alert("Error", "Failed to load conversations.");
- } finally {
- setIsLoading(false);
- }
- }, [client]);
-
- // Fetch conversations when modal opens
- useEffect(() => {
- if (isOpen) {
- void fetchConversations();
- }
- }, [fetchConversations, isOpen]);
-
- const handleDeleteConversation = useCallback(
- (id: string) => {
- Alert.alert(
- "Delete Conversation",
- "Are you sure you want to delete this conversation?",
- [
- { text: "Cancel", style: "cancel" },
- {
- text: "Delete",
- style: "destructive",
- onPress: () => {
- if (!client) {
- Alert.alert("Error", "Daemon unavailable.");
- return;
- }
-
- console.log("[ConversationSelector] Deleting conversation:", id);
- void (async () => {
- try {
- const response = await client.deleteVoiceConversation(id);
- if (response.success) {
- await fetchConversations();
- if (response.voiceConversationId === currentVoiceConversationId) {
- await handleNewConversation();
- }
- } else {
- Alert.alert(
- "Error",
- `Failed to delete conversation: ${response.error}`
- );
- }
- } catch (error) {
- console.error(
- "[ConversationSelector] Failed to delete conversation:",
- error
- );
- Alert.alert("Error", "Failed to delete conversation.");
- }
- })();
- },
- },
- ]
- );
- },
- [currentVoiceConversationId, fetchConversations, handleNewConversation, client]
- );
-
- const handleClearAll = useCallback(() => {
- Alert.alert(
- "Clear All Conversations",
- "Are you sure you want to delete all conversations?",
- [
- { text: "Cancel", style: "cancel" },
- {
- text: "Clear All",
- style: "destructive",
- onPress: () => {
- if (!client) {
- Alert.alert("Error", "Daemon unavailable.");
- return;
- }
-
- console.log("[ConversationSelector] Clearing all conversations");
- void (async () => {
- setIsLoading(true);
- try {
- for (const conv of conversations) {
- await client.deleteVoiceConversation(conv.id);
- }
- setConversations([]);
- await handleNewConversation();
- setIsOpen(false);
- } catch (error) {
- console.error(
- "[ConversationSelector] Failed to clear conversations:",
- error
- );
- Alert.alert("Error", "Failed to clear conversations.");
- } finally {
- setIsLoading(false);
- }
- })();
- },
- },
- ]
- );
- }, [conversations, handleNewConversation, client]);
-
- async function handleSelectConversation(id: string) {
- try {
- // Save to AsyncStorage for persistence
- await AsyncStorage.setItem(STORAGE_KEY, id);
- onSelectVoiceConversation(id);
- setIsOpen(false);
- } catch (error) {
- console.error(
- "[ConversationSelector] Failed to save conversation ID:",
- error
- );
- }
- }
-
- async function handleNewConversation() {
- try {
- // Clear saved conversation ID
- await AsyncStorage.removeItem(STORAGE_KEY);
- onSelectVoiceConversation(null);
- setIsOpen(false);
- } catch (error) {
- console.error(
- "[ConversationSelector] Failed to clear conversation ID:",
- error
- );
- }
- }
-
- function formatDate(dateString: string): string {
- const date = new Date(dateString);
- const now = new Date();
- const diffMs = now.getTime() - date.getTime();
- const diffMins = Math.floor(diffMs / 60000);
- const diffHours = Math.floor(diffMs / 3600000);
- const diffDays = Math.floor(diffMs / 86400000);
-
- if (diffMins < 1) return "Just now";
- if (diffMins < 60) return `${diffMins}m ago`;
- if (diffHours < 24) return `${diffHours}h ago`;
- if (diffDays < 7) return `${diffDays}d ago`;
- return date.toLocaleDateString();
- }
-
- return (
-
- setIsOpen(true)} style={styles.triggerButton}>
-
-
-
- setIsOpen(false)}
- >
-
- setIsOpen(false)}
- />
-
-
- {/* Header */}
-
-
- Conversations{" "}
- {conversations.length > 0 && `(${conversations.length})`}
-
- setIsOpen(false)}>
-
-
-
-
- {/* New Conversation Button */}
-
-
-
- New Conversation
-
-
-
- {/* Conversations List */}
-
- {isLoading && (
-
- Loading...
-
- )}
-
- {!isLoading && conversations.length === 0 && (
-
- No saved conversations
-
- )}
-
- {!isLoading &&
- conversations.map((conversation) => (
-
-
- handleSelectConversation(conversation.id)
- }
- >
-
- {conversation.messageCount} messages
-
-
- {formatDate(conversation.lastUpdated)}
-
-
-
- handleDeleteConversation(conversation.id)
- }
- style={styles.deleteButton}
- hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
- >
-
-
-
- ))}
-
- {!isLoading && conversations.length > 0 && (
-
-
-
- Clear All
-
-
- )}
-
-
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create((theme) => ({
- triggerButton: {
- backgroundColor: theme.colors.surface2,
- padding: theme.spacing[3],
- borderRadius: theme.borderRadius.lg,
- },
- modalOverlay: {
- flex: 1,
- backgroundColor: "rgba(0,0,0,0.5)",
- },
- modalBackdrop: {
- flex: 1,
- },
- modalContent: {
- backgroundColor: theme.colors.surface2,
- borderTopLeftRadius: theme.spacing[6],
- borderTopRightRadius: theme.spacing[6],
- height: "80%",
- },
- modalInner: {
- flex: 1,
- },
- header: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "space-between",
- padding: theme.spacing[6],
- borderBottomWidth: theme.borderWidth[1],
- borderBottomColor: theme.colors.border,
- },
- headerTitle: {
- color: theme.colors.foreground,
- fontSize: theme.fontSize.lg,
- fontWeight: theme.fontWeight.semibold,
- },
- newButtonContainer: {
- padding: theme.spacing[4],
- borderBottomWidth: theme.borderWidth[1],
- borderBottomColor: theme.colors.border,
- },
- newButton: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "center",
- gap: theme.spacing[2],
- backgroundColor: theme.colors.surface2,
- paddingVertical: theme.spacing[3],
- borderRadius: theme.borderRadius.lg,
- },
- newButtonText: {
- color: theme.colors.foreground,
- fontWeight: theme.fontWeight.semibold,
- },
- scrollView: {
- flex: 1,
- },
- scrollContent: {
- paddingBottom: theme.spacing[4] * 5,
- },
- loadingContainer: {
- padding: theme.spacing[8],
- alignItems: "center",
- },
- loadingText: {
- color: theme.colors.foregroundMuted,
- },
- emptyContainer: {
- padding: theme.spacing[8],
- alignItems: "center",
- },
- emptyText: {
- color: theme.colors.foregroundMuted,
- },
- conversationItem: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "space-between",
- padding: theme.spacing[4],
- borderBottomWidth: theme.borderWidth[1],
- borderBottomColor: theme.colors.border,
- backgroundColor: "transparent",
- },
- conversationItemActive: {
- backgroundColor: theme.colors.surface2,
- },
- conversationContent: {
- flex: 1,
- },
- conversationTitle: {
- color: theme.colors.foreground,
- fontWeight: theme.fontWeight.semibold,
- fontSize: theme.fontSize.base,
- },
- conversationDate: {
- color: theme.colors.foregroundMuted,
- fontSize: theme.fontSize.sm,
- marginTop: theme.spacing[1],
- },
- deleteButton: {
- padding: theme.spacing[2],
- },
- clearAllContainer: {
- padding: theme.spacing[4],
- },
- clearAllButton: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "center",
- gap: theme.spacing[2],
- backgroundColor: "rgba(127, 29, 29, 0.2)",
- paddingVertical: theme.spacing[3],
- borderRadius: theme.borderRadius.lg,
- borderWidth: theme.borderWidth[1],
- borderColor: theme.colors.palette.red[900],
- },
- clearAllText: {
- color: theme.colors.destructive,
- fontWeight: theme.fontWeight.semibold,
- },
-}));
diff --git a/packages/app/src/components/headers/home-header.tsx b/packages/app/src/components/headers/home-header.tsx
deleted file mode 100644
index 4af7603db..000000000
--- a/packages/app/src/components/headers/home-header.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { useCallback } from "react";
-import { Pressable } from "react-native";
-import { router } from "expo-router";
-import { StyleSheet, useUnistyles } from "react-native-unistyles";
-import { Settings, MessageSquare, Download, Plus } from "lucide-react-native";
-import { ScreenHeader } from "./screen-header";
-
-interface HomeHeaderProps {
- onCreateAgent: () => void;
- onImportAgent: () => void;
-}
-
-export function HomeHeader({ onCreateAgent, onImportAgent }: HomeHeaderProps) {
- const { theme } = useUnistyles();
- const handleCreatePress = useCallback(() => {
- onCreateAgent();
- }, [onCreateAgent]);
- const handleImportPress = useCallback(() => {
- onImportAgent();
- }, [onImportAgent]);
-
- return (
- router.push("/settings")}
- style={styles.iconButton}
- >
-
-
- }
- right={
- <>
- router.push("/orchestrator")}
- style={styles.iconButton}
- >
-
-
-
-
-
-
-
-
- >
- }
- />
- );
-}
-
-const styles = StyleSheet.create((theme) => ({
- iconButton: {
- padding: {
- xs: theme.spacing[3],
- md: theme.spacing[2],
- },
- borderRadius: theme.borderRadius.lg,
- },
-}));
diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx
index 537577f25..e1b330b56 100644
--- a/packages/app/src/components/message.tsx
+++ b/packages/app/src/components/message.tsx
@@ -51,13 +51,11 @@ import {
} from "@/styles/markdown-styles";
import { Colors, Fonts } from "@/constants/theme";
import * as Clipboard from "expo-clipboard";
-import type { TodoEntry, ThoughtStatus } from "@/types/stream";
+import type { TodoEntry } from "@/types/stream";
import { extractPrincipalParam } from "@/utils/tool-call-parsers";
import { getNowMs, isPerfLoggingEnabled, perfLog } from "@/utils/perf";
import { resolveToolCallPreview } from "./tool-call-preview";
import { useToolCallSheet } from "./tool-call-sheet";
-import { useThinkingSheet } from "./thinking-sheet";
-import { AgentThoughtContent } from "./agent-thought-content";
import {
ToolCallDetailsContent,
useToolCallDetails,
@@ -1051,13 +1049,6 @@ export const TodoListCard = memo(function TodoListCard({
);
});
-interface AgentThoughtMessageProps {
- message: string;
- status?: ThoughtStatus;
- isLastInSequence?: boolean;
- disableOuterSpacing?: boolean;
-}
-
interface ExpandableBadgeProps {
label: string;
secondaryLabel?: string;
@@ -1195,46 +1186,6 @@ const ExpandableBadge = memo(function ExpandableBadge({
);
});
-export const AgentThoughtMessage = memo(function AgentThoughtMessage({
- message,
- status = "ready",
- isLastInSequence = false,
- disableOuterSpacing,
-}: AgentThoughtMessageProps) {
- const { openThinking } = useThinkingSheet();
- const [isExpanded, setIsExpanded] = useState(false);
-
- const isMobile =
- UnistylesRuntime.breakpoint === "xs" ||
- UnistylesRuntime.breakpoint === "sm";
-
- const handleToggle = useCallback(() => {
- if (isMobile) {
- openThinking({ message, status });
- return;
- }
- setIsExpanded((prev) => !prev);
- }, [isMobile, openThinking, message, status]);
-
- const renderDetails = useCallback(() => {
- return ;
- }, [message]);
-
- return (
- null : renderDetails}
- isLoading={status !== "ready"}
- isLastInSequence={isLastInSequence}
- style={isLastInSequence ? undefined : { marginBottom: theme.spacing[1] }}
- disableOuterSpacing={disableOuterSpacing}
- />
- );
-});
-
interface ToolCallProps {
toolName: string;
args: any;
@@ -1252,6 +1203,7 @@ const toolKindIcons: Record = {
read: Eye,
execute: SquareTerminal,
search: Search,
+ thinking: Brain,
};
const TOOL_CALL_LOG_TAG = "[ToolCall]";
const TOOL_CALL_COMMIT_THRESHOLD_MS = 16;
@@ -1259,6 +1211,7 @@ const TOOL_CALL_COMMIT_THRESHOLD_MS = 16;
// Derive tool kind from tool name for icon selection
function getToolKindFromName(toolName: string): string {
const lower = toolName.toLowerCase();
+ if (lower === "thinking") return "thinking";
if (lower === "read" || lower === "read_file" || lower.startsWith("read"))
return "read";
if (lower === "edit" || lower === "write" || lower === "apply_patch")
diff --git a/packages/app/src/components/orchestrator-messages-view.tsx b/packages/app/src/components/orchestrator-messages-view.tsx
deleted file mode 100644
index c3598f389..000000000
--- a/packages/app/src/components/orchestrator-messages-view.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { ScrollView } from "react-native";
-import { StyleSheet } from "react-native-unistyles";
-import { forwardRef } from "react";
-import {
- UserMessage,
- AssistantMessage,
- ActivityLog,
- ToolCall,
-} from "@/components/message";
-import { ToolCallSheetProvider } from "@/components/tool-call-sheet";
-import { ThinkingSheetProvider } from "@/components/thinking-sheet";
-import type { MessageEntry } from "@/contexts/session-context";
-
-interface OrchestratorMessagesViewProps {
- messages: MessageEntry[];
- currentAssistantMessage: string;
- onArtifactClick: (artifactId: string) => void;
-}
-
-export const OrchestratorMessagesView = forwardRef(
- function OrchestratorMessagesView({ messages, currentAssistantMessage, onArtifactClick }, ref) {
-
- return (
-
-
-
- {messages.map((msg) => {
- if (msg.type === "user") {
- return (
-
- );
- }
-
- if (msg.type === "assistant") {
- return (
-
- );
- }
-
- if (msg.type === "activity") {
- return (
-
- );
- }
-
- if (msg.type === "artifact") {
- return (
-
- );
- }
-
- if (msg.type === "tool_call") {
- return (
-
- );
- }
-
- return null;
- })}
-
- {/* Streaming assistant message */}
- {currentAssistantMessage && (
-
- )}
-
-
-
- );
- }
-);
-
-const styles = StyleSheet.create((theme) => ({
- scrollView: {
- flex: 1,
- },
- scrollContent: {
- paddingHorizontal: theme.spacing[4],
- paddingTop: theme.spacing[4],
- paddingBottom: theme.spacing[6],
- },
-}));
diff --git a/packages/app/src/components/thinking-sheet.tsx b/packages/app/src/components/thinking-sheet.tsx
deleted file mode 100644
index d61646464..000000000
--- a/packages/app/src/components/thinking-sheet.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-import React, {
- createContext,
- useCallback,
- useContext,
- useMemo,
- useRef,
- ReactNode,
-} from "react";
-import { View, Text, Pressable } from "react-native";
-import { StyleSheet } from "react-native-unistyles";
-import {
- BottomSheetModal,
- BottomSheetScrollView,
- BottomSheetBackdrop,
-} from "@gorhom/bottom-sheet";
-import { Brain, X } from "lucide-react-native";
-import type { ThoughtStatus } from "@/types/stream";
-import { AgentThoughtContent } from "./agent-thought-content";
-
-export interface ThinkingSheetData {
- message: string;
- status?: ThoughtStatus;
-}
-
-interface ThinkingSheetContextValue {
- openThinking: (data: ThinkingSheetData) => void;
- closeThinking: () => void;
-}
-
-const ThinkingSheetContext = createContext(null);
-
-export function useThinkingSheet(): ThinkingSheetContextValue {
- const context = useContext(ThinkingSheetContext);
- if (!context) {
- throw new Error("useThinkingSheet must be used within a ThinkingSheetProvider");
- }
- return context;
-}
-
-export function ThinkingSheetProvider({ children }: { children: ReactNode }) {
- const bottomSheetRef = useRef(null);
- const [sheetData, setSheetData] = React.useState(null);
-
- const snapPoints = useMemo(() => ["60%", "95%"], []);
-
- const openThinking = useCallback((data: ThinkingSheetData) => {
- setSheetData(data);
- bottomSheetRef.current?.present();
- }, []);
-
- const closeThinking = useCallback(() => {
- bottomSheetRef.current?.dismiss();
- }, []);
-
- const handleSheetChange = useCallback((index: number) => {
- if (index === -1) {
- setSheetData(null);
- }
- }, []);
-
- const renderBackdrop = useCallback(
- (props: React.ComponentProps) => (
-
- ),
- []
- );
-
- const contextValue = useMemo(
- () => ({ openThinking, closeThinking }),
- [openThinking, closeThinking]
- );
-
- return (
-
- {children}
-
- {sheetData && (
-
- )}
-
-
- );
-}
-
-function ThinkingSheetContent({
- data,
- onClose,
-}: {
- data: ThinkingSheetData;
- onClose: () => void;
-}) {
- const { message } = data;
- return (
-
-
-
-
-
- Thinking
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create((theme) => ({
- sheetBackground: {
- backgroundColor: theme.colors.surface2,
- },
- handleIndicator: {
- backgroundColor: theme.colors.palette.zinc[600],
- },
- container: {
- flex: 1,
- backgroundColor: theme.colors.surface2,
- },
- header: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "space-between",
- paddingHorizontal: theme.spacing[4],
- paddingVertical: theme.spacing[3],
- borderBottomWidth: theme.borderWidth[1],
- borderBottomColor: theme.colors.border,
- },
- headerLeft: {
- flexDirection: "row",
- alignItems: "center",
- gap: theme.spacing[2],
- flex: 1,
- },
- headerIcon: {
- color: theme.colors.foreground,
- },
- headerTitle: {
- fontSize: theme.fontSize.lg,
- fontWeight: theme.fontWeight.semibold,
- color: theme.colors.foreground,
- flex: 1,
- },
- closeButton: {
- padding: theme.spacing[2],
- },
- closeIcon: {
- color: theme.colors.foregroundMuted,
- },
- content: {
- flex: 1,
- backgroundColor: theme.colors.surface2,
- },
- contentContainer: {
- paddingHorizontal: theme.spacing[4],
- paddingTop: theme.spacing[4],
- paddingBottom: theme.spacing[8],
- },
-}));
diff --git a/packages/app/src/components/tool-call-details.tsx b/packages/app/src/components/tool-call-details.tsx
index 87022ef32..16a46c8a2 100644
--- a/packages/app/src/components/tool-call-details.tsx
+++ b/packages/app/src/components/tool-call-details.tsx
@@ -87,6 +87,14 @@ function summarizeToolCallDisplay(display: ToolCallDisplay): ToolCallDisplaySumm
},
};
}
+ case "thinking": {
+ const contentLength = display.content.length;
+ return {
+ displayType: display.type,
+ totalChars: contentLength,
+ detail: { contentLength },
+ };
+ }
default:
return assertNever(display);
}
@@ -227,6 +235,20 @@ export function ToolCallDetailsContent({
) : null}
);
+ } else if (display.type === "thinking") {
+ // Thinking: display the content as plain text
+ sections.push(
+
+
+ {display.content}
+
+
+ );
} else {
// Generic tool: show input/output as key-value pairs
if (display.input.length > 0) {
diff --git a/packages/app/src/utils/tool-call-parsers.ts b/packages/app/src/utils/tool-call-parsers.ts
index 47c7369ba..205596a7f 100644
--- a/packages/app/src/utils/tool-call-parsers.ts
+++ b/packages/app/src/utils/tool-call-parsers.ts
@@ -1168,6 +1168,7 @@ const TOOL_NAME_MAP: Record = {
set_branch: "Set branch",
"mcp__agent-control__set_title": "Set title",
"mcp__agent-control__set_branch": "Set branch",
+ thinking: "Thinking",
};
const ToolCallDisplaySchema = z
@@ -1179,6 +1180,16 @@ const ToolCallDisplaySchema = z
.transform((data) => {
const normalizedToolName = TOOL_NAME_MAP[data.toolName] ?? data.toolName;
+ // Handle thinking - input is the thinking text content
+ if (data.toolName === "thinking") {
+ const content = typeof data.input === "string" ? data.input : "";
+ return {
+ type: "thinking" as const,
+ content,
+ toolName: normalizedToolName,
+ };
+ }
+
// Try each schema in order
const shellParsed = ShellToolCallSchema.safeParse({ input: data.input, result: data.result });
if (shellParsed.success) {