Compare commits

..

1 Commits

Author SHA1 Message Date
James Long f1030db291 chore: typecheck only affected packages in pre-push hook 2026-07-03 13:40:14 +00:00
129 changed files with 674 additions and 31663 deletions
+3 -1
View File
@@ -17,4 +17,6 @@ if (process.versions.bun !== expectedBunVersion) {
console.warn(`Warning: Bun version ${process.versions.bun} differs from expected ${expectedBunVersion}`);
}
'
bun typecheck
# Only typecheck packages affected since the merge base with origin/dev,
# plus their dependents. Override the base with TURBO_SCM_BASE if needed.
TURBO_SCM_BASE="${TURBO_SCM_BASE:-origin/dev}" bun turbo typecheck --affected
+1 -1
View File
@@ -125,4 +125,4 @@ OpenCode 内置两种 Agent,可用 `Tab` 键快速切换:
---
**加入我们的社区** [飞书](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=52ao9352-5623-4fa0-b7dd-3407c392c1af&qr_code=true) | [X.com](https://x.com/opencode)
**加入我们的社区** [飞书](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=738j8655-cd59-4633-a30a-1124e0096789&qr_code=true) | [X.com](https://x.com/opencode)
+1 -1
View File
@@ -125,4 +125,4 @@ OpenCode 內建了兩種 Agent,您可以使用 `Tab` 鍵快速切換。
---
**加入我們的社群** [飞书](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=52ao9352-5623-4fa0-b7dd-3407c392c1af&qr_code=true) | [X.com](https://x.com/opencode)
**加入我們的社群** [飞书](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=738j8655-cd59-4633-a30a-1124e0096789&qr_code=true) | [X.com](https://x.com/opencode)
-3
View File
@@ -1,3 +0,0 @@
node_modules
.remotion
out/frame-*.png
-483
View File
@@ -1,483 +0,0 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "glm52-rise-video",
"dependencies": {
"@remotion/cli": "^4.0.384",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"remotion": "^4.0.384",
},
"devDependencies": {
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.3",
"typescript": "^5.8.2",
},
},
},
"packages": {
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
"@babel/parser": ["@babel/parser@7.24.1", "", { "bin": "./bin/babel-parser.js" }, "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg=="],
"@babel/types": ["@babel/types@7.24.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w=="],
"@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
"@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="],
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@mediabunny/aac-encoder": ["@mediabunny/aac-encoder@1.47.0", "", { "peerDependencies": { "mediabunny": "^1.0.0" } }, "sha512-JNzgdJoHMFFnv5imi1+dmjZMudsJ1zNCUCEkKjBh90cqGhAFg8xu4V2gsyxE2i6oq/YpWx23P+OvoANLSMcDzA=="],
"@mediabunny/flac-encoder": ["@mediabunny/flac-encoder@1.47.0", "", { "peerDependencies": { "mediabunny": "^1.0.0" } }, "sha512-VpKmJO0xlYcFCRD6JvJlMbNQ6d/6YMHdO1gFIqWlZABHjSSL6BquNNEgWSCv5vdF8ELDBwIYBVZEslakIB/7GA=="],
"@mediabunny/mp3-encoder": ["@mediabunny/mp3-encoder@1.47.0", "", { "peerDependencies": { "mediabunny": "^1.0.0" } }, "sha512-JyzZyGeGRm2HVUQaGJ/VZT9OG+kG00mA8SLP/f3CO7+qWAmBncKc16WYXWHbZEo8Jn/ZGA6De32S5R2bTQbDqA=="],
"@module-federation/error-codes": ["@module-federation/error-codes@0.22.0", "", {}, "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug=="],
"@module-federation/runtime": ["@module-federation/runtime@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/runtime-core": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA=="],
"@module-federation/runtime-core": ["@module-federation/runtime-core@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA=="],
"@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/webpack-bundler-runtime": "0.22.0" } }, "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA=="],
"@module-federation/sdk": ["@module-federation/sdk@0.22.0", "", {}, "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g=="],
"@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="],
"@remotion/bundler": ["@remotion/bundler@4.0.483", "", { "dependencies": { "@remotion/media-parser": "4.0.483", "@remotion/studio": "4.0.483", "@remotion/studio-shared": "4.0.483", "@remotion/timeline-utils": "4.0.483", "@rspack/core": "1.7.11", "@rspack/plugin-react-refresh": "1.6.1", "css-loader": "7.1.4", "esbuild": "0.28.1", "react-refresh": "0.18.0", "remotion": "4.0.483", "style-loader": "4.0.0", "webpack": "5.105.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-5fQRMYgr2sxZPBOThnIVuFwHBq/FhusJ+Ke6xopga8io8ecK/mj9GaRXC3tgfMa+YWuu8ZSXZ0U9EsMZjjP5YQ=="],
"@remotion/canvas-capture": ["@remotion/canvas-capture@4.0.483", "", { "dependencies": { "mediabunny": "1.47.0", "remotion": "4.0.483" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JABgbfvXwjFp1E61tlDN9gVAH8OoeF4kUUELNwG9kcNOE4ex8+vCYEO6VKIaXZ4l8A1PEAQ5EK//NheTJ80gfw=="],
"@remotion/cli": ["@remotion/cli@4.0.483", "", { "dependencies": { "@remotion/bundler": "4.0.483", "@remotion/media-utils": "4.0.483", "@remotion/player": "4.0.483", "@remotion/renderer": "4.0.483", "@remotion/studio": "4.0.483", "@remotion/studio-server": "4.0.483", "@remotion/studio-shared": "4.0.483", "dotenv": "17.3.1", "minimist": "1.2.6", "prompts": "2.4.2", "remotion": "4.0.483" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" }, "bin": { "remotion": "remotion-cli.js", "remotionb": "remotionb-cli.js", "remotiond": "remotiond-cli.js" } }, "sha512-z4TQcgTfQbSIV4GQpCvIihDL77M+leQelbxv962PmfSKTT7z3XnNN732PcZ4sei2+Xg+d2SD95K4tI5XBYs5Gw=="],
"@remotion/compositor-darwin-arm64": ["@remotion/compositor-darwin-arm64@4.0.483", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CkEJXouGEoCs5PX3KOufkXrlT/Kgn0ZF7SEQ4meCYGYMZD+QPVtsmS1MXH8pQ4RHlFO6Tk7g0CIHMT3Y4yzpuA=="],
"@remotion/compositor-darwin-x64": ["@remotion/compositor-darwin-x64@4.0.483", "", { "os": "darwin", "cpu": "x64" }, "sha512-vDZK5U8FYbHGMjFC2lUgJRBz8KXwbwFSvEQi8FATD0roNg75SoaEJWrqAOIVv8v5eBjPvo18znDc36NLRYn1Eg=="],
"@remotion/compositor-linux-arm64-gnu": ["@remotion/compositor-linux-arm64-gnu@4.0.483", "", { "os": "linux", "cpu": "arm64" }, "sha512-1LzfYDY/DQxy3MIc9pCbTn1ObcBDcyI6MzODOwe1bTZl8TZxdUUt1M3hDrQ60AB6iHwruBAnzT9dnogiMIdSew=="],
"@remotion/compositor-linux-arm64-musl": ["@remotion/compositor-linux-arm64-musl@4.0.483", "", { "os": "linux", "cpu": "arm64" }, "sha512-nG1btxg0HXuLzulIqAg9NnxWyewZNrA0iLe82HdniaJjeWLORreGo4cEEJz1SjbDpQuPEY562XGkTO2DLKK5mA=="],
"@remotion/compositor-linux-x64-gnu": ["@remotion/compositor-linux-x64-gnu@4.0.483", "", { "os": "linux", "cpu": "x64" }, "sha512-CV4h6r2rNYh4P8utUzP6RM2vW2vq43nA3IvVOwRSuh9LwJh/7rwG53Oco4HanY/sstaNuymGpT57wdXwONU5CQ=="],
"@remotion/compositor-linux-x64-musl": ["@remotion/compositor-linux-x64-musl@4.0.483", "", { "os": "linux", "cpu": "x64" }, "sha512-moKvavbkoz5nteJZA4K6LMgTTlewhwIbtO7DYARZ3LK3ClGUKnYfFcmaPS15hMun02JPTOMIWbrlaszdR75mxA=="],
"@remotion/compositor-win32-x64-msvc": ["@remotion/compositor-win32-x64-msvc@4.0.483", "", { "os": "win32", "cpu": "x64" }, "sha512-eDpWe6Vy7ySHugwxlgaeohqdBwR1etxiymXzT13cgbPw2+p6d55z1tJ7VPTXyfu0o+UdyRTauWqD4msoxN0wow=="],
"@remotion/licensing": ["@remotion/licensing@4.0.483", "", {}, "sha512-GRtjykrxMmB5Cjpq1oVqeYIRXI95vid6afX0LTLfp1shD6MR4lTZRoRuopxrrZp1E4XFGXoY64b6dlIudgTyBQ=="],
"@remotion/media-parser": ["@remotion/media-parser@4.0.483", "", {}, "sha512-iva9Eof7QQX+3hGxH/N8pGi80wgdGuzOr825Zn7IuEe7IWdtRYWlFpCEghmyfhrwgaWa7RY51wg77ycR3c3nRg=="],
"@remotion/media-utils": ["@remotion/media-utils@4.0.483", "", { "dependencies": { "mediabunny": "1.47.0", "remotion": "4.0.483" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4T4WQh/U95kCBzgmz/EL/HL8Zeox/AKAL5ufsehrZFgwlcvH1EsKNJH/RWaAtNPRLF+pskEOgFzPYaczHeI8hg=="],
"@remotion/player": ["@remotion/player@4.0.483", "", { "dependencies": { "remotion": "4.0.483" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yyIQ9vinUqqS0fQeL52bPCWQIr8qDXCqQ4iys8P7dSZncdu8V29p4W2a0/4eKV8bv5+mjnJQ/U6nl9NxBF1hdA=="],
"@remotion/renderer": ["@remotion/renderer@4.0.483", "", { "dependencies": { "@remotion/licensing": "4.0.483", "@remotion/streaming": "4.0.483", "execa": "5.1.1", "remotion": "4.0.483", "source-map": "0.8.0-beta.0", "ws": "8.21.0" }, "optionalDependencies": { "@remotion/compositor-darwin-arm64": "4.0.483", "@remotion/compositor-darwin-x64": "4.0.483", "@remotion/compositor-linux-arm64-gnu": "4.0.483", "@remotion/compositor-linux-arm64-musl": "4.0.483", "@remotion/compositor-linux-x64-gnu": "4.0.483", "@remotion/compositor-linux-x64-musl": "4.0.483", "@remotion/compositor-win32-x64-msvc": "4.0.483" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-eSKdQqm8rcl+GNyOhFsBgcPYWMcxTJWHL5xaNrd037piCaL3sdJ57OEO/AbfEvvOO14zwUvlsONKjlkLlEVyKg=="],
"@remotion/streaming": ["@remotion/streaming@4.0.483", "", {}, "sha512-96rqrk+l5AfriHOqmqxMn8rI6UJfFY7r5UMDZoSKqXWzTOwj+0VWHTRl9ilZQMXhYACaMSA/5yrL9jgQEK8Yuw=="],
"@remotion/studio": ["@remotion/studio@4.0.483", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "@remotion/canvas-capture": "4.0.483", "@remotion/media-utils": "4.0.483", "@remotion/player": "4.0.483", "@remotion/renderer": "4.0.483", "@remotion/studio-shared": "4.0.483", "@remotion/timeline-utils": "4.0.483", "@remotion/web-renderer": "4.0.483", "@remotion/zod-types": "4.0.483", "mediabunny": "1.47.0", "memfs": "3.4.3", "open": "8.4.2", "remotion": "4.0.483", "semver": "7.5.3", "zod": "4.3.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-gNa1Lw8NmHl0jwFb5fk7rzA+WFTJQpGDT/49A38t9jpEwoF+h1mKTgY8gA202apzjBHrEECdFE450npnBOUpUw=="],
"@remotion/studio-server": ["@remotion/studio-server@4.0.483", "", { "dependencies": { "@babel/parser": "7.24.1", "@babel/types": "7.24.0", "@remotion/bundler": "4.0.483", "@remotion/renderer": "4.0.483", "@remotion/studio-shared": "4.0.483", "memfs": "3.4.3", "open": "8.4.2", "prettier": "3.8.1", "recast": "0.23.11", "remotion": "4.0.483", "semver": "7.5.3" } }, "sha512-2FSLbVN5N8bgtQPX+RQRbis557tJJnVTOZuEBZndfC9NOoPdARlSgEnwVer9tBNczUlc6B3KrPxyPSlS3UjL+Q=="],
"@remotion/studio-shared": ["@remotion/studio-shared@4.0.483", "", { "dependencies": { "remotion": "4.0.483" } }, "sha512-aXxBYUgsWgphHVq7T7bz+ICMGzfii7pcX1IxS5hbLMLJHqlDtk9g+qcxm4kJ2Fjj/Gb5gERKiNzYym8WZTIM2Q=="],
"@remotion/timeline-utils": ["@remotion/timeline-utils@4.0.483", "", { "dependencies": { "mediabunny": "1.47.0" } }, "sha512-KTTMdpNA5uRLX+iisAITO+V9cJBk17F8EWvuUnAEVmAFOQTbGIyPI9HNw5pZmL7SlzOJdFjAtksTjipeYGkEug=="],
"@remotion/web-renderer": ["@remotion/web-renderer@4.0.483", "", { "dependencies": { "@mediabunny/aac-encoder": "1.47.0", "@mediabunny/flac-encoder": "1.47.0", "@mediabunny/mp3-encoder": "1.47.0", "@remotion/licensing": "4.0.483", "mediabunny": "1.47.0", "remotion": "4.0.483" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-14iURfVWA/hcF/rJvkid1ZR5nmnVhkTIf071FabCFi6kdViOSVBugSglJ8tUPvWVbWmxlXGDGQD9CSU18IATww=="],
"@remotion/zod-types": ["@remotion/zod-types@4.0.483", "", { "dependencies": { "remotion": "4.0.483" }, "peerDependencies": { "zod": "4.3.6" } }, "sha512-KIVYIzFpZBDB0wxGGQu5tDBr4XOYRzvrPXjmo6O/FI5Xx7O8hgvJmBs9VDkEwonAOft86sg79hI19F1DTX1p4w=="],
"@rspack/binding": ["@rspack/binding@1.7.11", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.11", "@rspack/binding-darwin-x64": "1.7.11", "@rspack/binding-linux-arm64-gnu": "1.7.11", "@rspack/binding-linux-arm64-musl": "1.7.11", "@rspack/binding-linux-x64-gnu": "1.7.11", "@rspack/binding-linux-x64-musl": "1.7.11", "@rspack/binding-wasm32-wasi": "1.7.11", "@rspack/binding-win32-arm64-msvc": "1.7.11", "@rspack/binding-win32-ia32-msvc": "1.7.11", "@rspack/binding-win32-x64-msvc": "1.7.11" } }, "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q=="],
"@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.7.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig=="],
"@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.7.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-a1+TtTE9ap6RalgFi7FGIgkJP6O4Vy6ctv+9WGJy53E4kuqHR0RygzaiVxCI/GMc/vBT9vY23hyrpWb3d1vtXA=="],
"@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.7.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-P0QrGRPbTWu6RKWfN0bDtbnEps3rXH0MWIMreZABoUrVmNQKtXR6e73J3ub6a+di5s2+K0M2LJ9Bh2/H4UsDUA=="],
"@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.7.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-6ky7R43VMjWwmx3Yx7Jl7faLBBMAgMDt+/bN35RgwjiPgsIByz65EwytUVuW9rikB43BGHvA/eqlnjLrUzNBqw=="],
"@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.7.11", "", { "os": "linux", "cpu": "x64" }, "sha512-cuOJMfCOvb2Wgsry5enXJ3iT1FGUjdPqtGUBVupQlEG4ntSYsQ2PtF4wIDVasR3wdxC5nQbipOrDiN/u6fYsdQ=="],
"@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.7.11", "", { "os": "linux", "cpu": "x64" }, "sha512-CoK37hva4AmHGh3VCsQXmGr40L36m1/AdnN5LEjUX6kx5rEH7/1nEBN6Ii72pejqDVvk9anEROmPDiPw10tpFg=="],
"@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@1.7.11", "", { "dependencies": { "@napi-rs/wasm-runtime": "1.0.7" }, "cpu": "none" }, "sha512-OtrmnPUVJMxjNa3eDMfHyPdtlLRmmp/aIm0fQHlAOATbZvlGm12q7rhPW5BXTu1yh+1rQ1/uqvz+SzKEZXuJaQ=="],
"@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.7.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-lObFW6e5lCWNgTBNwT//yiEDbsxm9QG4BYUojqeXxothuzJ/L6ibXz6+gLMvbOvLGV3nKgkXmx8GvT9WDKR0mA=="],
"@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.7.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-0pYGnZd8PPqNR68zQ8skamqNAXEA1sUfXuAdYcknIIRq2wsbiwFzIc0Pov1cIfHYab37G7sSIPBiOUdOWF5Ivw=="],
"@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.7.11", "", { "os": "win32", "cpu": "x64" }, "sha512-EeQXayoQk/uBkI3pdoXfQBXNIUrADq56L3s/DFyM2pJeUDrWmhfIw2UFIGkYPTMSCo8F2JcdcGM32FGJrSnU0Q=="],
"@rspack/core": ["@rspack/core@1.7.11", "", { "dependencies": { "@module-federation/runtime-tools": "0.22.0", "@rspack/binding": "1.7.11", "@rspack/lite-tapable": "1.1.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew=="],
"@rspack/lite-tapable": ["@rspack/lite-tapable@1.1.0", "", {}, "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw=="],
"@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.6.1", "", { "dependencies": { "error-stack-parser": "^2.1.4", "html-entities": "^2.6.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
"@types/dom-mediacapture-transform": ["@types/dom-mediacapture-transform@0.1.11", "", { "dependencies": { "@types/dom-webcodecs": "*" } }, "sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ=="],
"@types/dom-webcodecs": ["@types/dom-webcodecs@0.1.13", "", {}, "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ=="],
"@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="],
"@types/eslint-scope": ["@types/eslint-scope@3.7.7", "", { "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg=="],
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@26.0.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw=="],
"@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="],
"@webassemblyjs/floating-point-hex-parser": ["@webassemblyjs/floating-point-hex-parser@1.13.2", "", {}, "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA=="],
"@webassemblyjs/helper-api-error": ["@webassemblyjs/helper-api-error@1.13.2", "", {}, "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ=="],
"@webassemblyjs/helper-buffer": ["@webassemblyjs/helper-buffer@1.14.1", "", {}, "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA=="],
"@webassemblyjs/helper-numbers": ["@webassemblyjs/helper-numbers@1.13.2", "", { "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA=="],
"@webassemblyjs/helper-wasm-bytecode": ["@webassemblyjs/helper-wasm-bytecode@1.13.2", "", {}, "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA=="],
"@webassemblyjs/helper-wasm-section": ["@webassemblyjs/helper-wasm-section@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", "@webassemblyjs/wasm-gen": "1.14.1" } }, "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw=="],
"@webassemblyjs/ieee754": ["@webassemblyjs/ieee754@1.13.2", "", { "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw=="],
"@webassemblyjs/leb128": ["@webassemblyjs/leb128@1.13.2", "", { "dependencies": { "@xtuc/long": "4.2.2" } }, "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw=="],
"@webassemblyjs/utf8": ["@webassemblyjs/utf8@1.13.2", "", {}, "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ=="],
"@webassemblyjs/wasm-edit": ["@webassemblyjs/wasm-edit@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", "@webassemblyjs/helper-wasm-section": "1.14.1", "@webassemblyjs/wasm-gen": "1.14.1", "@webassemblyjs/wasm-opt": "1.14.1", "@webassemblyjs/wasm-parser": "1.14.1", "@webassemblyjs/wast-printer": "1.14.1" } }, "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ=="],
"@webassemblyjs/wasm-gen": ["@webassemblyjs/wasm-gen@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", "@webassemblyjs/ieee754": "1.13.2", "@webassemblyjs/leb128": "1.13.2", "@webassemblyjs/utf8": "1.13.2" } }, "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg=="],
"@webassemblyjs/wasm-opt": ["@webassemblyjs/wasm-opt@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", "@webassemblyjs/wasm-gen": "1.14.1", "@webassemblyjs/wasm-parser": "1.14.1" } }, "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw=="],
"@webassemblyjs/wasm-parser": ["@webassemblyjs/wasm-parser@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", "@webassemblyjs/ieee754": "1.13.2", "@webassemblyjs/leb128": "1.13.2", "@webassemblyjs/utf8": "1.13.2" } }, "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ=="],
"@webassemblyjs/wast-printer": ["@webassemblyjs/wast-printer@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw=="],
"@xtuc/ieee754": ["@xtuc/ieee754@1.2.0", "", {}, "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="],
"@xtuc/long": ["@xtuc/long@4.2.2", "", {}, "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="],
"acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="],
"acorn-import-phases": ["acorn-import-phases@1.0.4", "", { "peerDependencies": { "acorn": "^8.14.0" } }, "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ=="],
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
"ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
"ajv-keywords": ["ajv-keywords@5.1.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "ajv": "^8.8.2" } }, "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw=="],
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.40", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw=="],
"browserslist": ["browserslist@4.28.4", "", { "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", "electron-to-chromium": "^1.5.376", "node-releases": "^2.0.48", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001799", "", {}, "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw=="],
"chrome-trace-event": ["chrome-trace-event@1.0.4", "", {}, "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ=="],
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"css-loader": ["css-loader@7.1.4", "", { "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.40", "postcss-modules-extract-imports": "^3.1.0", "postcss-modules-local-by-default": "^4.0.5", "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.6.3" }, "peerDependencies": { "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", "webpack": "^5.27.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw=="],
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.379", "", {}, "sha512-v/qV5aV5EUA2pGilzUCq5/eyOloZAqDZBu9UMBIzgPpLlprjSR6zswsWBTv0KpqxLGUAZEwhO95ZCt7srymNVA=="],
"enhanced-resolve": ["enhanced-resolve@5.24.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw=="],
"error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
"es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="],
"esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
"execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="],
"fs-monkey": ["fs-monkey@1.0.3", "", {}, "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q=="],
"get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
"glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
"human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
"icss-utils": ["icss-utils@5.1.0", "", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA=="],
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jest-worker": ["jest-worker@27.5.1", "", { "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg=="],
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"loader-runner": ["loader-runner@4.3.2", "", {}, "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w=="],
"lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="],
"lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"mediabunny": ["mediabunny@1.47.0", "", { "dependencies": { "@types/dom-mediacapture-transform": "^0.1.11", "@types/dom-webcodecs": "0.1.13" } }, "sha512-XQMZAcaKPkJ7hQ/Q2fvBdl3ZazQl2WVxDysUbJWh4PuAnLoerdsQBdPTDWdUdK6hh26LQ8Ue94MLLnmpWvlUYg=="],
"memfs": ["memfs@3.4.3", "", { "dependencies": { "fs-monkey": "1.0.3" } }, "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg=="],
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"minimist": ["minimist@1.2.6", "", {}, "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="],
"nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="],
"neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
"node-releases": ["node-releases@2.0.50", "", {}, "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg=="],
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="],
"postcss-modules-extract-imports": ["postcss-modules-extract-imports@3.1.0", "", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q=="],
"postcss-modules-local-by-default": ["postcss-modules-local-by-default@4.2.0", "", { "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw=="],
"postcss-modules-scope": ["postcss-modules-scope@3.2.1", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA=="],
"postcss-modules-values": ["postcss-modules-values@4.0.0", "", { "dependencies": { "icss-utils": "^5.0.0" }, "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ=="],
"postcss-selector-parser": ["postcss-selector-parser@7.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg=="],
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="],
"react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="],
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
"recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="],
"remotion": ["remotion@4.0.483", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-lf4xq4Twn75TQeTavFLrTE4zdck7EKBSx07ZrPv4om40Sist+3G0kq7NNzSjMeJ6G2Wx6vI+0oICD/vv4sGWtg=="],
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"schema-utils": ["schema-utils@4.3.3", "", { "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", "ajv-formats": "^2.1.1", "ajv-keywords": "^5.1.0" } }, "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA=="],
"semver": ["semver@7.5.3", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
"strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
"style-loader": ["style-loader@4.0.0", "", { "peerDependencies": { "webpack": "^5.27.0" } }, "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA=="],
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
"terser": ["terser@5.48.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q=="],
"terser-webpack-plugin": ["terser-webpack-plugin@5.6.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ=="],
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
"to-fast-properties": ["to-fast-properties@2.0.0", "", {}, "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="],
"tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"watchpack": ["watchpack@2.5.2", "", { "dependencies": { "graceful-fs": "^4.1.2" } }, "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg=="],
"webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
"webpack": ["webpack@5.105.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.19.0", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.16", "watchpack": "^2.5.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw=="],
"webpack-sources": ["webpack-sources@3.5.0", "", {}, "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ=="],
"whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"css-loader/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
-24
View File
@@ -1,24 +0,0 @@
{
"name": "glm52-rise-video",
"private": true,
"type": "module",
"scripts": {
"render": "remotion render src/index.tsx GLM52Rise out/glm-52-broke-out.mp4 --codec h264 --pixel-format yuv420p",
"render:sheep": "remotion render src/index.tsx NZSheep out/nz-sheep.mp4 --codec h264 --pixel-format yuv420p",
"render:novel": "remotion render src/index.tsx NovelTokens out/novel-1984.mp4 --codec h264 --pixel-format yuv420p",
"render:flash": "remotion render src/index.tsx FlashShare out/flash-share.mp4 --codec h264 --pixel-format yuv420p",
"render:minimax": "remotion render src/index.tsx MiniMaxClimb out/minimax-climb.mp4 --codec h264 --pixel-format yuv420p",
"still:june": "remotion still src/index.tsx JuneTotals out/june-totals.png --frame=0"
},
"dependencies": {
"@remotion/cli": "^4.0.384",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"remotion": "^4.0.384"
},
"devDependencies": {
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.3",
"typescript": "^5.8.2"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

-156
View File
@@ -1,156 +0,0 @@
// Verified against the production opencode-stats PlanetScale DB.
// Scope: tier='Go' (OpenCode Go), dataset='zen', client='all', source='all', grain='day'.
// metric = total_tokens. Daily token volume per model; GLM-5.2 (zhipu) launched Jun 17.
// Segments per day (tokens): glm-5.2, deepseek-v4-flash, deepseek-v4-pro, minimax-m3, all others.
export type Day = {
date: string
total: number
glm: number
dsf: number
dsp: number
mm: number
others: number
}
export const days: Day[] = [
{
date: "Jun 12",
total: 2_283_799_449_383,
glm: 0,
dsf: 1_176_701_653_509,
dsp: 569_527_034_307,
mm: 159_016_250_684,
others: 378_554_510_883,
},
{
date: "Jun 13",
total: 2_008_462_388_420,
glm: 0,
dsf: 995_338_131_997,
dsp: 445_817_536_548,
mm: 211_743_241_967,
others: 355_563_477_908,
},
{
date: "Jun 14",
total: 2_007_785_405_251,
glm: 0,
dsf: 983_954_176_228,
dsp: 428_151_999_341,
mm: 262_476_527_930,
others: 333_202_701_752,
},
{
date: "Jun 15",
total: 2_694_736_103_062,
glm: 0,
dsf: 1_255_893_953_859,
dsp: 632_223_338_376,
mm: 352_507_442_991,
others: 454_111_367_836,
},
{
date: "Jun 16",
total: 2_838_153_758_908,
glm: 0,
dsf: 1_336_625_283_800,
dsp: 676_480_415_730,
mm: 305_268_829_013,
others: 519_779_230_365,
},
{
date: "Jun 17",
total: 2_778_964_711_109,
glm: 70_095_977_043,
dsf: 1_339_831_523_555,
dsp: 660_414_395_220,
mm: 251_302_096_157,
others: 457_320_719_134,
},
{
date: "Jun 18",
total: 2_806_992_430_656,
glm: 201_130_231_172,
dsf: 1_295_599_996_869,
dsp: 595_665_008_776,
mm: 322_205_104_324,
others: 392_392_089_515,
},
{
date: "Jun 19",
total: 2_419_611_630_232,
glm: 199_086_413_910,
dsf: 1_115_750_468_802,
dsp: 475_965_869_304,
mm: 303_586_698_735,
others: 325_222_179_481,
},
{
date: "Jun 20",
total: 2_188_278_916_865,
glm: 193_931_516_396,
dsf: 1_050_194_681_012,
dsp: 395_303_435_278,
mm: 281_998_000_337,
others: 266_851_283_842,
},
{
date: "Jun 21",
total: 2_042_309_961_344,
glm: 181_894_043_118,
dsf: 985_164_570_580,
dsp: 368_194_079_542,
mm: 259_812_551_324,
others: 247_244_716_780,
},
{
date: "Jun 22",
total: 2_893_934_325_663,
glm: 301_759_048_475,
dsf: 1_298_124_282_989,
dsp: 581_012_596_194,
mm: 371_581_117_839,
others: 341_457_280_166,
},
{
date: "Jun 23",
total: 3_109_009_321_480,
glm: 282_277_235_158,
dsf: 1_423_571_678_821,
dsp: 627_374_654_587,
mm: 429_416_300_508,
others: 346_369_452_406,
},
{
date: "Jun 24",
total: 2_939_149_971_595,
glm: 256_497_442_533,
dsf: 1_373_583_023_234,
dsp: 601_270_997_775,
mm: 391_586_493_231,
others: 316_212_014_822,
},
{
date: "Jun 25",
total: 3_029_641_552_948,
glm: 256_279_657_734,
dsf: 1_481_084_002_776,
dsp: 602_077_167_287,
mm: 375_985_302_874,
others: 314_215_422_277,
},
]
export const launchIndex = 5 // Jun 17, first day of GLM-5.2 usage
// GLM-5.2 weekly token volume, Jun 19-25 (sum of glm): 1,671,725,357,324 = 1.67T
export const glmWeekTokensT = 1.672
// stacked segments, bottom -> top. GLM-5.2 is the hero (blue); the rest are the field it cut into.
export const segments = [
{ key: "glm", label: "GLM-5.2", color: "#3b5cf6", hero: true },
{ key: "dsf", label: "deepseek-v4-flash", color: "#9ca3ad" },
{ key: "dsp", label: "deepseek-v4-pro", color: "#b3b9c1" },
{ key: "mm", label: "minimax-m3", color: "#c8cdd3" },
{ key: "others", label: "other models", color: "#dde0e4" },
] as const
-185
View File
@@ -1,185 +0,0 @@
import React from "react"
import { AbsoluteFill, Easing, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"
// stats.opencode.ai design tokens (light theme)
const c = {
bg: "#ffffff",
ink: "#161616",
muted: "#5c5c5c",
faint: "#808080",
line: "#e6e6e6",
dot: "#e4e4e4",
gray: "#aab0b8",
accent: "#3b5cf6",
accentHi: "#5b78ff",
}
const MONO = '"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
const DOT_MASK =
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E\")"
// verified: OpenCode Go, week of Jun 22-28, 2026 (2026-W26). share = % of 19.64T total Go tokens.
const bars = [
{ label: "deepseek-v4-flash", share: 48.3, hero: true },
{ label: "deepseek-v4-pro", share: 19.4 },
{ label: "minimax-m3", share: 13.0 },
{ label: "glm-5.2", share: 8.3 },
{ label: "mimo-v2.5", share: 4.3 },
{ label: "kimi-k2.7-code", share: 2.6 },
{ label: "other models", share: 4.1 },
]
function DataWordmark({ height = 30 }: { height?: number }) {
return (
<svg width={(height * 66) / 20} height={height} viewBox="0 0 66 20" fill="none" style={{ color: c.ink }}>
<path opacity="0.2" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
export function FlashShare() {
const frame = useCurrentFrame()
const { fps } = useVideoConfig()
const grow = (i: number) =>
Math.min(
1,
Math.max(0, spring({ frame: frame - 18 - i * 7, fps, config: { damping: 18, stiffness: 120, mass: 0.6 } })),
)
return (
<AbsoluteFill style={{ background: c.bg, color: c.ink, fontFamily: MONO, padding: 72, boxSizing: "border-box" }}>
<div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
{/* header */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<DataWordmark height={30} />
<div style={{ fontSize: 20, fontWeight: 500, color: c.faint, letterSpacing: 1 }}>JUN 2228, 2026</div>
</div>
{/* headline (static) */}
<div style={{ marginTop: 50 }}>
<div style={{ fontSize: 23, fontWeight: 600, color: c.muted, letterSpacing: 2 }}>
OPENCODE GO · SHARE OF TOKENS
</div>
<div
style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 24, marginTop: 14 }}
>
<div style={{ fontSize: 62, fontWeight: 600, letterSpacing: -2, lineHeight: 1 }}>DeepSeek V4 Flash</div>
<div
style={{
fontSize: 88,
fontWeight: 600,
letterSpacing: -2,
lineHeight: 1,
color: c.accent,
fontVariantNumeric: "tabular-nums",
}}
>
48%
</div>
</div>
</div>
{/* bar chart */}
<div style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "center", gap: 16 }}>
{bars.map((b, i) => {
const g = grow(i)
const pct = b.share * g
return (
<div key={b.label} style={{ display: "flex", alignItems: "center", gap: 18 }}>
<div
style={{
width: 268,
fontSize: 23,
fontWeight: b.hero ? 600 : 500,
color: b.hero ? c.ink : c.muted,
textAlign: "right",
whiteSpace: "nowrap",
}}
>
{b.label}
</div>
<div style={{ position: "relative", flex: 1, height: 48 }}>
{/* dotted 100% track — height is a multiple of the 12px tile, anchored bottom, so dots never clip */}
<div
style={{
position: "absolute",
inset: 0,
background: c.dot,
WebkitMaskImage: DOT_MASK,
maskImage: DOT_MASK,
WebkitMaskSize: "12px 12px",
maskSize: "12px 12px",
WebkitMaskRepeat: "repeat",
maskRepeat: "repeat",
WebkitMaskPosition: "left bottom",
maskPosition: "left bottom",
}}
/>
{/* fill */}
<div
style={{
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: `${pct}%`,
background: b.hero ? c.accent : c.gray,
borderRight: b.hero ? `2px solid ${c.accentHi}` : "none",
}}
/>
</div>
<div
style={{
width: 84,
fontSize: 24,
fontWeight: b.hero ? 600 : 500,
color: b.hero ? c.accent : c.muted,
textAlign: "right",
fontVariantNumeric: "tabular-nums",
}}
>
{Math.round(pct)}%
</div>
</div>
)
})}
</div>
{/* footer */}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 24,
paddingTop: 22,
borderTop: `1px solid ${c.line}`,
fontSize: 20,
fontWeight: 500,
}}
>
<div style={{ display: "inline-flex", alignItems: "center", gap: 9, color: c.muted }}>
<span style={{ width: 13, height: 13, background: c.accent, display: "inline-block" }} />
DeepSeek V4 Flash · 9.48T tokens · 83.6M requests
</div>
<div style={{ color: c.ink }}>opencode.ai/data</div>
</div>
</div>
</AbsoluteFill>
)
}
-36
View File
@@ -1,36 +0,0 @@
import { Composition, registerRoot } from "remotion"
import { GLM52Rise } from "./video"
import { NZSheep } from "./sheep"
import { NovelTokens } from "./novel"
import { FlashShare } from "./flash"
import { MiniMaxClimb } from "./minimax"
import { JuneTotals } from "./june"
function Root() {
return (
<>
<Composition id="GLM52Rise" component={GLM52Rise} durationInFrames={240} fps={30} width={1080} height={1080} />
<Composition id="NZSheep" component={NZSheep} durationInFrames={150} fps={30} width={1080} height={1080} />
<Composition
id="NovelTokens"
component={NovelTokens}
durationInFrames={150}
fps={30}
width={1080}
height={1080}
/>
<Composition id="FlashShare" component={FlashShare} durationInFrames={165} fps={30} width={1080} height={1080} />
<Composition
id="MiniMaxClimb"
component={MiniMaxClimb}
durationInFrames={165}
fps={30}
width={1080}
height={1080}
/>
<Composition id="JuneTotals" component={JuneTotals} durationInFrames={1} fps={30} width={1080} height={1080} />
</>
)
}
registerRoot(Root)
-144
View File
@@ -1,144 +0,0 @@
import React from "react"
import { AbsoluteFill } from "remotion"
const c = {
bg: "#ffffff",
ink: "#161616",
muted: "#5c5c5c",
faint: "#808080",
line: "#e6e6e6",
dot: "#dcdcdc",
accent: "#3b5cf6",
}
const MONO = '"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
const DOT_MASK =
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E\")"
// verified: OpenCode Go (tier=Go, dataset=zen), June 1-30, 2026.
// 72.78T tokens · 651.4M requests · 11.42M sessions -> rounded headline figures.
function DataWordmark({ height = 30 }: { height?: number }) {
return (
<svg width={(height * 66) / 20} height={height} viewBox="0 0 66 20" fill="none" style={{ color: c.ink }}>
<path opacity="0.2" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
function DotBand() {
return (
<div
style={{
height: 12,
background: c.dot,
WebkitMaskImage: DOT_MASK,
maskImage: DOT_MASK,
WebkitMaskSize: "12px 12px",
maskSize: "12px 12px",
WebkitMaskRepeat: "repeat",
maskRepeat: "repeat",
WebkitMaskPosition: "left top",
maskPosition: "left top",
}}
/>
)
}
function Metric({ value, label }: { value: string; label: string }) {
return (
<div>
<div
style={{
fontSize: 92,
fontWeight: 600,
letterSpacing: -3,
lineHeight: 1,
fontVariantNumeric: "tabular-nums",
marginLeft: -8, // align the glyph's visual left edge to the column
}}
>
{value}
</div>
<div style={{ marginTop: 16, fontSize: 24, fontWeight: 500, color: c.muted, letterSpacing: 1 }}>{label}</div>
</div>
)
}
export function JuneTotals() {
return (
<AbsoluteFill style={{ background: c.bg, color: c.ink, fontFamily: MONO, padding: 72, boxSizing: "border-box" }}>
<div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
{/* header */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 22 }}>
<DataWordmark height={30} />
<div style={{ fontSize: 20, fontWeight: 500, color: c.faint, letterSpacing: 1 }}>MONTHLY RECAP</div>
</div>
<DotBand />
{/* hero */}
<div style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "center" }}>
<div style={{ fontSize: 25, fontWeight: 600, color: c.muted, letterSpacing: 3 }}>OPENCODE GO · JUNE 2026</div>
<div style={{ marginTop: 14 }}>
<div
style={{
fontSize: 300,
fontWeight: 600,
letterSpacing: -10,
lineHeight: 0.82,
color: c.accent,
marginLeft: -22,
}}
>
73T
</div>
<div style={{ fontSize: 50, fontWeight: 600, color: c.ink, marginTop: 10 }}>tokens processed</div>
</div>
{/* supporting metrics */}
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
marginTop: 60,
paddingTop: 40,
borderTop: `1px solid ${c.line}`,
}}
>
<Metric value="650M" label="requests" />
<Metric value="11M" label="sessions" />
</div>
</div>
{/* footer */}
<DotBand />
<div
style={{
display: "flex",
justifyContent: "flex-end",
alignItems: "center",
marginTop: 22,
fontSize: 20,
fontWeight: 500,
}}
>
<div style={{ color: c.ink }}>opencode.ai/data</div>
</div>
</div>
</AbsoluteFill>
)
}
-201
View File
@@ -1,201 +0,0 @@
import React from "react"
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"
const c = {
bg: "#ffffff",
ink: "#161616",
muted: "#5c5c5c",
faint: "#808080",
line: "#e6e6e6",
dot: "#e4e4e4",
accent: "#3b5cf6",
accentHi: "#5b78ff",
accentDim: "#aebcf3",
}
const MONO = '"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
const DOT_MASK =
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E\")"
// verified: minimax-m3, OpenCode Go (tier=Go, dataset=zen), weekly total_tokens.
// W26 2.559T is +23.2% / +482.3B vs W25 2.077T.
const weeks = [
{ label: "May 25", t: 0.008 },
{ label: "Jun 1", t: 0.429 },
{ label: "Jun 8", t: 1.192 },
{ label: "Jun 15", t: 2.077 },
{ label: "Jun 22", t: 2.559, latest: true },
]
const AXIS_MAX = 3.0
function DataWordmark({ height = 30 }: { height?: number }) {
return (
<svg width={(height * 66) / 20} height={height} viewBox="0 0 66 20" fill="none" style={{ color: c.ink }}>
<path opacity="0.2" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
const CHART_H = 420 // multiple of 12 so the dotted tracks never clip
export function MiniMaxClimb() {
const frame = useCurrentFrame()
const { fps } = useVideoConfig()
const grow = (i: number) =>
Math.min(
1,
Math.max(0, spring({ frame: frame - 22 - i * 9, fps, config: { damping: 18, stiffness: 110, mass: 0.6 } })),
)
return (
<AbsoluteFill style={{ background: c.bg, color: c.ink, fontFamily: MONO, padding: 72, boxSizing: "border-box" }}>
<div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
{/* header */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<DataWordmark height={30} />
<div style={{ fontSize: 20, fontWeight: 500, color: c.faint, letterSpacing: 1 }}>JUN 2228, 2026</div>
</div>
{/* headline (static) */}
<div style={{ marginTop: 50 }}>
<div style={{ fontSize: 23, fontWeight: 600, color: c.muted, letterSpacing: 2 }}>
OPENCODE GO · WEEKLY TOKENS
</div>
<div
style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 24, marginTop: 14 }}
>
<div style={{ fontSize: 62, fontWeight: 600, letterSpacing: -2, lineHeight: 1 }}>MiniMax M3</div>
<div style={{ textAlign: "right", flexShrink: 0 }}>
<div
style={{
fontSize: 84,
fontWeight: 600,
letterSpacing: -2,
lineHeight: 1,
color: c.accent,
fontVariantNumeric: "tabular-nums",
}}
>
+23.2%
</div>
<div style={{ fontSize: 19, fontWeight: 500, color: c.muted, letterSpacing: 1, marginTop: 6 }}>
WEEK OVER WEEK
</div>
</div>
</div>
</div>
{/* column chart */}
<div style={{ flex: 1, display: "flex", alignItems: "flex-end", marginTop: 30 }}>
<div style={{ width: "100%", display: "flex", alignItems: "flex-end", gap: 30 }}>
{weeks.map((w, i) => {
const g = grow(i)
const h = Math.round((w.t / AXIS_MAX) * CHART_H * g)
return (
<div key={w.label} style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center" }}>
{/* value + bar */}
<div style={{ position: "relative", width: "100%", height: CHART_H }}>
{/* dotted track */}
<div
style={{
position: "absolute",
inset: 0,
background: c.dot,
WebkitMaskImage: DOT_MASK,
maskImage: DOT_MASK,
WebkitMaskSize: "12px 12px",
maskSize: "12px 12px",
WebkitMaskRepeat: "repeat",
maskRepeat: "repeat",
WebkitMaskPosition: "left bottom",
maskPosition: "left bottom",
}}
/>
{/* fill */}
<div
style={{
position: "absolute",
left: 0,
right: 0,
bottom: 0,
height: h,
background: w.latest ? c.accent : c.accentDim,
borderTop: w.latest ? `3px solid ${c.accentHi}` : "none",
}}
/>
{/* value label */}
<div
style={{
position: "absolute",
left: 0,
right: 0,
bottom: h + 10,
textAlign: "center",
fontSize: 26,
fontWeight: w.latest ? 600 : 500,
color: w.latest ? c.accent : c.muted,
fontVariantNumeric: "tabular-nums",
opacity: interpolate(g, [0.5, 1], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
}),
}}
>
{w.t.toFixed(2)}T
</div>
</div>
{/* week label */}
<div
style={{
marginTop: 14,
fontSize: 18,
fontWeight: 500,
color: w.latest ? c.ink : c.faint,
}}
>
{w.label}
</div>
</div>
)
})}
</div>
</div>
{/* footer */}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 22,
paddingTop: 22,
borderTop: `1px solid ${c.line}`,
fontSize: 20,
fontWeight: 500,
}}
>
<div style={{ display: "inline-flex", alignItems: "center", gap: 9, color: c.muted }}>
<span style={{ width: 13, height: 13, background: c.accent, display: "inline-block" }} />
2.56T tokens last week · +482.3B added
</div>
<div style={{ color: c.ink }}>opencode.ai/data</div>
</div>
</div>
</AbsoluteFill>
)
}
-135
View File
@@ -1,135 +0,0 @@
import React from "react"
import { AbsoluteFill, Easing, Img, interpolate, staticFile, useCurrentFrame } from "remotion"
const c = {
white: "#ffffff",
dim: "rgba(255,255,255,0.74)",
}
const MONO = '"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
// verified: OpenCode Go, week of Jun 22-28, 2026 (2026-W26)
// 19,642,742,937,105 tokens / 173,651,197 requests = 113,116 tokens/request
const AVG = 113116
const K = Math.round(AVG / 1000) // 113
const nf = new Intl.NumberFormat("en-US")
function DataWordmark({ height = 30 }: { height?: number }) {
return (
<svg width={(height * 66) / 20} height={height} viewBox="0 0 66 20" fill="none" style={{ color: c.white }}>
<path opacity="0.35" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
export function NovelTokens() {
const frame = useCurrentFrame()
const k = Math.round(
K *
interpolate(frame, [18, 92], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
}),
)
const zoom = interpolate(frame, [0, 150], [1.06, 1.12], {
extrapolateRight: "clamp",
easing: Easing.inOut(Easing.quad),
})
return (
<AbsoluteFill style={{ background: "#0c0c0c", overflow: "hidden" }}>
<Img
src={staticFile("book.jpg")}
style={{
position: "absolute",
width: "100%",
height: "100%",
objectFit: "cover",
objectPosition: "center 34%",
transform: `scale(${zoom})`,
transformOrigin: "center 30%",
}}
/>
{/* legibility scrims */}
<div
style={{
position: "absolute",
inset: 0,
background:
"linear-gradient(to bottom, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 20%, rgba(0,0,0,0.1) 44%, rgba(0,0,0,0.86) 100%)",
}}
/>
<div
style={{
position: "absolute",
inset: 0,
boxSizing: "border-box",
padding: 64,
color: c.white,
fontFamily: MONO,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
}}
>
{/* header */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<DataWordmark height={30} />
<div style={{ fontSize: 20, fontWeight: 500, color: c.dim, letterSpacing: 1 }}>JUN 2228, 2026</div>
</div>
{/* bottom block */}
<div>
<div style={{ fontSize: 23, fontWeight: 600, color: c.dim, letterSpacing: 2, marginBottom: 8 }}>
OPENCODE GO · LAST WEEK
</div>
<div
style={{
fontSize: 168,
fontWeight: 600,
lineHeight: 0.92,
letterSpacing: -5,
fontVariantNumeric: "tabular-nums",
}}
>
{k}K
</div>
<div style={{ fontSize: 50, fontWeight: 600, letterSpacing: -1, marginTop: 4 }}>tokens per request</div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 30,
fontSize: 20,
fontWeight: 500,
}}
>
<div style={{ color: c.dim }}>{nf.format(AVG)} tokens / request · last week</div>
<div style={{ color: c.white }}>opencode.ai/data</div>
</div>
</div>
</div>
</AbsoluteFill>
)
}
-139
View File
@@ -1,139 +0,0 @@
import React from "react"
import { AbsoluteFill, Easing, Img, interpolate, staticFile, useCurrentFrame } from "remotion"
const c = {
white: "#ffffff",
dim: "rgba(255,255,255,0.72)",
faint: "rgba(255,255,255,0.55)",
}
const MONO = '"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
// verified: NZ OpenCode Go, week of Jun 22-28, 2026 (2026-W26)
const TOKENS = 40_915_594_381 // 40.9B
const SHEEP = 23_600_000 // 23.6M
const PER_SHEEP = Math.round(TOKENS / SHEEP) // 1,734
const nf = new Intl.NumberFormat("en-US")
// the correct opencode "DATA" wordmark (white, over photo)
function DataWordmark({ height = 30 }: { height?: number }) {
return (
<svg width={(height * 66) / 20} height={height} viewBox="0 0 66 20" fill="none" style={{ color: c.white }}>
<path opacity="0.35" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
export function NZSheep() {
const frame = useCurrentFrame()
const count = Math.round(
PER_SHEEP *
interpolate(frame, [18, 90], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
}),
)
// slow Ken Burns push-in (scale up only — never reveals an edge)
const zoom = interpolate(frame, [0, 150], [1.06, 1.12], {
extrapolateRight: "clamp",
easing: Easing.inOut(Easing.quad),
})
return (
<AbsoluteFill style={{ background: "#0c0c0c", overflow: "hidden" }}>
{/* the sheep, staring */}
<Img
src={staticFile("sheep.jpg")}
style={{
position: "absolute",
width: "100%",
height: "100%",
objectFit: "cover",
objectPosition: "center 38%",
transform: `scale(${zoom})`,
transformOrigin: "center 35%",
}}
/>
{/* legibility scrims */}
<div
style={{
position: "absolute",
inset: 0,
background:
"linear-gradient(to bottom, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 22%, rgba(0,0,0,0) 48%, rgba(0,0,0,0.78) 100%)",
}}
/>
{/* content */}
<div
style={{
position: "absolute",
inset: 0,
boxSizing: "border-box",
padding: 64,
color: c.white,
fontFamily: MONO,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
}}
>
{/* header */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<DataWordmark height={30} />
<div style={{ fontSize: 20, fontWeight: 500, color: c.dim, letterSpacing: 1 }}>JUN 2228, 2026</div>
</div>
{/* bottom block */}
<div>
<div style={{ fontSize: 23, fontWeight: 600, color: c.dim, letterSpacing: 2, marginBottom: 8 }}>
OPENCODE GO · NEW ZEALAND
</div>
<div
style={{
fontSize: 168,
fontWeight: 600,
lineHeight: 0.92,
letterSpacing: -5,
fontVariantNumeric: "tabular-nums",
}}
>
{nf.format(count)}
</div>
<div style={{ fontSize: 50, fontWeight: 600, letterSpacing: -1, marginTop: 4 }}>tokens per sheep</div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 34,
fontSize: 20,
fontWeight: 500,
}}
>
<div style={{ color: c.dim }}>40.9B tokens ÷ 23.6M sheep · last week</div>
<div style={{ color: c.white }}>opencode.ai/data</div>
</div>
</div>
</div>
</AbsoluteFill>
)
}
-254
View File
@@ -1,254 +0,0 @@
import React from "react"
import { AbsoluteFill, Easing, interpolate, useCurrentFrame, useVideoConfig } from "remotion"
import { days, launchIndex, glmWeekTokensT, segments } from "./data"
// stats.opencode.ai design tokens (light theme)
const c = {
bg: "#ffffff",
ink: "#161616",
muted: "#5c5c5c",
faint: "#808080",
line: "#e6e6e6",
dot: "#ededed",
accent: "#3b5cf6",
accentHi: "#5b78ff",
}
const MONO = '"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
const W = 1080
const DOT_MASK =
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E\")"
const field = segments.filter((s) => !s.hero)
const glmColor = segments.find((s) => s.hero)!.color
const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v))
// the correct opencode "DATA" wordmark (from stats.opencode.ai header)
function DataWordmark({ height = 30, color = c.ink }: { height?: number; color?: string }) {
return (
<svg width={(height * 66) / 20} height={height} viewBox="0 0 66 20" fill="none" style={{ color }}>
<path opacity="0.2" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
export function GLM52Rise() {
const frame = useCurrentFrame()
const { fps } = useVideoConfig()
// ---------- virtual camera ----------
// open zoomed-in on the field, pan right while the blue fills, then pull back to reveal.
const K = [0, 32, 150, 206, 240]
const ease = Easing.inOut(Easing.cubic)
const opt = { extrapolateLeft: "clamp" as const, extrapolateRight: "clamp" as const, easing: ease }
const s = interpolate(frame, K, [1.82, 1.72, 1.72, 1.0, 1.0], opt)
let fx = interpolate(frame, K, [420, 438, 760, 540, 540], opt)
let fy = interpolate(frame, K, [664, 664, 664, 540, 540], opt)
// keep the framing inside the 1080 canvas so edges never reveal black
fx = clamp(fx, 540 / s, W - 540 / s)
fy = clamp(fy, 540 / s, W - 540 / s)
const camera = `translate(${540 - fx * s}px, ${540 - fy * s}px) scale(${s})`
// ---------- blue sweep (synced to the pan) ----------
const p = interpolate(frame, [32, 150], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: ease })
const revealAmount = p * (days.length - launchIndex) + 0.35
const fillOf = (i: number) => clamp(revealAmount - (i - launchIndex), 0, 1)
// token number climbs as the camera pulls back and the headline re-enters frame
const tokensT =
glmWeekTokensT *
interpolate(frame, [150, 202], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
})
// chart geometry
const chartH = 440
const chartW = 936
const gap = 14
const EXAGGERATE = 2.876 // broken y-axis: GLM-5.2 magnified ~2.9x for emphasis
return (
<AbsoluteFill style={{ background: c.bg, overflow: "hidden" }}>
<div
style={{
position: "absolute",
width: W,
height: W,
background: c.bg,
transformOrigin: "0 0",
transform: camera,
}}
>
<div
style={{
width: W,
height: W,
boxSizing: "border-box",
padding: 72,
color: c.ink,
fontFamily: MONO,
display: "flex",
flexDirection: "column",
}}
>
{/* header */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<DataWordmark height={30} />
<div style={{ fontSize: 20, fontWeight: 500, color: c.faint, letterSpacing: 1 }}>JUN 1225, 2026</div>
</div>
{/* headline (static) */}
<div style={{ marginTop: 52 }}>
<div style={{ fontSize: 92, fontWeight: 600, lineHeight: 0.98, letterSpacing: -2 }}>
GLM-5.2 <span style={{ color: c.accent }}>broke out</span>
</div>
<div style={{ marginTop: 22, fontSize: 30, fontWeight: 500, color: c.muted }}>
From 0 to <span style={{ color: c.ink }}>{tokensT.toFixed(2)}T tokens</span> in a week.
</div>
</div>
{/* stacked chart */}
<div style={{ flex: 1, display: "flex", alignItems: "flex-end", marginTop: 40 }}>
<div
style={{
position: "relative",
width: chartW,
height: chartH,
margin: "0 auto",
borderBottom: `2px solid ${c.ink}`,
boxSizing: "border-box",
}}
>
{/* faint dotted backdrop */}
<div
style={{
position: "absolute",
left: -8,
right: -8,
top: -8,
bottom: 0,
background: c.dot,
WebkitMaskImage: DOT_MASK,
maskImage: DOT_MASK,
WebkitMaskSize: "12px 12px",
maskSize: "12px 12px",
WebkitMaskRepeat: "repeat",
maskRepeat: "repeat",
}}
/>
{/* columns */}
<div style={{ position: "absolute", inset: 0, display: "flex", gap, alignItems: "flex-end" }}>
{days.map((d, i) => {
const glmShare = d.glm / d.total
const blueH = Math.round(glmShare * EXAGGERATE * chartH)
const filled = Math.round(blueH * fillOf(i))
const slotGap = filled > 2 ? 5 : 0
const fieldH = chartH - filled - slotGap
const fieldTotal = d.dsf + d.dsp + d.mm + d.others
return (
<div key={i} style={{ position: "relative", flex: 1, height: chartH }}>
{/* gray field of other models (already in place) */}
<div
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
height: fieldH,
display: "flex",
flexDirection: "column-reverse",
}}
>
{field.map((seg) => (
<div
key={seg.key}
style={{
height: Math.round(((d[seg.key as keyof typeof d] as number) / fieldTotal) * fieldH),
background: seg.color,
borderTop: `2px solid ${c.bg}`,
}}
/>
))}
</div>
{/* GLM-5.2, animates in */}
{filled > 2 && (
<div
style={{
position: "absolute",
left: 0,
right: 0,
bottom: 0,
height: filled,
background: glmColor,
borderTop: `2px solid ${c.accentHi}`,
}}
/>
)}
</div>
)
})}
</div>
</div>
</div>
{/* day axis */}
<div style={{ display: "flex", gap, width: chartW, margin: "14px auto 0" }}>
{days.map((d, i) => (
<div
key={i}
style={{
flex: 1,
textAlign: "center",
fontSize: 15,
fontWeight: 500,
color: i === days.length - 1 ? c.ink : c.faint,
}}
>
{i === 0 || i === launchIndex || i === days.length - 1 ? d.date : ""}
</div>
))}
</div>
{/* footer */}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 28,
paddingTop: 22,
borderTop: `1px solid ${c.line}`,
fontSize: 20,
fontWeight: 500,
}}
>
<div style={{ display: "inline-flex", alignItems: "center", gap: 9, color: c.muted }}>
<span style={{ width: 13, height: 13, background: c.accent, display: "inline-block" }} />
GLM-5.2
</div>
<div style={{ color: c.ink }}>opencode.ai/data</div>
</div>
</div>
</div>
</AbsoluteFill>
)
}
+14 -14
View File
@@ -24,7 +24,7 @@
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "catalog:",
"turbo": "2.10.2",
"turbo": "2.8.13",
},
},
"packages/app": {
@@ -2791,18 +2791,6 @@
"@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="],
"@turbo/darwin-64": ["@turbo/darwin-64@2.10.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-wBM3ObqOWnKUDmg7QfUFDkDHPFUAJmrYlYqmEM8jMPAPA/I6wRJIbWimeQUqhOiQ8xPKhzyWM+xaiUP0wz8FEQ=="],
"@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/Cq0joWnuMjDPfhjbFP4sv+C/7gkQ415zlaO4XUzD5EZxbtrKgXKvuuydMvogG8GeUnN1aDltW71RlmEfpjbyw=="],
"@turbo/linux-64": ["@turbo/linux-64@2.10.2", "", { "os": "linux", "cpu": "x64" }, "sha512-mMsf5IIhiKuceEXNstd25IbadjBXZ0amxzFOqliEzJX6HyeeHdBQPVSY583PWqYDyqM/FB8d5ZjkthfBSeuH3Q=="],
"@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Wcng1i2kaKmXutmwxT9MUoYZvdaIekXAdlGr4+0TpgbhGLw7nDuEcRBFrxb5BbRoX1d1q8SpdRxLc45TvDZIdQ=="],
"@turbo/windows-64": ["@turbo/windows-64@2.10.2", "", { "os": "win32", "cpu": "x64" }, "sha512-SsNhM7Ho7EpAdwtrJKBOic9Hso23vu6Dp0gAfLOvUFjPzurr/sGQlXZEvr6z89ne4RDOypTwz5CBDrixpMKtXw=="],
"@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gf+S7ICAdimT/n02bOuVWKvhHnct/HYjZg3oBNIz5hZ9ZyWHbQim9J3P5Qip8WpX0ksxF7eaBVziJCuLnjhqDg=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
"@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
@@ -5333,7 +5321,19 @@
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
"turbo": ["turbo@2.10.2", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.2", "@turbo/darwin-arm64": "2.10.2", "@turbo/linux-64": "2.10.2", "@turbo/linux-arm64": "2.10.2", "@turbo/windows-64": "2.10.2", "@turbo/windows-arm64": "2.10.2" }, "bin": { "turbo": "bin/turbo" } }, "sha512-wTExrNrRjB8qzIcg+ZLm0A3GFNLDsWNwdS/RBXB0FPrBDyzk3i96Yx+TxWZC7a0k1SIreFB8ciUbxjmEqTH8IQ=="],
"turbo": ["turbo@2.8.13", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.13", "turbo-darwin-arm64": "2.8.13", "turbo-linux-64": "2.8.13", "turbo-linux-arm64": "2.8.13", "turbo-windows-64": "2.8.13", "turbo-windows-arm64": "2.8.13" }, "bin": { "turbo": "bin/turbo" } }, "sha512-nyM99hwFB9/DHaFyKEqatdayGjsMNYsQ/XBNO6MITc7roncZetKb97MpHxWf3uiU+LB9c9HUlU3Jp2Ixei2k1A=="],
"turbo-darwin-64": ["turbo-darwin-64@2.8.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-PmOvodQNiOj77+Zwoqku70vwVjKzL34RTNxxoARjp5RU5FOj/CGiC6vcDQhNtFPUOWSAaogHF5qIka9TBhX4XA=="],
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kI+anKcLIM4L8h+NsM7mtAUpElkCOxv5LgiQVQR8BASyDFfc8Efj5kCk3cqxuxOvIqx0sLfCX7atrHQ2kwuNJQ=="],
"turbo-linux-64": ["turbo-linux-64@2.8.13", "", { "os": "linux", "cpu": "x64" }, "sha512-j29KnQhHyzdzgCykBFeBqUPS4Wj7lWMnZ8CHqytlYDap4Jy70l4RNG46pOL9+lGu6DepK2s1rE86zQfo0IOdPw=="],
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-OEl1YocXGZDRDh28doOUn49QwNe82kXljO1HXApjU0LapkDiGpfl3jkAlPKxEkGDSYWc8MH5Ll8S16Rf5tEBYg=="],
"turbo-windows-64": ["turbo-windows-64@2.8.13", "", { "os": "win32", "cpu": "x64" }, "sha512-717bVk1+Pn2Jody7OmWludhEirEe0okoj1NpRbSm5kVZz/yNN/jfjbxWC6ilimXMz7xoMT3IDfQFJsFR3PMANA=="],
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-R819HShLIT0Wj6zWVnIsYvSNtRNj1q9VIyaUz0P24SMcLCbQZIm1sV09F4SDbg+KCCumqD2lcaR2UViQ8SnUJA=="],
"turndown": ["turndown@7.2.0", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A=="],
+1 -1
View File
@@ -104,7 +104,7 @@
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "catalog:",
"turbo": "2.10.2"
"turbo": "2.8.13"
},
"dependencies": {
"@aws-sdk/client-s3": "3.933.0",
@@ -48,40 +48,6 @@ test("does not reverse visible rows when the user wheels during shell remeasurem
await reportVisualStability(testInfo, "wheel-during-resize", trace, rowPairPlan(regions, 1))
})
test("keeps moving upward while drag-selecting above the timeline", async ({ page }) => {
await setupTimeline(page, {
messages: history(80),
viewport: { width: 1400, height: 700 },
reducedMotion: true,
})
const scroller = page.locator(".scroll-view__viewport", { has: page.locator("[data-timeline-row]") })
const text = page.getByText("History 79.", { exact: false })
await expect(text).toBeVisible()
await scroller.evaluate((element) => {
element.dataset.selectionLength = "0"
document.addEventListener("selectionchange", () => {
element.dataset.selectionLength = String(
Math.max(Number(element.dataset.selectionLength), window.getSelection()?.toString().length ?? 0),
)
})
})
const textBox = await text.boundingBox()
const scrollBox = await scroller.boundingBox()
expect(textBox).not.toBeNull()
expect(scrollBox).not.toBeNull()
if (!textBox || !scrollBox) return
await page.mouse.move(textBox.x + textBox.width - 10, textBox.y + textBox.height / 2)
await page.mouse.down()
await page.mouse.move(textBox.x + 20, scrollBox.y - 120, { steps: 30 })
await expect.poll(() => scroller.evaluate((element) => Number(element.dataset.selectionLength))).toBeGreaterThan(0)
await expect
.poll(() => scroller.evaluate((element) => element.scrollHeight - element.clientHeight - element.scrollTop))
.toBeGreaterThan(500)
await page.mouse.up()
})
test("does not pull a keyboard-scrolled user during shell remeasurement", async ({ page }, testInfo) => {
const shellID = "prt_keyboard_01_shell"
const followingID = "prt_keyboard_02_following"
@@ -1,151 +0,0 @@
import type { Page } from "@playwright/test"
import { expectSessionTitle } from "../../utils/waits"
import { mockOpenCodeServer } from "../../utils/mock-server"
import { benchmark, expect, withBenchmarkPage } from "../benchmark"
import { fixture } from "./session-timeline-stress.fixture"
import { installStressSessionTabs, stressSessionHref } from "./timeline-test-helpers"
import { measureSessionSwitch, waitForStableTimeline } from "./session-tab-switch-probe"
type ParentHydrationBenchmarkMode = "natural" | "candidate"
const mode = process.env.SESSION_PARENT_HYDRATION_BENCHMARK_MODE ?? "natural"
if (mode !== "natural" && mode !== "candidate") throw new Error(`Unknown parent hydration benchmark mode: ${mode}`)
const userID = "msg_parent_hydration_user"
const user = {
...fixture.messages[fixture.targetID][0]!,
info: { ...fixture.messages[fixture.targetID][0]!.info, id: userID, time: { created: 1700001000000 } },
parts: fixture.messages[fixture.targetID][0]!.parts.map((part, index) => ({
...part,
id: `prt_parent_hydration_user_${index}`,
messageID: userID,
})),
}
const assistantSeed = fixture.messages[fixture.targetID][3]!
const assistants = Array.from({ length: 14 }, (_, index) => {
const messageID = `msg_parent_hydration_${String(index).padStart(2, "0")}`
return {
...assistantSeed,
info: {
...assistantSeed.info,
id: messageID,
parentID: userID,
time: { created: 1700001001000 + index * 1_000, completed: 1700001001500 + index * 1_000 },
},
parts: assistantSeed.parts.map((part, partIndex) => ({
...part,
id: `prt_parent_hydration_${String(index).padStart(2, "0")}_${partIndex}`,
messageID,
})),
}
})
const messages = [user, ...assistants]
const target = fixture.sessions.find((session) => session.id === fixture.targetID)!
const lastID = userID
const lastPartID = assistants.at(-1)!.parts.at(-1)!.id
benchmark("hydrates an orphaned latest turn after a cold session click", async ({ browser, report }, testInfo) => {
benchmark.setTimeout(180_000)
const results = [] as Awaited<ReturnType<typeof trial>>[]
for (let run = 0; run < 5; run++) {
results.push(
await withBenchmarkPage(
browser,
`session-parent-hydration-${mode}-${run}`,
(page) => trial(page, mode),
testInfo,
),
)
}
const timing = results.map((result) => result.metrics.firstCorrectObservedMs!).sort((a, b) => a - b)
report(
{
results: results.map((result) => ({ ...result.metrics, historyGateCount: result.historyGateCount })),
summary: {
firstCorrectObservedMs: { min: timing[0], median: timing[2], max: timing.at(-1) },
blankSamples: results.map((result) => result.metrics.blankSamples),
requestCounts: {
list: results.map((result) => result.requestCounts.list),
parent: results.map((result) => result.requestCounts.parent),
},
historyGateCount: results.map((result) => result.historyGateCount),
},
},
{ mode },
)
})
async function trial(page: Page, mode: ParentHydrationBenchmarkMode) {
const requests: { type: "list" | "parent"; before?: string }[] = []
const history = mode === "candidate" ? Promise.withResolvers<void>() : undefined
let historyGates = 0
await mockOpenCodeServer(page, {
sessions: fixture.sessions.filter((session) => session.id === fixture.sourceID),
provider: fixture.provider,
directory: fixture.directory,
project: fixture.project,
messageDelay: 50,
onMessages: (request) => {
if (request.sessionID === fixture.targetID && request.phase === "start")
requests.push({ type: "list", before: request.before })
},
beforeMessagesResponse: (request) => {
if (mode !== "candidate" || request.sessionID !== fixture.targetID || !request.before) return Promise.resolve()
historyGates++
return history!.promise
},
onMessage: (request) => {
if (request.sessionID === fixture.targetID && request.messageID === userID) requests.push({ type: "parent" })
},
message: (sessionID, messageID) => {
if (sessionID !== fixture.targetID || messageID !== userID) return
return user
},
pageMessages: (sessionID, limit, before) => {
const items = sessionID === fixture.targetID ? messages : fixture.messages[fixture.sourceID]
const end = before ? items.findIndex((message) => message.info.id === before) : items.length
const start = Math.max(0, end - limit)
return { items: items.slice(start, end), cursor: start > 0 ? items[start]!.info.id : undefined }
},
})
await page.route(`**/session/${fixture.targetID}`, (route) =>
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(target) }),
)
await installStressSessionTabs(page, { sessionIDs: [fixture.sourceID] })
await page.goto(stressSessionHref(fixture.sourceID))
await expectSessionTitle(page, fixture.expected.sourceTitle)
await waitForStableTimeline(page, fixture.expected.sourceMessageIDs.at(-1)!)
const href = stressSessionHref(fixture.targetID)
await page.evaluate(
({ href, title }) => {
const link = document.createElement("a")
link.id = "parent-hydration-target"
link.href = href
link.textContent = title
document.body.append(link)
},
{ href, title: target.title },
)
const metrics = await measureSessionSwitch(page, {
destinationIDs: messages.map((message) => message.info.id),
sourceIDs: fixture.messages[fixture.sourceID].map((message) => message.info.id),
lastID,
requiredPartID: lastPartID,
requireBottomAnchor: false,
href,
switch: async () => {
await page.locator("#parent-hydration-target").click()
await expectSessionTitle(page, target.title)
},
}).finally(() => history?.resolve())
expect(metrics.firstCorrectObservedMs).not.toBeNull()
const requestCounts = {
list: requests.filter((request) => request.type === "list").length,
parent: requests.filter((request) => request.type === "parent").length,
}
if (mode === "candidate") {
expect(requestCounts.parent).toBe(1)
expect(historyGates).toBe(1)
}
return { metrics, requestCounts, historyGateCount: historyGates }
}
@@ -4,8 +4,6 @@ export type SessionSwitchSample = {
source: string[]
hasVisibleRows: boolean
last: boolean
requiredPartVisible?: boolean
bottomAnchorRequired?: boolean
bottomErrorPx?: number
review?: {
fileHost: boolean
@@ -43,8 +41,7 @@ export function isCorrectDestination(sample: SessionSwitchSample) {
sample.destination.length > 0 &&
sample.source.length === 0 &&
sample.last &&
sample.requiredPartVisible !== false &&
(sample.bottomAnchorRequired === false || Math.abs(sample.bottomErrorPx ?? Infinity) <= 1)
Math.abs(sample.bottomErrorPx ?? Infinity) <= 1
)
}
@@ -8,16 +8,9 @@ type SessionSwitchProbe = {
async function installSessionSwitchProbe(
page: Page,
input: {
destinationIDs: string[]
sourceIDs: string[]
lastID: string
requiredPartID?: string
requireBottomAnchor?: boolean
href: string
},
input: { destinationIDs: string[]; sourceIDs: string[]; lastID: string; href: string },
) {
await page.evaluate(({ destinationIDs, sourceIDs, lastID, requiredPartID, requireBottomAnchor, href }) => {
await page.evaluate(({ destinationIDs, sourceIDs, lastID, href }) => {
const destination = new Set(destinationIDs)
const source = new Set(sourceIDs)
const samples: SessionSwitchSample[] = []
@@ -73,13 +66,6 @@ async function installSessionSwitchProbe(
const rect = element.getBoundingClientRect()
return rect.bottom > view.top && rect.top < view.bottom
})
const requiredPartVisible = requiredPartID
? [...root.querySelectorAll<HTMLElement>("[data-timeline-part-id]")].some((element) => {
if (element.dataset.timelinePartId !== requiredPartID) return false
const rect = element.getBoundingClientRect()
return rect.width > 0 && rect.height > 0 && rect.bottom > view.top && rect.top < view.bottom
})
: undefined
const spacer = root.querySelector<HTMLElement>('[data-timeline-row="bottom-spacer"]')?.getBoundingClientRect()
samples.push({
observedAtMs,
@@ -87,22 +73,11 @@ async function installSessionSwitchProbe(
source: visible.filter((id) => source.has(id)),
hasVisibleRows,
last: visible.includes(lastID),
requiredPartVisible,
bottomAnchorRequired: requireBottomAnchor !== false,
bottomErrorPx: spacer ? spacer.bottom - view.bottom : undefined,
review,
})
} else {
samples.push({
observedAtMs,
destination: [],
source: [],
hasVisibleRows: false,
last: false,
requiredPartVisible: requiredPartID ? false : undefined,
bottomAnchorRequired: requireBottomAnchor !== false,
review,
})
samples.push({ observedAtMs, destination: [], source: [], hasVisibleRows: false, last: false, review })
}
requestAnimationFrame(sample)
}, 0)
@@ -142,8 +117,7 @@ async function waitForStableSessionSwitch(page: Page) {
sample.destination.length > 0 &&
sample.source.length === 0 &&
sample.last &&
sample.requiredPartVisible !== false &&
(sample.bottomAnchorRequired === false || Math.abs(sample.bottomErrorPx ?? Infinity) <= 1),
Math.abs(sample.bottomErrorPx ?? Infinity) <= 1,
)
)
})
@@ -161,27 +135,13 @@ async function collectSessionSwitchResult(page: Page) {
export async function measureSessionSwitch(
page: Page,
input: {
destinationIDs: string[]
sourceIDs: string[]
lastID: string
requiredPartID?: string
requireBottomAnchor?: boolean
href: string
switch: () => Promise<void>
},
input: { destinationIDs: string[]; sourceIDs: string[]; lastID: string; href: string; switch: () => Promise<void> },
) {
const { switch: run, ...probe } = input
await installSessionSwitchProbe(page, probe)
try {
await run()
await waitForStableSessionSwitch(page)
return await collectSessionSwitchResult(page)
} finally {
await page.evaluate(() => {
;(window as Window & { __sessionSwitchProbe?: SessionSwitchProbe }).__sessionSwitchProbe?.stop()
})
}
await run()
await waitForStableSessionSwitch(page)
return collectSessionSwitchResult(page)
}
export async function waitForStableTimeline(page: Page, lastID: string) {
@@ -1,46 +0,0 @@
import { expect, test } from "bun:test"
import type { Page, Route } from "@playwright/test"
import { mockOpenCodeServer } from "../../utils/mock-server"
test("applies message latency after a list response gate is released", async () => {
const events: string[] = []
const gate = Promise.withResolvers<void>()
let handler: ((route: Route) => Promise<void>) | undefined
const page = {
route: (_url: string, callback: (route: Route) => Promise<void>) => {
handler = callback
return Promise.resolve()
},
} as unknown as Page
await mockOpenCodeServer(page, {
provider: {},
directory: "C:/OpenCode",
project: {},
sessions: [{ id: "session" }],
messageDelay: 25,
beforeMessagesResponse: () => {
events.push("before")
return gate.promise
},
onMessages: (request) => events.push(request.phase),
pageMessages: () => {
events.push("page")
return { items: [] }
},
})
const response = handler!({
request: () => ({ url: () => "http://127.0.0.1:4096/session/session/message" }),
fulfill: () => {
events.push("fulfill")
return Promise.resolve()
},
} as unknown as Route)
expect(events).toEqual(["start", "before"])
const released = performance.now()
gate.resolve()
await response
expect(performance.now() - released).toBeGreaterThanOrEqual(20)
expect(events).toEqual(["start", "before", "page", "end", "fulfill"])
})
@@ -52,35 +52,3 @@ test("reports missing correctness without throwing", () => {
expect(result.firstCorrectObservedMs).toBeNull()
expect(result.stableObservedMs).toBeNull()
})
test("requires an explicitly tracked part to be visible", () => {
const result = classifySessionSwitch([
{
observedAtMs: 16,
destination: ["destination"],
source: [],
hasVisibleRows: true,
last: true,
requiredPartVisible: false,
bottomErrorPx: 0,
},
])
expect(result.firstCorrectObservedMs).toBeNull()
})
test("can measure content correctness without requiring a bottom anchor", () => {
const result = classifySessionSwitch([
{
observedAtMs: 16,
destination: ["destination"],
source: [],
hasVisibleRows: true,
last: true,
requiredPartVisible: true,
bottomAnchorRequired: false,
},
])
expect(result.firstCorrectObservedMs).toBe(16)
})
@@ -1,55 +0,0 @@
import { expect, test } from "bun:test"
import type { Page } from "@playwright/test"
import { measureSessionSwitch } from "../timeline/session-tab-switch-probe"
function testPage(waitFailure?: Error) {
const stops: unknown[] = []
const page = {
evaluate: async (_callback: unknown, input?: unknown) => {
if (input) return
stops.push(undefined)
},
waitForFunction: async () => {
if (waitFailure) throw waitFailure
},
} as unknown as Page
return { page, stops }
}
function input(run: () => Promise<void>) {
return {
destinationIDs: ["destination"],
sourceIDs: ["source"],
lastID: "destination",
href: "/session/destination",
switch: run,
}
}
test("stops sampling when the session switch fails", async () => {
const failure = new Error("switch failed")
const context = testPage()
await expect(
measureSessionSwitch(
context.page,
input(async () => Promise.reject(failure)),
),
).rejects.toBe(failure)
expect(context.stops).toHaveLength(1)
})
test("stops sampling when the stable wait fails", async () => {
const failure = new Error("stable wait failed")
const context = testPage(failure)
await expect(
measureSessionSwitch(
context.page,
input(async () => {}),
),
).rejects.toBe(failure)
expect(context.stops).toHaveLength(1)
})
@@ -1,83 +0,0 @@
import { expect, test } from "@playwright/test"
import { mockOpenCodeServer } from "../utils/mock-server"
import { expectAppVisible } from "../utils/waits"
const draftID = "draft_new_session_panel_corner"
const directory = "C:/OpenCode/NewSessionPanelCorner"
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
test.use({
viewport: { width: 935, height: 522 },
deviceScaleFactor: 1,
})
test("matches the rounded panel corners to the dark new-session background", async ({ page }, testInfo) => {
await mockOpenCodeServer(page, {
directory,
project: {
id: "proj_new_session_panel_corner",
worktree: directory,
vcs: "git",
name: "new-session-panel-corner",
time: { created: 1700000000000, updated: 1700000000000 },
sandboxes: [],
},
provider: { all: [], connected: [], default: {} },
sessions: [],
pageMessages: () => ({ items: [] }),
})
await page.addInitScript(
({ directory, draftID, server }) => {
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
localStorage.setItem("opencode-theme-id", "oc-2")
localStorage.setItem("opencode-color-scheme", "dark")
localStorage.setItem(
"opencode.global.dat:server",
JSON.stringify({
projects: { local: [{ worktree: directory, expanded: true }] },
lastProject: { local: directory },
}),
)
localStorage.setItem(
"opencode.window.browser.dat:tabs",
JSON.stringify([{ type: "draft", draftID, server, directory }]),
)
},
{ directory, draftID, server },
)
await page.goto(`/new-session?draftId=${draftID}`)
await expectAppVisible(page.locator('[data-component="prompt-input"]'))
await expect(page.locator("html")).toHaveAttribute("data-color-scheme", "dark")
const panel = page.locator('main div[class*="rounded-[10px]"][class*="overflow-hidden"]')
await expect(panel).toHaveCount(1)
const box = await panel.boundingBox()
if (!box) throw new Error("New-session panel bounds are unavailable")
const screenshot = await page.screenshot({ path: testInfo.outputPath("new-session-dark.png") })
const corners = await page.evaluate(
async ({ source, points }) => {
const image = new Image()
image.src = source
await image.decode()
const canvas = document.createElement("canvas")
canvas.width = image.naturalWidth
canvas.height = image.naturalHeight
const context = canvas.getContext("2d")
if (!context) throw new Error("2D canvas is unavailable")
context.drawImage(image, 0, 0)
return points.map((point) => Array.from(context.getImageData(point.x, point.y, 1, 1).data))
},
{
source: `data:image/png;base64,${screenshot.toString("base64")}`,
points: [
{ x: Math.floor(box.x), y: Math.floor(box.y) },
{ x: Math.ceil(box.x + box.width) - 1, y: Math.floor(box.y) },
{ x: Math.floor(box.x), y: Math.ceil(box.y + box.height) - 1 },
{ x: Math.ceil(box.x + box.width) - 1, y: Math.ceil(box.y + box.height) - 1 },
],
},
)
expect(corners.every(([red, green, blue, alpha]) => red <= 8 && green <= 8 && blue <= 8 && alpha === 255)).toBe(true)
})
@@ -1,220 +0,0 @@
import { base64Encode } from "@opencode-ai/core/util/encode"
import { expect, test, type Page } from "@playwright/test"
import {
assistantMessage,
directory,
messageUpdated,
project,
session,
sessionID,
status,
textPart,
title,
userID,
userMessage,
} from "../performance/timeline-stability/fixture"
import { mockOpenCodeServer } from "../utils/mock-server"
import { installSseTransport } from "../utils/sse-transport"
import { expectSessionTitle } from "../utils/waits"
const assistants = Array.from({ length: 14 }, (_, index) =>
assistantMessage([textPart(`prt_history_root_${index}`, `Assistant response ${index}`)], {
id: `msg_${String(index + 1001).padStart(4, "0")}_history_root_assistant`,
parentID: userID,
created: 1700000001000 + index * 1_000,
completed: index < 13,
}),
)
const messages = [userMessage(), ...assistants]
const lastAssistant = assistants.at(-1)!
const lastPartID = assistants.at(-1)!.parts[0]!.id
const userPartID = `prt_${userID}_text`
const completed = {
...lastAssistant.info,
time: { ...lastAssistant.info.time, completed: lastAssistant.info.time.created + 15_000 },
}
const scenarios = [
{ name: "completion", info: completed, idleFirst: false, interrupted: false },
{
name: "interruption",
info: { ...completed, error: { name: "MessageAbortedError", data: { message: "Stopped" } } },
idleFirst: true,
interrupted: true,
},
] as const
test.use({ viewport: { width: 646, height: 1385 } })
for (const scenario of scenarios) {
test(`keeps the latest user turn visible through ${scenario.name}`, async ({ page }) => {
const requests: { before?: string; phase: "start" | "end" }[] = []
const pages: { before?: string; limit: number }[] = []
const roots: { sessionID: string; messageID: string }[] = []
const sequence: string[] = []
const history = Promise.withResolvers<void>()
const transport = await installSseTransport<{ directory: string; payload: Record<string, unknown> }>(page, {
server: `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`,
retry: 20,
})
await mockOpenCodeServer(page, {
directory,
project: project(),
provider: {
all: [
{
id: "opencode",
name: "OpenCode",
models: {
"claude-opus-4-6": {
id: "claude-opus-4-6",
name: "Claude Opus 4.6",
limit: { context: 200_000 },
},
},
},
],
connected: ["opencode"],
default: { providerID: "opencode", modelID: "claude-opus-4-6" },
},
sessions: [session()],
sessionStatus: { [sessionID]: { type: "busy" } },
beforeMessagesResponse: (request) => (request.before ? history.promise : Promise.resolve()),
onMessages: (request) => {
requests.push(request)
sequence.push(`messages:${request.phase}:${request.before ?? "latest"}`)
},
onMessage: (request) => {
roots.push(request)
sequence.push(`message:${request.messageID}`)
},
message: (requestedSessionID, messageID) => {
if (requestedSessionID !== sessionID) return
return messages.find((item) => item.info.id === messageID)
},
pageMessages: (_, limit, before) => {
pages.push({ before, limit })
const end = before ? messages.findIndex((message) => message.info.id === before) : messages.length
const start = Math.max(0, end - limit)
return {
items: messages.slice(start, end),
cursor: start > 0 ? messages[start]!.info.id : undefined,
}
},
})
await page.addInitScript(
({ userPartID, lastPartID }) => {
const state = { armed: false, hidden: false, samples: 0, stop: false }
;(window as Window & { __historyRootProbe?: typeof state }).__historyRootProbe = state
const sample = () => {
if (state.armed) {
const virtual = document.querySelector<HTMLElement>("[data-timeline-virtual-content]")
const viewport = virtual?.closest<HTMLElement>(".scroll-view__viewport")
const view = viewport?.getBoundingClientRect()
const visible = (partID: string) => {
const part = viewport?.querySelector<HTMLElement>(`[data-timeline-part-id="${partID}"]`)
const rect = part?.getBoundingClientRect()
return (
!!rect &&
!!view &&
rect.width > 0 &&
rect.height > 0 &&
rect.bottom > view.top &&
rect.top < view.bottom
)
}
if (!virtual || !visible(userPartID) || !visible(lastPartID)) state.hidden = true
state.samples++
}
if (!state.stop) requestAnimationFrame(() => setTimeout(sample, 0))
}
requestAnimationFrame(() => setTimeout(sample, 0))
},
{ userPartID, lastPartID },
)
await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
await transport.waitForConnection()
await expectSessionTitle(page, title)
await expect(page.locator(`[data-timeline-part-id="${lastPartID}"]`)).toBeVisible()
await expect(page.locator(`[data-timeline-part-id="${userPartID}"]`)).toBeVisible()
await expect.poll(() => requests.filter((request) => request.phase === "start").length).toBe(2)
expect(requests.filter((request) => request.phase === "end")).toHaveLength(1)
expect(sequence.slice(0, 4)).toEqual([
"messages:start:latest",
"messages:end:latest",
`message:${userID}`,
`messages:start:${messages.at(-2)!.info.id}`,
])
await page.evaluate(() => {
;(
window as Window & {
__historyRootProbe?: { armed: boolean }
}
).__historyRootProbe!.armed = true
})
await waitForProbeSamples(page, 0)
expect(await historyRootHidden(page)).toBe(false)
const beforeHistory = await probeSamples(page)
history.resolve()
await expect(page.locator('[data-timeline-part-id^="prt_history_root_"]')).toHaveCount(14)
await expect(page.getByRole("button", { name: "Stop" })).toBeVisible()
await waitForProbeSamples(page, beforeHistory)
expect(pages[0]).toEqual({ before: undefined, limit: 2 })
expect(roots).toEqual([{ sessionID, messageID: userID }])
const message = messageUpdated(scenario.info)
const idle = status("idle")
for (const event of scenario.idleFirst ? [idle, message] : [message, idle]) {
const beforeEvent = await probeSamples(page)
await transport.send(event)
if (event === idle) await expect(page.getByRole("button", { name: "Stop" })).toHaveCount(0)
if (event === message && scenario.interrupted)
await expect(page.getByText("Interrupted", { exact: true })).toBeVisible()
await waitForProbeSamples(page, beforeEvent)
const current = await timelineState(page)
expect(current, JSON.stringify(current)).toMatchObject({ virtual: true })
expect(current.rows, JSON.stringify(current)).toBeGreaterThan(0)
}
expect(requests[0]).toEqual({ before: undefined, phase: "start", sessionID })
expect(requests[1]).toEqual({ before: undefined, phase: "end", sessionID })
await expect(page.getByRole("button", { name: "Stop" })).toHaveCount(0)
await expect(page.locator('[data-timeline-row="bottom-spacer"]')).toBeVisible()
if (scenario.interrupted) await expect(page.getByText("Interrupted", { exact: true })).toBeVisible()
expect(
await page.evaluate(() => {
const state = (window as Window & { __historyRootProbe?: { hidden: boolean; stop: boolean } })
.__historyRootProbe!
state.stop = true
return state.hidden
}),
).toBe(false)
})
}
function timelineState(page: Page) {
return page.evaluate(() => ({
virtual: !!document.querySelector("[data-timeline-virtual-content]"),
rows: document.querySelectorAll("[data-timeline-key]").length,
}))
}
function probeSamples(page: Page) {
return page.evaluate(
() => (window as Window & { __historyRootProbe?: { samples: number } }).__historyRootProbe!.samples,
)
}
async function waitForProbeSamples(page: Page, after: number) {
await page.waitForFunction(
(after) =>
(window as Window & { __historyRootProbe?: { samples: number } }).__historyRootProbe!.samples >= after + 3,
after,
)
}
function historyRootHidden(page: Page) {
return page.evaluate(
() => (window as Window & { __historyRootProbe?: { hidden: boolean } }).__historyRootProbe!.hidden,
)
}
@@ -1,89 +0,0 @@
import { base64Encode } from "@opencode-ai/core/util/encode"
import { expect, test } from "@playwright/test"
import { mockOpenCodeServer } from "../utils/mock-server"
import { expectSessionTitle } from "../utils/waits"
const directory = "C:/OpenCode/TerminalComposerFocus"
const projectID = "proj_terminal_composer_focus"
const sessionID = "ses_terminal_composer_focus"
const ptyID = "pty_terminal_composer_focus"
test.use({ viewport: { width: 1440, height: 900 } })
test("routes typing to the composer unless the open terminal is focused", async ({ page }) => {
await mockOpenCodeServer(page, {
directory,
project: {
id: projectID,
worktree: directory,
vcs: "git",
name: "terminal-composer-focus",
time: { created: 1700000000000, updated: 1700000000000 },
sandboxes: [],
},
provider: {
all: [
{
id: "opencode",
name: "OpenCode",
models: { test: { id: "test", name: "Test", limit: { context: 200_000 } } },
},
],
connected: ["opencode"],
default: { providerID: "opencode", modelID: "test" },
},
sessions: [
{
id: sessionID,
slug: "terminal-composer-focus",
projectID,
directory,
title: "Terminal composer focus",
version: "dev",
time: { created: 1700000000000, updated: 1700000000000 },
},
],
pageMessages: () => ({ items: [] }),
})
await page.route("**/pty", (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ id: ptyID, title: "Terminal 1" }),
}),
)
await page.route(`**/pty/${ptyID}`, (route) =>
route.fulfill({ status: 200, contentType: "application/json", body: "{}" }),
)
await page.route(`**/pty/${ptyID}/connect-token*`, (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
headers: { "access-control-allow-origin": "*" },
body: JSON.stringify({ ticket: "e2e-ticket" }),
}),
)
await page.routeWebSocket(new RegExp(`/pty/${ptyID}/connect`), () => undefined)
await page.addInitScript(() => {
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
})
await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
await expectSessionTitle(page, "Terminal composer focus")
const composer = page.locator('[data-component="prompt-input"]')
const terminal = page.locator('[data-component="terminal"]')
await page.keyboard.press("Control+Backquote")
await expect(terminal).toBeVisible()
await expect.poll(() => terminal.evaluate((element) => element.contains(document.activeElement))).toBe(true)
await page.keyboard.type("x")
await expect(composer).toHaveText("")
await page.waitForTimeout(300)
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur())
await page.keyboard.type("a")
await expect(composer).toBeFocused()
await expect(composer).toHaveText("a")
})
@@ -7,7 +7,7 @@ const projectID = "proj_hidden_terminal_regression"
const sessionID = "ses_hidden_terminal_regression"
const title = "Hidden terminal regression"
test("unmounts the terminal panel while it is hidden", async ({ page }) => {
test("unmounts the terminal renderer while the pane is hidden", async ({ page }) => {
await page.setViewportSize({ width: 1400, height: 900 })
await mockOpenCodeServer(page, {
directory,
@@ -64,14 +64,13 @@ test("unmounts the terminal panel while it is hidden", async ({ page }) => {
await expect(page.locator('[data-component="terminal"]')).toBeVisible()
await page.keyboard.press("Control+Backquote")
await expect(panel).toHaveCount(0)
await expect(panel).toHaveAttribute("aria-hidden", "true")
await expect(page.locator('[data-component="terminal"]')).toHaveCount(0)
await page.setViewportSize({ width: 1200, height: 700 })
await expect(page.locator('[data-component="terminal"]')).toHaveCount(0)
await page.keyboard.press("Control+Backquote")
await expect(panel).toBeVisible()
await expect(page.locator('[data-component="terminal"]')).toBeVisible()
})
-1
View File
@@ -10,7 +10,6 @@
"./performance/timeline-stability/fixture.test.ts",
"./performance/timeline-stability/fixture.ts",
"./performance/unit/visual-stability.test.ts",
"./regression/new-session-panel-corner.spec.ts",
"./regression/session-timeline-context-resize.spec.ts",
"./utils/**/*.ts"
]
+2 -27
View File
@@ -1,7 +1,7 @@
import type { Page, Route } from "@playwright/test"
const emptyList = new Set(["/skill", "/command", "/lsp", "/formatter", "/vcs/status", "/vcs/diff"])
const emptyObject = new Set(["/global/config", "/config", "/provider/auth", "/mcp", "/experimental/resource"])
const emptyObject = new Set(["/global/config", "/config", "/provider/auth", "/mcp"])
export interface MockServerConfig {
provider: unknown
@@ -11,10 +11,7 @@ export interface MockServerConfig {
pageMessages: (sessionId: string, limit: number, before?: string) => { items: unknown[]; cursor?: string }
vcsDiff?: unknown[]
messageDelay?: number
beforeMessagesResponse?: (input: { sessionID: string; before?: string }) => Promise<void>
onMessages?: (input: { sessionID: string; before?: string; phase: "start" | "end" }) => void
message?: (sessionID: string, messageID: string) => unknown
onMessage?: (input: { sessionID: string; messageID: string }) => void
events?: () => unknown[]
eventRetry?: number
todos?: (sessionID: string) => unknown[]
@@ -55,7 +52,6 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
const path = url.pathname
if (path === "/global/event" || path === "/event") return sse(route, config.events?.(), config.eventRetry)
if (path === "/global/health") return json(route, { healthy: true })
if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: false })
if (path === "/permission")
return json(route, typeof config.permissions === "function" ? config.permissions() : (config.permissions ?? []))
if (path === "/question")
@@ -66,14 +62,6 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
return json(route, await config.fileList(url.searchParams.get("path") ?? ""))
if (path === "/file/content" && config.fileContent)
return json(route, await config.fileContent(url.searchParams.get("path") ?? ""))
if (path === "/api/reference")
return json(route, {
location: {
directory: config.directory,
project: { id: (config.project as { id?: string }).id, directory: config.directory },
},
data: [],
})
if (emptyObject.has(path)) return json(route, {})
if (emptyList.has(path)) return json(route, [])
if (path in staticRoutes) return json(route, staticRoutes[path])
@@ -84,18 +72,6 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
return json(route, session ?? {})
}
const projectMatch = path.match(/^\/project\/([^/]+)$/)
if (projectMatch) return json(route, config.project)
const messageMatch = path.match(/^\/session\/([^/]+)\/message\/([^/]+)$/)
if (messageMatch) {
config.onMessage?.({ sessionID: messageMatch[1]!, messageID: messageMatch[2]! })
if (config.messageDelay !== undefined) await new Promise((resolve) => setTimeout(resolve, config.messageDelay))
const message = config.message?.(messageMatch[1]!, messageMatch[2]!)
if (message === undefined) return json(route, { error: "Message not found" }, undefined, 404)
return json(route, message)
}
const todoMatch = path.match(/^\/session\/([^/]+)\/todo$/)
if (todoMatch) return json(route, config.todos?.(todoMatch[1]!) ?? [])
if (/^\/session\/[^/]+\/(children|diff)$/.test(path)) return json(route, [])
@@ -106,8 +82,7 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
const before = token ? cursors.get(token) : undefined
if (token && !before) return json(route, { error: "Invalid cursor" }, undefined, 400)
config.onMessages?.({ sessionID: messagesMatch[1], before, phase: "start" })
await config.beforeMessagesResponse?.({ sessionID: messagesMatch[1]!, before })
if (config.messageDelay !== undefined) await new Promise((resolve) => setTimeout(resolve, config.messageDelay))
if (config.messageDelay) await new Promise((resolve) => setTimeout(resolve, config.messageDelay))
const limit = Number(url.searchParams.get("limit") ?? 80)
const pageData = config.pageMessages(messagesMatch[1], limit, before)
config.onMessages?.({ sessionID: messagesMatch[1], before, phase: "end" })
@@ -1,7 +1,7 @@
import { Component, For, Show } from "solid-js"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/core/util/path"
import type { ContextItem } from "@/context/prompt"
@@ -27,7 +27,7 @@ export const PromptContextItems: Component<ContextItemsProps> = (props) => {
const selected = props.active(item)
return (
<TooltipV2
<Tooltip
value={
<span class="flex max-w-[300px]">
<span class="text-text-invert-base truncate-start [unicode-bidi:plaintext] min-w-0">
@@ -78,7 +78,7 @@ export const PromptContextItems: Component<ContextItemsProps> = (props) => {
{(comment) => <div class="text-12-regular text-text-strong ml-5 pr-1 truncate">{comment()}</div>}
</Show>
</div>
</TooltipV2>
</Tooltip>
)
}}
</For>
@@ -1,271 +0,0 @@
import type { JSX } from "solid-js"
import { Show, createEffect, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
import { useSortable } from "@dnd-kit/solid/sortable"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tabs } from "@opencode-ai/ui/tabs"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Icon } from "@opencode-ai/ui/icon"
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
import { isDefaultTitle as isDefaultTerminalTitle } from "@/context/terminal-title"
import { useTerminal, type LocalPTY } from "@/context/terminal"
import { useLanguage } from "@/context/language"
import { focusTerminalById } from "@/pages/session/helpers"
export function SortableTerminalTabV2(props: {
terminal: LocalPTY
index: () => number
newLayout: boolean
onClose?: () => void
}): JSX.Element {
const terminal = useTerminal()
const language = useLanguage()
const sortable = useSortable({
get id() {
return props.terminal.id
},
get index() {
return props.index()
},
})
const [store, setStore] = createStore({
editing: false,
title: props.terminal.title,
menuOpen: false,
menuPosition: { x: 0, y: 0 },
blurEnabled: false,
})
let input: HTMLInputElement | undefined
let blurFrame: number | undefined
let editRequested = false
const isDefaultTitle = () => {
const number = props.terminal.titleNumber
if (!Number.isFinite(number) || number <= 0) return false
return isDefaultTerminalTitle(props.terminal.title, number)
}
const label = () => {
language.locale()
if (props.terminal.title && !isDefaultTitle()) return props.terminal.title
const number = props.terminal.titleNumber
if (Number.isFinite(number) && number > 0) return language.t("terminal.title.numbered", { number })
if (props.terminal.title) return props.terminal.title
return language.t("terminal.title")
}
const close = () => {
const count = terminal.all().length
void terminal.close(props.terminal.id)
if (count === 1) {
props.onClose?.()
}
}
const focus = () => {
if (store.editing) return
terminal.open(props.terminal.id)
if (document.activeElement instanceof HTMLElement) document.activeElement.blur()
focusTerminalById(props.terminal.id)
}
const edit = (e?: Event) => {
if (e) {
e.stopPropagation()
e.preventDefault()
}
setStore("blurEnabled", false)
setStore("title", props.terminal.title)
setStore("editing", true)
}
const save = () => {
if (!store.blurEnabled) return
const value = store.title.trim()
if (value && value !== props.terminal.title) {
terminal.update({ id: props.terminal.id, title: value })
}
setStore("editing", false)
}
const keydown = (e: KeyboardEvent) => {
if (e.key === "Enter") {
e.preventDefault()
save()
return
}
if (e.key === "Escape") {
e.preventDefault()
setStore("editing", false)
}
}
const menu = (e: MouseEvent) => {
e.preventDefault()
setStore("menuPosition", { x: e.clientX, y: e.clientY })
setStore("menuOpen", true)
}
createEffect(() => {
if (!store.editing) return
if (!input) return
input.focus()
input.select()
if (blurFrame !== undefined) cancelAnimationFrame(blurFrame)
blurFrame = requestAnimationFrame(() => {
blurFrame = undefined
setStore("blurEnabled", true)
})
})
onCleanup(() => {
if (blurFrame === undefined) return
cancelAnimationFrame(blurFrame)
})
return (
<div
ref={sortable.ref}
class="outline-none focus:outline-none focus-visible:outline-none"
classList={{
"h-full flex items-center": props.newLayout,
"h-full": !props.newLayout,
}}
>
<Show
when={props.newLayout}
fallback={
<div class="relative h-full">
<Tabs.Trigger
value={props.terminal.id}
onClick={focus}
onMouseDown={(e) => e.preventDefault()}
onContextMenu={menu}
class="!shadow-none"
classes={{
button: "border-0 outline-none focus:outline-none focus-visible:outline-none !shadow-none !ring-0",
}}
closeButton={
<IconButton
icon="close"
variant="ghost"
onClick={(e) => {
e.stopPropagation()
close()
}}
aria-label={language.t("terminal.close")}
/>
}
>
<span onDblClick={edit} classList={{ invisible: store.editing }}>
{label()}
</span>
</Tabs.Trigger>
<Show when={store.editing}>
<div class="absolute inset-0 flex items-center px-3 bg-muted z-10 pointer-events-auto">
<input
ref={input}
type="text"
value={store.title}
onInput={(e) => setStore("title", e.currentTarget.value)}
onBlur={save}
onKeyDown={keydown}
onMouseDown={(e) => e.stopPropagation()}
class="bg-transparent border-none outline-none text-sm min-w-0 flex-1"
/>
</div>
</Show>
<DropdownMenu open={store.menuOpen} onOpenChange={(open) => setStore("menuOpen", open)}>
<DropdownMenu.Portal>
<DropdownMenu.Content
class="fixed"
style={{
left: `${store.menuPosition.x}px`,
top: `${store.menuPosition.y}px`,
}}
onCloseAutoFocus={(e) => {
if (!editRequested) return
e.preventDefault()
editRequested = false
requestAnimationFrame(() => edit())
}}
>
<DropdownMenu.Item onSelect={() => (editRequested = true)}>
<Icon name="edit" class="w-4 h-4 mr-2" />
{language.t("common.rename")}
</DropdownMenu.Item>
<DropdownMenu.Item onSelect={close}>
<Icon name="close" class="w-4 h-4 mr-2" />
{language.t("common.close")}
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu>
</div>
}
>
<MenuV2.Context>
<MenuV2.Context.Trigger class="relative" as="div">
<Tabs.Trigger
value={props.terminal.id}
onClick={focus}
closeButton={
<IconButton
icon="close-small"
variant="ghost"
class="h-5 w-5"
onClick={(e) => {
e.stopPropagation()
close()
}}
aria-label={language.t("terminal.close")}
/>
}
hideCloseButton
onMiddleClick={close}
>
<span
class="truncate"
data-slot="terminal-tab-title"
onDblClick={edit}
classList={{ invisible: store.editing }}
>
{label()}
</span>
</Tabs.Trigger>
<Show when={store.editing}>
<div class="absolute inset-0 flex items-center bg-v2-background-bg-layer-01 z-10 pointer-events-auto rounded-[6px] shadow-[inset_0_0_0_0.5px_var(--v2-border-border-muted)] px-2">
<input
ref={input}
type="text"
value={store.title}
onInput={(e) => setStore("title", e.currentTarget.value)}
onBlur={save}
onKeyDown={keydown}
onMouseDown={(e) => e.stopPropagation()}
class="bg-transparent border-none outline-none min-w-0 flex-1 p-0 text-[13px] leading-4 tracking-[-0.04px] text-v2-text-text-base [font-weight:440] [font-variation-settings:'slnt'_0] [font-variant-numeric:tabular-nums]"
/>
</div>
</Show>
</MenuV2.Context.Trigger>
<MenuV2.Context.Portal>
<MenuV2.Context.Content
onCloseAutoFocus={(e) => {
if (!editRequested) return
e.preventDefault()
editRequested = false
requestAnimationFrame(() => edit())
}}
>
<MenuV2.Item onSelect={() => (editRequested = true)}>{language.t("common.rename")}</MenuV2.Item>
<MenuV2.Item onSelect={close}>{language.t("common.close")}</MenuV2.Item>
</MenuV2.Context.Content>
</MenuV2.Context.Portal>
</MenuV2.Context>
</Show>
</div>
)
}
@@ -3,7 +3,7 @@ import { createSignal, Show, type JSXElement } from "solid-js"
import "./titlebar-tab-popover.css"
// Initial hover delay before the preview appears, per design.
const OPEN_DELAY = 2_000
const OPEN_DELAY = 400
// Mouse-out delay: begin closing immediately (a brief exit animation plays).
const CLOSE_DELAY = 0
// After a preview closes, hovering a neighbouring tab within this window skips
@@ -17,6 +17,8 @@ import { createTabPromptState } from "@/context/prompt"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { canStartTabDrag, isTabCloseTarget } from "./titlebar-tab-gesture"
const sortableTransition = { duration: 0 }
function SessionTabSlot(props: {
tab: SessionTab
id: string
+2 -9
View File
@@ -374,16 +374,9 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
keybind: "mod+w",
hidden: true,
onSelect: () => {
tabsStoreActions.closeTab(tabsStore.findIndex((tab) => current === tab))
tabsStoreActions.removeTab(tabsStore.findIndex((tab) => current === tab))
},
},
{
id: "tab.reopenClosed",
category: language.t("command.category.file"),
title: language.t("command.tab.reopenClosed"),
keybind: "mod+shift+t",
onSelect: () => tabsStoreActions.reopenClosedTab(),
},
{
id: `tab.prev`,
category: "tab",
@@ -471,7 +464,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
}}
onClose={(tab) => {
const index = tabsStore.findIndex((item) => tabKey(item) === tabKey(tab))
if (index !== -1) tabsStoreActions.closeTab(index)
if (index !== -1) tabsStoreActions.removeTab(index)
}}
onReorder={(keys) => tabsStoreActions.reorder(keys)}
/>
-40
View File
@@ -1,40 +0,0 @@
import type { SessionTab, Tab } from "./tabs"
export type ClosedTab = {
tab: SessionTab
index: number
}
const CLOSED_TAB_LIMIT = 25
// Only session tabs are recorded; closing a draft tab deletes its persisted
// state, so a reopened draft would come back empty anyway.
export function pushClosedTab(stack: ClosedTab[], tab: Tab, index: number): ClosedTab[] {
if (tab.type !== "session") return stack
return [...stack, { tab: { ...tab }, index }].slice(-CLOSED_TAB_LIMIT)
}
// Pops the most recently closed tab that is not open again,
// discarding stale entries along the way.
export function takeClosedTab(stack: ClosedTab[], tabs: Tab[]): { entry?: ClosedTab; stack: ClosedTab[] } {
const remaining = [...stack]
while (remaining.length) {
const entry = remaining.pop()
if (entry && !isOpen(tabs, entry.tab)) return { entry, stack: remaining }
}
return { stack: remaining }
}
export function removeClosedTabs(stack: ClosedTab[], server: SessionTab["server"], sessionIDs: string[]) {
const removed = new Set(sessionIDs)
return stack.filter((entry) => entry.tab.server !== server || !removed.has(entry.tab.sessionId))
}
export function nextTabAfterClose(tabs: Tab[], index: number, active: boolean) {
if (!active) return undefined
return tabs[index + 1] ?? tabs[index - 1] ?? null
}
function isOpen(tabs: Tab[], tab: SessionTab) {
return tabs.some((item) => item.type === "session" && item.server === tab.server && item.sessionId === tab.sessionId)
}
@@ -15,13 +15,11 @@ const session = (id: string, parentID?: string): Session => ({
})
type UserMessage = Extract<Message, { role: "user" }>
type AssistantMessage = Extract<Message, { role: "assistant" }>
type TextPart = Extract<Part, { type: "text" }>
type MessageResponse = {
data: { info: Message; parts: Part[] }[]
response: { headers: Headers }
}
type SingleMessageResponse = { data: MessageResponse["data"][number] }
const userMessage = (id: string, input: Partial<UserMessage> = {}): UserMessage => ({
id,
@@ -33,22 +31,6 @@ const userMessage = (id: string, input: Partial<UserMessage> = {}): UserMessage
...input,
})
const assistantMessage = (id: string, parentID: string, input: Partial<AssistantMessage> = {}): AssistantMessage => ({
id,
sessionID: "child",
role: "assistant",
time: { created: Number(id.at(-1)), completed: Number(id.at(-1)) },
parentID,
modelID: "model",
providerID: "provider",
mode: "build",
agent: "build",
path: { cwd: "/repo", root: "/repo" },
cost: 0,
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
...input,
})
const textPart = (messageID: string, input: Partial<TextPart> = {}): TextPart => ({
id: "part",
sessionID: "child",
@@ -63,8 +45,6 @@ const response = (data: MessageResponse["data"] = [], cursor?: string): MessageR
response: { headers: new Headers(cursor ? { "x-next-cursor": cursor } : undefined) },
})
const singleResponse = (info: Message, parts: Part[] = []): SingleMessageResponse => ({ data: { info, parts } })
const deferredResponse = () => Promise.withResolvers<MessageResponse>()
function messageClient(...responses: Array<MessageResponse | Promise<MessageResponse>>) {
@@ -91,40 +71,6 @@ function messageClient(...responses: Array<MessageResponse | Promise<MessageResp
})
}
function rootMessageClient(
pages: Array<MessageResponse | Promise<MessageResponse>>,
roots: Array<SingleMessageResponse | Promise<SingleMessageResponse>>,
) {
let pageIndex = 0
let rootIndex = 0
const requests: unknown[] = []
const rootRequests: unknown[] = []
const rootWaiting = new Map<number, () => void>()
const client = {
session: {
get: async () => ({ data: session("child", "root") }),
messages: (input: unknown) => {
requests.push(input)
return pages[pageIndex++]
},
message: (input: unknown) => {
rootRequests.push(input)
rootWaiting.get(rootRequests.length)?.()
rootWaiting.delete(rootRequests.length)
return roots[rootIndex++]
},
},
} as unknown as OpencodeClient
return Object.assign(client, {
requests,
rootRequests,
rootRequested(count: number) {
if (rootRequests.length >= count) return Promise.resolve()
return new Promise<void>((resolve) => rootWaiting.set(count, resolve))
},
})
}
const retryImmediately: typeof retry = async (task, options = {}) => {
const attempts = options.attempts ?? 3
for (let attempt = 0; ; attempt++) {
@@ -178,238 +124,6 @@ describe("server session", () => {
expect(ctx.store.data.message.root).toEqual([])
})
test("backfills an assistant-only initial page through its user root", async () => {
const user = userMessage("message-1")
const assistants = [assistantMessage("message-2", user.id), assistantMessage("message-3", user.id)]
const client = rootMessageClient(
[
response(
assistants.map((info) => ({ info, parts: [] })),
"older",
),
],
[singleResponse(user)],
)
const store = createServerSession(client)
await store.sync("child")
expect(client.requests).toEqual([{ sessionID: "child", limit: 2, before: undefined }])
expect(client.rootRequests).toEqual([{ sessionID: "child", messageID: user.id }])
expect(store.data.message.child).toEqual([user, ...assistants])
expect(store.history.more("child")).toBe(true)
})
test("does not let an optimistic user suppress initial root backfill", async () => {
const user = userMessage("message-1")
const part = textPart(user.id)
const assistants = [assistantMessage("message-2", user.id), assistantMessage("message-3", user.id)]
const client = rootMessageClient(
[
response(
assistants.map((info) => ({ info, parts: [] })),
"older",
),
],
[singleResponse(user)],
)
const store = createServerSession(client)
store.optimistic.add({ sessionID: "child", message: user, parts: [part] })
await store.sync("child")
store.optimistic.remove({ sessionID: "child", messageID: user.id })
expect(client.requests).toHaveLength(1)
expect(client.rootRequests).toHaveLength(1)
expect(store.data.message.child).toEqual([user, ...assistants])
})
test("backfills the parent of fetched assistants when another user is cached", async () => {
const unrelated = userMessage("message-0", { time: { created: 0 } })
const user = userMessage("message-1")
const assistants = [assistantMessage("message-2", user.id), assistantMessage("message-3", user.id)]
const client = rootMessageClient(
[
response([{ info: unrelated, parts: [] }]),
response(
assistants.map((info) => ({ info, parts: [] })),
"older",
),
],
[singleResponse(user)],
)
const store = createServerSession(client)
await store.sync("child")
await store.sync("child", { force: true })
expect(client.requests).toHaveLength(2)
expect(client.rootRequests).toHaveLength(1)
expect(store.data.message.child).toEqual([unrelated, user, ...assistants])
})
test("preserves cached history between an injected parent and the page boundary", async () => {
const user = userMessage("message-1")
const cached = userMessage("message-3", { time: { created: 3 } })
const assistant = assistantMessage("message-4", user.id)
const client = rootMessageClient(
[response([{ info: cached, parts: [] }]), response([{ info: assistant, parts: [] }], "older")],
[singleResponse(user)],
)
const store = createServerSession(client)
await store.sync("child")
await store.sync("child", { force: true })
expect(store.data.message.child).toEqual([user, cached, assistant])
})
test("refreshes a cached parent omitted by an assistant-only replacement page", async () => {
const stale = userMessage("message-1", { summary: { title: "stale", diffs: [] } })
const fresh = { ...stale, summary: { title: "fresh", diffs: [] } }
const stalePart = textPart(stale.id, { text: "stale" })
const freshPart = { ...stalePart, text: "fresh" }
const assistant = assistantMessage("message-2", stale.id)
const client = rootMessageClient(
[response([{ info: stale, parts: [stalePart] }]), response([{ info: assistant, parts: [] }], "older")],
[singleResponse(fresh, [freshPart])],
)
const store = createServerSession(client)
await store.sync("child")
await store.sync("child", { force: true })
expect(client.rootRequests).toEqual([{ sessionID: "child", messageID: stale.id }])
expect(store.data.message.child).toEqual([fresh, assistant])
expect(store.data.part[stale.id]).toEqual([freshPart])
})
test("refreshes a confirmed optimistic parent while preserving pending parts", async () => {
const stale = userMessage("message-1", { summary: { title: "stale", diffs: [] } })
const fresh = { ...stale, summary: { title: "fresh", diffs: [] } }
const confirmed = textPart(stale.id, { id: "confirmed", text: "stale" })
const refreshed = { ...confirmed, text: "fresh" }
const pending = textPart(stale.id, { id: "pending", text: "pending" })
const assistant = assistantMessage("message-2", stale.id)
const client = rootMessageClient(
[response([{ info: stale, parts: [confirmed] }]), response([{ info: assistant, parts: [] }], "older")],
[singleResponse(fresh, [refreshed])],
)
const store = createServerSession(client)
store.optimistic.add({ sessionID: "child", message: stale, parts: [confirmed, pending] })
await store.sync("child")
await store.sync("child", { force: true })
expect(client.rootRequests).toEqual([{ sessionID: "child", messageID: stale.id }])
expect(store.data.message.child).toEqual([fresh, assistant])
expect(store.data.part[stale.id]).toEqual([refreshed, pending])
})
test("uses a parent received by SSE during the replacement load", async () => {
const pending = deferredResponse()
const user = userMessage("message-1")
const assistant = assistantMessage("message-2", user.id)
const client = rootMessageClient([pending.promise], [])
const store = createServerSession(client)
const loading = store.sync("child")
store.apply({ type: "message.updated", properties: { info: user } })
pending.resolve(response([{ info: assistant, parts: [] }], "older"))
await loading
expect(client.rootRequests).toEqual([])
expect(store.data.message.child).toEqual([user, assistant])
})
test("uses a successful retry over events received by a failed backfill attempt", async () => {
const failed = deferredResponse()
const user = userMessage("message-1")
const live = { ...user, agent: "stale" }
const assistants = [assistantMessage("message-2", user.id), assistantMessage("message-3", user.id)]
const client = rootMessageClient(
[
response(
assistants.map((info) => ({ info, parts: [] })),
"older",
),
],
[failed.promise.then((result) => ({ data: result.data[0]! })), singleResponse(user)],
)
const store = createServerSession(client, { retry: retryImmediately })
const loading = store.sync("child")
await client.rootRequested(1)
store.apply({ type: "message.updated", properties: { info: live } })
failed.reject(new Error("retry"))
await loading
expect(client.requests).toHaveLength(1)
expect(client.rootRequests).toHaveLength(2)
expect(store.data.message.child).toEqual([user, ...assistants])
})
test("preserves newer-page events across a failed parent retry", async () => {
const failed = deferredResponse()
const user = userMessage("message-1")
const assistant = assistantMessage("message-2", user.id)
const live = { ...assistant, cost: 1 }
const client = rootMessageClient(
[response([{ info: assistant, parts: [] }], "older")],
[failed.promise.then((result) => ({ data: result.data[0]! })), singleResponse(user)],
)
const store = createServerSession(client, { retry: retryImmediately })
const loading = store.sync("child")
await client.rootRequested(1)
store.apply({ type: "message.updated", properties: { info: live } })
failed.reject(new Error("retry"))
await loading
expect(store.data.message.child).toEqual([user, live])
})
test("preserves unrelated message events across a failed parent retry", async () => {
const failed = deferredResponse()
const user = userMessage("message-1")
const assistant = assistantMessage("message-2", user.id)
const live = userMessage("message-4", { time: { created: 4 } })
const client = rootMessageClient(
[response([{ info: assistant, parts: [] }], "older")],
[failed.promise.then((result) => ({ data: result.data[0]! })), singleResponse(user)],
)
const store = createServerSession(client, { retry: retryImmediately })
const loading = store.sync("child")
await client.rootRequested(1)
store.apply({ type: "message.updated", properties: { info: live } })
failed.reject(new Error("retry"))
await loading
expect(store.data.message.child).toEqual([user, assistant, live])
})
test("preserves newer-page part events across a failed parent retry", async () => {
const failed = deferredResponse()
const user = userMessage("message-1")
const assistant = assistantMessage("message-2", user.id)
const stale = textPart(assistant.id, { text: "stale" })
const live = { ...stale, text: "live" }
const client = rootMessageClient(
[response([{ info: assistant, parts: [stale] }], "older")],
[failed.promise.then((result) => ({ data: result.data[0]! })), singleResponse(user)],
)
const store = createServerSession(client, { retry: retryImmediately })
const loading = store.sync("child")
await client.rootRequested(1)
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: live, time: 2 } })
failed.reject(new Error("retry"))
await loading
expect(store.data.part[assistant.id]).toEqual([live])
})
test("merges live events into the initial page", async () => {
const pending = deferredResponse()
const user = userMessage("message-1")
@@ -1191,26 +905,6 @@ describe("server session", () => {
expect(store.data.message.child).toEqual([latest])
})
test("does not scan cached messages for user roots during history prepend", async () => {
const guard = { active: false }
const latest = new Proxy(userMessage("message-2", { time: { created: 2 } }), {
get(target, property, receiver) {
if (guard.active && property === "role") throw new Error("cached role accessed")
return Reflect.get(target, property, receiver)
},
})
const older = userMessage("message-1")
const store = createServerSession(
messageClient(response([{ info: latest, parts: [] }], "older"), response([{ info: older, parts: [] }])),
)
await store.sync("child")
guard.active = true
await store.history.loadMore("child")
expect(store.data.message.child).toEqual([older, latest])
})
test("preserves loaded history during an incomplete refresh", async () => {
const older = userMessage("message-1")
const latest = userMessage("message-2", { time: { created: 2 } })
+30 -109
View File
@@ -54,11 +54,6 @@ type MessageLoadState = {
clearedMessageParts: Set<string>
}
type MessageLoadBaseline = Pick<
MessageLoadState,
"touchedMessages" | "retainedMessages" | "touchedParts" | "clearedMessageParts"
>
function mergeOptimisticPage(page: MessagePage, items: OptimisticItem[]) {
if (items.length === 0) return { ...page, observed: [] as { messageID: string; parts: Part[] }[] }
const session = [...page.session]
@@ -352,7 +347,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
load.touchedParts.set(messageID, new Set([partID]))
}
const resetMessageLoad = (sessionID: string, load: MessageLoadState, baseline?: MessageLoadBaseline) => {
const resetMessageLoad = (sessionID: string, load: MessageLoadState) => {
load.touchedMessages.clear()
load.retainedMessages.clear()
load.touchedParts.clear()
@@ -385,27 +380,8 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
parts.forEach((partID) => touched.add(partID))
load.touchedParts.set(messageID, touched)
}
baseline?.touchedMessages.forEach((messageID) => load.touchedMessages.add(messageID))
baseline?.retainedMessages.forEach((messageID) => load.retainedMessages.add(messageID))
baseline?.clearedMessageParts.forEach((messageID) => load.clearedMessageParts.add(messageID))
baseline?.touchedParts.forEach((parts, messageID) => {
const touched = load.touchedParts.get(messageID) ?? new Set<string>()
parts.forEach((partID) => touched.add(partID))
load.touchedParts.set(messageID, touched)
})
}
const messageLoadBaseline = (load: MessageLoadState, exclude: string): MessageLoadBaseline => ({
touchedMessages: new Set([...load.touchedMessages].filter((messageID) => messageID !== exclude)),
retainedMessages: new Set([...load.retainedMessages].filter((messageID) => messageID !== exclude)),
touchedParts: new Map(
[...load.touchedParts]
.filter(([messageID]) => messageID !== exclude)
.map(([messageID, parts]) => [messageID, new Set(parts)]),
),
clearedMessageParts: new Set([...load.clearedMessageParts].filter((messageID) => messageID !== exclude)),
})
const evict = (sessionIDs: string[]) => {
if (sessionIDs.length === 0) return
const evicted = new Set(sessionIDs)
@@ -484,18 +460,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
}
}
const fetchMessage = async (sessionID: string, messageID: string, onAttempt?: () => void) => {
const response = await (options?.retry ?? retry)(() => {
onAttempt?.()
return client.session.message({ sessionID, messageID })
})
if (!response.data?.info?.id) throw new Error(`Message not found: ${messageID}`)
return {
message: cleanMessage(response.data.info),
parts: response.data.parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id)),
}
}
const replaceMessages = (sessionID: string, messages: Message[]) => {
const messageIDs = new Set(messages.map((message) => message.id))
const dropped = (data.message[sessionID] ?? []).filter((message) => !messageIDs.has(message.id))
@@ -614,79 +578,36 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
messageLoads.set(sessionID, load)
setMeta("loading", sessionID, true)
let applied = false
try {
const page = await fetchMessages(sessionID, limit, before, () => resetMessageLoad(sessionID, load))
const first = page.session.reduce<Message | undefined>(
(oldest, message) => (!oldest || cmpMessage(message, oldest) < 0 ? message : oldest),
undefined,
)
if (generations.get(sessionID) !== active) return
const parents = [] as Awaited<ReturnType<typeof fetchMessage>>[]
if (mode !== "prepend") {
const users = new Set([
...page.session.filter((message) => message.role === "user").map((message) => message.id),
...(data.message[sessionID] ?? [])
.filter((message) => {
if (message.role !== "user") return false
const item = optimistic.get(sessionID)?.get(message.id)
return load.touchedMessages.has(message.id) && (!item || item.confirmedMessage === true)
})
.map((message) => message.id),
])
const parentIDs = [
...new Set(
page.session.flatMap((message) =>
message.role === "assistant" && !users.has(message.parentID) ? [message.parentID] : [],
),
),
]
for (const parentID of parentIDs) {
if (generations.get(sessionID) !== active) break
const parent = await fetchMessage(sessionID, parentID, () =>
resetMessageLoad(sessionID, load, messageLoadBaseline(load, parentID)),
)
if (parent.message.role !== "user") throw new Error(`Assistant parent is not a user message: ${parentID}`)
parents.push(parent)
await fetchMessages(sessionID, limit, before, () => resetMessageLoad(sessionID, load))
.then((page) => {
if (generations.get(sessionID) !== active) return
const first = page.session.reduce<Message | undefined>(
(oldest, message) => (!oldest || cmpMessage(message, oldest) < 0 ? message : oldest),
undefined,
)
const preserveUnfetched =
mode === "prepend" || (!page.complete && (!first || ((message: Message) => cmpMessage(message, first) < 0)))
applyMessagePage(
sessionID,
page,
messageLoads.get(sessionID) === load ? load : undefined,
preserveUnfetched,
mode !== "prepend",
)
applied = true
})
.finally(() => {
if (!applied && generations.get(sessionID) === active && messageLoads.get(sessionID) === load) {
for (const messageID of load.orphanParents) {
if (!orphanParts.get(sessionID)?.has(messageID)) continue
setData(produce((draft) => deleteMessageParts(draft, messageID)))
orphanParts.get(sessionID)?.delete(messageID)
}
if (orphanParts.get(sessionID)?.size === 0) orphanParts.delete(sessionID)
}
}
if (generations.get(sessionID) !== active) return
const result =
mode === "prepend"
? page
: {
...page,
session: merge(
page.session,
parents.map((parent) => parent.message),
),
part: merge(
page.part,
parents.map((parent) => ({ id: parent.message.id, part: parent.parts })),
),
}
const preserveUnfetched =
mode === "prepend" || (!result.complete && (!first || ((message: Message) => cmpMessage(message, first) < 0)))
applyMessagePage(
sessionID,
result,
messageLoads.get(sessionID) === load ? load : undefined,
preserveUnfetched,
mode !== "prepend",
)
applied = true
} finally {
if (!applied && generations.get(sessionID) === active && messageLoads.get(sessionID) === load) {
for (const messageID of load.orphanParents) {
if (!orphanParts.get(sessionID)?.has(messageID)) continue
setData(produce((draft) => deleteMessageParts(draft, messageID)))
orphanParts.get(sessionID)?.delete(messageID)
}
if (orphanParts.get(sessionID)?.size === 0) orphanParts.delete(sessionID)
}
if (messageLoads.get(sessionID) === load) messageLoads.delete(sessionID)
if (generations.get(sessionID) === active) setMeta("loading", sessionID, false)
}
if (messageLoads.get(sessionID) === load) messageLoads.delete(sessionID)
if (generations.get(sessionID) === active) setMeta("loading", sessionID, false)
})
}
const sync = (sessionID: string, options?: { force?: boolean; messageLimit?: number }) => {
-81
View File
@@ -1,15 +1,6 @@
import { describe, expect, test } from "bun:test"
import { createRoot, getOwner, onCleanup } from "solid-js"
import { createTabMemory } from "./tab-memory"
import { nextTabAfterClose, pushClosedTab, removeClosedTabs, takeClosedTab, type ClosedTab } from "./closed-tabs"
import type { SessionTab, Tab } from "./tabs"
import type { ServerConnection } from "./server"
const server = "local\nhttp://localhost:4096" as ServerConnection.Key
function sessionTab(sessionId: string): SessionTab {
return { type: "session", server, sessionId }
}
describe("tab memory", () => {
test("keeps state until its tab is removed", () => {
@@ -31,75 +22,3 @@ describe("tab memory", () => {
})
})
})
describe("closed tab stack", () => {
test("records session tabs with their index", () => {
const stack = pushClosedTab([], sessionTab("a"), 2)
expect(stack).toEqual([{ tab: sessionTab("a"), index: 2 }])
})
test("ignores draft tabs", () => {
const draft: Tab = { type: "draft", draftID: "d1", server, directory: "/tmp" }
expect(pushClosedTab([], draft, 0)).toEqual([])
})
test("caps the stack size", () => {
const stack = Array.from({ length: 30 }, (_, i) => i).reduce<ClosedTab[]>(
(acc, i) => pushClosedTab(acc, sessionTab(`s${i}`), i),
[],
)
expect(stack).toHaveLength(25)
expect(stack[0]?.tab.sessionId).toBe("s5")
expect(stack.at(-1)?.tab.sessionId).toBe("s29")
})
test("pops the most recently closed tab", () => {
const stack = [
{ tab: sessionTab("a"), index: 0 },
{ tab: sessionTab("b"), index: 1 },
]
const result = takeClosedTab(stack, [])
expect(result.entry?.tab.sessionId).toBe("b")
expect(result.stack).toEqual([{ tab: sessionTab("a"), index: 0 }])
})
test("skips entries whose tab is already open", () => {
const stack = [
{ tab: sessionTab("a"), index: 0 },
{ tab: sessionTab("b"), index: 1 },
]
const result = takeClosedTab(stack, [sessionTab("b")])
expect(result.entry?.tab.sessionId).toBe("a")
expect(result.stack).toEqual([])
})
test("returns no entry when everything is open or empty", () => {
expect(takeClosedTab([], []).entry).toBeUndefined()
const result = takeClosedTab([{ tab: sessionTab("a"), index: 0 }], [sessionTab("a")])
expect(result.entry).toBeUndefined()
expect(result.stack).toEqual([])
})
test("purges removed sessions", () => {
const stack = [
{ tab: sessionTab("a"), index: 0 },
{ tab: sessionTab("b"), index: 1 },
]
expect(removeClosedTabs(stack, server, ["a"])).toEqual([{ tab: sessionTab("b"), index: 1 }])
})
test("does not navigate when a background tab closes", () => {
const tabs = [sessionTab("a"), sessionTab("b"), sessionTab("c")]
expect(nextTabAfterClose(tabs, 1, false)).toBeUndefined()
expect(nextTabAfterClose(tabs, 1, true)).toEqual(sessionTab("c"))
expect(nextTabAfterClose([sessionTab("a")], 0, true)).toBeNull()
})
})
+3 -56
View File
@@ -10,7 +10,6 @@ import { uuid } from "@/utils/uuid"
import { SessionTabsRemovedDetail } from "@/components/titlebar-session-events"
import { sessionHref } from "@/utils/session-route"
import { createTabMemory } from "./tab-memory"
import { nextTabAfterClose, pushClosedTab, removeClosedTabs, takeClosedTab, type ClosedTab } from "./closed-tabs"
export type SessionTab = {
type: "session"
@@ -64,7 +63,6 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
createStore<Tab[]>([]),
)
const [recent, setRecent, , recentReady] = persisted(Persist.window("tabs.recent"), createStore<RecentTab>({}))
const [closed, setClosed, , closedReady] = persisted(Persist.window("tabs.closed"), createStore<ClosedTab[]>([]))
const params = useParams()
const navigate = useNavigate()
@@ -89,15 +87,6 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
})
}
const updateClosed = (update: (stack: ClosedTab[]) => ClosedTab[]) => {
const apply = () => setClosed((stack) => update(stack))
if (closedReady()) {
apply()
return
}
void closedReady.promise?.then(apply)
}
const removeDraftPersisted = (draftID: string) => {
for (const key of draftPersistedKeys()) removePersisted(Persist.draft(draftID, key), platform)
}
@@ -117,13 +106,6 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
if (recent.key && !next.some((tab) => tabKey(tab) === recent.key)) setRecentKey(undefined)
})
createEffect(() => {
if (!closedReady()) return
const servers = new Set(server.list.map(ServerConnection.key))
const next = closed.filter((entry) => servers.has(entry.tab.server))
if (next.length !== closed.length) setClosed(() => next)
})
const navigateTab = (tab: Tab) => {
const href = tabHref(tab)
setRecentKey(tabKey(tab))
@@ -135,7 +117,7 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
if (!tab) return
const key = tabKey(tab)
const draftID = tab.type === "draft" ? tab.draftID : undefined
const nextTab = nextTabAfterClose(store, index, recentKey() === key && location.pathname !== "/")
const nextTab = store[index + 1] ?? store[index - 1]
closing.add(key)
void startTransition(() => {
setStore(
@@ -143,11 +125,9 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
tabs.splice(index, 1)
}),
)
if (nextTab === null) {
setRecentKey(undefined)
navigate("/")
}
if (recent.key === key) setRecentKey(nextTab && tabKey(nextTab))
if (nextTab) navigateTab(nextTab)
else navigate("/")
}).finally(() => closing.delete(key))
memory.remove(key)
if (draftID) removeDraftPersisted(draftID)
@@ -221,45 +201,13 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
removeDraftPersisted(draftID)
},
removeTab,
// User-initiated close: records the tab so it can be reopened.
// Cleanup paths (missing sessions, archive, server removal) go through
// removeTab and friends directly and are not recorded.
closeTab(index: number) {
const tab = store[index]
if (!tab) return
if (tab.type === "session") updateClosed((stack) => pushClosedTab(stack, tab, index))
removeTab(index)
},
reopenClosedTab() {
if (!closedReady()) {
void closedReady.promise?.then(() => actions.reopenClosedTab())
return
}
const result = takeClosedTab(closed, store)
if (result.stack.length === closed.length) return
setClosed(() => result.stack)
const entry = result.entry
if (!entry) return
const index = Math.min(entry.index, store.length)
void startTransition(() => {
setStore(
produce((tabs) => {
if (tabs.some((item) => tabKey(item) === tabKey(entry.tab))) return
tabs.splice(index, 0, entry.tab)
}),
)
navigateTab(entry.tab)
})
},
removeSessionTab(input: Omit<SessionTab, "type">) {
updateClosed((stack) => removeClosedTabs(stack, input.server, [input.sessionId]))
const index = store.findIndex(
(tab) => tab.type === "session" && tab.server === input.server && tab.sessionId === input.sessionId,
)
if (index !== -1) removeTab(index)
},
removeServer(key: ServerConnection.Key) {
updateClosed((stack) => stack.filter((entry) => entry.tab.server !== key))
const drafts = store.flatMap((tab) => (tab.type === "draft" && tab.server === key ? [tab.draftID] : []))
const removed = store.filter((tab) => tab.server === key).map(tabKey)
setStore((tabs) => tabs.filter((tab) => tab.server !== key))
@@ -270,7 +218,6 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
},
removeSessions: (input: SessionTabsRemovedDetail) => {
const targetServer = input.server ?? server.key
updateClosed((stack) => removeClosedTabs(stack, targetServer, input.sessionIDs))
const removed = store
.filter(
(tab) => tab.type === "session" && tab.server === targetServer && input.sessionIDs.includes(tab.sessionId),
-1
View File
@@ -48,7 +48,6 @@ export const dict = {
"command.session.new": "New session",
"command.file.open": "Open file",
"command.tab.close": "Close tab",
"command.tab.reopenClosed": "Reopen closed tab",
"command.context.addSelection": "Add selection to context",
"command.context.addSelection.description": "Add selected lines from the current file",
"command.input.focus": "Focus input",
@@ -1,12 +0,0 @@
import { describe, expect, test } from "bun:test"
import { shouldOpenSessionInBackground } from "./home-session-open"
describe("shouldOpenSessionInBackground", () => {
test("requires only the platform primary modifier", () => {
expect(shouldOpenSessionInBackground({ mac: true, meta: true, ctrl: false, shift: false, alt: false })).toBe(true)
expect(shouldOpenSessionInBackground({ mac: false, meta: false, ctrl: true, shift: false, alt: false })).toBe(true)
expect(shouldOpenSessionInBackground({ mac: true, meta: true, ctrl: false, shift: true, alt: false })).toBe(false)
expect(shouldOpenSessionInBackground({ mac: false, meta: false, ctrl: true, shift: false, alt: true })).toBe(false)
expect(shouldOpenSessionInBackground({ mac: false, meta: true, ctrl: false, shift: false, alt: false })).toBe(false)
})
})
@@ -1,11 +0,0 @@
export function shouldOpenSessionInBackground(input: {
mac: boolean
meta: boolean
ctrl: boolean
shift: boolean
alt: boolean
}) {
if (input.shift || input.alt) return false
if (input.mac) return input.meta && !input.ctrl
return input.ctrl && !input.meta
}
+10 -31
View File
@@ -66,7 +66,6 @@ import { Persist, persisted } from "@/utils/persist"
import { useMarked } from "@opencode-ai/ui/context/marked"
import { preloadMarkdown } from "@opencode-ai/session-ui/markdown-cache"
import { archiveHomeSession } from "./home-session-archive"
import { shouldOpenSessionInBackground } from "./home-session-open"
import { showToast } from "@/utils/toast"
const HOME_SESSION_LIMIT = 64
@@ -239,20 +238,6 @@ function useHomeSessionHeaderOpacity(groups: () => HomeSessionGroup[]) {
return { setViewport, setContentRef, setHeaderRef, update, titleOpacity }
}
// Cmd+click on macOS (Ctrl+click elsewhere) opens a session tab in the
// background without navigating, matching browser conventions.
function isBackgroundOpen(event: MouseEvent) {
return shouldOpenSessionInBackground({
mac: typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform),
meta: event.metaKey,
ctrl: event.ctrlKey,
shift: event.shiftKey,
alt: event.altKey,
})
}
type OpenSessionOptions = { background?: boolean }
export function NewHome() {
const sync = useServerSync()
const layout = useLayout()
@@ -392,11 +377,9 @@ export function NewHome() {
setState("searchFocused", false)
}
function selectSearchSession(session: Session, options?: OpenSessionOptions) {
openSession(session, options)
// Background opens keep the search visible so several results can be
// opened in a row.
if (!options?.background) closeSearch()
function selectSearchSession(session: Session) {
openSession(session)
closeSearch()
}
command.register("home", () => [
@@ -481,17 +464,13 @@ export function NewHome() {
.forEach((directory) => state.project.markViewed(directory))
}
function openSession(session: Session, options?: OpenSessionOptions) {
function openSession(session: Session) {
const project = projectForSession(session, projects(), projectByID())
const conn = focusedServer()
if (!conn) return
const directory = project?.worktree ?? session.directory
const ctx = global.ensureServerCtx(conn)
ctx.projects.open(directory)
if (options?.background) {
tabs.addSessionTab({ server: ServerConnection.key(conn), sessionId: session.id })
return
}
ctx.projects.touch(directory)
startTransition(() => {
const tab = tabs.addSessionTab({ server: ServerConnection.key(conn), sessionId: session.id })
@@ -1151,7 +1130,7 @@ function HomeSessionSearch(props: {
onInput: (value: string) => void
onFocus: () => void
onClose: () => void
onSelect: (session: Session, options?: OpenSessionOptions) => void
onSelect: (session: Session) => void
}) {
const language = useLanguage()
const [store, setStore] = createStore({ active: "" })
@@ -1265,7 +1244,7 @@ function HomeSessionSearch(props: {
server={props.server}
selected={store.active === homeSessionSearchKey(record)}
onHighlight={() => setStore("active", homeSessionSearchKey(record))}
onSelect={(session, options) => props.onSelect(session, options)}
onSelect={(session) => props.onSelect(session)}
/>
)}
</For>
@@ -1345,7 +1324,7 @@ function HomeSessionSearchResultRow(props: {
server: ServerConnection.Key
selected: boolean
onHighlight: () => void
onSelect: (session: Session, options?: OpenSessionOptions) => void
onSelect: (session: Session) => void
}) {
const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id)
const showProjectName = () => props.showProjectName && props.record.projectName
@@ -1366,7 +1345,7 @@ function HomeSessionSearchResultRow(props: {
group: !!showProjectName(),
}}
onMouseEnter={() => props.onHighlight()}
onClick={(event) => props.onSelect(props.record.session, { background: isBackgroundOpen(event) })}
onClick={() => props.onSelect(props.record.session)}
>
<HomeSessionLeading
project={props.record.project}
@@ -1410,7 +1389,7 @@ function HomeSessionRow(props: {
record: HomeSessionRecord
showProjectName: boolean
server: ServerConnection.Key
openSession: (session: Session, options?: OpenSessionOptions) => void
openSession: (session: Session) => void
archiveSession: (session: Session) => Promise<void>
}) {
const language = useLanguage()
@@ -1426,7 +1405,7 @@ function HomeSessionRow(props: {
type="button"
data-component="home-session-row"
class={`${HOME_ROW} h-10 min-w-0 flex-1 gap-2 py-3 pl-3 pr-10`}
onClick={(event) => props.openSession(props.record.session, { background: isBackgroundOpen(event) })}
onClick={() => props.openSession(props.record.session)}
>
<HomeSessionLeading
project={props.record.project}
+1 -1
View File
@@ -93,7 +93,7 @@ export default function NewSessionPage() {
return (
<div class="relative size-full overflow-hidden flex flex-col">
<div class="flex-1 min-h-0 flex flex-col gap-2 p-2">
<div class="@container relative flex flex-col min-h-0 h-full flex-1">
<div class="@container relative flex flex-col min-h-0 h-full bg-background-stronger flex-1">
<div class="flex-1 min-h-0 overflow-hidden rounded-[10px]">
<NewSessionDesignView>
<div class={NEW_SESSION_CONTENT_WIDTH}>
+32 -87
View File
@@ -64,20 +64,25 @@ import {
createSessionComposerRegionController,
SessionComposerRegion,
} from "@/pages/session/composer"
import { createOpenReviewFile, createSessionTabs, createSizing, shouldShowFileTree } from "@/pages/session/helpers"
import {
createOpenReviewFile,
createSessionTabs,
createSizing,
focusTerminalById,
shouldFocusTerminalOnKeyDown,
shouldShowFileTree,
} from "@/pages/session/helpers"
import { MessageTimeline } from "@/pages/session/timeline/message-timeline"
import { createTimelineModel } from "@/pages/session/timeline/model"
import { type DiffStyle, SessionReviewTab, type SessionReviewTabProps } from "@/pages/session/review-tab"
import { useSessionLayout } from "@/pages/session/session-layout"
import { syncSessionModel } from "@/pages/session/session-model-helpers"
import { SessionSidePanel } from "@/pages/session/session-side-panel"
import { sessionPanelLayout } from "@/pages/session/session-panel-layout"
import { SessionReviewEmptyChangesV2 } from "@opencode-ai/session-ui/v2/session-review-empty-changes-v2"
import { SessionReviewEmptyNoGitV2 } from "@opencode-ai/session-ui/v2/session-review-empty-no-git-v2"
import { ReviewPanelV2 } from "@/pages/session/v2/review-panel-v2"
import { createReviewPanelV2State } from "@/pages/session/v2/review-panel-v2-state"
import { TerminalPanel } from "@/pages/session/terminal-panel"
import { TerminalPanelV2 } from "@/pages/session/terminal-panel-v2"
import { useComposerCommands } from "@/pages/session/use-composer-commands"
import { useSessionCommands } from "@/pages/session/use-session-commands"
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
@@ -429,12 +434,6 @@ export default function Page() {
const isDesktop = createMediaQuery("(min-width: 768px)")
const size = createSizing()
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
const desktopV2ReviewOpen = createMemo(() => newSessionDesign() && desktopReviewOpen() && !!params.id)
const terminalOpen = createMemo(() => view().terminal.opened())
const desktopTerminalOpen = createMemo(() => isDesktop() && terminalOpen())
const desktopInlineTerminalOnlyOpen = createMemo(
() => newSessionDesign() && desktopTerminalOpen() && !desktopV2ReviewOpen(),
)
const desktopFileTreeOpen = createMemo(
() =>
isDesktop() &&
@@ -443,23 +442,13 @@ export default function Page() {
opened: layout.fileTree.opened(),
}),
)
const desktopSessionResizeOpen = createMemo(() =>
newSessionDesign() ? desktopV2ReviewOpen() || desktopTerminalOpen() : desktopReviewOpen(),
)
const desktopSidePanelOpen = createMemo(() => desktopSessionResizeOpen() || desktopFileTreeOpen())
const desktopSidePanelOpen = createMemo(() => desktopReviewOpen() || desktopFileTreeOpen())
const sessionPanelWidth = createMemo(() => {
if (!desktopSidePanelOpen()) return "100%"
if (desktopSessionResizeOpen()) return `${layout.session.width()}px`
if (desktopReviewOpen()) return `${layout.session.width()}px`
return `calc(100% - ${layout.fileTree.width()}px)`
})
const centered = createMemo(() => isDesktop() && !desktopReviewOpen())
const desktopV2PanelLayout = createMemo(() =>
sessionPanelLayout({
review: desktopV2ReviewOpen(),
terminal: desktopTerminalOpen(),
files: desktopFileTreeOpen(),
}),
)
function normalizeTab(tab: string) {
if (!tab.startsWith("file://")) return tab
@@ -953,6 +942,12 @@ export default function Page() {
return
}
// Prefer the open terminal over the composer when it can take focus
if (view().terminal.opened()) {
const id = terminal.active()
if (id && shouldFocusTerminalOnKeyDown(event) && focusTerminalById(id)) return
}
const key = scrollKey(event)
if (key) {
if (!scroller || !isScrollKeyTarget(target ?? null, key)) return
@@ -2111,7 +2106,7 @@ export default function Page() {
classList={{
"@container relative shrink-0 flex flex-col min-h-0 h-full flex-1 md:flex-none transition-[width]": true,
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!size.active() && !ui.reviewSnap && !desktopInlineTerminalOnlyOpen(),
!size.active() && !ui.reviewSnap,
}}
style={{
width: sessionPanelWidth(),
@@ -2131,7 +2126,7 @@ export default function Page() {
</SessionPanelFrame>
)}
<Show when={desktopSessionResizeOpen()}>
<Show when={desktopReviewOpen()}>
<div onPointerDown={() => size.start()}>
<ResizeHandle
classList={{
@@ -2150,72 +2145,22 @@ export default function Page() {
</Show>
</div>
<Show when={!newSessionDesign()}>
<SessionSidePanel
canReview={canReview}
diffs={reviewDiffs}
diffsReady={reviewReady}
empty={reviewEmptyText}
hasReview={hasReview}
reviewCount={reviewCount}
reviewPanel={reviewPanel}
activeDiff={tree.activeDiff}
focusReviewDiff={focusReviewDiff}
reviewSnap={ui.reviewSnap}
size={size}
/>
</Show>
<Show when={newSessionDesign()}>
<Show when={isDesktop() ? desktopV2PanelLayout().visible : terminalOpen()}>
<div class="min-w-0 h-full flex flex-1 flex-col">
<Show when={isDesktop() && (desktopV2ReviewOpen() || desktopFileTreeOpen())}>
<div class="min-h-0 flex-1">
<SessionSidePanel
canReview={canReview}
diffs={reviewDiffs}
diffsReady={reviewReady}
empty={reviewEmptyText}
hasReview={hasReview}
reviewCount={reviewCount}
reviewPanel={reviewPanelV2}
activeDiff={tree.activeDiff}
focusReviewDiff={focusReviewDiff}
reviewSnap={ui.reviewSnap}
size={size}
stacked={desktopV2PanelLayout().stacked}
/>
</div>
</Show>
<Show when={desktopV2PanelLayout().stacked}>
<div class="relative h-2 shrink-0" onPointerDown={() => size.start()}>
<ResizeHandle
class="!relative !inset-auto !h-full !w-full !transform-none"
direction="vertical"
size={layout.terminal.height()}
min={100}
max={typeof window === "undefined" ? 600 : window.innerHeight * 0.6}
collapseThreshold={50}
onResize={(height) => {
size.touch()
layout.terminal.resize(height)
}}
onCollapse={() => view().terminal.close()}
/>
</div>
</Show>
<Show when={terminalOpen()}>
<div class="min-h-0 flex-1">
<TerminalPanelV2 stacked={desktopV2PanelLayout().stacked} />
</div>
</Show>
</div>
</Show>
</Show>
<SessionSidePanel
canReview={canReview}
diffs={reviewDiffs}
diffsReady={reviewReady}
empty={reviewEmptyText}
hasReview={hasReview}
reviewCount={reviewCount}
reviewPanel={() => (newSessionDesign() ? reviewPanelV2() : reviewPanel())}
activeDiff={tree.activeDiff}
focusReviewDiff={focusReviewDiff}
reviewSnap={ui.reviewSnap}
size={size}
/>
</div>
<Show when={!newSessionDesign()}>
<TerminalPanel />
</Show>
<TerminalPanel />
</SessionRouteFrame>
)
}
@@ -7,6 +7,7 @@ import {
createSessionTabs,
focusTerminalById,
getTabReorderIndex,
shouldFocusTerminalOnKeyDown,
shouldShowFileTree,
} from "./helpers"
@@ -94,6 +95,26 @@ describe("focusTerminalById", () => {
})
})
describe("shouldFocusTerminalOnKeyDown", () => {
test("skips pure modifier keys", () => {
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "Meta", metaKey: true }))).toBe(false)
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "Control", ctrlKey: true }))).toBe(false)
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "Alt", altKey: true }))).toBe(false)
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "Shift", shiftKey: true }))).toBe(false)
})
test("skips shortcut key combos", () => {
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "c", metaKey: true }))).toBe(false)
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "c", ctrlKey: true }))).toBe(false)
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "ArrowLeft", altKey: true }))).toBe(false)
})
test("keeps plain typing focused on terminal", () => {
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "a" }))).toBe(true)
expect(shouldFocusTerminalOnKeyDown(new KeyboardEvent("keydown", { key: "A", shiftKey: true }))).toBe(true)
})
})
describe("getTabReorderIndex", () => {
test("returns target index for valid drag reorder", () => {
expect(getTabReorderIndex(["a", "b", "c"], "a", "c")).toBe(2)
@@ -98,6 +98,13 @@ export const focusTerminalById = (id: string) => {
return true
}
const skip = new Set(["Alt", "Control", "Meta", "Shift"])
export const shouldFocusTerminalOnKeyDown = (event: Pick<KeyboardEvent, "key" | "ctrlKey" | "metaKey" | "altKey">) => {
if (skip.has(event.key)) return false
return !(event.ctrlKey || event.metaKey || event.altKey)
}
export const createOpenReviewFile = (input: {
showAllFiles: () => void
tabForPath: (path: string) => string
@@ -1,19 +0,0 @@
import { describe, expect, test } from "bun:test"
import { sessionPanelLayout } from "./session-panel-layout"
describe("sessionPanelLayout", () => {
test("keeps one V2 owner while changing panel geometry", () => {
expect(sessionPanelLayout({ review: false, terminal: false, files: false })).toEqual({
visible: false,
stacked: false,
})
expect(sessionPanelLayout({ review: false, terminal: true, files: false })).toEqual({
visible: true,
stacked: false,
})
expect(sessionPanelLayout({ review: true, terminal: true, files: false })).toEqual({
visible: true,
stacked: true,
})
})
})
@@ -1,6 +0,0 @@
export function sessionPanelLayout(input: { review: boolean; terminal: boolean; files: boolean }) {
return {
visible: input.review || input.terminal || input.files,
stacked: input.review && input.terminal,
}
}
@@ -51,7 +51,6 @@ export function SessionSidePanel(props: {
focusReviewDiff: (path: string) => void
reviewSnap: boolean
size: Sizing
stacked?: boolean
}) {
const layout = useLayout()
const settings = useSettings()
@@ -220,10 +219,8 @@ export function SessionSidePanel(props: {
aria-label={language.t("session.panel.reviewAndFiles")}
aria-hidden={!open()}
inert={!open()}
class="relative min-w-0 flex overflow-hidden bg-background-base"
class="relative min-w-0 h-full flex shrink-0 overflow-hidden bg-background-base"
classList={{
"h-full shrink-0": !props.stacked,
"min-h-0 flex-1": props.stacked,
"pointer-events-none": !open(),
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!props.size.active() && !props.reviewSnap,
@@ -1,366 +0,0 @@
import { For, Show, createEffect, createMemo, on, onCleanup, onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { makeEventListener } from "@solid-primitives/event-listener"
import { createMediaQuery } from "@solid-primitives/media"
import { DragDropProvider, PointerSensor } from "@dnd-kit/solid"
import { isSortable } from "@dnd-kit/solid/sortable"
import { Accessibility, AutoScroller, Feedback, PointerActivationConstraints } from "@dnd-kit/dom"
import { RestrictToHorizontalAxis } from "@dnd-kit/abstract/modifiers"
import { RestrictToElement } from "@dnd-kit/dom/modifiers"
import { Tabs } from "@opencode-ai/ui/tabs"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
import { SortableTerminalTabV2 } from "@/components/session/session-sortable-terminal-tab-v2"
import { Terminal } from "@/components/terminal"
import { useCommand } from "@/context/command"
import { useLanguage } from "@/context/language"
import { useLayout } from "@/context/layout"
import { useSettings } from "@/context/settings"
import { useTerminal } from "@/context/terminal"
import { useSDK } from "@/context/sdk"
import { terminalTabLabel } from "@/pages/session/terminal-label"
import { createSizing, focusTerminalById } from "@/pages/session/helpers"
import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
import { useSessionLayout } from "@/pages/session/session-layout"
export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
const delays = [120, 240]
const layout = useLayout()
const terminal = useTerminal()
const sdk = useSDK()
const language = useLanguage()
const command = useCommand()
const settings = useSettings()
const { workspaceKey, view } = useSessionLayout()
const isDesktop = createMediaQuery("(min-width: 768px)")
const newLayout = createMemo(() => settings.general.newLayoutDesigns())
const opened = createMemo(() => view().terminal.opened())
const size = createSizing()
const height = createMemo(() => layout.terminal.height())
const close = () => view().terminal.close()
let root: HTMLDivElement | undefined
let tabList: HTMLDivElement | undefined
const [store, setStore] = createStore({
autoCreated: false,
recovered: {} as Record<string, boolean>,
view: typeof window === "undefined" ? 1000 : (window.visualViewport?.height ?? window.innerHeight),
})
const max = () => store.view * 0.6
const pane = () => Math.min(height(), max())
const stacked = createMemo(() => isDesktop() && props.stacked)
const panelHeight = createMemo(() =>
isDesktop() ? (stacked() ? `${pane()}px` : "100%") : opened() ? `${pane()}px` : "0px",
)
const contentHeight = createMemo(() => (isDesktop() ? (stacked() ? `${pane()}px` : "100%") : `${pane()}px`))
const newTerminalKeybind = createMemo(() => command.keybindParts("terminal.new"))
onMount(() => {
if (typeof window === "undefined") return
const sync = () => setStore("view", window.visualViewport?.height ?? window.innerHeight)
const port = window.visualViewport
sync()
makeEventListener(window, "resize", sync)
if (port) makeEventListener(port, "resize", sync)
})
createEffect(() => {
if (!opened()) {
setStore("autoCreated", false)
return
}
if (!terminal.ready() || terminal.all().length !== 0 || store.autoCreated) return
terminal.new()
setStore("autoCreated", true)
})
createEffect(
on(
() => terminal.all().length,
(count, prevCount) => {
if (prevCount === undefined || prevCount <= 0 || count !== 0) return
if (!opened()) return
close()
},
),
)
const focus = (id: string) => {
focusTerminalById(id)
const frame = requestAnimationFrame(() => {
if (!opened()) return
if (terminal.active() !== id) return
focusTerminalById(id)
})
const timers = delays.map((ms) =>
window.setTimeout(() => {
if (!opened()) return
if (terminal.active() !== id) return
focusTerminalById(id)
}, ms),
)
return () => {
cancelAnimationFrame(frame)
for (const timer of timers) clearTimeout(timer)
}
}
createEffect(
on(
() => [opened(), terminal.active()] as const,
([next, id]) => {
if (!next || !id) return
const stop = focus(id)
onCleanup(stop)
},
),
)
createEffect(() => {
if (opened()) return
const active = document.activeElement
if (!(active instanceof HTMLElement)) return
if (!root?.contains(active)) return
active.blur()
})
createEffect(() => {
const dir = sdk().directory
if (!dir) return
if (!terminal.ready()) return
language.locale()
setTerminalHandoff(
workspaceKey(),
terminal.all().map((pty) =>
terminalTabLabel({
title: pty.title,
titleNumber: pty.titleNumber,
t: language.t as (key: string, vars?: Record<string, string | number | boolean>) => string,
}),
),
)
})
const handoff = createMemo(() => {
const dir = sdk().directory
if (!dir) return []
return getTerminalHandoff(workspaceKey()) ?? []
})
const all = terminal.all
const ids = createMemo(() => all().map((pty) => pty.id))
const recoverTerminal = (key: string, id: string, clone: (id: string) => Promise<void>) => {
if (store.recovered[key]) return
setStore("recovered", key, true)
void clone(id)
}
const terminalRecoveryKey = (pty: { id: string; title: string; titleNumber: number }) => {
return String(pty.titleNumber || pty.title || pty.id)
}
const markTerminalConnected = (key: string, id: string, trim: (id: string) => void) => {
setStore("recovered", key, false)
trim(id)
}
const handleTerminalDragEnd = () => {
const activeId = terminal.active()
if (!activeId) return
requestAnimationFrame(() => {
if (terminal.active() !== activeId) return
focusTerminalById(activeId)
})
}
return (
<aside
ref={root}
id="terminal-panel"
role="region"
aria-label={language.t("terminal.title")}
aria-hidden={!opened()}
inert={!opened()}
class="relative shrink-0 overflow-hidden bg-background-stronger"
classList={{
"w-full": !isDesktop() || stacked(),
"min-w-0 h-full flex-1": isDesktop() && opened() && !stacked(),
"w-0 h-full pointer-events-none": isDesktop() && !opened(),
"rounded-[10px] shadow-[var(--v2-elevation-raised)]": isDesktop() && newLayout(),
"transition-[height] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[height] motion-reduce:transition-none":
!isDesktop() && !size.active(),
}}
style={{ height: panelHeight() }}
>
<div classList={{ "md:hidden": !stacked(), hidden: stacked() }} onPointerDown={() => size.start()}>
<ResizeHandle
classList={{
"-top-1": newLayout(),
}}
direction="vertical"
size={pane()}
min={100}
max={max()}
collapseThreshold={50}
onResize={(next) => {
size.touch()
layout.terminal.resize(next)
}}
onCollapse={close}
/>
</div>
<div
class="absolute inset-0 flex flex-col overflow-hidden"
classList={{
"border-t border-border-weak-base": opened() && !isDesktop(),
"border-t border-border-weaker-base": opened() && stacked() && !newLayout(),
"border-l border-border-weaker-base": opened() && isDesktop() && !newLayout(),
"pointer-events-none": !opened(),
}}
style={{ height: contentHeight() }}
>
<Show
when={terminal.ready()}
fallback={
<div class="flex flex-col h-full pointer-events-none">
<div class="h-10 flex items-center gap-2 px-2 border-b border-border-weaker-base bg-background-stronger overflow-hidden">
<For each={handoff()}>
{(title) => (
<div class="px-2 py-1 rounded-md bg-surface-base text-14-regular text-text-weak truncate max-w-40">
{title}
</div>
)}
</For>
<div class="flex-1" />
<div class="text-text-weak pr-2">
{language.t("common.loading")}
{language.t("common.loading.ellipsis")}
</div>
</div>
<div class="flex-1 flex items-center justify-center text-text-weak">{language.t("terminal.loading")}</div>
</div>
}
>
<DragDropProvider
sensors={[
PointerSensor.configure({
activationConstraints: [new PointerActivationConstraints.Distance({ value: 4 })],
preventActivation: (event) =>
event.target instanceof Element &&
!!event.target.closest('[data-slot="tabs-trigger-close-button"], input, [contenteditable="true"]'),
}),
]}
modifiers={[RestrictToHorizontalAxis, RestrictToElement.configure({ element: () => tabList ?? null })]}
plugins={(defaults) => [
...defaults.filter((plugin) => plugin !== Accessibility),
AutoScroller.configure({ acceleration: 8, threshold: { x: 0.05, y: 0 } }),
Feedback.configure({ dropAnimation: null }),
]}
onDragEnd={(event) => {
const source = event.operation.source
if (!event.canceled && isSortable(source) && source.initialIndex !== source.index) {
terminal.move(source.id.toString(), source.index)
}
handleTerminalDragEnd()
}}
>
<div class="flex flex-col h-full">
<Tabs
variant={newLayout() ? "normal" : "alt"}
value={terminal.active()}
onChange={(id) => terminal.open(id)}
class={newLayout() ? "!h-[52px] !flex-none" : "!h-auto !flex-none"}
>
<Tabs.List ref={tabList} class={newLayout() ? undefined : "h-10 border-b border-border-weaker-base"}>
<For each={all()}>
{(pty, index) => (
<SortableTerminalTabV2 terminal={pty} index={index} newLayout={newLayout()} onClose={close} />
)}
</For>
<div class="h-full flex items-center justify-center">
<Show
when={newLayout()}
fallback={
<TooltipKeybind
title={language.t("command.terminal.new")}
keybind={command.keybind("terminal.new")}
class="flex items-center"
>
<IconButton
icon="plus-small"
variant="ghost"
iconSize="large"
onClick={terminal.new}
aria-label={language.t("command.terminal.new")}
/>
</TooltipKeybind>
}
>
<TooltipV2
value={
<>
{language.t("command.terminal.new")}
<Show when={newTerminalKeybind().length > 0}>
<KeybindV2 keys={newTerminalKeybind()} variant="neutral" />
</Show>
</>
}
placement="bottom"
class="flex items-center"
>
<IconButton
icon="plus-small"
variant="ghost"
iconSize="large"
onClick={terminal.new}
aria-label={language.t("command.terminal.new")}
/>
</TooltipV2>
</Show>
</div>
</Tabs.List>
</Tabs>
<div class="flex-1 min-h-0 relative">
<Show when={opened() && terminal.active()} keyed>
{(id) => {
const ops = terminal.bind()
return (
<Show when={all().find((pty) => pty.id === id)}>
{(pty) => (
<div id={`terminal-wrapper-${id}`} class="absolute inset-0">
<Terminal
pty={pty()}
autoFocus={opened()}
class="!px-[14px]"
onConnect={() => markTerminalConnected(terminalRecoveryKey(pty()), id, ops.trim)}
onCleanup={ops.update}
onConnectError={() => recoverTerminal(terminalRecoveryKey(pty()), id, ops.clone)}
/>
</div>
)}
</Show>
)
}}
</Show>
</div>
</div>
</DragDropProvider>
</Show>
</div>
</aside>
)
}
@@ -598,12 +598,8 @@ export function MessageTimeline(props: {
const handleListPointerDown = (event: PointerEvent & { currentTarget: HTMLDivElement }) => {
if (!prependLoading) clearPrependAnchor()
props.onMarkScrollGesture(event.target)
}
const handleListPointerMove = (event: PointerEvent) => {
if (event.buttons !== 1) return
props.onMarkScrollGesture(event.target)
if (event.target !== event.currentTarget) return
props.onMarkScrollGesture(event.currentTarget)
}
const handleListKeyDown = (event: KeyboardEvent & { currentTarget: HTMLDivElement }) => {
@@ -1360,7 +1356,6 @@ export function MessageTimeline(props: {
onTouchEnd={handleListTouchEnd}
onTouchCancel={handleListTouchEnd}
onPointerDown={handleListPointerDown}
onPointerMove={handleListPointerMove}
onKeyDown={handleListKeyDown}
onScroll={handleListScroll}
onClick={props.onAutoScrollInteraction}
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import type { AssistantMessage, Message, UserMessage } from "@opencode-ai/sdk/v2"
import { isTimelineReady, loadOlderTimeline, selectUserMessages, selectVisibleUserMessages } from "./model"
import { loadOlderTimeline, selectUserMessages, selectVisibleUserMessages } from "./model"
const user = (id: string) => ({ id, role: "user" }) as UserMessage
const assistant = (id: string) => ({ id, role: "assistant" }) as AssistantMessage
@@ -15,12 +15,6 @@ describe("timeline model", () => {
expect(selectVisibleUserMessages(users)).toBe(users)
})
test("waits for an assistant-only load to hydrate its user root", () => {
expect(isTimelineReady([assistant("msg_2")], true)).toBe(false)
expect(isTimelineReady([user("msg_1"), assistant("msg_2")], true)).toBe(true)
expect(isTimelineReady([], false)).toBe(true)
})
test("loads exactly one opaque cursor page", async () => {
let calls = 0
const anchors: Array<string | boolean> = []
@@ -45,7 +45,7 @@ export function createTimelineModel(input: {
})
const ready = createMemo(() => {
const id = input.sessionID()
return !id || isTimelineReady(sync().data.message[id], serverSync().session.history.loading(id))
return !id || sync().data.message[id] !== undefined
})
const userMessages = createMemo(() => selectUserMessages(messages()), emptyUserMessages, { equals: same })
const visibleUserMessages = createMemo(
@@ -98,10 +98,6 @@ export function selectUserMessages(messages: Message[]) {
return messages.filter((message): message is UserMessage => message.role === "user")
}
export function isTimelineReady(messages: Message[] | undefined, loading: boolean) {
return messages !== undefined && (messages.some((message) => message.role === "user") || !loading)
}
export function selectVisibleUserMessages(messages: UserMessage[], revertMessageID?: string) {
if (!revertMessageID) return messages
return messages.filter((message) => message.id < revertMessageID)
-8
View File
@@ -5,14 +5,6 @@
- Keep Code Mode unaware of host session, channel, and conversation models. The hosting application supplies trusted execution scope around it.
- Tool schemas are the model-facing Interface. Keep arguments minimal and natural to the operation; never add unrelated IDs as ambient capability tokens.
## OpenAPI
- Generate an operation only when its transport semantics are supported; otherwise return a precise `skipped` reason.
- Never guess parameter serialization or malformed security semantics. Unsupported serialization is skipped and malformed security fails closed.
- Render unresolved schema constructs as `unknown`, never as invented TypeScript names.
- Keep network reads bounded and map expected encoding, transport, and decoding failures to model-safe `ToolError` values.
- Test supported behavior directly; do not reproduce adapter algorithms in tests.
## Future Design Notes
- If a captured user-visible output channel returns (an earlier `output.text`/`output.file`/`output.image` API was removed from v1), keep `output` as its name, distinct from the program return value: `return` stays the structured result for the model, while `output.*` describes artifacts the host may render into a conversation or UI after execution. Keep this host-neutral and let applications decide how captured output is delivered. In v1, hosts collect media host-side (outside the sandbox) instead.
+8 -27
View File
@@ -4,7 +4,7 @@ Effect-native confined code execution over explicit, schema-described tools.
CodeMode lets a model write a small JavaScript program that can call only the tools supplied by the host. The program can sequence calls, transform plain data, branch, loop, and run independent calls in parallel without receiving ambient filesystem, process, network, module, or application authority.
The package is currently private to this workspace. Its API is designed around one-shot and reusable execution:
The package is currently private to this workspace. Its API is designed around three uses:
```ts
// One execution
@@ -13,6 +13,9 @@ yield * CodeMode.execute({ tools, code })
// A reusable runtime
const runtime = CodeMode.make({ tools, limits })
yield * runtime.execute(code)
// One agent-facing code tool
const codeTool = runtime.agentTool()
```
## Install
@@ -114,9 +117,10 @@ const runtime = CodeMode.make({
runtime.catalog() // structured tool descriptions
runtime.instructions() // model-facing syntax and tool guide
runtime.execute(source) // ExecuteResult
runtime.agentTool() // { name, description, input, output, execute }
```
`CodeMode.Input` and `CodeMode.Result` are Effect schemas for the execution request and result. Hosts can combine them with `runtime.instructions()` and `runtime.execute()` when constructing a framework-specific agent tool.
`catalog`, `instructions`, and `agentTool` are projections of the same configured tool tree. `agentTool().description` is exactly `instructions()`.
### Results
@@ -148,31 +152,6 @@ interface ExecuteFailure {
`onToolCallEnd` receives `{ index, name, input, durationMs, outcome, message? }` when an admitted call settles. `outcome` is `"success"` or `"failure"`; `message` is the model-safe failure message and is present only on failure. Interrupted calls (for example when the execution timeout fires) do not produce an end event. Both hooks are Effect-returning and must not fail.
### OpenAPI tools
`OpenAPI.fromSpec` turns an OpenAPI 3.x document into a tool subtree - one tool per operation. Dotted `operationId` values form namespaces such as `v2.session.get`. Missing IDs receive a flat method/path fallback such as `getUsersById`; names are sanitized and deduplicated. The host places the subtree under a key in its `tools` tree; that key is the model-visible namespace.
```ts
import { CodeMode, OpenAPI } from "@opencode-ai/codemode"
import { Effect } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
const api = OpenAPI.fromSpec({
spec: await Bun.file("openapi.json").json(), // parsed document (no YAML)
auth: {
resolve: ({ name, scopes, operation }) =>
name === "BearerAuth" ? Effect.succeed({ type: "bearer", token }) : Effect.succeed(undefined),
},
})
const runtime = CodeMode.make({ tools: { opencode: api.tools } })
const result = await Effect.runPromise(runtime.execute(code).pipe(Effect.provide(FetchHttpClient.layer)))
```
`fromSpec` is synchronous and returns `{ tools, skipped }`. The initial adapter supports query `form`/`deepObject`, path/header `simple`, JSON request bodies, JSON responses, and text responses; unsupported parameter encodings, non-JSON request bodies, binary responses, and streaming operations land in `skipped` instead of producing broken tools. Operation and path servers take precedence over document servers unless `baseUrl` explicitly overrides all of them. Tool inputs flatten path, query, header, and closed object-body fields into one model-facing object while retaining their HTTP locations internally. Cross-location name collisions receive a location prefix such as `path_id` and `query_id`; composed, nullable, dictionary, conditionally-required, and non-object JSON bodies remain under `body`. Auth is never model-visible. Responses are limited to 50 MiB, and non-2xx responses become safe tool failures carrying the status and a size-capped body summary. Deferred capabilities are tracked in `src/openapi/TODO.md`.
Supported bearer, basic, header, and query authentication follows OpenAPI `security` semantics and is resolved host-side via `auth.resolve` - credential storage, OAuth flows, and token refresh never enter the compiler. Cookie authentication alternatives are discarded; an operation is skipped when it has no supported alternative. See the option docstrings in `src/openapi/types.ts` for the full semantics. Generated tools require `HttpClient.HttpClient` (from `effect/unstable/http`) in the Effect environment - provide `FetchHttpClient.layer` or a custom/test client layer at execution. The supplied client owns redirect policy; credentialed hosts should reject redirects or strip credentials when the origin changes.
## Discovery
The agent-tool instructions use a budgeted catalog. Every tool namespace is always listed with its tool count regardless of budget, and as many complete tool signatures (each with a one-line description) as fit an estimated-token budget are inlined. Selection is round-robin across namespaces for fairness: in each round (namespaces alphabetical), every namespace still holding un-inlined tools attempts to place its next-cheapest signature line against the shared budget, and a namespace whose next line does not fit drops out while the others keep going - so every namespace gets some representation before any namespace gets everything. The instructions state exactly how comprehensive the list is, both overall (`COMPLETE list` vs `PARTIAL - N of M shown`) and per namespace (`(3 tools)`, `(3 tools, 1 shown)`, `(3 tools, none shown)`).
@@ -329,6 +308,8 @@ A program cannot gain authority through prose or generated code. It can only exe
The public contract is guided by these equivalences:
- `CodeMode.execute({ ...options, code })` is equivalent to `CodeMode.make(options).execute(code)`.
- `CodeMode.make(options).agentTool().execute({ code })` is equivalent to `CodeMode.make(options).execute(code)`.
- `CodeMode.make(options).agentTool().description` equals `CodeMode.make(options).instructions()`.
- A tool implementation is not invoked unless its input has decoded successfully.
- A tool result is not visible to the program unless its output has decoded and crossed the plain-data boundary successfully.
- Unknown host failures do not become model-visible diagnostics; `ToolError` is the explicit safe-message channel.
+2 -2
View File
@@ -246,7 +246,7 @@ maxOutputBytes? }` (defaults 10_000 / 100 / 32_000). This wave kept the other kn
serialized values become truncated text + ` [result truncated: N bytes exceeds the M-byte
output limit; return a smaller value]`; logs keep leading lines within the remaining budget
- `[logs truncated: showing K of N lines]`; result gains `truncated: true` (also added to
`CodeMode.Result`). UTF-8-safe truncation (no split code points). (The in-sandbox
`ExecuteResultSchema`). UTF-8-safe truncation (no split code points). (The in-sandbox
`maxDataBytes` check that used to throw first on oversized raw values died in Fix 5 -
truncation is now the only result-size mechanism.)
- **Search polish**: default limit 12 -> **10** (`defaultSearchLimit`); exact-path lookup - a
@@ -575,7 +575,7 @@ configurable knobs; the internal limit system dies):
`maxCollectionLength` (every array-length/object-field-count check - this knob was
actively harmful: an MCP tool returning 20k rows failed). The `OperationLimitExceeded`
and `AuditLimitExceeded` diagnostic kinds are gone from the `DiagnosticKind` union and
`CodeMode.Result` (fine - the package is unreleased).
`ExecuteResultSchema` (fine - the package is unreleased).
- **Fixed constants, not knobs**: `TOOL_CALL_CONCURRENCY = 8` (codemode.ts; the fork
semaphore) and `MAX_VALUE_DEPTH = 32` (tool-runtime.ts; the `copyIn` depth check - kept
only because it produces a clearer error than a native stack-overflow RangeError; still
+27 -8
View File
@@ -108,14 +108,14 @@ export type ExecuteFailure = {
/** Result of executing a CodeMode program. Program failures are data, not Effect failures. */
export type ExecuteResult = ExecuteSuccess | ExecuteFailure
/** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */
/** Reusable CodeMode configuration shared by `execute` and `agentTool`. */
export type CodeModeOptions<Tools extends Record<string, unknown> = {}> = Omit<ExecuteOptions<Tools>, "code"> & {
/** Progressive-disclosure configuration for the agent-facing tool catalog. */
readonly discovery?: DiscoveryOptions
}
/** Schema for a CodeMode execution request. */
const Input = Schema.Struct({ code: Schema.String })
/** Input schema for the single agent-facing tool produced by `runtime.agentTool()`. */
export const ExecuteInputSchema = Schema.Struct({ code: Schema.String })
const DiagnosticKindSchema = Schema.Literals([
"ParseError",
@@ -130,8 +130,8 @@ const DiagnosticKindSchema = Schema.Literals([
"ExecutionFailure",
])
/** Schema for the structured success or diagnostic returned by CodeMode execution. */
const Result = Schema.Union([
/** Structured success or diagnostic result schema returned by CodeMode execution. */
export const ExecuteResultSchema = Schema.Union([
Schema.Struct({
ok: Schema.Literal(true),
value: Schema.Json,
@@ -153,12 +153,23 @@ const Result = Schema.Union([
}),
])
/** Agent-facing projection of a configured CodeMode runtime. */
export type AgentToolDefinition<R = never> = {
readonly name: "code"
readonly description: string
readonly input: typeof ExecuteInputSchema
readonly output: typeof ExecuteResultSchema
readonly execute: (input: { readonly code: string }) => Effect.Effect<ExecuteResult, never, R>
}
/** Reusable confined runtime over one explicit tool tree. */
export type CodeModeRuntime<R = never> = {
/** Lists schema-described tool paths provided by the host. */
readonly catalog: () => ReadonlyArray<ToolDescription>
/** Builds model-facing syntax guidance and visible tool signatures. */
readonly instructions: () => string
/** Projects the configured runtime as one agent-facing `code` tool. */
readonly agentTool: () => AgentToolDefinition<R>
/** Executes a program using this runtime's configured host tools. */
readonly execute: (code: string) => Effect.Effect<ExecuteResult, never, R>
}
@@ -4077,12 +4088,13 @@ export const execute = <const Tools extends Record<string, unknown>>(
/**
* Creates an Effect-native runtime over explicit, schema-described tools.
*
* Use `execute` for host-driven execution. Tool requirements remain in the returned Effect environment.
* Use `execute` for host-driven execution or `agentTool` to expose one confined code tool to an
* agent framework. Tool requirements remain in the returned Effect environment.
*
* @example
* ```ts
* const runtime = CodeMode.make({ tools: { orders: { lookup } } })
* const result = runtime.execute("return await tools.orders.lookup({ id: 'order_42' })")
* const code = runtime.agentTool()
* ```
*/
export const make = <const Tools extends Record<string, unknown> = {}>(
@@ -4099,9 +4111,16 @@ export const make = <const Tools extends Record<string, unknown> = {}>(
return {
catalog: () => catalog,
instructions: () => instructions,
agentTool: () => ({
name: "code",
description: instructions,
input: ExecuteInputSchema,
output: ExecuteResultSchema,
execute: ({ code }) => executeProgram(code),
}),
execute: executeProgram,
}
}
/** Constructors for one-shot and reusable CodeMode execution. */
export const CodeMode = { Input, Result, make, execute }
export const CodeMode = { make, execute }
+2 -2
View File
@@ -1,9 +1,9 @@
export { ToolError, CodeMode, toolError } from "./codemode.js"
export { ToolError, CodeMode, ExecuteInputSchema, ExecuteResultSchema, toolError } from "./codemode.js"
export { Tool } from "./tool.js"
export * as OpenAPI from "./openapi/index.js"
export type { Definition as ToolDefinition, JsonSchema, ToolSchema } from "./tool.js"
export type { ToolCallEnded, ToolCallHooks } from "./tool-runtime.js"
export type {
AgentToolDefinition,
CodeModeOptions,
CodeModeRuntime,
DataValue,
-19
View File
@@ -1,19 +0,0 @@
# OpenAPI Follow-ups
The initial adapter intentionally skips operations it cannot execute correctly. Future work may add:
- Cookie parameters, authentication, and cookie-header merging.
- Matrix, label, space-delimited, pipe-delimited, `allowReserved`, and parameter `content` serialization.
- External references and complete nested `$defs` support.
- Relative or templated server URLs and server variables.
- Base URLs containing query strings or fragments.
- Runtime response-schema validation and full content negotiation.
- Binary response values and explicit byte-oriented return types.
- Request/response projection for `readOnly` and `writeOnly` properties.
- SSE, WebSocket, and other streaming transports.
- Recovery of responses rejected by a status-filtering `HttpClient`.
- Configurable request and response size limits.
- Adapter-enforced redirect policy independent of the supplied `HttpClient`.
- Strict UTF-8 and empty-body validation for JSON responses.
- Compile-time rejection of parameter schemas with nested values unsupported by their serialization style; runtime rejects them before auth resolution.
- Complete malformed-security-scheme validation and broader auth-combination coverage.
-130
View File
@@ -1,130 +0,0 @@
import { HttpClient } from "effect/unstable/http"
import { Tool, type Definition } from "../tool.js"
import { invoke } from "./runtime.js"
import {
componentDefinitions,
inputSchema,
isRecord,
methods,
nonEmptyString,
operationInput,
operationOutput,
operationPath,
operationSecurityRequirements,
securityRequirements,
securitySchemes,
specServerUrl,
validateBaseUrl,
} from "./spec.js"
import type { Operation, Options, Result, Skipped, Tools } from "./types.js"
export type {
AuthResolver,
Credential,
Document,
Operation,
Options,
Result,
SecurityScheme,
Skipped,
Tools,
} from "./types.js"
/**
* Builds a CodeMode tool subtree from an OpenAPI 3.x document, one tool per
* operation. Auth is resolved host-side via `auth.resolve` and never
* model-visible. Tools require `HttpClient.HttpClient`; unrepresentable
* operations land in `skipped`.
*/
export const fromSpec = (options: Options): Result => {
const document = options.spec
const schemes = securitySchemes(document)
const defaultSecurity = securityRequirements(document.security)
const definitions = componentDefinitions(document)
const paths = isRecord(document.paths) ? document.paths : {}
const used = new Set<string>()
const namespaces = new Set<string>()
const skipped: Array<Skipped> = []
const tools = Object.create(null) as Tools
for (const [path, pathValue] of Object.entries(paths)) {
if (!isRecord(pathValue)) continue
for (const [method, operationValue] of Object.entries(pathValue)) {
if (!methods.has(method) || !isRecord(operationValue)) continue
const segments = operationPath(method, path, operationValue, used, namespaces)
const operation: Operation = {
operationId: nonEmptyString(operationValue.operationId),
method: method.toUpperCase(),
path,
summary: nonEmptyString(operationValue.summary),
description: nonEmptyString(operationValue.description),
}
const output = operationOutput(document, operationValue, definitions)
if (!output.ok) {
skipped.push({ method: operation.method, path, reason: output.reason })
continue
}
const resolvedBaseUrl = (() => {
if (options.baseUrl !== undefined) return validateBaseUrl(options.baseUrl)
if (operationValue.servers !== undefined) return specServerUrl(operationValue)
if (pathValue.servers !== undefined) return specServerUrl(pathValue)
return specServerUrl(document)
})()
if (!resolvedBaseUrl.ok) {
skipped.push({ method: operation.method, path, reason: resolvedBaseUrl.reason })
continue
}
const parsedInput = operationInput(document, pathValue, operationValue)
if (!parsedInput.ok) {
skipped.push({ method: operation.method, path, reason: parsedInput.reason })
continue
}
const input = parsedInput.value
const security = operationSecurityRequirements(operationValue.security, defaultSecurity, schemes)
if (!security.ok) {
skipped.push({ method: operation.method, path, reason: security.reason })
continue
}
const plan = {
operation,
url: `${resolvedBaseUrl.value.replace(/\/+$/, "")}${path}`,
fields: input.fields,
body: input.body,
security: security.value,
schemes,
auth: options.auth,
headers: options.headers ?? {},
}
used.add(segments.join("."))
for (const index of segments.slice(0, -1).keys()) namespaces.add(segments.slice(0, index + 1).join("."))
setTool(
tools,
segments,
Tool.make({
description: operation.description ?? operation.summary ?? `${operation.method} ${path}`,
input: inputSchema(input.fields, definitions),
output: output.value,
run: (input) => invoke(plan, input),
}),
)
}
}
return { tools, skipped }
}
const setTool = (tools: Tools, path: ReadonlyArray<string>, definition: Definition<HttpClient.HttpClient>): void => {
const [head, ...rest] = path
if (head === undefined) return
if (rest.length === 0) {
tools[head] = definition
return
}
const child = tools[head]
if (child === undefined || !isRecord(child) || child._tag === "CodeModeTool") {
tools[head] = Object.create(null) as Tools
}
setTool(tools[head] as Tools, rest, definition)
}
-326
View File
@@ -1,326 +0,0 @@
import { Effect, Option, Schema, Stream } from "effect"
import { HttpClient, HttpClientRequest, HttpClientResponse, type HttpMethod } from "effect/unstable/http"
import { ToolError, toolError } from "../tool-error.js"
import { isRecord, own } from "./spec.js"
import type { AppliedAuth, Credential, Plan, SecurityScheme } from "./types.js"
const decodeJson = Schema.decodeUnknownOption(Schema.UnknownFromJsonString)
const maxErrorBodyChars = 1_024
const maxResponseBodyBytes = 50 * 1024 * 1024
export const invoke = (plan: Plan, input: unknown): Effect.Effect<unknown, unknown, HttpClient.HttpClient> =>
Effect.gen(function* () {
const value = isRecord(input) ? input : {}
let request = yield* buildRequest(plan, value)
const auth = yield* resolveAuth(plan)
for (const [name, item] of Object.entries(auth.query)) {
request = HttpClientRequest.setUrlParam(request, name, item)
}
request = HttpClientRequest.setHeaders(request, auth.headers)
const client = yield* HttpClient.HttpClient
const response = yield* client
.execute(request)
.pipe(
Effect.catch((cause) =>
Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} failed: transport error`, cause)),
),
)
const text = yield* readResponseBody(response, plan)
const mediaType = response.headers["content-type"]?.split(";")[0]?.trim().toLowerCase()
const json = mediaType === "application/json" || mediaType?.endsWith("+json") === true
const decoded = text === "" ? Option.some(null) : json ? decodeJson(text) : Option.none()
const parsed = json ? Option.getOrElse(decoded, () => text) : text === "" ? null : text
if (response.status < 200 || response.status >= 300) {
const rendered = typeof parsed === "string" ? parsed : (JSON.stringify(parsed) ?? "")
const summary =
rendered === "" || rendered === "null"
? "no response body"
: rendered.length > maxErrorBodyChars
? `${rendered.slice(0, maxErrorBodyChars)}...`
: rendered
return yield* Effect.fail(
toolError(`${plan.operation.method} ${plan.operation.path} failed with HTTP ${response.status}: ${summary}`),
)
}
if (json && Option.isNone(decoded)) {
return yield* Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} returned malformed JSON.`))
}
return parsed
})
const buildRequest = (
plan: Plan,
input: Readonly<Record<string, unknown>>,
): Effect.Effect<HttpClientRequest.HttpClientRequest, ToolError> =>
Effect.gen(function* () {
// Validate every model-controlled value before auth resolution, which may refresh tokens.
const url = buildUrl(plan, input)
if (url instanceof ToolError) return yield* Effect.fail(url)
const missing = plan.fields.find(
(field) => field.required && field.location !== "path" && own(input, field.inputName) === undefined,
)
if (missing !== undefined) {
const label = missing.location === "body" ? "body field" : `${missing.location} parameter`
return yield* Effect.fail(toolError(`Missing required ${label} '${missing.inputName}'.`))
}
let request = HttpClientRequest.make(plan.operation.method as HttpMethod.HttpMethod)(url)
for (const field of plan.fields) {
if (field.location !== "query") continue
const item = own(input, field.inputName)
if (item === undefined) continue
const serialized = serializeQuery(request, field, item)
if (serialized instanceof ToolError) return yield* Effect.fail(serialized)
request = serialized
}
// Host headers first, then declared header parameters.
request = HttpClientRequest.setHeaders(request, plan.headers)
for (const field of plan.fields) {
if (field.location !== "header") continue
const item = own(input, field.inputName)
if (item === undefined) continue
const serialized = serializeSimple(field, item, String)
if (serialized instanceof ToolError) return yield* Effect.fail(serialized)
request = HttpClientRequest.setHeader(request, field.name, serialized)
}
const setBody = (value: unknown, mediaType: string) =>
HttpClientRequest.bodyJson(request, value).pipe(
Effect.map((next) => HttpClientRequest.setHeader(next, "content-type", mediaType)),
Effect.mapError((cause) =>
toolError(`Invalid JSON body for ${plan.operation.method} ${plan.operation.path}.`, cause),
),
)
if (plan.body?.mode === "value") {
const field = plan.fields.find((field) => field.location === "body")
const body = field === undefined ? undefined : own(input, field.inputName)
if (body !== undefined) request = yield* setBody(body, plan.body.mediaType)
}
if (plan.body?.mode === "object") {
const entries = plan.fields.flatMap((field) => {
if (field.location !== "body") return []
const item = own(input, field.inputName)
return item === undefined ? [] : [[field.name, item] as const]
})
if (plan.body.required || entries.length > 0) {
request = yield* setBody(Object.fromEntries(entries), plan.body.mediaType)
}
}
return request
})
const resolveAuth = (plan: Plan): Effect.Effect<AppliedAuth, unknown> =>
Effect.gen(function* () {
const none: AppliedAuth = { headers: {}, query: {} }
if (plan.security.length === 0) return none
const unavailable: Array<string> = []
alternatives: for (const requirement of plan.security) {
const names = Object.keys(requirement)
if (names.length === 0) return none
const credentials: Array<readonly [string, SecurityScheme, Credential]> = []
for (const name of names) {
const scheme = own(plan.schemes, name)
if (scheme === undefined || plan.auth === undefined) {
unavailable.push(name)
continue alternatives
}
const credential = yield* plan.auth.resolve({
name,
definition: scheme,
scopes: requirement[name] ?? [],
operation: plan.operation,
})
if (credential === undefined) {
unavailable.push(name)
continue alternatives
}
credentials.push([name, scheme, credential])
}
const applied = applyCredentials(credentials)
return applied instanceof ToolError ? yield* Effect.fail(applied) : applied
}
return yield* Effect.fail(
toolError(
`${plan.operation.method} ${plan.operation.path} requires authentication; no credential available for: ${[...new Set(unavailable)].join(", ")}.`,
),
)
})
const applyCredentials = (
credentials: ReadonlyArray<readonly [string, SecurityScheme, Credential]>,
): AppliedAuth | ToolError => {
const headers = new Map<string, string>()
const query = new Map<string, string>()
const add = (carrier: "header" | "query", name: string, value: string): ToolError | undefined => {
const target = carrier === "header" ? headers : query
if (target.has(name)) return toolError(`Authentication resolves multiple credentials for ${carrier} '${name}'.`)
target.set(name, value)
}
for (const [name, definition, credential] of credentials) {
if (credential.type === "bearer") {
const duplicate = add("header", "authorization", `Bearer ${credential.token}`)
if (duplicate !== undefined) return duplicate
continue
}
if (credential.type === "basic") {
// Buffer instead of btoa: btoa throws on non-Latin-1 credentials.
const duplicate = add(
"header",
"authorization",
`Basic ${Buffer.from(`${credential.username}:${credential.password}`, "utf8").toString("base64")}`,
)
if (duplicate !== undefined) return duplicate
continue
}
if (credential.type === "header") {
const duplicate = add("header", credential.name.toLowerCase(), credential.value)
if (duplicate !== undefined) return duplicate
continue
}
// apiKey: the carrier comes from the scheme declaration.
if (definition.type !== "apiKey") {
return toolError(
`Security scheme '${name}' is not an apiKey scheme; resolve a bearer, basic, or header credential for it.`,
)
}
if (definition.in === "cookie") return toolError(`Cookie authentication '${name}' is not supported.`)
const parameter = definition.in === "header" ? definition.name.toLowerCase() : definition.name
const duplicate = add(definition.in, parameter, credential.value)
if (duplicate !== undefined) return duplicate
}
return { headers: Object.fromEntries(headers), query: Object.fromEntries(query) }
}
const buildUrl = (plan: Plan, input: Readonly<Record<string, unknown>>): string | ToolError => {
let url = plan.url
for (const field of plan.fields) {
if (field.location !== "path") continue
const item = own(input, field.inputName)
if (item === undefined) {
return toolError(`Missing required path parameter '${field.inputName}'.`)
}
const fieldValue = serializeSimple(field, item, (value) =>
encodeURIComponent(value).replace(
/[!'()*]/g,
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
),
)
if (fieldValue instanceof ToolError) return fieldValue
// '.'/'..' survive encoding and URL normalization collapses them, letting a
// model-supplied value retarget the request to a different endpoint.
if (fieldValue === "" || fieldValue === "." || fieldValue === "..") {
return toolError(`Invalid path parameter '${field.inputName}'.`)
}
url = url.replaceAll(`{${field.name}}`, fieldValue)
}
const unresolved = url.match(/\{[^{}]+\}/)
if (unresolved !== null) return toolError(`Unresolved path parameter ${unresolved[0]}.`)
return url
}
const serializeSimple = (
field: Plan["fields"][number],
value: unknown,
encode: (value: string) => string,
): string | ToolError => {
const scalar = (item: unknown): string | ToolError =>
item !== null && typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean"
? toolError(`Parameter '${field.inputName}' contains an unsupported nested value.`)
: encode(String(item))
if (Array.isArray(value)) {
const items = value.map(scalar)
const invalid = items.find((item): item is ToolError => item instanceof ToolError)
return invalid ?? items.join(",")
}
if (!isRecord(value)) return scalar(value)
const entries = Object.entries(value).flatMap<string | ToolError>(([name, item]) => {
const rendered = scalar(item)
if (rendered instanceof ToolError) return [rendered]
return field.explode ? [`${encode(name)}=${rendered}`] : [encode(name), rendered]
})
const invalid = entries.find((item): item is ToolError => item instanceof ToolError)
return invalid ?? entries.join(",")
}
const serializeQuery = (
request: HttpClientRequest.HttpClientRequest,
field: Plan["fields"][number],
value: unknown,
): HttpClientRequest.HttpClientRequest | ToolError => {
if (field.style === "deepObject") {
if (!isRecord(value)) return toolError(`Deep-object parameter '${field.inputName}' must be an object.`)
return Object.entries(value).reduce<HttpClientRequest.HttpClientRequest | ToolError>((current, [name, item]) => {
if (current instanceof ToolError) return current
if (item === undefined || (item !== null && typeof item === "object")) {
return toolError(`Deep-object parameter '${field.inputName}' contains an unsupported nested value.`)
}
return HttpClientRequest.appendUrlParam(current, `${field.name}[${name}]`, String(item))
}, request)
}
if (Array.isArray(value)) {
const rendered = serializeSimple(field, value, String)
if (rendered instanceof ToolError) return rendered
if (!field.explode) return HttpClientRequest.appendUrlParam(request, field.name, rendered)
if (value.some((item) => item === undefined || (item !== null && typeof item === "object"))) {
return toolError(`Query parameter '${field.inputName}' contains an unsupported nested value.`)
}
return value.reduce((current, item) => HttpClientRequest.appendUrlParam(current, field.name, String(item)), request)
}
if (isRecord(value) && field.explode) {
return Object.entries(value).reduce<HttpClientRequest.HttpClientRequest | ToolError>((current, [name, item]) => {
if (current instanceof ToolError) return current
if (item === undefined || (item !== null && typeof item === "object")) {
return toolError(`Query parameter '${field.inputName}' contains an unsupported nested value.`)
}
return HttpClientRequest.appendUrlParam(current, name, String(item))
}, request)
}
const rendered = serializeSimple(field, value, String)
return rendered instanceof ToolError ? rendered : HttpClientRequest.appendUrlParam(request, field.name, rendered)
}
const readResponseBody = (
response: HttpClientResponse.HttpClientResponse,
plan: Plan,
): Effect.Effect<string, ToolError> =>
Effect.gen(function* () {
const contentLength = response.headers["content-length"]
const parsedSize = contentLength === undefined ? undefined : Number.parseInt(contentLength, 10)
const declaredSize =
parsedSize !== undefined && Number.isSafeInteger(parsedSize) && parsedSize >= 0 ? parsedSize : undefined
if (declaredSize !== undefined && declaredSize > maxResponseBodyBytes) {
return yield* Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} response exceeds 50 MiB.`))
}
let body = Buffer.allocUnsafe(Math.min(maxResponseBodyBytes, declaredSize ?? 64 * 1024))
let size = 0
yield* Stream.runForEach(response.stream, (chunk) => {
if (size + chunk.byteLength > maxResponseBodyBytes) {
return Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} response exceeds 50 MiB.`))
}
if (size + chunk.byteLength > body.byteLength) {
const grown = Buffer.allocUnsafe(
Math.min(maxResponseBodyBytes, Math.max(size + chunk.byteLength, body.byteLength * 2)),
)
body.copy(grown, 0, 0, size)
body = grown
}
body.set(chunk, size)
size += chunk.byteLength
return Effect.void
}).pipe(
Effect.catch((cause) => {
if (cause instanceof ToolError) return Effect.fail(cause)
if (cause.reason._tag === "EmptyBodyError") return Effect.void
return Effect.fail(
toolError(`${plan.operation.method} ${plan.operation.path} failed while reading the response body.`, cause),
)
}),
)
return new TextDecoder().decode(body.subarray(0, size))
})
-511
View File
@@ -1,511 +0,0 @@
import { fromSchemaOpenApi3_0, fromSchemaOpenApi3_1 } from "effect/JsonSchema"
import type { JsonSchema } from "../tool.js"
import { isBlockedMember } from "../tool-runtime.js"
import type {
Body,
Document,
InputField,
OperationInput,
Parsed,
SecurityRequirement,
SecurityScheme,
} from "./types.js"
export const methods = new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"])
const parameterLocations = ["path", "query", "header"] as const
const ignoredHeaderParameters = new Set(["accept", "content-type", "authorization"])
export const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value)
const asArray = (value: unknown): ReadonlyArray<unknown> => (Array.isArray(value) ? value : [])
export const nonEmptyString = (value: unknown): string | undefined =>
typeof value === "string" && value !== "" ? value : undefined
// Guards record lookups keyed by spec- or model-controlled names against
// prototype-inherited values (e.g. a parameter named `toString`).
export const own = <T>(record: Readonly<Record<string, T>>, key: string): T | undefined =>
Object.hasOwn(record, key) ? record[key] : undefined
export const resolve = (document: Document, value: unknown): unknown => {
const next = (current: unknown, seen: ReadonlySet<string>): unknown => {
if (!isRecord(current)) return current
const ref = nonEmptyString(current.$ref)
if (ref === undefined || !ref.startsWith("#/") || seen.has(ref)) return current
const target = ref
.slice(2)
.split("/")
.map((segment) => segment.replaceAll("~1", "/").replaceAll("~0", "~"))
.reduce<unknown>((item, segment) => (isRecord(item) ? own(item, segment) : undefined), document)
return target === undefined ? current : next(target, new Set([...seen, ref]))
}
return next(value, new Set())
}
const projectSchema = (document: Document, value: unknown): JsonSchema => {
if (!isRecord(value)) return {}
const normalized = nonEmptyString(document.openapi)?.startsWith("3.0")
? fromSchemaOpenApi3_0(value)
: fromSchemaOpenApi3_1(value)
return Object.keys(normalized.definitions).length === 0
? normalized.schema
: { ...normalized.schema, $defs: normalized.definitions }
}
export const componentDefinitions = (document: Document): Readonly<Record<string, JsonSchema>> => {
const components = isRecord(document.components) ? document.components : {}
const schemas = isRecord(components.schemas) ? components.schemas : {}
return Object.fromEntries(Object.entries(schemas).map(([name, value]) => [name, projectSchema(document, value)]))
}
const withDefinitions = (schema: JsonSchema, definitions: Readonly<Record<string, JsonSchema>>): JsonSchema => {
if (Object.keys(definitions).length === 0) return schema
const local = isRecord(schema.$defs) ? schema.$defs : {}
return { ...schema, $defs: { ...definitions, ...local } }
}
const isJsonMediaType = (mediaType: string): boolean => {
const normalized = mediaType.split(";")[0]?.trim().toLowerCase() ?? ""
return normalized === "application/json" || normalized.endsWith("+json")
}
const isBinaryMediaType = (document: Document, mediaType: string, value: unknown): boolean => {
const normalized = mediaType.split(";")[0]?.trim().toLowerCase() ?? ""
if (!isJsonMediaType(normalized) && !normalized.startsWith("text/")) return true
if (!isRecord(value)) return false
const schema = resolve(document, value.schema)
return isRecord(schema) && schema.format === "binary"
}
const jsonContent = (
content: Record<string, unknown>,
): { readonly mediaType: string; readonly schema: unknown } | undefined => {
const entry = Object.entries(content).find(([mediaType]) => isJsonMediaType(mediaType))
return entry !== undefined && isRecord(entry[1]) ? { mediaType: entry[0], schema: entry[1].schema } : undefined
}
const isFlattenableObjectBody = (
schema: unknown,
requestRequired: boolean,
): schema is Record<string, unknown> & { readonly properties: Record<string, unknown> } =>
isRecord(schema) &&
requestRequired &&
schema.type === "object" &&
isRecord(schema.properties) &&
schema.additionalProperties === false &&
schema.nullable !== true &&
schema.allOf === undefined &&
schema.anyOf === undefined &&
schema.oneOf === undefined
type PlannedField = Omit<InputField, "inputName">
const operationParameters = (
document: Document,
pathItem: Record<string, unknown>,
operation: Record<string, unknown>,
): Parsed<ReadonlyArray<PlannedField>> => {
// Operation-level parameters override path-level ones sharing (location, name).
const declared = new Map<
string,
{ readonly name: string; readonly location: string; readonly parameter: Record<string, unknown> }
>()
for (const raw of [...asArray(pathItem.parameters), ...asArray(operation.parameters)]) {
const resolved = resolve(document, raw)
if (!isRecord(resolved)) return { ok: false, reason: "parameter declaration is invalid or unresolved" }
const name = nonEmptyString(resolved.name)
const location = nonEmptyString(resolved.in)
if (name === undefined || location === undefined)
return { ok: false, reason: "parameter declaration is missing name or location" }
declared.set(`${location}:${name}`, { name, location, parameter: resolved })
}
const unordered: Array<PlannedField> = []
for (const item of declared.values()) {
const name = item.name
const location = item.location
const resolved = item.parameter
if (location === "cookie") return { ok: false, reason: `cookie parameter '${name}' is not supported` }
if (location !== "path" && location !== "query" && location !== "header") {
return { ok: false, reason: `parameter '${name}' uses unsupported location '${location}'` }
}
if (location === "header" && ignoredHeaderParameters.has(name.toLowerCase())) continue
if (resolved.schema === undefined && resolved.content === undefined) {
return { ok: false, reason: `parameter '${name}' declares neither schema nor content` }
}
if (resolved.content !== undefined)
return { ok: false, reason: `parameter '${name}' uses unsupported content encoding` }
if (resolved.style !== undefined && nonEmptyString(resolved.style) === undefined) {
return { ok: false, reason: `parameter '${name}' has an invalid style` }
}
if (resolved.explode !== undefined && typeof resolved.explode !== "boolean") {
return { ok: false, reason: `parameter '${name}' has an invalid explode value` }
}
if (resolved.allowReserved !== undefined && typeof resolved.allowReserved !== "boolean") {
return { ok: false, reason: `parameter '${name}' has an invalid allowReserved value` }
}
if (resolved.allowReserved === true)
return { ok: false, reason: `parameter '${name}' uses unsupported allowReserved encoding` }
const declaredStyle = nonEmptyString(resolved.style) ?? (location === "query" ? "form" : "simple")
if (location === "query" && declaredStyle !== "form" && declaredStyle !== "deepObject") {
return { ok: false, reason: `query parameter '${name}' uses unsupported style '${declaredStyle}'` }
}
if (location !== "query" && declaredStyle !== "simple") {
return { ok: false, reason: `${location} parameter '${name}' uses unsupported style '${declaredStyle}'` }
}
const style = declaredStyle === "deepObject" ? "deepObject" : declaredStyle === "form" ? "form" : "simple"
const explode = typeof resolved.explode === "boolean" ? resolved.explode : style === "form"
if (style === "deepObject" && !explode) {
return { ok: false, reason: `query parameter '${name}' uses deepObject with explode=false` }
}
const base = projectSchema(document, resolved.schema)
const description = nonEmptyString(resolved.description)
unordered.push({
name,
location,
required: resolved.required === true || location === "path",
style,
explode,
schema: {
...base,
...(base.description === undefined && description !== undefined ? { description } : {}),
},
})
}
return {
ok: true,
value: parameterLocations.flatMap((location) => unordered.filter((field) => field.location === location)),
}
}
const operationBody = (
document: Document,
operation: Record<string, unknown>,
): Parsed<{ readonly fields: ReadonlyArray<PlannedField>; readonly body: Body | undefined }> => {
const resolved = resolve(document, operation.requestBody)
if (!isRecord(resolved)) return { ok: true, value: { fields: [], body: undefined } }
const content = isRecord(resolved.content) ? resolved.content : {}
const selected = jsonContent(content)
if (selected === undefined) {
return {
ok: false,
reason: `request body has no JSON content (declared: ${Object.keys(content).join(", ") || "none"})`,
}
}
const schema = resolve(document, selected.schema)
const required = resolved.required === true
if (!isFlattenableObjectBody(schema, required)) {
return {
ok: true,
value: {
fields: [
{
name: "body",
location: "body",
required,
schema: projectSchema(document, selected.schema),
style: undefined,
explode: undefined,
},
],
body: { required, mode: "value", mediaType: selected.mediaType },
},
}
}
const requiredProperties = new Set(
Array.isArray(schema.required) ? schema.required.filter((item): item is string => typeof item === "string") : [],
)
return {
ok: true,
value: {
fields: Object.entries(schema.properties).map(([name, value]) => ({
name,
location: "body" as const,
required: required && requiredProperties.has(name),
schema: projectSchema(document, value),
style: undefined,
explode: undefined,
})),
body: { required, mode: "object", mediaType: selected.mediaType },
},
}
}
export const operationInput = (
document: Document,
pathItem: Record<string, unknown>,
operation: Record<string, unknown>,
): Parsed<OperationInput> => {
const parameters = operationParameters(document, pathItem, operation)
if (!parameters.ok) return parameters
const requestBody = operationBody(document, operation)
if (!requestBody.ok) return requestBody
const fields = [...parameters.value, ...requestBody.value.fields]
const conflicts = new Set(
[...Map.groupBy(fields, (field) => field.name)]
.filter(([, matches]) => new Set(matches.map((field) => field.location)).size > 1)
.map(([name]) => name),
)
const used = new Set<string>()
return {
ok: true,
value: {
fields: fields.map((field) => {
const visibleName = isBlockedMember(field.name) ? `${field.name}_2` : field.name
const base = conflicts.has(field.name) ? `${field.location}_${visibleName}` : visibleName
const next = (index: number): string => {
const candidate = index === 1 ? base : `${base}_${index}`
return used.has(candidate) ? next(index + 1) : candidate
}
const inputName = next(1)
used.add(inputName)
return { ...field, inputName }
}),
body: requestBody.value.body,
},
}
}
export const inputSchema = (
fields: ReadonlyArray<InputField>,
definitions: Readonly<Record<string, JsonSchema>>,
): JsonSchema => {
const required = fields.filter((field) => field.required).map((field) => field.inputName)
return withDefinitions(
{
type: "object",
properties: Object.fromEntries(fields.map((field) => [field.inputName, field.schema])),
...(required.length === 0 ? {} : { required }),
},
definitions,
)
}
const successfulResponses = (
document: Document,
operation: Record<string, unknown>,
): Parsed<ReadonlyArray<Record<string, unknown>>> => {
if (!isRecord(operation.responses)) return { ok: true, value: [] }
const entries = Object.entries(operation.responses)
const selected = [
...entries.filter(([status]) => /^2\d\d$/.test(status)).sort(([a], [b]) => a.localeCompare(b)),
...entries.filter(([status]) => status.toUpperCase() === "2XX"),
]
const responses: Array<Record<string, unknown>> = []
for (const [, value] of selected) {
const resolved = resolve(document, value)
if (!isRecord(resolved) || nonEmptyString(resolved.$ref) !== undefined) {
return { ok: false, reason: "successful response declaration is invalid or unresolved" }
}
responses.push(resolved)
}
return { ok: true, value: responses }
}
export const operationOutput = (
document: Document,
operation: Record<string, unknown>,
definitions: Readonly<Record<string, JsonSchema>>,
): Parsed<JsonSchema | undefined> => {
if (operation["x-websocket"] === true) return { ok: false, reason: "WebSocket operations are not supported" }
const responses = successfulResponses(document, operation)
if (!responses.ok) return responses
const streams = responses.value.some(
(response) =>
isRecord(response.content) &&
Object.keys(response.content).some(
(mediaType) => mediaType.split(";")[0]?.trim().toLowerCase() === "text/event-stream",
),
)
if (streams) return { ok: false, reason: "SSE operations are not supported" }
const binary = responses.value.some(
(response) =>
isRecord(response.content) &&
Object.entries(response.content).some(([mediaType, value]) => isBinaryMediaType(document, mediaType, value)),
)
if (binary) return { ok: false, reason: "binary responses are not supported" }
const outcomes: Array<JsonSchema> = []
for (const response of responses.value) {
if (response.content !== undefined && !isRecord(response.content)) return { ok: true, value: undefined }
const content = isRecord(response.content) ? response.content : {}
if (Object.keys(content).length === 0) {
outcomes.push({ type: "null" })
continue
}
for (const [mediaType, value] of Object.entries(content)) {
if (!isJsonMediaType(mediaType)) {
outcomes.push({ type: "string" })
continue
}
if (!isRecord(value) || value.schema === undefined) return { ok: true, value: undefined }
outcomes.push(projectSchema(document, value.schema))
}
}
if (outcomes.length === 0) return { ok: true, value: undefined }
return {
ok: true,
value: withDefinitions(outcomes.length === 1 ? (outcomes[0] ?? {}) : { anyOf: outcomes }, definitions),
}
}
const sanitizeOperationSegment = (raw: string): string => {
const base =
raw
.replaceAll(/[^A-Za-z0-9_$]+/g, "_")
.replace(/^_+|_+$/g, "")
.replace(/^([0-9])/, "_$1") || "operation"
return isBlockedMember(base) ? `${base}_2` : base
}
const fallbackOperationId = (method: string, path: string): string =>
[
method,
...path
.split("/")
.filter((part) => part !== "")
.flatMap((part) => (part.startsWith("{") && part.endsWith("}") ? ["by", part.slice(1, -1)] : [part]))
.flatMap((part) => part.split(/[^A-Za-z0-9]+/).filter((word) => word !== "")),
]
.map((word, index) => {
const lower = word.toLowerCase()
return index === 0 ? lower : `${lower.charAt(0).toUpperCase()}${lower.slice(1)}`
})
.join("")
export const operationPath = (
method: string,
path: string,
operation: Record<string, unknown>,
used: ReadonlySet<string>,
namespaces: ReadonlySet<string>,
): ReadonlyArray<string> => {
const raw = nonEmptyString(operation.operationId)
const segments = (raw === undefined ? [fallbackOperationId(method, path)] : raw.split(".")).map(
sanitizeOperationSegment,
)
if (isOperationPathAvailable(segments, used, namespaces)) return segments
const conflict = segments.slice(0, -1).findIndex((_, index) => used.has(segments.slice(0, index + 1).join(".")))
if (conflict >= 0 && conflict + 1 < segments.length) {
const collapsed = segments.flatMap((segment, index) => {
if (index === conflict) {
const next = segments[index + 1] ?? ""
return [`${segment}${next.charAt(0).toUpperCase()}${next.slice(1)}`]
}
return index === conflict + 1 ? [] : [segment]
})
if (isOperationPathAvailable(collapsed, used, namespaces)) return collapsed
}
const fallback = segments.join("_")
const next = (index: number): string => {
const candidate = `${fallback}_${index}`
return isOperationPathAvailable([candidate], used, namespaces) ? candidate : next(index + 1)
}
return [next(2)]
}
const isOperationPathAvailable = (
segments: ReadonlyArray<string>,
used: ReadonlySet<string>,
namespaces: ReadonlySet<string>,
): boolean => {
const key = segments.join(".")
if (used.has(key) || namespaces.has(key)) return false
return segments.slice(0, -1).every((_, index) => !used.has(segments.slice(0, index + 1).join(".")))
}
export const specServerUrl = (source: Record<string, unknown>): Parsed<string> => {
const server = asArray(source.servers).find(isRecord)
const url = server === undefined ? undefined : nonEmptyString(server.url)
if (url === undefined) return { ok: false, reason: "spec declares no servers; pass baseUrl" }
if (/\{[^{}]+\}/.test(url)) {
return { ok: false, reason: `server URL '${url}' is not an absolute URL; pass baseUrl` }
}
return validateBaseUrl(url)
}
export const validateBaseUrl = (value: string): Parsed<string> => {
if (!/^https?:\/\//i.test(value)) return { ok: false, reason: `server URL '${value}' is not an absolute HTTP(S) URL` }
const url = URL.parse(value)
if (url === null || (url.protocol !== "http:" && url.protocol !== "https:")) {
return { ok: false, reason: `server URL '${value}' is not an absolute HTTP(S) URL` }
}
if (url.search !== "" || url.hash !== "") {
return { ok: false, reason: `server URL '${value}' contains an unsupported query string or fragment` }
}
return { ok: true, value }
}
export const securityRequirements = (value: unknown): Parsed<ReadonlyArray<SecurityRequirement>> => {
if (value === undefined) return { ok: true, value: [] }
if (!Array.isArray(value)) return { ok: false, reason: "security declaration is not an array" }
const requirements: Array<SecurityRequirement> = []
for (const item of value) {
if (!isRecord(item)) return { ok: false, reason: "security requirement is not an object" }
const requirement = Object.create(null) as Record<string, ReadonlyArray<string>>
for (const [name, scopes] of Object.entries(item)) {
if (!Array.isArray(scopes)) return { ok: false, reason: "security requirement scopes are not string arrays" }
const parsed = scopes.filter((scope): scope is string => typeof scope === "string")
if (parsed.length !== scopes.length) {
return { ok: false, reason: "security requirement scopes are not string arrays" }
}
requirement[name] = parsed
}
requirements.push(requirement)
}
return { ok: true, value: requirements }
}
export const operationSecurityRequirements = (
value: unknown,
defaults: Parsed<ReadonlyArray<SecurityRequirement>>,
schemes: Readonly<Record<string, SecurityScheme>>,
): Parsed<ReadonlyArray<SecurityRequirement>> => {
const parsed = value === undefined ? defaults : securityRequirements(value)
if (!parsed.ok) return parsed
const supported = parsed.value.filter((requirement) =>
Object.keys(requirement).every((name) => {
const scheme = own(schemes, name)
return scheme !== undefined && !(scheme.type === "apiKey" && scheme.in === "cookie")
}),
)
if (parsed.value.length === 0 || supported.length > 0) return { ok: true, value: supported }
const names = [...new Set(parsed.value.flatMap((requirement) => Object.keys(requirement)))]
const cookieScheme = names.find((name) => {
const definition = own(schemes, name)
return definition?.type === "apiKey" && definition.in === "cookie"
})
return {
ok: false,
reason:
cookieScheme === undefined
? `security requirement references missing or malformed scheme: ${names.join(", ")}`
: `cookie authentication '${cookieScheme}' is not supported`,
}
}
export const securitySchemes = (document: Document): Readonly<Record<string, SecurityScheme>> => {
const components = isRecord(document.components) ? document.components : {}
const declared = isRecord(components.securitySchemes) ? components.securitySchemes : {}
return Object.fromEntries(
Object.entries(declared).flatMap<readonly [string, SecurityScheme]>(([name, value]) => {
const resolved = resolve(document, value)
if (!isRecord(resolved)) return []
const type = nonEmptyString(resolved.type)
if (type === "apiKey") {
const carrier = nonEmptyString(resolved.in)
const parameter = nonEmptyString(resolved.name)
if (parameter === undefined || (carrier !== "header" && carrier !== "query" && carrier !== "cookie")) return []
return [[name, { type, name: parameter, in: carrier }] as const]
}
if (type === "http") {
const scheme = nonEmptyString(resolved.scheme)?.toLowerCase()
return scheme === undefined ? [] : [[name, { type, scheme }] as const]
}
if (type === "oauth2" || type === "openIdConnect") return [[name, { type }] as const]
return []
}),
)
}
-112
View File
@@ -1,112 +0,0 @@
import { Effect } from "effect"
import { HttpClient } from "effect/unstable/http"
import type { Definition, JsonSchema } from "../tool.js"
/** A parsed OpenAPI 3.x document. YAML must be parsed by the host. */
export type Document = Record<string, unknown>
/** The operation identity handed to auth resolution and errors. */
export type Operation = {
readonly operationId: string | undefined
readonly method: string
readonly path: string
readonly summary: string | undefined
readonly description: string | undefined
}
/** A resolved OpenAPI security scheme from `components.securitySchemes`. */
export type SecurityScheme =
| { readonly type: "apiKey"; readonly name: string; readonly in: "header" | "query" | "cookie" }
| { readonly type: "http"; readonly scheme: string }
| { readonly type: "oauth2" }
| { readonly type: "openIdConnect" }
/**
* Credential material returned by a host auth resolver. The carrier for `apiKey`
* comes from the scheme definition, not the credential. `header` is the escape
* hatch for nonstandard schemes.
*/
export type Credential =
| { readonly type: "bearer"; readonly token: string }
| { readonly type: "basic"; readonly username: string; readonly password: string }
| { readonly type: "apiKey"; readonly value: string }
| { readonly type: "header"; readonly name: string; readonly value: string }
/**
* Resolves credential material for one named security scheme at call time.
* `undefined` means unavailable, try the next OR alternative; a failure aborts
* the call rather than falling through.
*/
export type AuthResolver = (context: {
readonly name: string
readonly definition: SecurityScheme
readonly scopes: ReadonlyArray<string>
readonly operation: Operation
}) => Effect.Effect<Credential | undefined, unknown>
export type Options = {
readonly spec: Document
/** Overrides all document, path, and operation `servers`. Required when no applicable absolute server URL exists. */
readonly baseUrl?: string | undefined
/** Host credential resolution, keyed by security scheme name. */
readonly auth?: { readonly resolve: AuthResolver } | undefined
/** Static headers on every request. Not model-visible; declared header params may override them, auth always wins. */
readonly headers?: Readonly<Record<string, string>> | undefined
}
/** An operation that could not be represented as a tool, and why. */
export type Skipped = {
readonly method: string
readonly path: string
readonly reason: string
}
export type Tools = { [name: string]: Definition<HttpClient.HttpClient> | Tools }
export type Result = {
/** Tool subtree; the host places it under a key in its `tools` tree. */
readonly tools: Tools
readonly skipped: ReadonlyArray<Skipped>
}
export type Parsed<T> = { readonly ok: true; readonly value: T } | { readonly ok: false; readonly reason: string }
export type InputLocation = "path" | "query" | "header" | "body"
export type InputField = {
/** Model-visible field name after cross-location collision handling. */
readonly inputName: string
/** Original parameter or body-property name used on the wire. */
readonly name: string
readonly location: InputLocation
readonly required: boolean
readonly schema: JsonSchema
readonly style: "simple" | "form" | "deepObject" | undefined
readonly explode: boolean | undefined
}
export type Body = { readonly required: boolean; readonly mode: "object" | "value"; readonly mediaType: string }
export type OperationInput = {
readonly fields: ReadonlyArray<InputField>
readonly body: Body | undefined
}
/** One OR alternative: scheme name -> required scopes. Empty object = unauthenticated is acceptable. */
export type SecurityRequirement = Readonly<Record<string, ReadonlyArray<string>>>
export type Plan = {
readonly operation: Operation
readonly url: string
readonly fields: ReadonlyArray<InputField>
readonly body: Body | undefined
readonly security: ReadonlyArray<SecurityRequirement>
readonly schemes: Readonly<Record<string, SecurityScheme>>
readonly auth: { readonly resolve: AuthResolver } | undefined
readonly headers: Readonly<Record<string, string>>
}
export type AppliedAuth = {
readonly headers: Readonly<Record<string, string>>
readonly query: Readonly<Record<string, string>>
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Token estimation for budgeting model-facing text. Copied from
* `@opencode-ai/core/util/token` (chars / 4) so this package stays
* dependency-free; keep the two in sync if the heuristic ever changes.
*/
export * as Token from "./token.js"
const CHARS_PER_TOKEN = 4
export const estimate = (input: string) => Math.max(0, Math.round(input.length / CHARS_PER_TOKEN))
+45 -50
View File
@@ -10,32 +10,27 @@ import {
outputTypeScript,
type Definition,
} from "./tool.js"
import { estimate } from "./token.js"
import { SandboxDate, SandboxMap, SandboxPromise, SandboxRegExp, SandboxSet } from "./values.js"
const estimateTokens = (input: string) => Math.max(0, Math.round(input.length / 4))
export type HostTool<R = never> = (...args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
export type HostTools<R = never> = {
[name: string]: HostTool<R> | Definition<R> | HostTools<R>
}
export type Services<Tools> = ServicesOf<Tools, []>
type ServicesOf<Tools, Depth extends ReadonlyArray<unknown>> = Depth["length"] extends 8
? never
: Tools extends (...args: Array<unknown>) => Effect.Effect<unknown, unknown, infer R>
export type Services<Tools> = Tools extends (...args: Array<unknown>) => Effect.Effect<unknown, unknown, infer R>
? R
: Tools extends {
readonly _tag: "CodeModeTool"
readonly run: (input: unknown) => Effect.Effect<unknown, unknown, infer R>
}
? R
: Tools extends {
readonly _tag: "CodeModeTool"
readonly run: (input: unknown) => Effect.Effect<unknown, unknown, infer R>
}
? R
: Tools extends object
? string extends keyof Tools
? ServicesOf<Tools[string], [...Depth, unknown]>
: ServicesOf<Tools[keyof Tools], [...Depth, unknown]>
: never
: Tools extends object
? string extends keyof Tools
? never
: Services<Tools[keyof Tools]>
: never
/** Minimal audit record retained for each admitted tool call. */
export type ToolCall = {
@@ -295,16 +290,17 @@ const definitions = <R>(
return entries
}
const describeDefinition = <R>(path: string, definition: Definition<R>): ToolDescription => ({
path,
description: definition.description,
signature: `${toolExpression(path)}(input: ${inputTypeScript(definition)}): Promise<${outputTypeScript(definition)}>`,
})
const visibleDefinitions = <R>(tools: HostTools<R>) =>
definitions(tools).map(({ path, definition }) => ({
path,
definition,
description: {
path,
description: definition.description,
signature: `${toolExpression(path)}(input: ${inputTypeScript(definition)}): Promise<${outputTypeScript(definition)}>`,
},
}))
definitions(tools).flatMap(({ path, definition }) => {
const description = describeDefinition(path, definition)
return [{ path, definition, description }]
})
export const catalog = <R>(tools: HostTools<R>): ReadonlyArray<ToolDescription> =>
visibleDefinitions(tools).map(({ description }) => description)
@@ -355,10 +351,16 @@ const termForms = (term: string): Array<string> => {
return forms
}
const firstLine = (text: string) => text.split("\n", 1)[0]!.trim()
/** One-line description used on inline catalog lines; the full text stays in search results. */
const brief = (text: string, max = 120) => {
const line = firstLine(text)
return line.length > max ? line.slice(0, max - 1) + "..." : line
}
const catalogLine = (tool: ToolDescription) => {
// Inline catalog lines use only a compact first line; full text stays in search results.
const line = tool.description.split("\n", 1)[0]!.trim()
const description = line.length > 120 ? line.slice(0, 119) + "..." : line
const description = brief(tool.description)
return description === "" ? ` - ${tool.signature}` : ` - ${tool.signature} // ${description}`
}
@@ -428,7 +430,7 @@ export const discoveryPlan = <R>(
picked: new Set<ToolDescription>(),
queue: [...group].sort(
(left, right) =>
estimateTokens(catalogLine(left)) - estimateTokens(catalogLine(right)) || left.path.localeCompare(right.path),
estimate(catalogLine(left)) - estimate(catalogLine(right)) || left.path.localeCompare(right.path),
),
}))
let used = 0
@@ -437,7 +439,7 @@ export const discoveryPlan = <R>(
const stillActive: typeof active = []
for (const selection of active) {
const tool = selection.queue[0]!
const cost = estimateTokens(catalogLine(tool))
const cost = estimate(catalogLine(tool))
if (used + cost > maxInlineCatalogTokens) continue
selection.queue.shift()
selection.picked.add(tool)
@@ -456,8 +458,8 @@ export const discoveryPlan = <R>(
// Section order is deliberate: workflow first (the top is the least likely part of a long
// description to be truncated or skimmed away), then rules, then syntax, with the budgeted
// catalog at the bottom. Example call forms use placeholders - never a real or fabricated
// tool name - and show both dot and bracket notation so non-identifier names are not normalized.
// catalog at the bottom. Example call forms use explicit `<namespace>.<tool>` placeholders -
// never a real or fabricated tool name.
const intro = [
"Write a CodeMode program to answer the request. Return code only.",
empty
@@ -465,9 +467,6 @@ export const discoveryPlan = <R>(
: complete
? "Execute JavaScript in a confined runtime. Inside this program, `tools` contains only the host-provided tools listed below; surrounding agent tools are not available unless listed here."
: "Execute JavaScript in a confined runtime. Inside this program, `tools` contains only the host-provided tools listed or searchable below; surrounding agent tools are not available unless listed here.",
...(empty
? []
: ["Do not infer or normalize tool names; use only exact signatures shown below or returned by search."]),
]
// The search step exists only when search is advertised (PARTIAL catalog); a COMPLETE
@@ -481,14 +480,14 @@ export const discoveryPlan = <R>(
...(complete
? [
"1. Pick a tool from the list under `## Available tools` - each line is the exact call signature; use it as-is rather than guessing segments.",
"2. Call it using the exact signature shown; bracket notation and quotes are part of the path.",
"2. Call it using the exact signature shown: `const res = await tools.<namespace>.<tool>(input)` - bracket notation may appear for names that are not JavaScript identifiers.",
'3. Parse text results: `const data = typeof res === "string" ? JSON.parse(res) : res` - most tools return JSON as a string.',
"4. Return only the fields you need: `return { <field>: data.<field> }` - raw payloads get truncated and waste context.",
]
: [
'1. If the exact signature is not listed below, first search: `const { items } = await tools.$codemode.search({ query: "<intent + key nouns>" })`.',
'1. Find a tool (skip when it is already listed below): `const { items } = await tools.$codemode.search({ query: "<intent + key nouns>" })` - short phrases like "list issues" work best.',
"2. Read the matches: each item is `{ path, description, signature }` - read the description before using an unfamiliar tool.",
"3. Call the result's `path` as-is; bracket notation and quotes are part of the path.",
"3. Call it with the result's `path` as-is (never guess segments): `const res = await tools.<namespace>.<tool>(input)` - bracket notation may appear for names that are not JavaScript identifiers.",
'4. Parse text results: `const data = typeof res === "string" ? JSON.parse(res) : res` - most tools return JSON as a string.',
"5. Return only the fields you need: `return { <field>: data.<field> }` - raw payloads get truncated and waste context.",
]),
@@ -505,7 +504,7 @@ export const discoveryPlan = <R>(
: "- Only tools listed here or returned by `tools.$codemode.search` are available inside `tools`; tools from the surrounding agent/runtime are not implicitly exposed.",
"- Filter, aggregate, and transform collections in code - never return them raw or call a tool per item across messages.",
"- A result typed `Promise<unknown>` has no guaranteed shape - verify what actually came back before relying on its fields.",
'- Run independent calls in parallel: `await Promise.all(items.map((item) => tools.<namespace>.<tool>(item)))`, or use `tools.<namespace>["tool-name"](item)` when the listed signature uses bracket notation.',
"- Run independent calls in parallel: `await Promise.all(items.map((item) => tools.<namespace>.<tool>(item)))`.",
"- `Object.keys(tools)` lists namespaces; `Object.keys(tools.<namespace>)` lists its tools; `for...in` works on both.",
...(complete
? []
@@ -636,6 +635,9 @@ export type ToolRuntime<R = never> = {
readonly keys: (path: ReadonlyArray<string>) => ReadonlyArray<string>
}
const failureMessage = (error: unknown): string =>
error instanceof ToolError || error instanceof ToolRuntimeError ? error.message : "Tool execution failed"
export const make = <R>(
tools: HostTools<R>,
/** Undefined means unlimited tool calls. */
@@ -654,16 +656,9 @@ export const make = <R>(
const startedAt = Date.now()
return effect.pipe(
Effect.tap(() => onEnd({ ...call, durationMs: Date.now() - startedAt, outcome: "success" })),
Effect.tapError((error) => {
const message =
error instanceof ToolError || error instanceof ToolRuntimeError ? error.message : "Tool execution failed"
return onEnd({
...call,
durationMs: Date.now() - startedAt,
outcome: "failure",
message,
})
}),
Effect.tapError((error) =>
onEnd({ ...call, durationMs: Date.now() - startedAt, outcome: "failure", message: failureMessage(error) }),
),
)
}
+11 -61
View File
@@ -1,4 +1,4 @@
import { Effect, JsonPointer, Schema } from "effect"
import { Effect, Schema } from "effect"
/**
* JSON Schema subset accepted for render-only tool schemas.
@@ -13,7 +13,6 @@ export type JsonSchema = {
readonly const?: unknown
readonly anyOf?: ReadonlyArray<JsonSchema>
readonly oneOf?: ReadonlyArray<JsonSchema>
readonly allOf?: ReadonlyArray<JsonSchema>
readonly properties?: Readonly<Record<string, JsonSchema>>
readonly required?: ReadonlyArray<string>
readonly items?: JsonSchema
@@ -77,13 +76,6 @@ const effectNumberSentinel = (schema: JsonSchema) =>
schema.enum.length === 1 &&
(schema.enum[0] === "NaN" || schema.enum[0] === "Infinity" || schema.enum[0] === "-Infinity")
const intersection = (members: ReadonlyArray<string>): string => {
const concrete = members.filter((member) => member !== "unknown")
if (concrete.length === 0) return "unknown"
if (concrete.length === 1) return concrete[0] ?? "unknown"
return concrete.map((member) => (member.includes(" | ") ? `(${member})` : member)).join(" & ")
}
/**
* Recursion ceiling for schema rendering. Object, array, and union recursion all increment
* depth, so this bounds every recursion path - pathological or structurally cyclic schemas
@@ -97,30 +89,6 @@ type RenderContext = {
readonly pretty: boolean
}
const hasUnresolvedRef = (
schema: JsonSchema,
definitions: Readonly<Record<string, JsonSchema>>,
seen: ReadonlySet<string> = new Set(),
visited: ReadonlySet<JsonSchema> = new Set(),
): boolean => {
if (visited.has(schema)) return false
const nextVisited = new Set([...visited, schema])
if (schema.$ref !== undefined) {
const segment = schema.$ref.match(/^#\/(?:\$defs|definitions)\/([^/]+)$/)?.[1]
const name = segment === undefined ? undefined : JsonPointer.unescapeToken(segment)
if (name === undefined || definitions[name] === undefined || seen.has(name)) return true
if (hasUnresolvedRef(definitions[name], definitions, new Set([...seen, name]), nextVisited)) return true
}
return [
...(schema.anyOf ?? []),
...(schema.oneOf ?? []),
...(schema.allOf ?? []),
...Object.values(schema.properties ?? {}),
...(schema.items === undefined ? [] : [schema.items]),
...(typeof schema.additionalProperties === "object" ? [schema.additionalProperties] : []),
].some((item) => hasUnresolvedRef(item, definitions, seen, nextVisited))
}
/**
* Schema constraints a TypeScript type cannot express natively but a model benefits from,
* surfaced as JSDoc tags (`@deprecated`, `@default`, `@format`, `@minItems`, `@maxItems`).
@@ -168,18 +136,11 @@ const renderSchema = (
seen: ReadonlySet<string> = new Set(),
): string => {
if (depth > MAX_RENDER_DEPTH) return "unknown"
const nested =
schema.definitions === undefined && schema.$defs === undefined
? ctx
: { ...ctx, definitions: { ...ctx.definitions, ...(schema.definitions ?? {}), ...(schema.$defs ?? {}) } }
if (schema.$ref) {
const segment = schema.$ref.match(/^#\/(?:\$defs|definitions)\/([^/]+)$/)?.[1]
const name = segment === undefined ? undefined : JsonPointer.unescapeToken(segment)
if (!name || !nested.definitions[name] || seen.has(name)) return "unknown"
return intersection([
renderSchema(nested.definitions[name], nested, depth, new Set([...seen, name])),
renderSchema({ ...schema, $ref: undefined }, nested, depth + 1, seen),
])
const name = schema.$ref.split("/").pop()
if (!name || !ctx.definitions[name]) return name ?? "unknown"
if (seen.has(name)) return name // recursive type: reference by name rather than loop
return renderSchema(ctx.definitions[name], ctx, depth, new Set([...seen, name]))
}
if (schema.const !== undefined) return renderLiteral(schema.const)
if (schema.enum) return schema.enum.map(renderLiteral).join(" | ")
@@ -205,34 +166,24 @@ const renderSchema = (
) {
return "{}"
}
const members = alternatives.map((item) => renderSchema(item, nested, depth + 1, seen))
if (members.some((member) => member === "unknown")) return "unknown"
return intersection([
members.join(" | "),
renderSchema({ ...schema, anyOf: undefined, oneOf: undefined }, nested, depth + 1, seen),
])
}
if (schema.allOf) {
const members = schema.allOf.map((item) => renderSchema(item, nested, depth + 1, seen))
if (schema.allOf.some((item) => hasUnresolvedRef(item, nested.definitions))) return "unknown"
return intersection([renderSchema({ ...schema, allOf: undefined }, nested, depth + 1, seen), ...members])
return alternatives.map((item) => renderSchema(item, ctx, depth + 1, seen)).join(" | ")
}
if (Array.isArray(schema.type)) {
return schema.type.map((item) => renderSchema({ ...schema, type: item }, nested, depth + 1, seen)).join(" | ")
return schema.type.map((item) => renderSchema({ type: item }, ctx, depth + 1, seen)).join(" | ")
}
if (schema.type === "string") return "string"
if (schema.type === "number" || schema.type === "integer") return "number"
if (schema.type === "boolean") return "boolean"
if (schema.type === "null") return "null"
if (schema.type === "array") return `Array<${renderSchema(schema.items ?? {}, nested, depth + 1, seen)}>`
if (schema.type === "array") return `Array<${renderSchema(schema.items ?? {}, ctx, depth + 1, seen)}>`
if (schema.type === "object" || schema.properties) {
const required = new Set(schema.required ?? [])
const properties = Object.entries(schema.properties ?? {})
const additional = schema.additionalProperties
const indexType =
additional && typeof additional === "object" ? renderSchema(additional, nested, depth + 1, seen) : undefined
additional && typeof additional === "object" ? renderSchema(additional, ctx, depth + 1, seen) : undefined
const field = ([name, value]: readonly [string, JsonSchema]) =>
`${renderKey(name)}${required.has(name) ? "" : "?"}: ${renderSchema(value, nested, depth + 1, seen)}`
`${renderKey(name)}${required.has(name) ? "" : "?"}: ${renderSchema(value, ctx, depth + 1, seen)}`
if (!ctx.pretty) {
const fields = properties.map(field)
@@ -302,8 +253,7 @@ export const inputProperties = <R>(definition: Definition<R>): Array<InputProper
const definitions = document.definitions ?? {}
let schema = document.schema
if (schema.$ref !== undefined) {
const segment = schema.$ref.match(/^#\/(?:\$defs|definitions)\/([^/]+)$/)?.[1]
const name = segment === undefined ? undefined : JsonPointer.unescapeToken(segment)
const name = schema.$ref.split("/").pop()
const resolved = name === undefined ? undefined : definitions[name]
if (resolved === undefined) return []
schema = resolved
+28 -13
View File
@@ -1,6 +1,13 @@
import { describe, expect, test } from "bun:test"
import { Cause, Effect, Schema } from "effect"
import { CodeMode, Tool, toolError, type ExecutionLimits } from "../src/index.js"
import {
CodeMode,
ExecuteInputSchema,
ExecuteResultSchema,
Tool,
toolError,
type ExecutionLimits,
} from "../src/index.js"
import type { Definition } from "../src/tool.js"
const run = (tool: Definition<never>) =>
@@ -228,7 +235,7 @@ describe("CodeMode console capture", () => {
logs: ['Thread info: {"name":"Demo","count":2}', "[warn] careful"],
toolCalls: [],
})
expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
expect(Schema.decodeUnknownSync(ExecuteResultSchema)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
})
test("keeps logs captured before failures", async () => {
@@ -364,7 +371,7 @@ describe("CodeMode output budget", () => {
expect(result.value).toMatch(
/^\{"data":"x+ \[result truncated: \d+ bytes exceeds the 40-byte output limit; return a smaller value\]$/,
)
expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
expect(Schema.decodeUnknownSync(ExecuteResultSchema)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
})
test("keeps leading logs within the remaining budget and marks the cut", async () => {
@@ -494,16 +501,24 @@ describe("CodeMode public contract", () => {
const tools = { orders: { lookup } }
const source = `return await tools.orders.lookup({ id: "order_42" })`
test("keeps one-shot and reusable execution equivalent", async () => {
test("keeps one-shot, reusable, and agent-tool execution equivalent", async () => {
const runtime = CodeMode.make({ tools })
const [oneShot, reusable] = await Promise.all([
const agentTool = runtime.agentTool()
const [oneShot, reusable, projected] = await Promise.all([
Effect.runPromise(CodeMode.execute({ tools, code: source })),
Effect.runPromise(runtime.execute(source)),
Effect.runPromise(agentTool.execute({ code: source })),
])
expect(reusable).toStrictEqual(oneShot)
expect(Schema.decodeUnknownSync(CodeMode.Input)({ code: source })).toStrictEqual({ code: source })
expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(reusable)))).toStrictEqual(reusable)
expect(projected).toStrictEqual(oneShot)
expect(agentTool.name).toBe("code")
expect(agentTool.input).toBe(ExecuteInputSchema)
expect(agentTool.output).toBe(ExecuteResultSchema)
expect(agentTool.description).toBe(runtime.instructions())
expect(Schema.decodeUnknownSync(ExecuteResultSchema)(JSON.parse(JSON.stringify(projected)))).toStrictEqual(
projected,
)
})
test("inlines a COMPLETE small catalog and keeps search registered but unadvertised", async () => {
@@ -607,12 +622,12 @@ describe("CodeMode public contract", () => {
)
expect(instructions).toContain("Return only the fields you need")
expect(instructions).toContain("raw payloads get truncated and waste context")
expect(instructions).toContain("Do not infer or normalize tool names")
expect(instructions).toContain("bracket notation and quotes are part of the path")
expect(instructions).toContain("`const res = await tools.<namespace>.<tool>(input)`")
expect(instructions).toContain("surrounding agent tools are not available unless listed here")
expect(instructions).toContain("Only tools listed here are available inside `tools`")
// Placeholders use generic namespace/tool/field names only - no fabricated real tools
// and no real catalog tools cherry-picked into example lines.
expect(instructions).toContain("bracket notation may appear for names that are not JavaScript identifiers")
// Placeholders use the <namespace>.<tool>/<field> style ONLY - no fabricated tool
// names, and no real catalog tools cherry-picked into example lines.
expect(instructions).toContain("`return { <field>: data.<field> }`")
expect(instructions).not.toContain("total_count")
expect(instructions).not.toContain("list_issues")
@@ -625,7 +640,7 @@ describe("CodeMode public contract", () => {
// PARTIAL: the workflow starts with search (with query-style guidance that is clearly
// a query string, never a tool name) and the browse-namespace rule appears.
expect(partial).toContain(
'1. If the exact signature is not listed below, first search: `const { items } = await tools.$codemode.search({ query: "<intent + key nouns>" })`.',
'1. Find a tool (skip when it is already listed below): `const { items } = await tools.$codemode.search({ query: "<intent + key nouns>" })` - short phrases like "list issues" work best.',
)
expect(partial).toContain(
"Only tools listed here or returned by `tools.$codemode.search` are available inside `tools`",
@@ -1020,7 +1035,7 @@ describe("CodeMode public contract", () => {
value: { top: null, nested: [1, null] },
toolCalls: [],
})
expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
expect(Schema.decodeUnknownSync(ExecuteResultSchema)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
})
test("rejects invalid configuration and discovery limits", async () => {
-230
View File
@@ -1,230 +0,0 @@
{
"openapi": "3.1.0",
"info": {
"title": "CodeMode Happy Path",
"version": "1.0.0"
},
"servers": [
{
"url": "https://api.example.test/v1"
}
],
"security": [
{
"BearerAuth": []
}
],
"paths": {
"/users/{userId}": {
"parameters": [
{
"$ref": "#/components/parameters/UserId"
}
],
"get": {
"operationId": "users.get",
"summary": "Get a user",
"parameters": [
{
"name": "include",
"in": "query",
"style": "form",
"explode": false,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "verbose",
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "X-Trace-ID",
"in": "header",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/UserResponse"
}
}
},
"delete": {
"operationId": "users.remove",
"summary": "Remove a user",
"responses": {
"204": {
"description": "Removed"
}
}
}
},
"/users": {
"post": {
"operationId": "users.create",
"summary": "Create a user",
"security": [
{
"ApiKey": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"role": {
"type": "string",
"enum": ["admin", "member"]
}
},
"required": ["name", "email"],
"additionalProperties": false
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/vnd.example+json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
},
"/search": {
"get": {
"operationId": "search.run",
"summary": "Search users",
"security": [],
"parameters": [
{
"name": "filter",
"in": "query",
"style": "deepObject",
"explode": true,
"schema": {
"type": "object",
"properties": {
"query": {
"type": "string"
},
"page": {
"type": "integer"
}
},
"required": ["query"],
"additionalProperties": false
}
},
{
"name": "tags",
"in": "query",
"style": "form",
"explode": true,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
],
"responses": {
"200": {
"description": "Summary",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
},
"components": {
"parameters": {
"UserId": {
"name": "userId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
},
"responses": {
"UserResponse": {
"description": "A user",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
},
"schemas": {
"User": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"role": {
"type": "string",
"enum": ["admin", "member"]
}
},
"required": ["id", "name", "email"],
"additionalProperties": false
}
},
"securitySchemes": {
"BearerAuth": {
"type": "http",
"scheme": "bearer"
},
"ApiKey": {
"type": "apiKey",
"in": "query",
"name": "api_key"
}
}
}
}
-23730
View File
@@ -1,23730 +0,0 @@
{
"openapi": "3.1.0",
"info": {
"title": "opencode HttpApi",
"version": "0.0.1",
"description": "Experimental HttpApi surface for selected instance routes."
},
"paths": {
"/api/health": {
"get": {
"tags": ["server.health"],
"operationId": "v2.health.get",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"healthy": {
"type": "boolean",
"enum": [true]
}
},
"required": ["healthy"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Check whether the API server is ready to accept requests.",
"summary": "Check server health"
}
},
"/api/location": {
"get": {
"tags": ["server.location"],
"operationId": "v2.location.get",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Location.Info",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Location.Info"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Resolve the requested location or the server default location.",
"summary": "Get location"
}
},
"/api/agent": {
"get": {
"tags": ["server.agent"],
"operationId": "v2.agent.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AgentV2.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve currently registered agents.",
"summary": "List agents"
}
},
"/api/plugin": {
"get": {
"tags": ["plugins"],
"operationId": "v2.plugin.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Plugin.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve currently loaded plugins.",
"summary": "List plugins"
}
},
"/api/session": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.list",
"parameters": [
{
"name": "workspace",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^wrk"
}
]
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "limit",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Maximum number of sessions to return. Defaults to the newest 50 sessions."
},
"required": false
},
{
"name": "order",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string",
"enum": ["asc", "desc"]
},
{
"type": "null"
}
],
"description": "Session order for the first page. Use desc for newest first or asc for oldest first."
},
"required": false
},
{
"name": "search",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "parentID",
"in": "query",
"schema": {
"anyOf": [
{
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
{
"type": "string",
"enum": ["null"]
}
],
"description": "Filter by parent session. Use null to return only root sessions."
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "directory",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "project",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "subpath",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "cursor",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string",
"description": "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response."
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "SessionsResponse",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionsResponse"
}
}
}
},
"400": {
"description": "InvalidCursorError | InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidCursorError"
},
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve sessions in the requested order. Items keep that order across pages; use cursor.next or cursor.previous to move through the ordered list.",
"summary": "List sessions"
},
"post": {
"tags": ["sessions"],
"operationId": "v2.session.create",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/SessionV2.Info"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Create a session at the requested location.",
"summary": "Create session",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
{
"type": "null"
}
]
},
"agent": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"model": {
"anyOf": [
{
"$ref": "#/components/schemas/Model.Ref"
},
{
"type": "null"
}
]
},
"location": {
"anyOf": [
{
"$ref": "#/components/schemas/Location.Ref"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/active": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.active",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"patternProperties": {
"^ses": {
"$ref": "#/components/schemas/SessionActive"
}
}
},
"watermarks": {
"$ref": "#/components/schemas/SessionWatermarks"
}
},
"required": ["data", "watermarks"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive. Watermarks are the durable log positions read alongside the activity snapshot; activity itself is process state, so the pairing is advisory rather than transactional.",
"summary": "List active sessions"
}
},
"/api/session/{sessionID}": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.get",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/SessionV2.Info"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve a session by ID.",
"summary": "Get session"
}
},
"/api/session/{sessionID}/fork": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.fork",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/SessionV2.Info"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | MessageNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/MessageNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Create a child session by copying projected history from the parent. When messageID is supplied, copy messages before that boundary.",
"summary": "Fork session",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"messageID": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/agent": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.switchAgent",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Switch the agent used by subsequent provider turns.",
"summary": "Switch session agent",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"agent": {
"type": "string"
}
},
"required": ["agent"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/model": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.switchModel",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Switch the model used by subsequent provider turns.",
"summary": "Switch session model",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"model": {
"$ref": "#/components/schemas/Model.Ref"
}
},
"required": ["model"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/rename": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.rename",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Update the session title.",
"summary": "Rename session",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
}
},
"required": ["title"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/prompt": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.prompt",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/SessionInput.Admitted"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConflictError"
}
}
}
}
},
"description": "Durably admit one session input and schedule agent-loop execution unless resume is false.",
"summary": "Send message",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
{
"type": "null"
}
]
},
"prompt": {
"$ref": "#/components/schemas/PromptInput"
},
"delivery": {
"anyOf": [
{
"type": "string",
"enum": ["steer", "queue"]
},
{
"type": "null"
}
]
},
"resume": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
}
},
"required": ["prompt"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/command": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.command",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/SessionInput.Admitted"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | CommandNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/CommandNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConflictError"
}
}
}
},
"500": {
"description": "CommandEvaluationError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandEvaluationError"
}
}
}
}
},
"description": "Resolve a slash command into prompt input, admit it durably, and schedule execution unless resume is false.",
"summary": "Run command",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
{
"type": "null"
}
]
},
"command": {
"type": "string"
},
"arguments": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"agent": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"model": {
"anyOf": [
{
"$ref": "#/components/schemas/Model.Ref"
},
{
"type": "null"
}
]
},
"files": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PromptInput.FileAttachment"
}
},
"agents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.AgentAttachment"
}
},
"delivery": {
"anyOf": [
{
"type": "string",
"enum": ["steer", "queue"]
},
{
"type": "null"
}
]
},
"resume": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
}
},
"required": ["command"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/skill": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.skill",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | SkillNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SkillNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Activate a skill for a session by appending a skill message and resuming execution.",
"summary": "Activate skill",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
{
"type": "null"
}
]
},
"skill": {
"type": "string"
},
"resume": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
}
},
"required": ["skill"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/synthetic": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.synthetic",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Append a synthetic message to a session and resume execution.",
"summary": "Add synthetic message",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"metadata": {
"type": "object"
}
},
"required": ["text"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/compact": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.compact",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "SessionBusyError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionBusyError"
}
}
}
},
"500": {
"description": "UnknownError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnknownError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Compact a session conversation.",
"summary": "Compact session"
}
},
"/api/session/{sessionID}/wait": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.wait",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Wait for a session agent loop to become idle.",
"summary": "Wait for session"
}
},
"/api/session/{sessionID}/revert/stage": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.revert.stage",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/Revert.State"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "MessageNotFoundError | SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/MessageNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "SessionBusyError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionBusyError"
}
}
}
},
"500": {
"description": "UnknownError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnknownError"
}
}
}
}
},
"description": "Stage or move a reversible session boundary and optionally apply its file changes.",
"summary": "Stage session revert",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"files": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
}
},
"required": ["messageID"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/revert/clear": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.revert.clear",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "SessionBusyError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionBusyError"
}
}
}
},
"500": {
"description": "UnknownError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnknownError"
}
}
}
}
},
"summary": "Clear staged revert"
}
},
"/api/session/{sessionID}/revert/commit": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.revert.commit",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "SessionBusyError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionBusyError"
}
}
}
}
},
"summary": "Commit staged revert"
}
},
"/api/session/{sessionID}/context": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.context",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Session.Message"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"500": {
"description": "UnknownError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnknownError"
}
}
}
}
},
"description": "Retrieve the active context messages for a session (all messages after the last compaction).",
"summary": "Get session context"
}
},
"/api/session/{sessionID}/context-entry": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.context.entry.list",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SessionContextEntry.Info"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "List API-managed context entries attached to the session's system context.",
"summary": "List context entries"
}
},
"/api/session/{sessionID}/context-entry/{key}": {
"put": {
"tags": ["sessions"],
"operationId": "v2.session.context.entry.put",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "key",
"in": "path",
"schema": {
"$ref": "#/components/schemas/SessionContextEntry.Key"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Attach or replace one durable context entry. The value is rendered into the session's system context; changes announce as updates at the next turn boundary.",
"summary": "Put context entry",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"value": {}
},
"required": ["value"],
"additionalProperties": false
}
}
},
"required": true
}
},
"delete": {
"tags": ["sessions"],
"operationId": "v2.session.context.entry.remove",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "key",
"in": "path",
"schema": {
"$ref": "#/components/schemas/SessionContextEntry.Key"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Remove one context entry; the removal is announced to the model at the next turn boundary.",
"summary": "Remove context entry"
}
},
"/api/session/{sessionID}/log": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.log",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "after",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "follow",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string",
"enum": ["true", "false"]
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"text/event-stream": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"event": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/SessionLogItemStream"
}
},
"required": ["id", "event", "data"],
"additionalProperties": false
},
"x-effect-stream": {
"encoding": "sse",
"causeSchema": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Fail"]
},
"error": {
"not": {}
}
},
"required": ["_tag", "error"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Die"]
},
"defect": {}
},
"required": ["_tag", "defect"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Interrupt"]
},
"fiberId": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "fiberId"],
"additionalProperties": false
}
]
}
},
"errorSchema": {
"not": {}
},
"failureEvent": "effect/httpapi/stream/failure"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Durable, ordered, gap-free read of public session events after an exclusive aggregate sequence. Emits a synced marker once replay reaches the captured watermark, then completes; with follow=true it continues with live events instead. The only event API that promises reliability: attach after a snapshot watermark to compose fetch and stream without a race window.",
"summary": "Read the session log"
}
},
"/api/session/{sessionID}/interrupt": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.interrupt",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Interrupt active execution owned by this OpenCode process. Idle interruption is a no-op.",
"summary": "Interrupt session execution"
}
},
"/api/session/{sessionID}/background": {
"post": {
"tags": ["sessions"],
"operationId": "v2.session.background",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Move active foreground backgroundable tools for this session into background observation. Idle requests are a no-op.",
"summary": "Background blocking session tools"
}
},
"/api/session/{sessionID}/message/{messageID}": {
"get": {
"tags": ["sessions"],
"operationId": "v2.session.message",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "messageID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/Session.Message"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | MessageNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/MessageNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve one projected message owned by the Session.",
"summary": "Get session message"
}
},
"/api/session/{sessionID}/message": {
"get": {
"tags": ["messages"],
"operationId": "v2.session.messages",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "limit",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Maximum number of messages to return. When omitted, the endpoint returns its default page size."
},
"required": false
},
{
"name": "order",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string",
"enum": ["asc", "desc"]
},
{
"type": "null"
}
],
"description": "Message order for the first page. Use desc for newest first or asc for oldest first."
},
"required": false
},
{
"name": "cursor",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string",
"description": "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response. Do not combine with order."
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "SessionMessagesResponse",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionMessagesResponse"
}
}
}
},
"400": {
"description": "InvalidCursorError | InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidCursorError"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"500": {
"description": "UnknownError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnknownError"
}
}
}
}
},
"description": "Retrieve projected messages for a session. Items keep the requested order across pages; use cursor.next or cursor.previous to move through the ordered timeline.",
"summary": "Get session messages"
}
},
"/api/model": {
"get": {
"tags": ["models"],
"operationId": "v2.model.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ModelV2.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Retrieve available models ordered by release date.",
"summary": "List models"
}
},
"/api/model/default": {
"get": {
"tags": ["models"],
"operationId": "v2.model.default",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"anyOf": [
{
"$ref": "#/components/schemas/ModelV2.Info"
},
{
"type": "null"
}
]
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Retrieve the model used when a session has no explicit model selection.",
"summary": "Get default model"
}
},
"/api/generate": {
"post": {
"tags": ["generate"],
"operationId": "v2.generate.text",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "GenerateTextResponse",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenerateTextResponse"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Run one stateless model generation at the requested location and return the assistant text. Uses the location's default model when none is specified.",
"summary": "Generate text",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"prompt": {
"type": "string"
},
"model": {
"anyOf": [
{
"$ref": "#/components/schemas/Model.Ref"
},
{
"type": "null"
}
]
}
},
"required": ["prompt"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/provider": {
"get": {
"tags": ["providers"],
"operationId": "v2.provider.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ProviderV2.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Retrieve active AI providers so clients can show provider availability and configuration.",
"summary": "List providers"
}
},
"/api/provider/{providerID}": {
"get": {
"tags": ["providers"],
"operationId": "v2.provider.get",
"parameters": [
{
"name": "providerID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/ProviderV2.Info"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "ProviderNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProviderNotFoundError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
}
}
}
}
},
"description": "Retrieve a single AI provider so clients can inspect its availability and endpoint settings.",
"summary": "Get provider"
}
},
"/api/integration": {
"get": {
"tags": ["integrations"],
"operationId": "v2.integration.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Integration.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve available integrations and their authentication methods.",
"summary": "List integrations"
}
},
"/api/integration/{integrationID}": {
"get": {
"tags": ["integrations"],
"operationId": "v2.integration.get",
"parameters": [
{
"name": "integrationID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"anyOf": [
{
"$ref": "#/components/schemas/Integration.Info"
},
{
"type": "null"
}
]
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve one integration and its authentication methods.",
"summary": "Get integration"
}
},
"/api/integration/{integrationID}/connect/key": {
"post": {
"tags": ["integrations"],
"operationId": "v2.integration.connect.key",
"parameters": [
{
"name": "integrationID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Run a key authentication method and store the resulting credential.",
"summary": "Connect with key",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["key"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/integration/{integrationID}/connect/oauth": {
"post": {
"tags": ["integrations"],
"operationId": "v2.integration.connect.oauth",
"parameters": [
{
"name": "integrationID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Integration.Attempt"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Start an OAuth attempt and return the authorization details.",
"summary": "Begin OAuth connection",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"methodID": {
"type": "string"
},
"inputs": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["methodID", "inputs"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/integration/attempt/{attemptID}": {
"get": {
"tags": ["integrations"],
"operationId": "v2.integration.attempt.status",
"parameters": [
{
"name": "attemptID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Integration.AttemptStatus"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Poll the current status of an OAuth attempt.",
"summary": "Get OAuth attempt status"
},
"delete": {
"tags": ["integrations"],
"operationId": "v2.integration.attempt.cancel",
"parameters": [
{
"name": "attemptID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Cancel an OAuth attempt and release its resources.",
"summary": "Cancel OAuth connection"
}
},
"/api/integration/attempt/{attemptID}/complete": {
"post": {
"tags": ["integrations"],
"operationId": "v2.integration.attempt.complete",
"parameters": [
{
"name": "attemptID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Complete a code-based OAuth attempt and store the resulting credential.",
"summary": "Complete OAuth connection",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/mcp": {
"get": {
"tags": ["mcp"],
"operationId": "v2.mcp.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Mcp.Server"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve configured MCP servers and their connection status.",
"summary": "List MCP servers"
}
},
"/api/credential/{credentialID}": {
"patch": {
"tags": ["server.credential"],
"operationId": "v2.credential.update",
"parameters": [
{
"name": "credentialID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Update a stored credential label.",
"summary": "Update credential",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"label": {
"type": "string"
}
},
"required": ["label"],
"additionalProperties": false
}
}
},
"required": true
}
},
"delete": {
"tags": ["server.credential"],
"operationId": "v2.credential.remove",
"parameters": [
{
"name": "credentialID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Remove a stored integration credential.",
"summary": "Remove credential"
}
},
"/api/project/current": {
"get": {
"tags": ["projects"],
"operationId": "v2.project.current",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Project.Current",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Project.Current"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Resolve the project for the requested location.",
"summary": "Get current project"
}
},
"/api/project/{projectID}/directories": {
"get": {
"tags": ["projects"],
"operationId": "v2.project.directories",
"parameters": [
{
"name": "projectID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Project.Directories",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Project.Directories"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "List known local absolute directories for a project.",
"summary": "List project directories"
}
},
"/api/form/request": {
"get": {
"tags": ["forms"],
"operationId": "v2.form.request.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.FormInfo"
},
{
"$ref": "#/components/schemas/Form.UrlInfo"
}
]
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve pending forms for a location.",
"summary": "List pending form requests"
}
},
"/api/session/{sessionID}/form": {
"get": {
"tags": ["forms"],
"operationId": "v2.session.form.list",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.FormInfo"
},
{
"$ref": "#/components/schemas/Form.UrlInfo"
}
]
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve pending forms for a session.",
"summary": "List session forms"
},
"post": {
"tags": ["forms"],
"operationId": "v2.session.form.create",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.FormInfo"
},
{
"$ref": "#/components/schemas/Form.UrlInfo"
}
]
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConflictError"
}
}
}
}
},
"description": "Create a form for a session.",
"summary": "Create session form",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Form.CreatePayload"
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/form/{formID}": {
"get": {
"tags": ["forms"],
"operationId": "v2.session.form.get",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "formID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.FormInfo"
},
{
"$ref": "#/components/schemas/Form.UrlInfo"
}
]
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | FormNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/FormNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve a form for a session.",
"summary": "Get session form"
}
},
"/api/session/{sessionID}/form/{formID}/state": {
"get": {
"tags": ["forms"],
"operationId": "v2.session.form.state",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "formID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/Form.State"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | FormNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/FormNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve the current state for a form.",
"summary": "Get form state"
}
},
"/api/session/{sessionID}/form/{formID}/reply": {
"post": {
"tags": ["forms"],
"operationId": "v2.session.form.reply",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "formID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "FormInvalidAnswerError | InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/FormInvalidAnswerError"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | FormNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/FormNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "FormAlreadySettledError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/FormAlreadySettledError"
}
}
}
}
},
"description": "Submit an answer to a pending form.",
"summary": "Reply to form",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Form.Reply"
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/form/{formID}/cancel": {
"post": {
"tags": ["forms"],
"operationId": "v2.session.form.cancel",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "formID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | FormNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/FormNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
},
"409": {
"description": "FormAlreadySettledError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/FormAlreadySettledError"
}
}
}
}
},
"description": "Cancel a pending form.",
"summary": "Cancel form"
}
},
"/api/permission/request": {
"get": {
"tags": ["permissions"],
"operationId": "v2.permission.request.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PermissionV2.Request"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve pending permission requests for a location.",
"summary": "List pending permission requests"
}
},
"/api/permission/saved": {
"get": {
"tags": ["permissions"],
"operationId": "v2.permission.saved.list",
"parameters": [
{
"name": "projectID",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PermissionSaved.Info"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve saved permissions, optionally filtered by project.",
"summary": "List saved permissions"
}
},
"/api/permission/saved/{id}": {
"delete": {
"tags": ["permissions"],
"operationId": "v2.permission.saved.remove",
"parameters": [
{
"name": "id",
"in": "path",
"schema": {
"type": "string"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Remove a saved permission by ID.",
"summary": "Remove saved permission"
}
},
"/api/session/{sessionID}/permission": {
"post": {
"tags": ["permissions"],
"operationId": "v2.session.permission.create",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"effect": {
"$ref": "#/components/schemas/PermissionV2.Effect"
}
},
"required": ["id", "effect"],
"additionalProperties": false
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Evaluate and, when approval is required, create a permission request for a session.",
"summary": "Create permission request",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
{
"type": "null"
}
]
},
"action": {
"type": "string"
},
"resources": {
"type": "array",
"items": {
"type": "string"
}
},
"save": {
"type": "array",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object"
},
"source": {
"$ref": "#/components/schemas/PermissionV2.Source"
},
"agent": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["action", "resources"],
"additionalProperties": false
}
}
},
"required": true
}
},
"get": {
"tags": ["permissions"],
"operationId": "v2.session.permission.list",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PermissionV2.Request"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve pending permission requests owned by a session.",
"summary": "List session permission requests"
}
},
"/api/session/{sessionID}/permission/{requestID}": {
"get": {
"tags": ["permissions"],
"operationId": "v2.session.permission.get",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "requestID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/PermissionV2.Request"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | PermissionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/PermissionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve a pending permission request owned by a session.",
"summary": "Get permission request"
}
},
"/api/session/{sessionID}/permission/{requestID}/reply": {
"post": {
"tags": ["permissions"],
"operationId": "v2.session.permission.reply",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "requestID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | PermissionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/PermissionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Respond to a pending permission request owned by a session.",
"summary": "Reply to pending permission request",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"reply": {
"$ref": "#/components/schemas/PermissionV2.Reply"
},
"message": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["reply"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/fs/read/*": {
"get": {
"tags": ["filesystem"],
"operationId": "v2.fs.read",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Serve one file relative to the requested location.",
"summary": "Read file"
}
},
"/api/fs/list": {
"get": {
"tags": ["filesystem"],
"operationId": "v2.fs.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
},
{
"name": "path",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FileSystem.Entry"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "List direct children of one directory relative to the requested location.",
"summary": "List directory"
}
},
"/api/fs/find": {
"get": {
"tags": ["filesystem"],
"operationId": "v2.fs.find",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
},
{
"name": "query",
"in": "query",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "type",
"in": "query",
"schema": {
"type": "string",
"enum": ["file", "directory"]
},
"required": false
},
{
"name": "limit",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FileSystem.Entry"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Find recursively ranked filesystem entries relative to the requested location.",
"summary": "Find files"
}
},
"/api/command": {
"get": {
"tags": ["commands"],
"operationId": "v2.command.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CommandV2.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve currently registered commands.",
"summary": "List commands"
}
},
"/api/skill": {
"get": {
"tags": ["skills"],
"operationId": "v2.skill.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SkillV2.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve currently registered skills.",
"summary": "List skills"
}
},
"/api/event": {
"get": {
"tags": ["events"],
"operationId": "v2.event.subscribe",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"text/event-stream": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"event": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/V2EventStream"
}
},
"required": ["id", "event", "data"],
"additionalProperties": false
},
"x-effect-stream": {
"encoding": "sse",
"causeSchema": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Fail"]
},
"error": {
"not": {}
}
},
"required": ["_tag", "error"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Die"]
},
"defect": {}
},
"required": ["_tag", "defect"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Interrupt"]
},
"fiberId": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "fiberId"],
"additionalProperties": false
}
]
}
},
"errorSchema": {
"not": {}
},
"failureEvent": "effect/httpapi/stream/failure"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Subscribe to native event payloads for the server. Volatile by contract: a slow consumer overflows and fails the stream, and events during disconnection are missed. Consumers that need reliability should combine the changes feed with durable session log reads.",
"summary": "Subscribe to events"
}
},
"/api/event/changes": {
"get": {
"tags": ["events"],
"operationId": "v2.event.changes",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"text/event-stream": {
"schema": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"event": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/EventLog.ChangeStream"
}
},
"required": ["id", "event", "data"],
"additionalProperties": false
},
"x-effect-stream": {
"encoding": "sse",
"causeSchema": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Fail"]
},
"error": {
"not": {}
}
},
"required": ["_tag", "error"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Die"]
},
"defect": {}
},
"required": ["_tag", "defect"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["Interrupt"]
},
"fiberId": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "fiberId"],
"additionalProperties": false
}
]
}
},
"errorSchema": {
"not": {}
},
"failureEvent": "effect/httpapi/stream/failure"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Payload-free hint channel: after an event commits, a subscriber eventually receives a hint for that aggregate with seq at or beyond the event, or a sweep-required marker. Hints coalesce to the latest seq per aggregate under backpressure and the stream never fails from overflow. No consumer may derive correctness from receiving a hint; correctness always comes from durable log reads plus the consumer's own checkpoint. A sweep-required marker is emitted first on every (re)subscribe and whenever hint retention is exceeded: treat every aggregate as potentially dirty and recover via bounded sweep plus log reads.",
"summary": "Subscribe to change hints"
}
},
"/api/pty": {
"get": {
"tags": ["pty"],
"operationId": "v2.pty.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pty"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "List PTY sessions for a location, including exited sessions retained until removal.",
"summary": "List PTY sessions"
},
"post": {
"tags": ["pty"],
"operationId": "v2.pty.create",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Pty"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Create a pseudo-terminal session for a location.",
"summary": "Create PTY session",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"command": {
"type": "string"
},
"args": {
"type": "array",
"items": {
"type": "string"
}
},
"cwd": {
"type": "string"
},
"title": {
"type": "string"
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/pty/{ptyID}": {
"get": {
"tags": ["pty"],
"operationId": "v2.pty.get",
"parameters": [
{
"name": "ptyID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Pty"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "PtyNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PtyNotFoundError"
}
}
}
}
},
"description": "Get one PTY session, including its exit code once exited.",
"summary": "Get PTY session"
},
"put": {
"tags": ["pty"],
"operationId": "v2.pty.update",
"parameters": [
{
"name": "ptyID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Pty"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "PtyNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PtyNotFoundError"
}
}
}
}
},
"description": "Update the title or viewport size of one PTY session.",
"summary": "Update PTY session",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"size": {
"type": "object",
"properties": {
"rows": {
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
},
"cols": {
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
}
},
"required": ["rows", "cols"],
"additionalProperties": false
}
},
"additionalProperties": false
}
}
},
"required": true
}
},
"delete": {
"tags": ["pty"],
"operationId": "v2.pty.remove",
"parameters": [
{
"name": "ptyID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "PtyNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PtyNotFoundError"
}
}
}
}
},
"description": "Terminate and remove one PTY session.",
"summary": "Remove PTY session"
}
},
"/api/pty/{ptyID}/connect-token": {
"post": {
"tags": ["pty"],
"operationId": "v2.pty.connectToken",
"parameters": [
{
"name": "ptyID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/PtyTicket.ConnectToken"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"403": {
"description": "ForbiddenError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ForbiddenError"
}
}
}
},
"404": {
"description": "PtyNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PtyNotFoundError"
}
}
}
}
},
"description": "Create a short-lived single-use ticket for opening a PTY WebSocket connection.",
"summary": "Create PTY WebSocket token"
}
},
"/api/pty/{ptyID}/connect": {
"get": {
"tags": ["pty"],
"operationId": "v2.pty.connect",
"x-websocket": true,
"parameters": [
{
"name": "ptyID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"required": true
},
{
"in": "query",
"name": "location[directory]",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "location[workspace]",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "ticket",
"schema": {
"type": "string"
}
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"403": {
"description": "ForbiddenError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ForbiddenError"
}
}
}
},
"404": {
"description": "PtyNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PtyNotFoundError"
}
}
}
}
},
"description": "Establish a WebSocket connection streaming PTY output and accepting terminal input.",
"summary": "Connect to PTY session"
}
},
"/api/shell": {
"get": {
"tags": ["shell"],
"operationId": "v2.shell.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Shell1"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "List currently running shell commands for a location. Exited commands are not included.",
"summary": "List running shell commands"
},
"post": {
"tags": ["shell"],
"operationId": "v2.shell.create",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Shell1"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Spawn one non-interactive shell command for a location. Combined stdout/stderr is captured to a file pageable via output.",
"summary": "Run shell command",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"command": {
"type": "string"
},
"cwd": {
"type": "string"
},
"timeout": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"metadata": {
"type": "object"
}
},
"required": ["command"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/shell/{id}": {
"get": {
"tags": ["shell"],
"operationId": "v2.shell.get",
"parameters": [
{
"name": "id",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Shell1"
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "ShellNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ShellNotFoundError"
}
}
}
}
},
"description": "Get one shell command, including its status and exit code once exited.",
"summary": "Get shell command"
},
"delete": {
"tags": ["shell"],
"operationId": "v2.shell.remove",
"parameters": [
{
"name": "id",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "ShellNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ShellNotFoundError"
}
}
}
}
},
"description": "Terminate and remove one shell command and its retained output.",
"summary": "Remove shell command"
}
},
"/api/shell/{id}/output": {
"get": {
"tags": ["shell"],
"operationId": "v2.shell.output",
"parameters": [
{
"name": "id",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
},
{
"name": "cursor",
"in": "query",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?$"
}
]
},
"required": false
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?$"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "object",
"properties": {
"output": {
"type": "string"
},
"cursor": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"size": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"truncated": {
"type": "boolean"
}
},
"required": ["output", "cursor", "size", "truncated"],
"additionalProperties": false
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "ShellNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ShellNotFoundError"
}
}
}
}
},
"description": "Page through captured combined output by absolute byte cursor.",
"summary": "Read shell output"
}
},
"/api/question/request": {
"get": {
"tags": ["session questions"],
"operationId": "v2.question.request.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Request"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Retrieve pending question requests for a location.",
"summary": "List pending question requests"
}
},
"/api/session/{sessionID}/question": {
"get": {
"tags": ["session questions"],
"operationId": "v2.session.question.list",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Request"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Retrieve pending question requests owned by a session.",
"summary": "List session question requests"
}
},
"/api/session/{sessionID}/question/{requestID}/reply": {
"post": {
"tags": ["session questions"],
"operationId": "v2.session.question.reply",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "requestID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | QuestionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/QuestionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Answer a pending question request owned by a session.",
"summary": "Reply to pending question request",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QuestionV2.Reply"
}
}
},
"required": true
}
}
},
"/api/session/{sessionID}/question/{requestID}/reject": {
"post": {
"tags": ["session questions"],
"operationId": "v2.session.question.reject",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"required": true
},
{
"name": "requestID",
"in": "path",
"schema": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
},
"404": {
"description": "SessionNotFoundError | QuestionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/QuestionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
},
{
"$ref": "#/components/schemas/SessionNotFoundError"
}
]
}
}
}
}
},
"description": "Reject a pending question request owned by a session.",
"summary": "Reject pending question request"
}
},
"/api/reference": {
"get": {
"tags": ["reference"],
"operationId": "v2.reference.list",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Reference.Info"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "List references available in the requested location.",
"summary": "List references"
}
},
"/experimental/project/{projectID}/copy": {
"post": {
"tags": ["projectCopy"],
"operationId": "v2.projectCopy.create",
"parameters": [
{
"name": "projectID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "ProjectCopy.Copy",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProjectCopy.Copy"
}
}
}
},
"400": {
"description": "ProjectCopyError | InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/ProjectCopyError"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"strategy": {
"type": "string"
},
"directory": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": ["strategy", "directory"],
"additionalProperties": false
}
}
},
"required": true
}
},
"delete": {
"tags": ["projectCopy"],
"operationId": "v2.projectCopy.remove",
"parameters": [
{
"name": "projectID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "ProjectCopyError | InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/ProjectCopyError"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"directory": {
"type": "string"
},
"force": {
"type": "boolean"
}
},
"required": ["directory", "force"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/experimental/project/{projectID}/copy/refresh": {
"post": {
"tags": ["projectCopy"],
"operationId": "v2.projectCopy.refresh",
"parameters": [
{
"name": "projectID",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "ProjectCopyError | InvalidRequestError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/ProjectCopyError"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
}
}
},
"/api/vcs/status": {
"get": {
"tags": ["vcs"],
"operationId": "v2.vcs.status",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Vcs.FileStatus"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "List uncommitted working-copy changes relative to the requested location.",
"summary": "VCS status"
}
},
"/api/vcs/diff": {
"get": {
"tags": ["vcs"],
"operationId": "v2.vcs.diff",
"parameters": [
{
"name": "location",
"in": "query",
"schema": {
"anyOf": [
{
"type": "object",
"properties": {
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workspace": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"required": false,
"style": "deepObject",
"explode": true
},
{
"name": "mode",
"in": "query",
"schema": {
"$ref": "#/components/schemas/Vcs.Mode"
},
"required": true
},
{
"name": "context",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SnapshotFileDiff"
}
}
},
"required": ["location", "data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
}
}
},
"description": "Diff the working copy against HEAD (mode git) or the default-branch merge base (mode branch) for the requested location.",
"summary": "VCS diff"
}
}
},
"components": {
"schemas": {
"UnauthorizedError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["UnauthorizedError"]
},
"message": {
"type": "string"
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"InvalidRequestError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["InvalidRequestError"]
},
"message": {
"type": "string"
},
"kind": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"Location.Info": {
"type": "object",
"properties": {
"directory": {
"type": "string"
},
"workspaceID": {
"type": "string",
"allOf": [
{
"pattern": "^wrk"
}
]
},
"project": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"directory": {
"type": "string"
}
},
"required": ["id", "directory"],
"additionalProperties": false
}
},
"required": ["directory", "project"],
"additionalProperties": false
},
"Model.Ref": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"providerID": {
"type": "string"
},
"variant": {
"type": "string"
}
},
"required": ["id", "providerID"],
"additionalProperties": false
},
"Provider.Settings": {
"type": "object"
},
"Provider.Request": {
"type": "object",
"properties": {
"settings": {
"$ref": "#/components/schemas/Provider.Settings"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
}
},
"required": ["settings", "headers", "body"],
"additionalProperties": false
},
"Agent.Color": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^#[0-9a-fA-F]{6}$"
}
]
},
{
"type": "string",
"enum": ["primary", "secondary", "accent", "success", "warning", "error", "info"]
}
]
},
"PermissionV2.Effect": {
"type": "string",
"enum": ["allow", "deny", "ask"]
},
"PermissionV2.Rule": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"resource": {
"type": "string"
},
"effect": {
"$ref": "#/components/schemas/PermissionV2.Effect"
}
},
"required": ["action", "resource", "effect"],
"additionalProperties": false
},
"PermissionV2.Ruleset": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PermissionV2.Rule"
}
},
"AgentV2.Info": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
},
"request": {
"$ref": "#/components/schemas/Provider.Request"
},
"system": {
"type": "string"
},
"description": {
"type": "string"
},
"mode": {
"type": "string",
"enum": ["subagent", "primary", "all"]
},
"hidden": {
"type": "boolean"
},
"color": {
"$ref": "#/components/schemas/Agent.Color"
},
"steps": {
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
},
"permissions": {
"$ref": "#/components/schemas/PermissionV2.Ruleset"
}
},
"required": ["id", "request", "mode", "hidden", "permissions"],
"additionalProperties": false
},
"Plugin.Info": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": ["id"],
"additionalProperties": false
},
"Location.Ref": {
"type": "object",
"properties": {
"directory": {
"type": "string"
},
"workspaceID": {
"type": "string",
"allOf": [
{
"pattern": "^wrk"
}
]
}
},
"required": ["directory"],
"additionalProperties": false
},
"File.Diff": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"status": {
"type": "string",
"enum": ["added", "modified", "deleted"]
},
"additions": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"deletions": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"patch": {
"type": "string"
}
},
"required": ["path", "status", "additions", "deletions", "patch"],
"additionalProperties": false
},
"Revert.State": {
"type": "object",
"properties": {
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"partID": {
"type": "string"
},
"snapshot": {
"type": "string"
},
"diff": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"$ref": "#/components/schemas/File.Diff"
}
}
},
"required": ["messageID"],
"additionalProperties": false
},
"SessionV2.Info": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"parentID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"projectID": {
"type": "string"
},
"agent": {
"type": "string"
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
},
"cost": {
"type": "number"
},
"tokens": {
"type": "object",
"properties": {
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"reasoning": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "reasoning", "cache"],
"additionalProperties": false
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
},
"updated": {
"type": "number"
},
"archived": {
"type": "number"
}
},
"required": ["created", "updated"],
"additionalProperties": false
},
"title": {
"type": "string"
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"subpath": {
"type": "string"
},
"revert": {
"$ref": "#/components/schemas/Revert.State"
}
},
"required": ["id", "projectID", "cost", "tokens", "time", "title", "location"],
"additionalProperties": false
},
"SessionWatermarks": {
"type": "object",
"patternProperties": {
"^ses": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"description": "Durable log seq each session's snapshot was computed at. Attach a live log read after the watermark to compose fetch and stream gap-free; apply a snapshot only where its watermark is at or beyond already-applied events. Sessions without durable events are absent."
},
"SessionsResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SessionV2.Info"
}
},
"watermarks": {
"$ref": "#/components/schemas/SessionWatermarks"
},
"cursor": {
"type": "object",
"properties": {
"previous": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"next": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
},
"required": ["data", "watermarks", "cursor"],
"additionalProperties": false
},
"InvalidCursorError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["InvalidCursorError"]
},
"message": {
"type": "string"
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"InvalidRequestError1": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["InvalidRequestError"]
},
"message": {
"type": "string"
},
"kind": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"SessionActive": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["running"]
}
},
"required": ["type"],
"additionalProperties": false
},
"SessionNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["SessionNotFoundError"]
},
"sessionID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "sessionID", "message"],
"additionalProperties": false
},
"MessageNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["MessageNotFoundError"]
},
"sessionID": {
"type": "string"
},
"messageID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "sessionID", "messageID", "message"],
"additionalProperties": false
},
"Prompt.Source": {
"type": "object",
"properties": {
"start": {
"type": "number"
},
"end": {
"type": "number"
},
"text": {
"type": "string"
}
},
"required": ["start", "end", "text"],
"additionalProperties": false
},
"PromptInput.FileAttachment": {
"type": "object",
"properties": {
"uri": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.Source"
}
},
"required": ["uri"],
"additionalProperties": false
},
"Prompt.AgentAttachment": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.Source"
}
},
"required": ["name"],
"additionalProperties": false
},
"PromptInput": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PromptInput.FileAttachment"
}
},
"agents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.AgentAttachment"
}
}
},
"required": ["text"],
"additionalProperties": false
},
"Prompt.FileAttachment": {
"type": "object",
"properties": {
"uri": {
"type": "string"
},
"mime": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.Source"
}
},
"required": ["uri", "mime"],
"additionalProperties": false
},
"Prompt": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.FileAttachment"
}
},
"agents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.AgentAttachment"
}
}
},
"required": ["text"],
"additionalProperties": false
},
"SessionInput.Admitted": {
"type": "object",
"properties": {
"admittedSeq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"prompt": {
"$ref": "#/components/schemas/Prompt"
},
"delivery": {
"type": "string",
"enum": ["steer", "queue"]
},
"timeCreated": {
"type": "number"
},
"promotedSeq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["admittedSeq", "id", "sessionID", "prompt", "delivery", "timeCreated"],
"additionalProperties": false
},
"ConflictError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["ConflictError"]
},
"message": {
"type": "string"
},
"resource": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"CommandNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["CommandNotFoundError"]
},
"command": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "command", "message"],
"additionalProperties": false
},
"CommandEvaluationError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["CommandEvaluationError"]
},
"command": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "command", "message"],
"additionalProperties": false
},
"SkillNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["SkillNotFoundError"]
},
"skill": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "skill", "message"],
"additionalProperties": false
},
"SessionBusyError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["SessionBusyError"]
},
"sessionID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "sessionID", "message"],
"additionalProperties": false
},
"ServiceUnavailableError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["ServiceUnavailableError"]
},
"message": {
"type": "string"
},
"service": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"UnknownError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["UnknownError"]
},
"message": {
"type": "string"
},
"ref": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"Session.Message.AgentSwitched": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["agent-switched"]
},
"agent": {
"type": "string"
}
},
"required": ["id", "time", "type", "agent"],
"additionalProperties": false
},
"Session.Message.ModelSwitched": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["model-switched"]
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
}
},
"required": ["id", "time", "type", "model"],
"additionalProperties": false
},
"Session.Message.User": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"text": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.FileAttachment"
}
},
"agents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.AgentAttachment"
}
},
"type": {
"type": "string",
"enum": ["user"]
}
},
"required": ["id", "time", "text", "type"],
"additionalProperties": false
},
"Session.Message.Synthetic": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"text": {
"type": "string"
},
"description": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["synthetic"]
}
},
"required": ["id", "time", "sessionID", "text", "type"],
"additionalProperties": false
},
"Session.Message.System": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["system"]
},
"text": {
"type": "string"
}
},
"required": ["id", "time", "type", "text"],
"additionalProperties": false
},
"Session.Message.Skill": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["skill"]
},
"name": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["id", "time", "type", "name", "text"],
"additionalProperties": false
},
"Session.Message.Shell": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
},
"completed": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["shell"]
},
"callID": {
"type": "string"
},
"command": {
"type": "string"
},
"output": {
"type": "string"
}
},
"required": ["id", "time", "type", "callID", "command", "output"],
"additionalProperties": false
},
"Session.Message.Assistant.Text": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["text"]
},
"id": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["type", "id", "text"],
"additionalProperties": false
},
"LLM.ProviderMetadata": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"Session.Message.Assistant.Reasoning": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["reasoning"]
},
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"providerMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
},
"completed": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
}
},
"required": ["type", "id", "text"],
"additionalProperties": false
},
"Session.Message.ToolState.Pending": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["pending"]
},
"input": {
"type": "string"
}
},
"required": ["status", "input"],
"additionalProperties": false
},
"Tool.TextContent": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["text"]
},
"text": {
"type": "string"
}
},
"required": ["type", "text"],
"additionalProperties": false
},
"Tool.FileContent": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["file"]
},
"uri": {
"type": "string"
},
"mime": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": ["type", "uri", "mime"],
"additionalProperties": false
},
"LLM.ToolContent": {
"anyOf": [
{
"$ref": "#/components/schemas/Tool.TextContent"
},
{
"$ref": "#/components/schemas/Tool.FileContent"
}
]
},
"Session.Message.ToolState.Running": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["running"]
},
"input": {
"type": "object"
},
"structured": {
"type": "object"
},
"content": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LLM.ToolContent"
}
}
},
"required": ["status", "input", "structured", "content"],
"additionalProperties": false
},
"Session.Message.ToolState.Completed": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["completed"]
},
"input": {
"type": "object"
},
"attachments": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Prompt.FileAttachment"
}
},
"content": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LLM.ToolContent"
}
},
"outputPaths": {
"type": "array",
"items": {
"type": "string"
}
},
"structured": {
"type": "object"
},
"result": {}
},
"required": ["status", "input", "content", "structured"],
"additionalProperties": false
},
"Session.Error.Unknown": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["unknown"]
},
"message": {
"type": "string"
}
},
"required": ["type", "message"],
"additionalProperties": false
},
"Session.Message.ToolState.Error": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["error"]
},
"input": {
"type": "object"
},
"content": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LLM.ToolContent"
}
},
"structured": {
"type": "object"
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
},
"result": {}
},
"required": ["status", "input", "content", "structured", "error"],
"additionalProperties": false
},
"Session.Message.Assistant.Tool": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["tool"]
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata"
},
"resultMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata"
}
},
"required": ["executed"],
"additionalProperties": false
},
"state": {
"anyOf": [
{
"$ref": "#/components/schemas/Session.Message.ToolState.Pending"
},
{
"$ref": "#/components/schemas/Session.Message.ToolState.Running"
},
{
"$ref": "#/components/schemas/Session.Message.ToolState.Completed"
},
{
"$ref": "#/components/schemas/Session.Message.ToolState.Error"
}
]
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
},
"ran": {
"type": "number"
},
"completed": {
"type": "number"
},
"pruned": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
}
},
"required": ["type", "id", "name", "state", "time"],
"additionalProperties": false
},
"Session.Message.Assistant": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
},
"completed": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
},
"type": {
"type": "string",
"enum": ["assistant"]
},
"agent": {
"type": "string"
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
},
"content": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Session.Message.Assistant.Text"
},
{
"$ref": "#/components/schemas/Session.Message.Assistant.Reasoning"
},
{
"$ref": "#/components/schemas/Session.Message.Assistant.Tool"
}
]
}
},
"snapshot": {
"type": "object",
"properties": {
"start": {
"type": "string"
},
"end": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"finish": {
"type": "string"
},
"cost": {
"type": "number"
},
"tokens": {
"type": "object",
"properties": {
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"reasoning": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "reasoning", "cache"],
"additionalProperties": false
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
}
},
"required": ["id", "time", "type", "agent", "model", "content"],
"additionalProperties": false
},
"Session.Message.Compaction": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["compaction"]
},
"reason": {
"type": "string",
"enum": ["auto", "manual"]
},
"summary": {
"type": "string"
},
"recent": {
"type": "string"
},
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number"
}
},
"required": ["created"],
"additionalProperties": false
}
},
"required": ["type", "reason", "summary", "recent", "id", "time"],
"additionalProperties": false
},
"Session.Message": {
"anyOf": [
{
"$ref": "#/components/schemas/Session.Message.AgentSwitched"
},
{
"$ref": "#/components/schemas/Session.Message.ModelSwitched"
},
{
"$ref": "#/components/schemas/Session.Message.User"
},
{
"$ref": "#/components/schemas/Session.Message.Synthetic"
},
{
"$ref": "#/components/schemas/Session.Message.System"
},
{
"$ref": "#/components/schemas/Session.Message.Skill"
},
{
"$ref": "#/components/schemas/Session.Message.Shell"
},
{
"$ref": "#/components/schemas/Session.Message.Assistant"
},
{
"$ref": "#/components/schemas/Session.Message.Compaction"
}
]
},
"SessionContextEntry.Key": {
"type": "string",
"allOf": [
{
"pattern": "^[a-z0-9][a-z0-9._-]*$",
"description": "Context entry key (lowercase alphanumerics plus . _ -)"
}
]
},
"SessionContextEntry.Info": {
"type": "object",
"properties": {
"key": {
"$ref": "#/components/schemas/SessionContextEntry.Key"
},
"value": {}
},
"required": ["key", "value"],
"additionalProperties": false
},
"session.next.agent.switched": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.agent.switched"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"agent": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "messageID", "agent"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.model.switched": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.model.switched"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
}
},
"required": ["timestamp", "sessionID", "messageID", "model"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.moved": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.moved"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"subdirectory": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "location"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.renamed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.renamed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"title": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "title"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.forked": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.forked"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"parentID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
}
},
"required": ["timestamp", "sessionID", "parentID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.prompted": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.prompted"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"prompt": {
"$ref": "#/components/schemas/Prompt"
},
"delivery": {
"type": "string",
"enum": ["steer", "queue"]
}
},
"required": ["timestamp", "sessionID", "messageID", "prompt", "delivery"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.prompt.admitted": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.prompt.admitted"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"prompt": {
"$ref": "#/components/schemas/Prompt"
},
"delivery": {
"type": "string",
"enum": ["steer", "queue"]
}
},
"required": ["timestamp", "sessionID", "messageID", "prompt", "delivery"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.context.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.context.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"text": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "messageID", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.synthetic": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.synthetic"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"text": {
"type": "string"
},
"description": {
"type": "string"
},
"metadata": {
"type": "object"
}
},
"required": ["timestamp", "sessionID", "messageID", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.skill.activated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.skill.activated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"name": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "messageID", "name", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.shell.started": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.shell.started"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"command": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "messageID", "callID", "command"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.shell.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.shell.ended"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"callID": {
"type": "string"
},
"output": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "callID", "output"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.step.started": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.step.started"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"agent": {
"type": "string"
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
},
"snapshot": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "agent", "model"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.step.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.step.ended"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"finish": {
"type": "string"
},
"cost": {
"type": "number"
},
"tokens": {
"type": "object",
"properties": {
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"reasoning": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "reasoning", "cache"],
"additionalProperties": false
},
"snapshot": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "finish", "cost", "tokens"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.step.failed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.step.failed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "error"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.text.started": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.text.started"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"textID": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "textID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.text.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.text.ended"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"textID": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "textID", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.tool.input.started": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.input.started"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "name"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.tool.input.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.input.ended"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"LLM.ProviderMetadata3": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.tool.called": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.called"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"tool": {
"type": "string"
},
"input": {
"type": "object"
},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata3"
}
},
"required": ["executed"],
"additionalProperties": false
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "tool", "input", "provider"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.tool.progress": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.progress"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"structured": {
"type": "object"
},
"content": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LLM.ToolContent"
}
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "structured", "content"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"LLM.ProviderMetadata4": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.tool.success": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.success"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"structured": {
"type": "object"
},
"content": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LLM.ToolContent"
}
},
"outputPaths": {
"type": "array",
"items": {
"type": "string"
}
},
"result": {},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata4"
}
},
"required": ["executed"],
"additionalProperties": false
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "structured", "content", "provider"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"LLM.ProviderMetadata5": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.tool.failed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.failed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
},
"result": {},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata5"
}
},
"required": ["executed"],
"additionalProperties": false
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "error", "provider"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"LLM.ProviderMetadata6": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.reasoning.started": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.reasoning.started"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reasoningID": {
"type": "string"
},
"providerMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata6"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "reasoningID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"LLM.ProviderMetadata7": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.reasoning.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.reasoning.ended"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reasoningID": {
"type": "string"
},
"text": {
"type": "string"
},
"providerMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata7"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "reasoningID", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.retry_error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"statusCode": {
"type": "number"
},
"isRetryable": {
"type": "boolean"
},
"responseHeaders": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"responseBody": {
"type": "string"
},
"metadata": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"required": ["message", "isRetryable"],
"additionalProperties": false
},
"session.next.retried": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.retried"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"attempt": {
"type": "number"
},
"error": {
"$ref": "#/components/schemas/session.next.retry_error"
}
},
"required": ["timestamp", "sessionID", "attempt", "error"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.compaction.started": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.compaction.started"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reason": {
"type": "string",
"enum": ["auto", "manual"]
}
},
"required": ["timestamp", "sessionID", "messageID", "reason"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.compaction.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.compaction.ended"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reason": {
"type": "string",
"enum": ["auto", "manual"]
},
"text": {
"type": "string"
},
"recent": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "messageID", "reason", "text", "recent"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.revert.staged": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.revert.staged"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"revert": {
"$ref": "#/components/schemas/Revert.State"
}
},
"required": ["timestamp", "sessionID", "revert"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.revert.cleared": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.revert.cleared"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
}
},
"required": ["timestamp", "sessionID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.revert.committed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.revert.committed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
}
},
"required": ["timestamp", "sessionID", "messageID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"SessionDurableEvent": {
"oneOf": [
{
"$ref": "#/components/schemas/session.next.agent.switched"
},
{
"$ref": "#/components/schemas/session.next.model.switched"
},
{
"$ref": "#/components/schemas/session.next.moved"
},
{
"$ref": "#/components/schemas/session.next.renamed"
},
{
"$ref": "#/components/schemas/session.next.forked"
},
{
"$ref": "#/components/schemas/session.next.prompted"
},
{
"$ref": "#/components/schemas/session.next.prompt.admitted"
},
{
"$ref": "#/components/schemas/session.next.context.updated"
},
{
"$ref": "#/components/schemas/session.next.synthetic"
},
{
"$ref": "#/components/schemas/session.next.skill.activated"
},
{
"$ref": "#/components/schemas/session.next.shell.started"
},
{
"$ref": "#/components/schemas/session.next.shell.ended"
},
{
"$ref": "#/components/schemas/session.next.step.started"
},
{
"$ref": "#/components/schemas/session.next.step.ended"
},
{
"$ref": "#/components/schemas/session.next.step.failed"
},
{
"$ref": "#/components/schemas/session.next.text.started"
},
{
"$ref": "#/components/schemas/session.next.text.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.input.started"
},
{
"$ref": "#/components/schemas/session.next.tool.input.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.called"
},
{
"$ref": "#/components/schemas/session.next.tool.progress"
},
{
"$ref": "#/components/schemas/session.next.tool.success"
},
{
"$ref": "#/components/schemas/session.next.tool.failed"
},
{
"$ref": "#/components/schemas/session.next.reasoning.started"
},
{
"$ref": "#/components/schemas/session.next.reasoning.ended"
},
{
"$ref": "#/components/schemas/session.next.retried"
},
{
"$ref": "#/components/schemas/session.next.compaction.started"
},
{
"$ref": "#/components/schemas/session.next.compaction.ended"
},
{
"$ref": "#/components/schemas/session.next.revert.staged"
},
{
"$ref": "#/components/schemas/session.next.revert.cleared"
},
{
"$ref": "#/components/schemas/session.next.revert.committed"
}
]
},
"EventLog.Synced": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["log.synced"]
},
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["type", "aggregateID"],
"additionalProperties": false,
"description": "Marker emitted once when a log read reaches its captured watermark. The reader holds every event committed at or below seq."
},
"SessionLogItem": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionDurableEvent"
},
{
"$ref": "#/components/schemas/EventLog.Synced"
}
]
},
"SessionLogItemStream": {
"type": "string",
"contentSchema": {
"$ref": "#/components/schemas/SessionLogItem"
},
"contentMediaType": "application/json"
},
"SessionMessagesResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Session.Message"
}
},
"watermark": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"cursor": {
"type": "object",
"properties": {
"previous": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"next": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
},
"required": ["data", "cursor"],
"additionalProperties": false
},
"Model.Api": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["aisdk"]
},
"package": {
"type": "string"
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": ["id", "type", "package"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["native"]
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": ["id", "type", "settings"],
"additionalProperties": false
}
]
},
"Model.Capabilities": {
"type": "object",
"properties": {
"tools": {
"type": "boolean"
},
"input": {
"type": "array",
"items": {
"type": "string"
}
},
"output": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["tools", "input", "output"],
"additionalProperties": false
},
"Model.Cost": {
"type": "object",
"properties": {
"tier": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["context"]
},
"size": {
"type": "integer"
}
},
"required": ["type", "size"],
"additionalProperties": false
},
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "cache"],
"additionalProperties": false
},
"ModelV2.Info": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"providerID": {
"type": "string"
},
"family": {
"type": "string"
},
"name": {
"type": "string"
},
"api": {
"$ref": "#/components/schemas/Model.Api"
},
"capabilities": {
"$ref": "#/components/schemas/Model.Capabilities"
},
"request": {
"type": "object",
"properties": {
"settings": {
"$ref": "#/components/schemas/Provider.Settings"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
},
"variant": {
"type": "string"
}
},
"required": ["settings", "headers", "body"],
"additionalProperties": false
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"settings": {
"$ref": "#/components/schemas/Provider.Settings"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
}
},
"required": ["id", "settings", "headers", "body"],
"additionalProperties": false
}
},
"time": {
"type": "object",
"properties": {
"released": {
"type": "number"
}
},
"required": ["released"],
"additionalProperties": false
},
"cost": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Model.Cost"
}
},
"status": {
"type": "string",
"enum": ["alpha", "beta", "deprecated", "active"]
},
"enabled": {
"type": "boolean"
},
"limit": {
"type": "object",
"properties": {
"context": {
"type": "integer"
},
"input": {
"type": "integer"
},
"output": {
"type": "integer"
}
},
"required": ["context", "output"],
"additionalProperties": false
}
},
"required": [
"id",
"providerID",
"name",
"api",
"capabilities",
"request",
"variants",
"time",
"cost",
"status",
"enabled",
"limit"
],
"additionalProperties": false
},
"GenerateTextResponse": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": ["text"],
"additionalProperties": false
}
},
"required": ["data"],
"additionalProperties": false
},
"Provider.AISDK": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["aisdk"]
},
"package": {
"type": "string"
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": ["type", "package"],
"additionalProperties": false
},
"Provider.Native": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["native"]
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": ["type", "settings"],
"additionalProperties": false
},
"Provider.Api": {
"anyOf": [
{
"$ref": "#/components/schemas/Provider.AISDK"
},
{
"$ref": "#/components/schemas/Provider.Native"
}
]
},
"ProviderV2.Info": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"name": {
"type": "string"
},
"disabled": {
"type": "boolean"
},
"api": {
"$ref": "#/components/schemas/Provider.Api"
},
"request": {
"$ref": "#/components/schemas/Provider.Request"
}
},
"required": ["id", "name", "api", "request"],
"additionalProperties": false
},
"ProviderNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["ProviderNotFoundError"]
},
"providerID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "providerID", "message"],
"additionalProperties": false
},
"Integration.When": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"op": {
"type": "string",
"enum": ["eq", "neq"]
},
"value": {
"type": "string"
}
},
"required": ["key", "op", "value"],
"additionalProperties": false
},
"Integration.TextPrompt": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["text"]
},
"key": {
"type": "string"
},
"message": {
"type": "string"
},
"placeholder": {
"type": "string"
},
"when": {
"$ref": "#/components/schemas/Integration.When"
}
},
"required": ["type", "key", "message"],
"additionalProperties": false
},
"Integration.SelectPrompt": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["select"]
},
"key": {
"type": "string"
},
"message": {
"type": "string"
},
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
},
"hint": {
"type": "string"
}
},
"required": ["label", "value"],
"additionalProperties": false
}
},
"when": {
"$ref": "#/components/schemas/Integration.When"
}
},
"required": ["type", "key", "message", "options"],
"additionalProperties": false
},
"Integration.OAuthMethod": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["oauth"]
},
"label": {
"type": "string"
},
"prompts": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Integration.TextPrompt"
},
{
"$ref": "#/components/schemas/Integration.SelectPrompt"
}
]
}
}
},
"required": ["id", "type", "label"],
"additionalProperties": false
},
"Integration.KeyMethod": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["key"]
},
"label": {
"type": "string"
}
},
"required": ["type"],
"additionalProperties": false
},
"Integration.EnvMethod": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["env"]
},
"names": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["type", "names"],
"additionalProperties": false
},
"Integration.Method": {
"anyOf": [
{
"$ref": "#/components/schemas/Integration.OAuthMethod"
},
{
"$ref": "#/components/schemas/Integration.KeyMethod"
},
{
"$ref": "#/components/schemas/Integration.EnvMethod"
}
]
},
"Connection.CredentialInfo": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["credential"]
},
"id": {
"type": "string"
},
"label": {
"type": "string"
}
},
"required": ["type", "id", "label"],
"additionalProperties": false
},
"Connection.EnvInfo": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["env"]
},
"name": {
"type": "string"
}
},
"required": ["type", "name"],
"additionalProperties": false
},
"Connection.Info": {
"anyOf": [
{
"$ref": "#/components/schemas/Connection.CredentialInfo"
},
{
"$ref": "#/components/schemas/Connection.EnvInfo"
}
]
},
"Integration.Info": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"methods": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Integration.Method"
}
},
"connections": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Connection.Info"
}
}
},
"required": ["id", "name", "methods", "connections"],
"additionalProperties": false
},
"Integration.Attempt": {
"type": "object",
"properties": {
"attemptID": {
"type": "string"
},
"url": {
"type": "string"
},
"instructions": {
"type": "string"
},
"mode": {
"type": "string",
"enum": ["auto", "code"]
},
"time": {
"type": "object",
"properties": {
"created": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"expires": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["created", "expires"],
"additionalProperties": false
}
},
"required": ["attemptID", "url", "instructions", "mode", "time"],
"additionalProperties": false
},
"Integration.AttemptStatus": {
"anyOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["pending"]
},
"time": {
"type": "object",
"properties": {
"created": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"expires": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["created", "expires"],
"additionalProperties": false
}
},
"required": ["status", "time"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["complete"]
},
"time": {
"type": "object",
"properties": {
"created": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"expires": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["created", "expires"],
"additionalProperties": false
}
},
"required": ["status", "time"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["failed"]
},
"message": {
"type": "string"
},
"time": {
"type": "object",
"properties": {
"created": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"expires": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["created", "expires"],
"additionalProperties": false
}
},
"required": ["status", "message", "time"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["expired"]
},
"time": {
"type": "object",
"properties": {
"created": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"expires": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["created", "expires"],
"additionalProperties": false
}
},
"required": ["status", "time"],
"additionalProperties": false
}
]
},
"Mcp.Status.Connected": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["connected"]
}
},
"required": ["status"],
"additionalProperties": false
},
"Mcp.Status.Disconnected": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["disconnected"]
}
},
"required": ["status"],
"additionalProperties": false
},
"Mcp.Status.Disabled": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["disabled"]
}
},
"required": ["status"],
"additionalProperties": false
},
"Mcp.Status.Failed": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["failed"]
},
"error": {
"type": "string"
}
},
"required": ["status", "error"],
"additionalProperties": false
},
"Mcp.Status.NeedsAuth": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["needs_auth"]
}
},
"required": ["status"],
"additionalProperties": false
},
"Mcp.Status.NeedsClientRegistration": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["needs_client_registration"]
},
"error": {
"type": "string"
}
},
"required": ["status", "error"],
"additionalProperties": false
},
"Mcp.Server": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"anyOf": [
{
"$ref": "#/components/schemas/Mcp.Status.Connected"
},
{
"$ref": "#/components/schemas/Mcp.Status.Disconnected"
},
{
"$ref": "#/components/schemas/Mcp.Status.Disabled"
},
{
"$ref": "#/components/schemas/Mcp.Status.Failed"
},
{
"$ref": "#/components/schemas/Mcp.Status.NeedsAuth"
},
{
"$ref": "#/components/schemas/Mcp.Status.NeedsClientRegistration"
}
]
},
"integrationID": {
"type": "string"
}
},
"required": ["name", "status"],
"additionalProperties": false
},
"Project.Current": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"directory": {
"type": "string"
}
},
"required": ["id", "directory"],
"additionalProperties": false
},
"Project.Directory": {
"type": "object",
"properties": {
"directory": {
"type": "string"
},
"strategy": {
"type": "string"
}
},
"required": ["directory"],
"additionalProperties": false
},
"Project.Directories": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Project.Directory"
}
},
"Form.Metadata": {
"type": "object"
},
"Form.When": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"op": {
"type": "string",
"enum": ["eq", "neq"]
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
{
"type": "boolean"
}
]
}
},
"required": ["key", "op", "value"],
"additionalProperties": false
},
"Form.Option": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"label": {
"type": "string"
},
"description": {
"type": "string"
}
},
"required": ["value", "label"],
"additionalProperties": false
},
"Form.StringField": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When"
}
},
"type": {
"type": "string",
"enum": ["string"]
},
"format": {
"type": "string",
"enum": ["email", "uri", "date", "date-time"]
},
"minLength": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"maxLength": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"pattern": {
"type": "string"
},
"placeholder": {
"type": "string"
},
"default": {
"type": "string"
},
"options": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.Option"
}
},
"custom": {
"type": "boolean"
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.NumberField": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When"
}
},
"type": {
"type": "string",
"enum": ["number"]
},
"minimum": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"maximum": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"default": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.IntegerField": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When"
}
},
"type": {
"type": "string",
"enum": ["integer"]
},
"minimum": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"maximum": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"default": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.BooleanField": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When"
}
},
"type": {
"type": "string",
"enum": ["boolean"]
},
"default": {
"type": "boolean"
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.MultiselectField": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When"
}
},
"type": {
"type": "string",
"enum": ["multiselect"]
},
"options": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.Option"
}
},
"minItems": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"maxItems": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"custom": {
"type": "boolean"
},
"default": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["key", "type", "options"],
"additionalProperties": false
},
"Form.FormInfo": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"sessionID": {
"type": "string"
},
"title": {
"type": "string"
},
"metadata": {
"$ref": "#/components/schemas/Form.Metadata"
},
"mode": {
"type": "string",
"enum": ["form"]
},
"fields": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.StringField"
},
{
"$ref": "#/components/schemas/Form.NumberField"
},
{
"$ref": "#/components/schemas/Form.IntegerField"
},
{
"$ref": "#/components/schemas/Form.BooleanField"
},
{
"$ref": "#/components/schemas/Form.MultiselectField"
}
]
}
}
},
"required": ["id", "sessionID", "mode", "fields"],
"additionalProperties": false
},
"Form.UrlInfo": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"sessionID": {
"type": "string"
},
"title": {
"type": "string"
},
"metadata": {
"$ref": "#/components/schemas/Form.Metadata"
},
"mode": {
"type": "string",
"enum": ["url"]
},
"url": {
"type": "string"
}
},
"required": ["id", "sessionID", "mode", "url"],
"additionalProperties": false
},
"Form.CreatePayload": {
"type": "object",
"properties": {
"id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"metadata": {
"$ref": "#/components/schemas/Form.Metadata"
},
"mode": {
"type": "string",
"enum": ["form", "url"]
},
"fields": {
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.StringField"
},
{
"$ref": "#/components/schemas/Form.NumberField"
},
{
"$ref": "#/components/schemas/Form.IntegerField"
},
{
"$ref": "#/components/schemas/Form.BooleanField"
},
{
"$ref": "#/components/schemas/Form.MultiselectField"
}
]
}
},
{
"type": "null"
}
]
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode"],
"additionalProperties": false
},
"FormNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["FormNotFoundError"]
},
"id": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "id", "message"],
"additionalProperties": false
},
"Form.Value": {
"anyOf": [
{
"type": "string"
},
{
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"Form.Answer": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.State": {
"anyOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["pending"]
}
},
"required": ["status"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["answered"]
},
"answer": {
"$ref": "#/components/schemas/Form.Answer"
}
},
"required": ["status", "answer"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["cancelled"]
}
},
"required": ["status"],
"additionalProperties": false
}
]
},
"Form.Reply": {
"type": "object",
"properties": {
"answer": {
"$ref": "#/components/schemas/Form.Answer"
}
},
"required": ["answer"],
"additionalProperties": false
},
"FormAlreadySettledError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["FormAlreadySettledError"]
},
"id": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "id", "message"],
"additionalProperties": false
},
"FormInvalidAnswerError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["FormInvalidAnswerError"]
},
"id": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "id", "message"],
"additionalProperties": false
},
"PermissionV2.Source": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["tool"]
},
"messageID": {
"type": "string"
},
"callID": {
"type": "string"
}
},
"required": ["type", "messageID", "callID"],
"additionalProperties": false
}
]
},
"PermissionV2.Request": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"action": {
"type": "string"
},
"resources": {
"type": "array",
"items": {
"type": "string"
}
},
"save": {
"type": "array",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object"
},
"source": {
"$ref": "#/components/schemas/PermissionV2.Source"
}
},
"required": ["id", "sessionID", "action", "resources"],
"additionalProperties": false
},
"PermissionSaved.Info": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"projectID": {
"type": "string"
},
"action": {
"type": "string"
},
"resource": {
"type": "string"
}
},
"required": ["id", "projectID", "action", "resource"],
"additionalProperties": false
},
"PermissionNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["PermissionNotFoundError"]
},
"requestID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "requestID", "message"],
"additionalProperties": false
},
"PermissionV2.Reply": {
"type": "string",
"enum": ["once", "always", "reject"]
},
"FileSystem.Entry": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["file", "directory"]
}
},
"required": ["path", "type"],
"additionalProperties": false
},
"CommandV2.Info": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"template": {
"type": "string"
},
"description": {
"type": "string"
},
"agent": {
"type": "string"
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
},
"subtask": {
"type": "boolean"
}
},
"required": ["name", "template"],
"additionalProperties": false
},
"SkillV2.Info": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"slash": {
"type": "boolean"
},
"autoinvoke": {
"type": "boolean"
},
"location": {
"type": "string"
},
"content": {
"type": "string"
}
},
"required": ["name", "location", "content"],
"additionalProperties": false
},
"models-dev.refreshed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["models-dev.refreshed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"integration.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["integration.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"integration.connection.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["integration.connection.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"integrationID": {
"type": "string"
}
},
"required": ["integrationID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"catalog.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["catalog.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"agent.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["agent.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"SnapshotFileDiff": {
"type": "object",
"properties": {
"file": {
"type": "string"
},
"patch": {
"type": "string"
},
"additions": {
"type": "number"
},
"deletions": {
"type": "number"
},
"status": {
"type": "string",
"enum": ["added", "deleted", "modified"]
}
},
"required": ["additions", "deletions"],
"additionalProperties": false
},
"PermissionAction": {
"type": "string",
"enum": ["allow", "deny", "ask"]
},
"PermissionRule": {
"type": "object",
"properties": {
"permission": {
"type": "string"
},
"pattern": {
"type": "string"
},
"action": {
"$ref": "#/components/schemas/PermissionAction"
}
},
"required": ["permission", "pattern", "action"],
"additionalProperties": false
},
"PermissionRuleset": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PermissionRule"
}
},
"Session": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"slug": {
"type": "string"
},
"projectID": {
"type": "string"
},
"workspaceID": {
"type": "string",
"allOf": [
{
"pattern": "^wrk"
}
]
},
"directory": {
"type": "string"
},
"path": {
"type": "string"
},
"parentID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"summary": {
"type": "object",
"properties": {
"additions": {
"type": "number"
},
"deletions": {
"type": "number"
},
"files": {
"type": "number"
},
"diffs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SnapshotFileDiff"
}
}
},
"required": ["additions", "deletions", "files"],
"additionalProperties": false
},
"cost": {
"type": "number"
},
"tokens": {
"type": "object",
"properties": {
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"reasoning": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "reasoning", "cache"],
"additionalProperties": false
},
"share": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
},
"required": ["url"],
"additionalProperties": false
},
"title": {
"type": "string"
},
"agent": {
"type": "string"
},
"model": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"providerID": {
"type": "string"
},
"variant": {
"type": "string"
}
},
"required": ["id", "providerID"],
"additionalProperties": false
},
"version": {
"type": "string"
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"updated": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"compacting": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"archived": {
"type": "number"
}
},
"required": ["created", "updated"],
"additionalProperties": false
},
"permission": {
"$ref": "#/components/schemas/PermissionRuleset"
},
"revert": {
"type": "object",
"properties": {
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"partID": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"snapshot": {
"type": "string"
},
"diff": {
"type": "string"
}
},
"required": ["messageID"],
"additionalProperties": false
}
},
"required": ["id", "slug", "projectID", "directory", "title", "version", "time"],
"additionalProperties": false
},
"session.created": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.created"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"info": {
"$ref": "#/components/schemas/Session"
}
},
"required": ["sessionID", "info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"info": {
"$ref": "#/components/schemas/Session"
}
},
"required": ["sessionID", "info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.deleted": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.deleted"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"info": {
"$ref": "#/components/schemas/Session"
}
},
"required": ["sessionID", "info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"JSONSchema": {
"type": "object"
},
"OutputFormat": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["text"]
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["json_schema"]
},
"schema": {
"$ref": "#/components/schemas/JSONSchema"
},
"retryCount": {
"anyOf": [
{
"anyOf": [
{
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
{
"type": "null"
}
]
},
{
"type": "null"
}
]
}
},
"required": ["type", "schema"],
"additionalProperties": false
}
]
},
"UserMessage": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"role": {
"type": "string",
"enum": ["user"]
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "number",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["created"],
"additionalProperties": false
},
"format": {
"anyOf": [
{
"$ref": "#/components/schemas/OutputFormat"
},
{
"type": "null"
}
]
},
"summary": {
"anyOf": [
{
"type": "object",
"properties": {
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"body": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"diffs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SnapshotFileDiff"
}
}
},
"required": ["diffs"],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"agent": {
"type": "string"
},
"model": {
"type": "object",
"properties": {
"providerID": {
"type": "string"
},
"modelID": {
"type": "string"
},
"variant": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["providerID", "modelID"],
"additionalProperties": false
},
"system": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"tools": {
"anyOf": [
{
"type": "object",
"additionalProperties": {
"type": "boolean"
}
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "role", "time", "agent", "model"],
"additionalProperties": false
},
"ProviderAuthError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["ProviderAuthError"]
},
"data": {
"type": "object",
"properties": {
"providerID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["providerID", "message"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"UnknownError1": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["UnknownError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"ref": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["message"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"MessageOutputLengthError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["MessageOutputLengthError"]
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"MessageAbortedError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["MessageAbortedError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"required": ["message"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"StructuredOutputError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["StructuredOutputError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"retries": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["message", "retries"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"ContextOverflowError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["ContextOverflowError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"responseBody": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["message"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"ContentFilterError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["ContentFilterError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"required": ["message"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"APIError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["APIError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"statusCode": {
"anyOf": [
{
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
{
"type": "null"
}
]
},
"isRetryable": {
"type": "boolean"
},
"responseHeaders": {
"anyOf": [
{
"type": "object",
"additionalProperties": {
"type": "string"
}
},
{
"type": "null"
}
]
},
"responseBody": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"metadata": {
"anyOf": [
{
"type": "object",
"additionalProperties": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": ["message", "isRetryable"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"AssistantMessage": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"role": {
"type": "string",
"enum": ["assistant"]
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"completed": {
"anyOf": [
{
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
{
"type": "null"
}
]
}
},
"required": ["created"],
"additionalProperties": false
},
"error": {
"anyOf": [
{
"anyOf": [
{
"$ref": "#/components/schemas/ProviderAuthError"
},
{
"$ref": "#/components/schemas/UnknownError1"
},
{
"$ref": "#/components/schemas/MessageOutputLengthError"
},
{
"$ref": "#/components/schemas/MessageAbortedError"
},
{
"$ref": "#/components/schemas/StructuredOutputError"
},
{
"$ref": "#/components/schemas/ContextOverflowError"
},
{
"$ref": "#/components/schemas/ContentFilterError"
},
{
"$ref": "#/components/schemas/APIError"
}
]
},
{
"type": "null"
}
]
},
"parentID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"modelID": {
"type": "string"
},
"providerID": {
"type": "string"
},
"mode": {
"type": "string"
},
"agent": {
"type": "string"
},
"path": {
"type": "object",
"properties": {
"cwd": {
"type": "string"
},
"root": {
"type": "string"
}
},
"required": ["cwd", "root"],
"additionalProperties": false
},
"summary": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cost": {
"type": "number"
},
"tokens": {
"type": "object",
"properties": {
"total": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"reasoning": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "reasoning", "cache"],
"additionalProperties": false
},
"structured": {
"anyOf": [
{},
{
"type": "null"
}
]
},
"variant": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"finish": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"sessionID",
"role",
"time",
"parentID",
"modelID",
"providerID",
"mode",
"agent",
"path",
"cost",
"tokens"
],
"additionalProperties": false
},
"Message": {
"anyOf": [
{
"$ref": "#/components/schemas/UserMessage"
},
{
"$ref": "#/components/schemas/AssistantMessage"
}
]
},
"message.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["message.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"info": {
"$ref": "#/components/schemas/Message"
}
},
"required": ["sessionID", "info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"message.removed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["message.removed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
}
},
"required": ["sessionID", "messageID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"TextPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["text"]
},
"text": {
"type": "string"
},
"synthetic": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"ignored": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"time": {
"anyOf": [
{
"type": "object",
"properties": {
"start": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"end": {
"anyOf": [
{
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
{
"type": "null"
}
]
}
},
"required": ["start"],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"metadata": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type", "text"],
"additionalProperties": false
},
"SubtaskPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["subtask"]
},
"prompt": {
"type": "string"
},
"description": {
"type": "string"
},
"agent": {
"type": "string"
},
"model": {
"anyOf": [
{
"type": "object",
"properties": {
"providerID": {
"type": "string"
},
"modelID": {
"type": "string"
}
},
"required": ["providerID", "modelID"],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"command": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type", "prompt", "description", "agent"],
"additionalProperties": false
},
"ReasoningPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["reasoning"]
},
"text": {
"type": "string"
},
"metadata": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
]
},
"time": {
"type": "object",
"properties": {
"start": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"end": {
"anyOf": [
{
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
{
"type": "null"
}
]
}
},
"required": ["start"],
"additionalProperties": false
}
},
"required": ["id", "sessionID", "messageID", "type", "text", "time"],
"additionalProperties": false
},
"FilePartSourceText": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"start": {
"type": "number"
},
"end": {
"type": "number"
}
},
"required": ["value", "start", "end"],
"additionalProperties": false
},
"FileSource": {
"type": "object",
"properties": {
"text": {
"$ref": "#/components/schemas/FilePartSourceText"
},
"type": {
"type": "string",
"enum": ["file"]
},
"path": {
"type": "string"
}
},
"required": ["text", "type", "path"],
"additionalProperties": false
},
"Range": {
"type": "object",
"properties": {
"start": {
"type": "object",
"properties": {
"line": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"character": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["line", "character"],
"additionalProperties": false
},
"end": {
"type": "object",
"properties": {
"line": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"character": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["line", "character"],
"additionalProperties": false
}
},
"required": ["start", "end"],
"additionalProperties": false
},
"SymbolSource": {
"type": "object",
"properties": {
"text": {
"$ref": "#/components/schemas/FilePartSourceText"
},
"type": {
"type": "string",
"enum": ["symbol"]
},
"path": {
"type": "string"
},
"range": {
"$ref": "#/components/schemas/Range"
},
"name": {
"type": "string"
},
"kind": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["text", "type", "path", "range", "name", "kind"],
"additionalProperties": false
},
"ResourceSource": {
"type": "object",
"properties": {
"text": {
"$ref": "#/components/schemas/FilePartSourceText"
},
"type": {
"type": "string",
"enum": ["resource"]
},
"clientName": {
"type": "string"
},
"uri": {
"type": "string"
}
},
"required": ["text", "type", "clientName", "uri"],
"additionalProperties": false
},
"FilePartSource": {
"anyOf": [
{
"$ref": "#/components/schemas/FileSource"
},
{
"$ref": "#/components/schemas/SymbolSource"
},
{
"$ref": "#/components/schemas/ResourceSource"
}
]
},
"FilePart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["file"]
},
"mime": {
"type": "string"
},
"filename": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"url": {
"type": "string"
},
"source": {
"anyOf": [
{
"$ref": "#/components/schemas/FilePartSource"
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type", "mime", "url"],
"additionalProperties": false
},
"ToolStatePending": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["pending"]
},
"input": {
"type": "object"
},
"raw": {
"type": "string"
}
},
"required": ["status", "input", "raw"],
"additionalProperties": false
},
"ToolStateRunning": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["running"]
},
"input": {
"type": "object"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"metadata": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
]
},
"time": {
"type": "object",
"properties": {
"start": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["start"],
"additionalProperties": false
}
},
"required": ["status", "input", "time"],
"additionalProperties": false
},
"ToolStateCompleted": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["completed"]
},
"input": {
"type": "object"
},
"output": {
"type": "string"
},
"title": {
"type": "string"
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"start": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"end": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"compacted": {
"anyOf": [
{
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
{
"type": "null"
}
]
}
},
"required": ["start", "end"],
"additionalProperties": false
},
"attachments": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/components/schemas/FilePart"
}
},
{
"type": "null"
}
]
}
},
"required": ["status", "input", "output", "title", "metadata", "time"],
"additionalProperties": false
},
"ToolStateError": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["error"]
},
"input": {
"type": "object"
},
"error": {
"type": "string"
},
"metadata": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
]
},
"time": {
"type": "object",
"properties": {
"start": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"end": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["start", "end"],
"additionalProperties": false
}
},
"required": ["status", "input", "error", "time"],
"additionalProperties": false
},
"ToolState": {
"anyOf": [
{
"$ref": "#/components/schemas/ToolStatePending"
},
{
"$ref": "#/components/schemas/ToolStateRunning"
},
{
"$ref": "#/components/schemas/ToolStateCompleted"
},
{
"$ref": "#/components/schemas/ToolStateError"
}
]
},
"ToolPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["tool"]
},
"callID": {
"type": "string"
},
"tool": {
"type": "string"
},
"state": {
"$ref": "#/components/schemas/ToolState"
},
"metadata": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type", "callID", "tool", "state"],
"additionalProperties": false
},
"StepStartPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["step-start"]
},
"snapshot": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type"],
"additionalProperties": false
},
"StepFinishPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["step-finish"]
},
"reason": {
"type": "string"
},
"snapshot": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"cost": {
"type": "number"
},
"tokens": {
"type": "object",
"properties": {
"total": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"input": {
"type": "number"
},
"output": {
"type": "number"
},
"reasoning": {
"type": "number"
},
"cache": {
"type": "object",
"properties": {
"read": {
"type": "number"
},
"write": {
"type": "number"
}
},
"required": ["read", "write"],
"additionalProperties": false
}
},
"required": ["input", "output", "reasoning", "cache"],
"additionalProperties": false
}
},
"required": ["id", "sessionID", "messageID", "type", "reason", "cost", "tokens"],
"additionalProperties": false
},
"SnapshotPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["snapshot"]
},
"snapshot": {
"type": "string"
}
},
"required": ["id", "sessionID", "messageID", "type", "snapshot"],
"additionalProperties": false
},
"PatchPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["patch"]
},
"hash": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["id", "sessionID", "messageID", "type", "hash", "files"],
"additionalProperties": false
},
"AgentPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["agent"]
},
"name": {
"type": "string"
},
"source": {
"anyOf": [
{
"type": "object",
"properties": {
"value": {
"type": "string"
},
"start": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"end": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["value", "start", "end"],
"additionalProperties": false
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type", "name"],
"additionalProperties": false
},
"RetryPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["retry"]
},
"attempt": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"error": {
"$ref": "#/components/schemas/APIError"
},
"time": {
"type": "object",
"properties": {
"created": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["created"],
"additionalProperties": false
}
},
"required": ["id", "sessionID", "messageID", "type", "attempt", "error", "time"],
"additionalProperties": false
},
"CompactionPart": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"type": {
"type": "string",
"enum": ["compaction"]
},
"auto": {
"type": "boolean"
},
"overflow": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"tail_start_id": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "messageID", "type", "auto"],
"additionalProperties": false
},
"Part": {
"anyOf": [
{
"$ref": "#/components/schemas/TextPart"
},
{
"$ref": "#/components/schemas/SubtaskPart"
},
{
"$ref": "#/components/schemas/ReasoningPart"
},
{
"$ref": "#/components/schemas/FilePart"
},
{
"$ref": "#/components/schemas/ToolPart"
},
{
"$ref": "#/components/schemas/StepStartPart"
},
{
"$ref": "#/components/schemas/StepFinishPart"
},
{
"$ref": "#/components/schemas/SnapshotPart"
},
{
"$ref": "#/components/schemas/PatchPart"
},
{
"$ref": "#/components/schemas/AgentPart"
},
{
"$ref": "#/components/schemas/RetryPart"
},
{
"$ref": "#/components/schemas/CompactionPart"
}
]
},
"message.part.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["message.part.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"part": {
"$ref": "#/components/schemas/Part"
},
"time": {
"type": "number"
}
},
"required": ["sessionID", "part", "time"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"message.part.removed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["message.part.removed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"partID": {
"type": "string",
"allOf": [
{
"pattern": "^prt"
}
]
}
},
"required": ["sessionID", "messageID", "partID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.execution.settled": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.execution.settled"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"outcome": {
"type": "string",
"enum": ["success", "failure", "interrupted"]
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
}
},
"required": ["timestamp", "sessionID", "outcome"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.text.delta": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.text.delta"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"textID": {
"type": "string"
},
"delta": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "textID", "delta"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.reasoning.delta": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.reasoning.delta"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reasoningID": {
"type": "string"
},
"delta": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "reasoningID", "delta"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.tool.input.delta": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.tool.input.delta"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"delta": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "assistantMessageID", "callID", "delta"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.next.compaction.delta": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.next.compaction.delta"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"text": {
"type": "string"
}
},
"required": ["timestamp", "sessionID", "messageID", "text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"file.edited": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["file.edited"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"file": {
"type": "string"
}
},
"required": ["file"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"reference.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["reference.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"permission.v2.asked": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["permission.v2.asked"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"action": {
"type": "string"
},
"resources": {
"type": "array",
"items": {
"type": "string"
}
},
"save": {
"type": "array",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object"
},
"source": {
"$ref": "#/components/schemas/PermissionV2.Source"
}
},
"required": ["id", "sessionID", "action", "resources"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"permission.v2.replied": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["permission.v2.replied"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"requestID": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"reply": {
"$ref": "#/components/schemas/PermissionV2.Reply"
}
},
"required": ["sessionID", "requestID", "reply"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"plugin.added": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["plugin.added"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": ["id"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"project.directories.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["project.directories.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"projectID": {
"type": "string"
}
},
"required": ["projectID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"command.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["command.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"skill.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["skill.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"file.watcher.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["file.watcher.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"file": {
"type": "string"
},
"event": {
"type": "string",
"enum": ["add", "change", "unlink"]
}
},
"required": ["file", "event"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"Pty": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"title": {
"type": "string"
},
"command": {
"type": "string"
},
"args": {
"type": "array",
"items": {
"type": "string"
}
},
"cwd": {
"type": "string"
},
"status": {
"type": "string",
"enum": ["running", "exited"]
},
"pid": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"exitCode": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["id", "title", "command", "args", "cwd", "status", "pid"],
"additionalProperties": false
},
"pty.created": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["pty.created"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"info": {
"$ref": "#/components/schemas/Pty"
}
},
"required": ["info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"pty.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["pty.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"info": {
"$ref": "#/components/schemas/Pty"
}
},
"required": ["info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"pty.exited": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["pty.exited"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
},
"exitCode": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["id", "exitCode"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"pty.deleted": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["pty.deleted"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^pty"
}
]
}
},
"required": ["id"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"Shell": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
},
"status": {
"type": "string",
"enum": ["running", "exited", "timeout", "killed"]
},
"command": {
"type": "string"
},
"cwd": {
"type": "string"
},
"shell": {
"type": "string"
},
"file": {
"type": "string"
},
"pid": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"exit": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"started": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"completed": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
}
},
"required": ["started"],
"additionalProperties": false
}
},
"required": ["id", "status", "command", "cwd", "shell", "file", "metadata", "time"],
"additionalProperties": false
},
"shell.created": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["shell.created"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"info": {
"$ref": "#/components/schemas/Shell"
}
},
"required": ["info"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"shell.exited": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["shell.exited"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
},
"exit": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"status": {
"type": "string",
"enum": ["running", "exited", "timeout", "killed"]
}
},
"required": ["id", "status"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"shell.deleted": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["shell.deleted"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
}
},
"required": ["id"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"QuestionV2.Option": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Display text (1-5 words, concise)"
},
"description": {
"type": "string",
"description": "Explanation of choice"
}
},
"required": ["label", "description"],
"additionalProperties": false
},
"QuestionV2.Info": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "Complete question"
},
"header": {
"type": "string",
"description": "Very short label (max 30 chars)"
},
"options": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Option"
},
"description": "Available choices"
},
"multiple": {
"type": "boolean"
},
"custom": {
"type": "boolean"
}
},
"required": ["question", "header", "options"],
"additionalProperties": false
},
"QuestionV2.Tool": {
"type": "object",
"properties": {
"messageID": {
"type": "string"
},
"callID": {
"type": "string"
}
},
"required": ["messageID", "callID"],
"additionalProperties": false
},
"question.v2.asked": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["question.v2.asked"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"questions": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Info"
},
"description": "Questions to ask"
},
"tool": {
"$ref": "#/components/schemas/QuestionV2.Tool"
}
},
"required": ["id", "sessionID", "questions"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"QuestionV2.Answer": {
"type": "array",
"items": {
"type": "string"
}
},
"question.v2.replied": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["question.v2.replied"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"requestID": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"answers": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Answer"
}
}
},
"required": ["sessionID", "requestID", "answers"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"question.v2.rejected": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["question.v2.rejected"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"requestID": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
}
},
"required": ["sessionID", "requestID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"Form.Metadata1": {
"type": "object"
},
"Form.When1": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"op": {
"type": "string",
"enum": ["eq", "neq"]
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "boolean"
}
]
}
},
"required": ["key", "op", "value"],
"additionalProperties": false
},
"Form.StringField1": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When1"
}
},
"type": {
"type": "string",
"enum": ["string"]
},
"format": {
"type": "string",
"enum": ["email", "uri", "date", "date-time"]
},
"minLength": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"maxLength": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"pattern": {
"type": "string"
},
"placeholder": {
"type": "string"
},
"default": {
"type": "string"
},
"options": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.Option"
}
},
"custom": {
"type": "boolean"
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.NumberField1": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When1"
}
},
"type": {
"type": "string",
"enum": ["number"]
},
"minimum": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"maximum": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"default": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.IntegerField1": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When1"
}
},
"type": {
"type": "string",
"enum": ["integer"]
},
"minimum": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"maximum": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
"default": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.BooleanField1": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When1"
}
},
"type": {
"type": "string",
"enum": ["boolean"]
},
"default": {
"type": "boolean"
}
},
"required": ["key", "type"],
"additionalProperties": false
},
"Form.MultiselectField1": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean"
},
"when": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.When1"
}
},
"type": {
"type": "string",
"enum": ["multiselect"]
},
"options": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form.Option"
}
},
"minItems": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"maxItems": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"custom": {
"type": "boolean"
},
"default": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["key", "type", "options"],
"additionalProperties": false
},
"Form.FormInfo1": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"sessionID": {
"type": "string"
},
"title": {
"type": "string"
},
"metadata": {
"$ref": "#/components/schemas/Form.Metadata1"
},
"mode": {
"type": "string",
"enum": ["form"]
},
"fields": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.StringField1"
},
{
"$ref": "#/components/schemas/Form.NumberField1"
},
{
"$ref": "#/components/schemas/Form.IntegerField1"
},
{
"$ref": "#/components/schemas/Form.BooleanField1"
},
{
"$ref": "#/components/schemas/Form.MultiselectField1"
}
]
}
}
},
"required": ["id", "sessionID", "mode", "fields"],
"additionalProperties": false
},
"Form.UrlInfo1": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"sessionID": {
"type": "string"
},
"title": {
"type": "string"
},
"metadata": {
"$ref": "#/components/schemas/Form.Metadata1"
},
"mode": {
"type": "string",
"enum": ["url"]
},
"url": {
"type": "string"
}
},
"required": ["id", "sessionID", "mode", "url"],
"additionalProperties": false
},
"form.created": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["form.created"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"form": {
"anyOf": [
{
"$ref": "#/components/schemas/Form.FormInfo1"
},
{
"$ref": "#/components/schemas/Form.UrlInfo1"
}
]
}
},
"required": ["form"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"Form.Value1": {
"anyOf": [
{
"type": "string"
},
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"Form.Answer1": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Form.Value1"
}
},
"form.replied": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["form.replied"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"sessionID": {
"type": "string"
},
"answer": {
"$ref": "#/components/schemas/Form.Answer1"
}
},
"required": ["id", "sessionID", "answer"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"form.cancelled": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["form.cancelled"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^frm_"
}
]
},
"sessionID": {
"type": "string"
}
},
"required": ["id", "sessionID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"Todo": {
"type": "object",
"properties": {
"content": {
"type": "string",
"description": "Brief description of the task"
},
"status": {
"type": "string",
"description": "Current status of the task: pending, in_progress, completed, cancelled"
},
"priority": {
"type": "string",
"description": "Priority level of the task: high, medium, low"
}
},
"required": ["content", "status", "priority"],
"additionalProperties": false
},
"todo.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["todo.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"todos": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Todo"
}
}
},
"required": ["sessionID", "todos"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"SessionStatus": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["idle"]
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["retry"]
},
"attempt": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"message": {
"type": "string"
},
"action": {
"type": "object",
"properties": {
"reason": {
"type": "string"
},
"provider": {
"type": "string"
},
"title": {
"type": "string"
},
"message": {
"type": "string"
},
"label": {
"type": "string"
},
"link": {
"type": "string"
}
},
"required": ["reason", "provider", "title", "message", "label"],
"additionalProperties": false
},
"next": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["type", "attempt", "message", "next"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["busy"]
}
},
"required": ["type"],
"additionalProperties": false
}
]
},
"session.status": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.status"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"status": {
"$ref": "#/components/schemas/SessionStatus"
}
},
"required": ["sessionID", "status"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.idle": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.idle"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
}
},
"required": ["sessionID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"tui.prompt.append": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["tui.prompt.append"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": ["text"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"tui.command.execute": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["tui.command.execute"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"command": {
"anyOf": [
{
"type": "string",
"enum": [
"session.list",
"session.new",
"session.share",
"session.interrupt",
"session.background",
"session.compact",
"session.page.up",
"session.page.down",
"session.line.up",
"session.line.down",
"session.half.page.up",
"session.half.page.down",
"session.first",
"session.last",
"prompt.clear",
"prompt.submit",
"agent.cycle"
]
},
{
"type": "string"
}
]
}
},
"required": ["command"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"tui.toast.show": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["tui.toast.show"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"message": {
"type": "string"
},
"variant": {
"type": "string",
"enum": ["info", "success", "warning", "error"]
},
"duration": {
"anyOf": [
{
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
},
{
"type": "null"
}
]
}
},
"required": ["message", "variant"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"tui.session.select": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["tui.session.select"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses",
"description": "Session ID to navigate to"
}
]
}
},
"required": ["sessionID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"installation.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["installation.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"version": {
"type": "string"
}
},
"required": ["version"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"installation.update-available": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["installation.update-available"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"version": {
"type": "string"
}
},
"required": ["version"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"vcs.branch.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["vcs.branch.updated"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"branch": {
"type": "string"
}
},
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"mcp.status.changed": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["mcp.status.changed"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"server": {
"type": "string"
}
},
"required": ["server"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"permission.asked": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["permission.asked"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"permission": {
"type": "string"
},
"patterns": {
"type": "array",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object"
},
"always": {
"type": "array",
"items": {
"type": "string"
}
},
"tool": {
"anyOf": [
{
"type": "object",
"properties": {
"messageID": {
"type": "string"
},
"callID": {
"type": "string"
}
},
"required": ["messageID", "callID"],
"additionalProperties": false
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "permission", "patterns", "metadata", "always"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"permission.replied": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["permission.replied"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"requestID": {
"type": "string",
"allOf": [
{
"pattern": "^per"
}
]
},
"reply": {
"type": "string",
"enum": ["once", "always", "reject"]
}
},
"required": ["sessionID", "requestID", "reply"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"QuestionOption": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Display text (1-5 words, concise)"
},
"description": {
"type": "string",
"description": "Explanation of choice"
}
},
"required": ["label", "description"],
"additionalProperties": false
},
"QuestionInfo": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "Complete question"
},
"header": {
"type": "string",
"description": "Very short label (max 30 chars)"
},
"options": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionOption"
},
"description": "Available choices"
},
"multiple": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Allow selecting multiple choices"
},
"custom": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Allow typing a custom answer (default: true)"
}
},
"required": ["question", "header", "options"],
"additionalProperties": false
},
"QuestionTool": {
"type": "object",
"properties": {
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg"
}
]
},
"callID": {
"type": "string"
}
},
"required": ["messageID", "callID"],
"additionalProperties": false
},
"question.asked": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["question.asked"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"questions": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionInfo"
},
"description": "Questions to ask"
},
"tool": {
"anyOf": [
{
"$ref": "#/components/schemas/QuestionTool"
},
{
"type": "null"
}
]
}
},
"required": ["id", "sessionID", "questions"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"QuestionAnswer": {
"type": "array",
"items": {
"type": "string"
}
},
"question.replied": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["question.replied"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"requestID": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"answers": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionAnswer"
}
}
},
"required": ["sessionID", "requestID", "answers"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"question.rejected": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["question.rejected"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"requestID": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
}
},
"required": ["sessionID", "requestID"],
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"session.error": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": ["session.error"]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"anyOf": [
{
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
{
"type": "null"
}
]
},
"error": {
"anyOf": [
{
"anyOf": [
{
"$ref": "#/components/schemas/ProviderAuthError"
},
{
"$ref": "#/components/schemas/UnknownError1"
},
{
"$ref": "#/components/schemas/MessageOutputLengthError"
},
{
"$ref": "#/components/schemas/MessageAbortedError"
},
{
"$ref": "#/components/schemas/StructuredOutputError"
},
{
"$ref": "#/components/schemas/ContextOverflowError"
},
{
"$ref": "#/components/schemas/ContentFilterError"
},
{
"$ref": "#/components/schemas/APIError"
}
]
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"V2Event.server.connected": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"anyOf": [
{
"type": "object"
},
{
"type": "null"
}
]
},
"durable": {
"anyOf": [
{
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": ["aggregateID", "seq", "version"],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"location": {
"anyOf": [
{
"$ref": "#/components/schemas/Location.Ref"
},
{
"type": "null"
}
]
},
"type": {
"type": "string",
"enum": ["server.connected"]
},
"data": {
"anyOf": [
{
"type": "object"
},
{
"type": "array"
}
]
}
},
"required": ["id", "type", "data"],
"additionalProperties": false
},
"V2Event": {
"anyOf": [
{
"$ref": "#/components/schemas/models-dev.refreshed"
},
{
"$ref": "#/components/schemas/integration.updated"
},
{
"$ref": "#/components/schemas/integration.connection.updated"
},
{
"$ref": "#/components/schemas/catalog.updated"
},
{
"$ref": "#/components/schemas/agent.updated"
},
{
"$ref": "#/components/schemas/session.created"
},
{
"$ref": "#/components/schemas/session.updated"
},
{
"$ref": "#/components/schemas/session.deleted"
},
{
"$ref": "#/components/schemas/message.updated"
},
{
"$ref": "#/components/schemas/message.removed"
},
{
"$ref": "#/components/schemas/message.part.updated"
},
{
"$ref": "#/components/schemas/message.part.removed"
},
{
"$ref": "#/components/schemas/session.next.agent.switched"
},
{
"$ref": "#/components/schemas/session.next.model.switched"
},
{
"$ref": "#/components/schemas/session.next.moved"
},
{
"$ref": "#/components/schemas/session.next.renamed"
},
{
"$ref": "#/components/schemas/session.next.forked"
},
{
"$ref": "#/components/schemas/session.next.prompted"
},
{
"$ref": "#/components/schemas/session.next.prompt.admitted"
},
{
"$ref": "#/components/schemas/session.next.execution.settled"
},
{
"$ref": "#/components/schemas/session.next.context.updated"
},
{
"$ref": "#/components/schemas/session.next.synthetic"
},
{
"$ref": "#/components/schemas/session.next.skill.activated"
},
{
"$ref": "#/components/schemas/session.next.shell.started"
},
{
"$ref": "#/components/schemas/session.next.shell.ended"
},
{
"$ref": "#/components/schemas/session.next.step.started"
},
{
"$ref": "#/components/schemas/session.next.step.ended"
},
{
"$ref": "#/components/schemas/session.next.step.failed"
},
{
"$ref": "#/components/schemas/session.next.text.started"
},
{
"$ref": "#/components/schemas/session.next.text.delta"
},
{
"$ref": "#/components/schemas/session.next.text.ended"
},
{
"$ref": "#/components/schemas/session.next.reasoning.started"
},
{
"$ref": "#/components/schemas/session.next.reasoning.delta"
},
{
"$ref": "#/components/schemas/session.next.reasoning.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.input.started"
},
{
"$ref": "#/components/schemas/session.next.tool.input.delta"
},
{
"$ref": "#/components/schemas/session.next.tool.input.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.called"
},
{
"$ref": "#/components/schemas/session.next.tool.progress"
},
{
"$ref": "#/components/schemas/session.next.tool.success"
},
{
"$ref": "#/components/schemas/session.next.tool.failed"
},
{
"$ref": "#/components/schemas/session.next.retried"
},
{
"$ref": "#/components/schemas/session.next.compaction.started"
},
{
"$ref": "#/components/schemas/session.next.compaction.delta"
},
{
"$ref": "#/components/schemas/session.next.compaction.ended"
},
{
"$ref": "#/components/schemas/session.next.revert.staged"
},
{
"$ref": "#/components/schemas/session.next.revert.cleared"
},
{
"$ref": "#/components/schemas/session.next.revert.committed"
},
{
"$ref": "#/components/schemas/file.edited"
},
{
"$ref": "#/components/schemas/reference.updated"
},
{
"$ref": "#/components/schemas/permission.v2.asked"
},
{
"$ref": "#/components/schemas/permission.v2.replied"
},
{
"$ref": "#/components/schemas/plugin.added"
},
{
"$ref": "#/components/schemas/project.directories.updated"
},
{
"$ref": "#/components/schemas/command.updated"
},
{
"$ref": "#/components/schemas/skill.updated"
},
{
"$ref": "#/components/schemas/file.watcher.updated"
},
{
"$ref": "#/components/schemas/pty.created"
},
{
"$ref": "#/components/schemas/pty.updated"
},
{
"$ref": "#/components/schemas/pty.exited"
},
{
"$ref": "#/components/schemas/pty.deleted"
},
{
"$ref": "#/components/schemas/shell.created"
},
{
"$ref": "#/components/schemas/shell.exited"
},
{
"$ref": "#/components/schemas/shell.deleted"
},
{
"$ref": "#/components/schemas/question.v2.asked"
},
{
"$ref": "#/components/schemas/question.v2.replied"
},
{
"$ref": "#/components/schemas/question.v2.rejected"
},
{
"$ref": "#/components/schemas/form.created"
},
{
"$ref": "#/components/schemas/form.replied"
},
{
"$ref": "#/components/schemas/form.cancelled"
},
{
"$ref": "#/components/schemas/todo.updated"
},
{
"$ref": "#/components/schemas/session.status"
},
{
"$ref": "#/components/schemas/session.idle"
},
{
"$ref": "#/components/schemas/tui.prompt.append"
},
{
"$ref": "#/components/schemas/tui.command.execute"
},
{
"$ref": "#/components/schemas/tui.toast.show"
},
{
"$ref": "#/components/schemas/tui.session.select"
},
{
"$ref": "#/components/schemas/installation.updated"
},
{
"$ref": "#/components/schemas/installation.update-available"
},
{
"$ref": "#/components/schemas/vcs.branch.updated"
},
{
"$ref": "#/components/schemas/mcp.status.changed"
},
{
"$ref": "#/components/schemas/permission.asked"
},
{
"$ref": "#/components/schemas/permission.replied"
},
{
"$ref": "#/components/schemas/question.asked"
},
{
"$ref": "#/components/schemas/question.replied"
},
{
"$ref": "#/components/schemas/question.rejected"
},
{
"$ref": "#/components/schemas/session.error"
},
{
"$ref": "#/components/schemas/V2Event.server.connected"
}
]
},
"V2EventStream": {
"type": "string",
"contentSchema": {
"$ref": "#/components/schemas/V2Event"
},
"contentMediaType": "application/json"
},
"EventLog.Hint": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["log.hint"]
},
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": ["type", "aggregateID", "seq"],
"additionalProperties": false,
"description": "Payload-free change hint: the aggregate's durable log advanced to at least seq. Hints coalesce under backpressure (latest per aggregate) and are never a delivery guarantee."
},
"EventLog.SweepRequired": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["log.sweep_required"]
}
},
"required": ["type"],
"additionalProperties": false,
"description": "Hints may have been lost; treat every aggregate as potentially dirty and recover via bounded sweep plus durable log reads. Emitted first on every (re)subscribe."
},
"EventLog.Change": {
"anyOf": [
{
"$ref": "#/components/schemas/EventLog.Hint"
},
{
"$ref": "#/components/schemas/EventLog.SweepRequired"
}
]
},
"EventLog.ChangeStream": {
"type": "string",
"contentSchema": {
"$ref": "#/components/schemas/EventLog.Change"
},
"contentMediaType": "application/json"
},
"PtyNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["PtyNotFoundError"]
},
"ptyID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "ptyID", "message"],
"additionalProperties": false
},
"PtyTicket.ConnectToken": {
"type": "object",
"properties": {
"ticket": {
"type": "string"
},
"expires_in": {
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
}
},
"required": ["ticket", "expires_in"],
"additionalProperties": false
},
"ForbiddenError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["ForbiddenError"]
},
"message": {
"type": "string"
}
},
"required": ["_tag", "message"],
"additionalProperties": false
},
"Shell1": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^sh_"
}
]
},
"status": {
"type": "string",
"enum": ["running", "exited", "timeout", "killed"]
},
"command": {
"type": "string"
},
"cwd": {
"type": "string"
},
"shell": {
"type": "string"
},
"file": {
"type": "string"
},
"pid": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"exit": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"metadata": {
"type": "object"
},
"time": {
"type": "object",
"properties": {
"started": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
},
"completed": {
"anyOf": [
{
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"enum": ["NaN"]
},
{
"type": "string",
"enum": ["Infinity"]
},
{
"type": "string",
"enum": ["-Infinity"]
}
]
},
{
"type": "string",
"enum": ["Infinity", "-Infinity", "NaN"]
}
]
}
},
"required": ["started"],
"additionalProperties": false
}
},
"required": ["id", "status", "command", "cwd", "shell", "file", "metadata", "time"],
"additionalProperties": false
},
"ShellNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["ShellNotFoundError"]
},
"id": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "id", "message"],
"additionalProperties": false
},
"QuestionV2.Request": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^que"
}
]
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"questions": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Info"
},
"description": "Questions to ask"
},
"tool": {
"$ref": "#/components/schemas/QuestionV2.Tool"
}
},
"required": ["id", "sessionID", "questions"],
"additionalProperties": false
},
"QuestionV2.Reply": {
"type": "object",
"properties": {
"answers": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QuestionV2.Answer"
},
"description": "User answers in order of questions (each answer is an array of selected labels)"
}
},
"required": ["answers"],
"additionalProperties": false
},
"QuestionNotFoundError": {
"type": "object",
"properties": {
"_tag": {
"type": "string",
"enum": ["QuestionNotFoundError"]
},
"requestID": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": ["_tag", "requestID", "message"],
"additionalProperties": false
},
"Reference.LocalSource": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["local"]
},
"path": {
"type": "string"
},
"description": {
"type": "string"
},
"hidden": {
"type": "boolean"
}
},
"required": ["type", "path"],
"additionalProperties": false
},
"Reference.GitSource": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["git"]
},
"repository": {
"type": "string"
},
"branch": {
"type": "string"
},
"description": {
"type": "string"
},
"hidden": {
"type": "boolean"
}
},
"required": ["type", "repository"],
"additionalProperties": false
},
"Reference.Source": {
"anyOf": [
{
"$ref": "#/components/schemas/Reference.LocalSource"
},
{
"$ref": "#/components/schemas/Reference.GitSource"
}
]
},
"Reference.Info": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"description": {
"type": "string"
},
"hidden": {
"type": "boolean"
},
"source": {
"$ref": "#/components/schemas/Reference.Source"
}
},
"required": ["name", "path", "source"],
"additionalProperties": false
},
"ProjectCopy.Copy": {
"type": "object",
"properties": {
"directory": {
"type": "string"
}
},
"required": ["directory"],
"additionalProperties": false
},
"ProjectCopyError": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["ProjectCopyError"]
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"forceRequired": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
}
},
"required": ["message"],
"additionalProperties": false
}
},
"required": ["name", "data"],
"additionalProperties": false
},
"Vcs.FileStatus": {
"type": "object",
"properties": {
"file": {
"type": "string"
},
"additions": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"deletions": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"status": {
"type": "string",
"enum": ["added", "deleted", "modified"]
}
},
"required": ["file", "additions", "deletions", "status"],
"additionalProperties": false
},
"Vcs.Mode": {
"type": "string",
"enum": ["working", "branch"]
}
},
"securitySchemes": {}
},
"security": [],
"tags": [
{
"name": "server.health"
},
{
"name": "server.location"
},
{
"name": "server.agent"
},
{
"name": "plugins",
"description": "Experimental plugin routes."
},
{
"name": "sessions",
"description": "Experimental session routes."
},
{
"name": "messages",
"description": "Experimental message routes."
},
{
"name": "models",
"description": "Experimental model routes."
},
{
"name": "generate",
"description": "Experimental one-shot generation routes."
},
{
"name": "providers",
"description": "Experimental provider routes."
},
{
"name": "integrations",
"description": "Integration discovery and authentication routes."
},
{
"name": "mcp",
"description": "MCP server status routes."
},
{
"name": "server.credential"
},
{
"name": "projects",
"description": "Location-scoped project routes."
},
{
"name": "forms",
"description": "Session form routes."
},
{
"name": "permissions",
"description": "Experimental permission routes."
},
{
"name": "filesystem",
"description": "Experimental location-scoped filesystem routes."
},
{
"name": "commands",
"description": "Experimental command routes."
},
{
"name": "skills",
"description": "Experimental skill routes."
},
{
"name": "events",
"description": "Experimental event stream routes."
},
{
"name": "pty",
"description": "Experimental location-scoped PTY routes."
},
{
"name": "shell",
"description": "Experimental location-scoped shell command routes."
},
{
"name": "session questions",
"description": "Experimental session question routes."
},
{
"name": "reference",
"description": "Location-scoped project references."
},
{
"name": "projectCopy",
"description": "Project copy management routes."
},
{
"name": "vcs",
"description": "Location-scoped version control routes."
}
]
}
-964
View File
@@ -1,964 +0,0 @@
import { describe, expect, test } from "bun:test"
import { Effect, Layer, Option } from "effect"
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { CodeMode, OpenAPI } from "../src/index.js"
import { inputTypeScript, outputTypeScript, Tool } from "../src/tool.js"
const baseUrl = "http://localhost:4096"
type Document = OpenAPI.Document
type Recorded = {
readonly method: string
readonly url: string
readonly headers: Record<string, string>
readonly body: unknown
}
const opencodeSpec = async (): Promise<Document> => {
return Bun.file(new URL("./fixtures/opencode-v2-openapi.json", import.meta.url)).json() as Promise<Document>
}
const happyPathSpec = async (): Promise<Document> => {
return Bun.file(new URL("./fixtures/openapi-happy-path.json", import.meta.url)).json() as Promise<Document>
}
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value)
const toolAt = (tools: unknown, name: string) =>
name.split(".").reduce<unknown>((current, segment) => (isRecord(current) ? current[segment] : undefined), tools)
const recordingClient = (respond: (request: HttpClientRequest.HttpClientRequest) => Response) => {
const requests: Array<Recorded> = []
const layer = Layer.succeed(HttpClient.HttpClient)(
HttpClient.make((request) =>
Effect.gen(function* () {
const body =
request.body._tag === "Uint8Array" ? JSON.parse(new TextDecoder().decode(request.body.body)) : undefined
const url = Option.map(HttpClientRequest.toUrl(request), (resolved) => resolved.toString())
requests.push({
method: request.method,
url: Option.getOrElse(url, () => request.url),
headers: { ...request.headers },
body,
})
return HttpClientResponse.fromWeb(request, respond(request))
}),
),
)
return { requests, layer }
}
const json = (value: unknown, status = 200) =>
new Response(JSON.stringify(value), { status, headers: { "content-type": "application/json" } })
const singleOperation = (operation: Record<string, unknown>, method = "get"): Document => ({
openapi: "3.1.0",
paths: {
"/test": { [method]: { operationId: "test", responses: { 200: { description: "Success" } }, ...operation } },
},
})
describe("OpenAPI.fromSpec", () => {
test("covers a representative API from generation through execution", async () => {
const resolutions: Array<string> = []
const client = recordingClient((request) => {
const url = Option.getOrElse(HttpClientRequest.toUrl(request), () => new URL(request.url))
if (request.method === "POST") {
return new Response(
JSON.stringify({ id: "user-2", name: "Grace", email: "grace@example.test", role: "admin" }),
{ status: 201, headers: { "content-type": "application/vnd.example+json" } },
)
}
if (request.method === "DELETE") return new Response(null, { status: 204 })
if (url.pathname === "/search") {
return new Response("2 matches", { headers: { "content-type": "text/plain" } })
}
return json({ id: "user-1", name: "Ada", email: "ada@example.test", role: "member" })
})
const api = OpenAPI.fromSpec({
spec: await happyPathSpec(),
baseUrl,
auth: {
resolve: ({ name }) => {
resolutions.push(name)
return Effect.succeed(
name === "BearerAuth"
? { type: "bearer", token: "bearer-secret" }
: { type: "apiKey", value: "api-secret" },
)
},
},
})
const get = toolAt(api.tools, "users.get")
const create = toolAt(api.tools, "users.create")
const search = toolAt(api.tools, "search.run")
const remove = toolAt(api.tools, "users.remove")
expect(api.skipped).toEqual([])
if (
!Tool.isDefinition(get) ||
!Tool.isDefinition(create) ||
!Tool.isDefinition(search) ||
!Tool.isDefinition(remove)
) {
throw new Error("happy-path fixture did not generate every operation")
}
expect(inputTypeScript(get)).toBe(
'{ userId: string; include?: Array<string>; verbose?: boolean; "X-Trace-ID"?: string }',
)
expect(inputTypeScript(create)).toBe('{ name: string; email: string; role?: "admin" | "member" }')
expect(inputTypeScript(search)).toBe("{ filter?: { query: string; page?: number }; tags?: Array<string> }")
expect(inputTypeScript(remove)).toBe("{ userId: string }")
expect(outputTypeScript(get)).toContain("id: string")
expect(outputTypeScript(create)).toContain('role?: "admin" | "member"')
expect(outputTypeScript(search)).toBe("string")
expect(outputTypeScript(remove)).toBe("null")
const result = await Effect.runPromise(
CodeMode.make({ tools: { api: api.tools } })
.execute(
`
const user = await tools.api.users.get({
userId: "user-1",
include: ["profile", "permissions"],
verbose: true,
"X-Trace-ID": "trace-1",
})
const created = await tools.api.users.create({
name: "Grace",
email: "grace@example.test",
role: "admin",
})
const summary = await tools.api.search.run({
filter: { query: "effect", page: 2 },
tags: ["typescript", "runtime"],
})
const removed = await tools.api.users.remove({ userId: "user-1" })
return { user, created, summary, removed }
`,
)
.pipe(Effect.provide(client.layer)),
)
expect(result).toMatchObject({
ok: true,
value: {
user: { id: "user-1", name: "Ada" },
created: { id: "user-2", name: "Grace" },
summary: "2 matches",
removed: null,
},
})
expect(resolutions).toEqual(["BearerAuth", "ApiKey", "BearerAuth"])
expect(client.requests).toHaveLength(4)
const getUrl = new URL(client.requests[0]!.url)
expect(getUrl.pathname).toBe("/users/user-1")
expect(getUrl.searchParams.get("include")).toBe("profile,permissions")
expect(getUrl.searchParams.get("verbose")).toBe("true")
expect(client.requests[0]!.headers["x-trace-id"]).toBe("trace-1")
expect(client.requests[0]!.headers.authorization).toBe("Bearer bearer-secret")
const createUrl = new URL(client.requests[1]!.url)
expect(createUrl.searchParams.get("api_key")).toBe("api-secret")
expect(client.requests[1]!.body).toEqual({ name: "Grace", email: "grace@example.test", role: "admin" })
const searchUrl = new URL(client.requests[2]!.url)
expect(searchUrl.searchParams.get("filter[query]")).toBe("effect")
expect(searchUrl.searchParams.get("filter[page]")).toBe("2")
expect(searchUrl.searchParams.getAll("tags")).toEqual(["typescript", "runtime"])
expect(client.requests[2]!.headers.authorization).toBeUndefined()
expect(new URL(client.requests[3]!.url).pathname).toBe("/users/user-1")
expect(client.requests[3]!.headers.authorization).toBe("Bearer bearer-secret")
})
test("converts representative opencode operations into the expected tool shape", async () => {
const spec = await opencodeSpec()
const result = OpenAPI.fromSpec({ spec, baseUrl })
expect(result.skipped).toHaveLength(5)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/pty/{ptyID}/connect",
reason: "WebSocket operations are not supported",
})
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(3)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/fs/read/*",
reason: "binary responses are not supported",
})
expect(toolAt(result.tools, "v2.health.get")).not.toBeUndefined()
expect(toolAt(result.tools, "v2.session.get")).not.toBeUndefined()
expect(toolAt(result.tools, "v2.session.create")).not.toBeUndefined()
const sessionGet = toolAt(result.tools, "v2.session.get")
expect(Tool.isDefinition(sessionGet)).toBe(true)
if (!Tool.isDefinition(sessionGet)) throw new Error("v2.session.get was not generated")
expect(inputTypeScript(sessionGet)).toBe("{ sessionID: string }")
expect(outputTypeScript(sessionGet)).toContain("id: string")
expect(outputTypeScript(sessionGet)).toContain("additions: number")
const switchAgent = toolAt(result.tools, "v2.session.switchAgent")
expect(Tool.isDefinition(switchAgent)).toBe(true)
if (!Tool.isDefinition(switchAgent)) throw new Error("v2.session.switchAgent was not generated")
expect(inputTypeScript(switchAgent)).toBe("{ sessionID: string; agent: string }")
const contextEntryPut = toolAt(result.tools, "v2.session.contextEntry.put")
expect(Tool.isDefinition(contextEntryPut)).toBe(true)
if (!Tool.isDefinition(contextEntryPut)) throw new Error("v2.session.contextEntry.put was not generated")
expect(inputTypeScript(contextEntryPut)).toBe("{ sessionID: string; key: string; value: unknown }")
expect(toolAt(result.tools, "v2_session_context_entry_put_2")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect")).toBeUndefined()
expect(toolAt(result.tools, "v2.session.log")).toBeUndefined()
expect(toolAt(result.tools, "v2.event.subscribe")).toBeUndefined()
expect(toolAt(result.tools, "v2.event.changes")).toBeUndefined()
expect(toolAt(result.tools, "v2.fs.read")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connectToken")).not.toBeUndefined()
})
test("preserves operation path sanitization and collision handling", () => {
const response = { responses: { 200: { description: "Success" } } }
const result = OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.1.0",
paths: {
"/first": { get: { ...response, operationId: "group.item" } },
"/second": { get: { ...response, operationId: "group.item" } },
"/third": { get: { ...response, operationId: "group..other" } },
},
},
})
expect(Tool.isDefinition(toolAt(result.tools, "group.item"))).toBe(true)
expect(Tool.isDefinition(toolAt(result.tools, "group_item_2"))).toBe(true)
expect(Tool.isDefinition(toolAt(result.tools, "group.operation.other"))).toBe(true)
})
test("synthesizes flat operation IDs from methods and paths", () => {
const response = { responses: { 200: { description: "Success" } } }
const tools = OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.1.0",
paths: {
"/users": { get: response, post: response },
"/users/{id}": { get: response, patch: response, delete: response },
"/organizations/{organizationId}/users/{id}": { get: response },
},
},
}).tools
for (const path of [
"getUsers",
"postUsers",
"getUsersById",
"patchUsersById",
"deleteUsersById",
"getOrganizationsByOrganizationidUsersById",
]) {
expect(Tool.isDefinition(toolAt(tools, path))).toBe(true)
}
})
test("lets operation parameters override matching path parameters", () => {
const tool = toolAt(
OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.1.0",
paths: {
"/test": {
parameters: [{ name: "limit", in: "query", schema: { type: "string" } }],
get: {
operationId: "test",
parameters: [{ name: "limit", in: "query", required: true, schema: { type: "number" } }],
responses: { 200: { description: "Success" } },
},
},
},
},
}).tools,
"test",
)
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
expect(inputTypeScript(tool)).toBe("{ limit: number }")
})
test("normalizes OpenAPI 3.0 schemas with Effect", () => {
const result = OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.0.3",
paths: {
"/search": {
get: {
operationId: "search",
parameters: [
{
in: "query",
name: "value",
schema: { type: "string", nullable: true, minLength: 2 },
},
],
responses: { 200: { description: "Success" } },
},
},
},
},
})
const search = toolAt(result.tools, "search")
expect(Tool.isDefinition(search)).toBe(true)
if (!Tool.isDefinition(search)) throw new Error("search was not generated")
expect(inputTypeScript(search)).toBe("{ value?: string | null }")
const schema: unknown = search.input
const input = isRecord(schema) ? schema : {}
const properties = isRecord(input.properties) ? input.properties : {}
const value = isRecord(properties.value) ? properties.value : {}
expect(value.minLength).toBe(2)
})
test("preserves schema-local definitions alongside component definitions", () => {
const tool = toolAt(
OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.1.0",
paths: {
"/test": {
get: {
operationId: "test",
responses: {
200: {
description: "Success",
content: {
"application/json": {
schema: { $ref: "#/$defs/Local", $defs: { Local: { type: "string" } } },
},
},
},
},
},
},
},
components: { schemas: { Global: { type: "number" } } },
},
}).tools,
"test",
)
if (!Tool.isDefinition(tool) || !isRecord(tool.output)) throw new Error("test output was not generated")
expect(tool.output.$defs).toMatchObject({ Local: { type: "string" }, Global: { type: "number" } })
})
test("documents that the opencode fixture is unauthenticated", async () => {
const spec = await opencodeSpec()
const components = isRecord(spec.components) ? spec.components : {}
const result = OpenAPI.fromSpec({ spec, baseUrl })
expect(spec.security).toStrictEqual([])
expect(isRecord(components.securitySchemes) ? Object.keys(components.securitySchemes) : []).toStrictEqual([])
const health = toolAt(result.tools, "v2.health.get")
const healthInput = isRecord(health) ? health.input : undefined
expect(healthInput).toMatchObject({ type: "object", properties: {} })
const input = isRecord(healthInput) ? healthInput : {}
expect(Object.keys(isRecord(input.properties) ? input.properties : {})).toStrictEqual([])
})
test("exposes real opencode operations through CodeMode discovery", async () => {
const { layer } = recordingClient(() => json({}))
const runtime = CodeMode.make({
tools: { opencode: OpenAPI.fromSpec({ spec: await opencodeSpec(), baseUrl }).tools },
})
const result = await Effect.runPromise(
runtime
.execute(
`
return await tools.$codemode.search({ query: "global health", namespace: "opencode", limit: 1 })
`,
)
.pipe(Effect.provide(layer)),
)
expect(result).toMatchObject({ ok: true })
if (!result.ok) return
expect(result.value).toMatchObject({
items: [
{
path: "tools.opencode.v2.health.get",
description: "Check whether the API server is ready to accept requests.",
},
],
})
expect(JSON.stringify(result.value)).toContain("healthy: true")
})
test("invokes real opencode path parameters and JSON request bodies", async () => {
const { requests, layer } = recordingClient((request) => {
if (request.method === "GET") return json({ id: "ses_123" })
return json({ id: "ses_456" })
})
const runtime = CodeMode.make({
tools: { opencode: OpenAPI.fromSpec({ spec: await opencodeSpec(), baseUrl }).tools },
})
const result = await Effect.runPromise(
runtime
.execute(
`
const existing = await tools.opencode.v2.session.get({ sessionID: "ses_123" })
const created = await tools.opencode.v2.session.create({ id: "ses_456" })
return { existing, created }
`,
)
.pipe(Effect.provide(layer)),
)
expect(result).toMatchObject({ ok: true })
expect(requests).toHaveLength(2)
expect(requests[0]).toMatchObject({ method: "GET", body: undefined })
expect(new URL(requests[0]!.url).pathname).toBe("/api/session/ses_123")
expect(requests[1]).toMatchObject({
method: "POST",
url: "http://localhost:4096/api/session",
body: { id: "ses_456" },
})
})
test("serializes deep-object query parameters from the opencode fixture", async () => {
const client = recordingClient(() => json({ directory: "/tmp" }))
const location = toolAt(OpenAPI.fromSpec({ spec: await opencodeSpec(), baseUrl }).tools, "v2.location.get")
if (!Tool.isDefinition(location)) throw new Error("v2.location.get was not generated")
await Effect.runPromise(
location.run({ location: { directory: "/tmp", workspace: "workspace-1" } }).pipe(Effect.provide(client.layer)),
)
const url = new URL(client.requests[0]!.url)
expect(url.searchParams.get("location[directory]")).toBe("/tmp")
expect(url.searchParams.get("location[workspace]")).toBe("workspace-1")
})
test("serializes supported simple and form parameter shapes", async () => {
const client = recordingClient(() => json({ ok: true }))
const result = OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.1.0",
paths: {
"/items/{keys}": {
get: {
operationId: "items",
parameters: [
{ name: "keys", in: "path", required: true, schema: { type: "array", items: { type: "string" } } },
{ name: "tags", in: "query", style: "form", explode: false, schema: { type: "array" } },
{ name: "filter", in: "query", style: "form", explode: true, schema: { type: "object" } },
{ name: "nullable", in: "query", required: true, schema: { type: ["string", "null"] } },
{ name: "constructor", in: "query", schema: { type: "string" } },
{ name: "meta", in: "header", style: "simple", explode: true, schema: { type: "object" } },
],
responses: { 200: { description: "Success" } },
},
},
},
},
})
const tool = toolAt(result.tools, "items")
if (!Tool.isDefinition(tool)) throw new Error("items was not generated")
await Effect.runPromise(
tool
.run({
keys: ["a!", "b*"],
tags: ["x", "y"],
filter: { state: "open", page: 2 },
nullable: null,
constructor_2: "safe",
meta: { a: "b", c: "d" },
})
.pipe(Effect.provide(client.layer)),
)
const url = new URL(client.requests[0]!.url)
expect(url.pathname).toBe("/items/a%21,b%2A")
expect(url.searchParams.get("tags")).toBe("x,y")
expect(url.searchParams.get("state")).toBe("open")
expect(url.searchParams.get("page")).toBe("2")
expect(url.searchParams.get("nullable")).toBe("null")
expect(url.searchParams.get("constructor")).toBe("safe")
expect(client.requests[0]!.headers.meta).toBe("a=b,c=d")
await expect(Effect.runPromise(tool.run({ keys: [undefined] }).pipe(Effect.provide(client.layer)))).rejects.toThrow(
"unsupported nested value",
)
})
test("skips unsupported parameter encodings and malformed security", () => {
const result = OpenAPI.fromSpec({
baseUrl,
spec: {
openapi: "3.1.0",
security: [{ bearer: [] }],
paths: {
"/cookie": {
get: {
operationId: "cookie",
parameters: [{ name: "session", in: "cookie", schema: { type: "string" } }],
responses: { 200: { description: "Success" } },
},
},
"/reserved": {
get: {
operationId: "reserved",
parameters: [{ name: "query", in: "query", allowReserved: true, schema: { type: "string" } }],
responses: { 200: { description: "Success" } },
},
},
"/invalid-style": {
get: {
operationId: "invalidStyle",
parameters: [{ name: "query", in: "query", style: 42, schema: { type: "string" } }],
responses: { 200: { description: "Success" } },
},
},
"/security": {
get: { operationId: "security", security: null, responses: { 200: { description: "Success" } } },
},
},
},
})
expect(result.tools).toEqual({})
expect(result.skipped.map((item) => item.reason)).toEqual([
"cookie parameter 'session' is not supported",
"parameter 'query' uses unsupported allowReserved encoding",
"parameter 'query' has an invalid style",
"security declaration is not an array",
])
})
test("fails closed on prototype-named missing security schemes", () => {
const result = OpenAPI.fromSpec({
baseUrl,
spec: singleOperation({ security: [JSON.parse('{"__proto__":[]}')] }),
})
expect(result.tools).toEqual({})
expect(result.skipped[0]?.reason).toBe("security requirement references missing or malformed scheme: __proto__")
})
test("resolves bearer authentication without exposing it as input", async () => {
const contexts: Array<Parameters<OpenAPI.AuthResolver>[0]> = []
const client = recordingClient(() => json({ ok: true }))
const spec = {
...singleOperation({ operationId: undefined }),
security: [{ bearer: [] }],
components: { securitySchemes: { bearer: { type: "http", scheme: "bearer" } } },
} satisfies Document
const tool = toolAt(
OpenAPI.fromSpec({
baseUrl,
spec,
auth: {
resolve: (context) => {
contexts.push(context)
return Effect.succeed({ type: "bearer", token: "secret" })
},
},
}).tools,
"getTest",
)
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
await Effect.runPromise(tool.run({}).pipe(Effect.provide(client.layer)))
expect(inputTypeScript(tool)).toBe("{}")
expect(client.requests[0]!.headers.authorization).toBe("Bearer secret")
expect(contexts).toEqual([
{
name: "bearer",
definition: { type: "http", scheme: "bearer" },
scopes: [],
operation: {
operationId: undefined,
method: "GET",
path: "/test",
summary: undefined,
description: undefined,
},
},
])
})
test("applies authentication carriers without prototype or collision loss", async () => {
const client = recordingClient(() => json({ ok: true }))
const authenticated = (
security: ReadonlyArray<Record<string, ReadonlyArray<string>>>,
schemes: Record<string, unknown>,
) =>
OpenAPI.fromSpec({
baseUrl,
spec: { ...singleOperation({}), security, components: { securitySchemes: schemes } },
auth: { resolve: () => Effect.succeed({ type: "apiKey", value: "secret" }) },
})
const prototype = toolAt(
authenticated([{ key: [] }], { key: { type: "apiKey", in: "query", name: "__proto__" } }).tools,
"test",
)
if (!Tool.isDefinition(prototype)) throw new Error("prototype auth tool was not generated")
await Effect.runPromise(prototype.run({}).pipe(Effect.provide(client.layer)))
expect(new URL(client.requests[0]!.url).searchParams.get("__proto__")).toBe("secret")
const duplicate = toolAt(
authenticated([{ first: [], second: [] }], {
first: { type: "apiKey", in: "header", name: "x-key" },
second: { type: "apiKey", in: "header", name: "x-key" },
}).tools,
"test",
)
if (!Tool.isDefinition(duplicate)) throw new Error("duplicate auth tool was not generated")
await expect(Effect.runPromise(duplicate.run({}).pipe(Effect.provide(client.layer)))).rejects.toThrow(
"multiple credentials",
)
const cookie = authenticated([{ key: [] }], { key: { type: "apiKey", in: "cookie", name: "session" } })
expect(cookie.tools).toEqual({})
expect(cookie.skipped[0]?.reason).toBe("cookie authentication 'key' is not supported")
const alternative = OpenAPI.fromSpec({
baseUrl,
spec: {
...singleOperation({}),
security: [{ cookie: [] }, { bearer: [] }],
components: {
securitySchemes: {
cookie: { type: "apiKey", in: "cookie", name: "session" },
bearer: { type: "http", scheme: "bearer" },
},
},
},
auth: {
resolve: ({ name }) => Effect.succeed(name === "bearer" ? { type: "bearer", token: "secret" } : undefined),
},
})
const alternativeTool = toolAt(alternative.tools, "test")
if (!Tool.isDefinition(alternativeTool)) throw new Error("supported auth alternative was not generated")
await Effect.runPromise(alternativeTool.run({}).pipe(Effect.provide(client.layer)))
expect(client.requests.at(-1)?.headers.authorization).toBe("Bearer secret")
})
test("honors server precedence and rejects ambiguous base URLs", async () => {
const client = recordingClient(() => json({ ok: true }))
const spec = {
...singleOperation({ servers: [{ url: "https://operation.example/v1" }] }),
servers: [{ url: "https://document.example" }],
} satisfies Document
const tool = toolAt(OpenAPI.fromSpec({ spec }).tools, "test")
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
await Effect.runPromise(tool.run({}).pipe(Effect.provide(client.layer)))
expect(client.requests[0]?.url).toBe("https://operation.example/v1/test")
const invalid = OpenAPI.fromSpec({ spec, baseUrl: "https://example.com/api?tenant=one" })
expect(invalid.tools).toEqual({})
expect(invalid.skipped[0]?.reason).toContain("unsupported query string or fragment")
const malformed = OpenAPI.fromSpec({ spec, baseUrl: "https:/example.com" })
expect(malformed.tools).toEqual({})
expect(malformed.skipped[0]?.reason).toContain("not an absolute HTTP(S) URL")
})
test("resolves chained response refs before detecting unsupported transports", () => {
const result = OpenAPI.fromSpec({
baseUrl,
spec: {
...singleOperation({ responses: { 200: { $ref: "#/components/responses/First" } } }),
components: {
responses: {
First: { $ref: "#/components/responses/Stream" },
Stream: { content: { "text/event-stream": { schema: { type: "string" } } } },
},
},
},
})
expect(result.tools).toEqual({})
expect(result.skipped[0]?.reason).toBe("SSE operations are not supported")
})
test("resolves response schemas before detecting binary output", () => {
const result = OpenAPI.fromSpec({
baseUrl,
spec: {
...singleOperation({
responses: {
200: {
content: { "text/plain": { schema: { $ref: "#/components/schemas/File" } } },
},
},
}),
components: { schemas: { File: { type: "string", format: "binary" } } },
},
})
expect(result.tools).toEqual({})
expect(result.skipped[0]?.reason).toBe("binary responses are not supported")
})
test("validates composite parameters before resolving auth", async () => {
const resolutions: Array<string> = []
const client = recordingClient(() => json({ ok: true }))
const tool = toolAt(
OpenAPI.fromSpec({
baseUrl,
spec: {
...singleOperation({
parameters: [{ name: "filter", in: "query", style: "form", explode: true, schema: { type: "object" } }],
}),
security: [{ bearer: [] }],
components: { securitySchemes: { bearer: { type: "http", scheme: "bearer" } } },
},
auth: {
resolve: ({ name }) => {
resolutions.push(name)
return Effect.succeed({ type: "bearer", token: "secret" })
},
},
}).tools,
"test",
)
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
await expect(
Effect.runPromise(tool.run({ filter: { value: undefined } }).pipe(Effect.provide(client.layer))),
).rejects.toThrow("unsupported nested value")
expect(resolutions).toEqual([])
expect(client.requests).toEqual([])
})
test("preserves JSON media types and rejects unencodable bodies", async () => {
const client = recordingClient(() => json({ ok: true }))
const tool = toolAt(
OpenAPI.fromSpec({
baseUrl,
spec: singleOperation(
{
requestBody: {
required: true,
content: { "application/merge-patch+json": { schema: { type: "object" } } },
},
},
"post",
),
}).tools,
"test",
)
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
await Effect.runPromise(tool.run({ body: { name: "updated" } }).pipe(Effect.provide(client.layer)))
expect(client.requests[0]!.headers["content-type"]).toBe("application/merge-patch+json")
const cyclic: Record<string, unknown> = {}
cyclic.self = cyclic
await expect(Effect.runPromise(tool.run({ body: cyclic }).pipe(Effect.provide(client.layer)))).rejects.toThrow(
"Invalid JSON body",
)
})
test("rejects oversized and malformed JSON responses", async () => {
const tool = toolAt(OpenAPI.fromSpec({ baseUrl, spec: singleOperation({}) }).tools, "test")
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
const oversized = recordingClient(
() => new Response(null, { headers: { "content-length": String(50 * 1024 * 1024 + 1) } }),
)
const malformed = recordingClient(() => new Response("{", { headers: { "content-type": "application/json" } }))
const chunked = recordingClient(() => new Response(new Uint8Array(50 * 1024 * 1024 + 1)))
await expect(Effect.runPromise(tool.run({}).pipe(Effect.provide(oversized.layer)))).rejects.toThrow(
"response exceeds 50 MiB",
)
await expect(Effect.runPromise(tool.run({}).pipe(Effect.provide(malformed.layer)))).rejects.toThrow(
"returned malformed JSON",
)
await expect(Effect.runPromise(tool.run({}).pipe(Effect.provide(chunked.layer)))).rejects.toThrow(
"response exceeds 50 MiB",
)
})
test("keeps non-JSON responses raw and unions every success output", async () => {
const spec = singleOperation({
responses: {
200: { description: "Text", content: { "text/plain": { schema: { type: "string" } } } },
204: { description: "Empty" },
},
})
const tool = toolAt(OpenAPI.fromSpec({ baseUrl, spec }).tools, "test")
if (!Tool.isDefinition(tool)) throw new Error("test was not generated")
const client = recordingClient(() => new Response("123", { headers: { "content-type": "text/plain" } }))
expect(outputTypeScript(tool)).toBe("string | null")
await expect(Effect.runPromise(tool.run({}).pipe(Effect.provide(client.layer)))).resolves.toBe("123")
})
test("fails missing required parameters before auth and network", async () => {
const { requests, layer } = recordingClient(() => json({}))
const runtime = CodeMode.make({
tools: { opencode: OpenAPI.fromSpec({ spec: await opencodeSpec(), baseUrl }).tools },
})
const result = await Effect.runPromise(
runtime.execute("return await tools.opencode.v2.session.get({})").pipe(Effect.provide(layer)),
)
expect(result).toMatchObject({ ok: false })
expect(JSON.stringify(result)).toContain("Missing required path parameter 'sessionID'")
expect(requests).toHaveLength(0)
})
test("prefixes cross-location collisions and reconstructs the HTTP request", async () => {
const spec = {
openapi: "3.1.0",
info: { title: "collision", version: "1.0.0" },
paths: {
"/echo": {
post: {
operationId: "echo",
requestBody: {
required: true,
content: { "application/json": { schema: { type: "string" } } },
},
responses: { "204": { description: "Echoed" } },
},
},
"/things/{id}": {
post: {
operationId: "things.update",
parameters: [
{ name: "id", in: "path", required: true, schema: { type: "string" } },
{ name: "id", in: "query", required: true, schema: { type: "string" } },
{ name: "path_id", in: "query", schema: { type: "string" } },
{ name: "id", in: "header", required: true, schema: { type: "string" } },
],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: { id: { type: "string" } },
required: ["id"],
additionalProperties: false,
},
},
},
},
responses: { "204": { description: "Updated" } },
},
},
},
} satisfies Document
const { requests, layer } = recordingClient(() => new Response(null, { status: 204 }))
const tools = OpenAPI.fromSpec({ spec, baseUrl }).tools
const update = toolAt(tools, "things.update")
const echo = toolAt(tools, "echo")
expect(Tool.isDefinition(update)).toBe(true)
if (!Tool.isDefinition(update)) throw new Error("things.update was not generated")
expect(inputTypeScript(update)).toBe(
"{ path_id: string; query_id: string; path_id_2?: string; header_id: string; body_id: string }",
)
expect(Tool.isDefinition(echo)).toBe(true)
if (!Tool.isDefinition(echo)) throw new Error("echo was not generated")
expect(inputTypeScript(echo)).toBe("{ body: string }")
const runtime = CodeMode.make({ tools })
const result = await Effect.runPromise(
runtime
.execute(
`
const updated = await tools.things.update({ path_id: "path", query_id: "query", path_id_2: "literal", header_id: "header", body_id: "body" })
const echoed = await tools.echo({ body: "hello" })
return { updated, echoed }
`,
)
.pipe(Effect.provide(layer)),
)
expect(result).toMatchObject({ ok: true })
expect(requests).toHaveLength(2)
expect(new URL(requests[0]!.url).pathname).toBe("/things/path")
expect(new URL(requests[0]!.url).searchParams.get("id")).toBe("query")
expect(new URL(requests[0]!.url).searchParams.get("path_id")).toBe("literal")
expect(requests[0]!.headers.id).toBe("header")
expect(requests[0]!.body).toStrictEqual({ id: "body" })
expect(requests[1]!.body).toBe("hello")
})
test("keeps bodies nested when flattening would lose schema semantics", () => {
const body = (schema: Record<string, unknown>, required = true) => ({
required,
content: { "application/json": { schema } },
})
const spec = {
openapi: "3.1.0",
info: { title: "bodies", version: "1.0.0" },
paths: Object.fromEntries(
[
[
"optional",
body(
{
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
additionalProperties: false,
},
false,
),
],
["dictionary", body({ type: "object", additionalProperties: { type: "string" } })],
[
"composed",
body({
type: "object",
allOf: [{ type: "object", properties: { name: { type: "string" } }, required: ["name"] }],
additionalProperties: false,
}),
],
[
"nullable",
body({
type: ["object", "null"],
properties: { name: { type: "string" } },
additionalProperties: false,
}),
],
].map(([name, requestBody]) => [
`/body/${name}`,
{
post: {
operationId: `body.${name}`,
requestBody,
responses: { "204": { description: "Accepted" } },
},
},
]),
),
} satisfies Document
const tools = OpenAPI.fromSpec({ spec, baseUrl }).tools
for (const name of ["optional", "dictionary", "composed", "nullable"]) {
const tool = toolAt(tools, `body.${name}`)
expect(Tool.isDefinition(tool)).toBe(true)
if (!Tool.isDefinition(tool)) throw new Error(`body.${name} was not generated`)
const input = isRecord(tool.input) ? tool.input : {}
expect(Object.keys(isRecord(input.properties) ? input.properties : {})).toStrictEqual(["body"])
}
const optional = toolAt(tools, "body.optional")
if (!Tool.isDefinition(optional)) throw new Error("body.optional was not generated")
expect(inputTypeScript(optional)).toBe("{ body?: { name: string } }")
})
})
+2 -104
View File
@@ -159,8 +159,8 @@ describe("pretty signature rendering", () => {
$ref: "#/$defs/Node",
$defs: { Node: { type: "object", properties: { child: { $ref: "#/$defs/Node" }, name: { type: "string" } } } },
} as const
expect(jsonSchemaToTypeScript(cyclic)).toBe("{ child?: unknown; name?: string }")
expect(jsonSchemaToTypeScript(cyclic, true)).toContain("child?: unknown")
expect(jsonSchemaToTypeScript(cyclic)).toBe("{ child?: Node; name?: string }")
expect(jsonSchemaToTypeScript(cyclic, true)).toContain("child?: Node")
let deep: Record<string, unknown> = { type: "string" }
for (let level = 0; level < 12; level += 1) deep = { type: "object", properties: { next: deep } }
@@ -170,43 +170,6 @@ describe("pretty signature rendering", () => {
expect(rendered).toContain("next?:")
}
})
test("intersects ref and union siblings instead of discarding them", () => {
expect(
jsonSchemaToTypeScript({
$ref: "#/$defs/User",
properties: { active: { type: "boolean" } },
required: ["active"],
$defs: {
User: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
},
}),
).toBe("{ id: string } & { active: boolean }")
expect(
jsonSchemaToTypeScript({
type: "object",
properties: { common: { type: "boolean" } },
required: ["common"],
anyOf: [
{ type: "object", properties: { name: { type: "string" } }, required: ["name"] },
{ type: "object", properties: { count: { type: "number" } }, required: ["count"] },
],
}),
).toBe("({ name: string } | { count: number }) & { common: boolean }")
expect(jsonSchemaToTypeScript({ $ref: "https://example.com/schema.json" })).toBe("unknown")
expect(
jsonSchemaToTypeScript({
$ref: "#/$defs/User/properties/id",
$defs: { User: { type: "object" }, id: { type: "string" } },
}),
).toBe("unknown")
expect(
jsonSchemaToTypeScript({
type: ["object", "null"],
properties: { name: { type: "string" } },
}),
).toBe("{ name?: string } | null")
})
})
describe("non-identifier property names render as quoted keys", () => {
@@ -305,31 +268,6 @@ describe("union schemas render every alternative", () => {
expect(inputTypeScript(tool)).toBe("{ value?: string | number }")
expect(outputTypeScript(tool)).toBe("number | boolean")
})
test("allOf renders intersections with parenthesized union members", () => {
const schema = {
allOf: [{ type: "object", properties: { id: { type: "string" } } }, { type: ["string", "null"] }],
} as const
expect(jsonSchemaToTypeScript(schema)).toBe("{ id?: string } & (string | null)")
})
test("allOf does not discard an unresolved constraint", () => {
expect(jsonSchemaToTypeScript({ allOf: [{ type: "string" }, { $ref: "https://example.com/external.json" }] })).toBe(
"unknown",
)
expect(
jsonSchemaToTypeScript({
allOf: [{ type: "string" }, { allOf: [{ $ref: "https://example.com/external.json" }] }],
}),
).toBe("unknown")
expect(
jsonSchemaToTypeScript({
type: "string",
allOf: [{ $ref: "#/$defs/Constraint" }],
$defs: { Constraint: { description: "TypeScript-neutral constraint" } },
}),
).toBe("string")
})
})
describe("pretty signatures in search results", () => {
@@ -401,43 +339,3 @@ describe("pretty signatures in search results", () => {
expect(instructions).not.toContain("/**")
})
})
describe("non-identifier tool paths", () => {
const resolveLibrary = Tool.make({
description: "Resolve a Context7 library ID",
input: {
type: "object",
properties: {
query: { type: "string" },
libraryName: { type: "string" },
},
required: ["query", "libraryName"],
} as const,
run: () => Effect.succeed("/reactjs/react.dev"),
})
const runtime = CodeMode.make({ tools: { context7: { "resolve-library-id": resolveLibrary } } })
test("inline catalog uses bracket notation for dashed tool names", () => {
const instructions = runtime.instructions()
expect(instructions).toContain(
'tools.context7["resolve-library-id"](input: { query: string; libraryName: string }): Promise<unknown>',
)
expect(instructions).toContain("Do not infer or normalize tool names")
expect(instructions).toContain("bracket notation and quotes are part of the path")
expect(instructions).not.toContain("tools.context7.resolve-library-id")
expect(instructions).not.toContain("tools.context7.resolve_library_id")
})
test("search results return callable bracket-notation paths and signatures", async () => {
const result = await Effect.runPromise(
runtime.execute(`return await tools.$codemode.search({ query: "resolve library" })`),
)
expect(result.ok).toBe(true)
if (!result.ok) throw new Error("search failed")
const value = result.value as { items: Array<{ path: string; signature: string }> }
expect(value.items[0]?.path).toBe('tools.context7["resolve-library-id"]')
expect(value.items[0]?.signature).toContain('tools.context7["resolve-library-id"](input: {')
})
})
+1 -1
View File
@@ -2,6 +2,6 @@ import { redirect } from "@solidjs/router"
export async function GET() {
return redirect(
"https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=52ao9352-5623-4fa0-b7dd-3407c392c1af&qr_code=true",
"https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=738j8655-cd59-4633-a30a-1124e0096789&qr_code=true",
)
}
@@ -1,7 +1,7 @@
import type { Stripe } from "stripe"
import { Billing } from "@opencode-ai/console-core/billing.js"
import type { APIEvent } from "@solidjs/start/server"
import { and, Database, eq, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js"
import { and, Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js"
import { BillingTable, LiteTable, PaymentTable } from "@opencode-ai/console-core/schema/billing.sql.js"
import { Identifier } from "@opencode-ai/console-core/identifier.js"
import { centsToMicroCents } from "@opencode-ai/console-core/util/price.js"
@@ -350,32 +350,21 @@ export async function POST(input: APIEvent) {
.then((rows) => rows[0]),
)
if (!payment) throw new Error("Payment not found")
const amountRefunded = Math.min(centsToMicroCents(body.data.object.amount_refunded), payment.amount)
if (!amountRefunded) throw new Error("Refund amount not found")
await Database.transaction(async (tx) => {
// Payment rows record one refund transition. Claim it atomically so Stripe webhook retries
// cannot deduct the refunded credits more than once.
const claimed = await tx
await tx
.update(PaymentTable)
.set({
timeRefunded: new Date(body.created * 1000),
})
.where(
and(
eq(PaymentTable.paymentID, paymentIntentID),
eq(PaymentTable.workspaceID, workspaceID),
isNull(PaymentTable.timeRefunded),
),
)
if (claimed.rowsAffected === 0) return
.where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID)))
// deduct balance only for top up
if (!payment.enrichment?.type) {
await tx
.update(BillingTable)
.set({
balance: sql`GREATEST(0, ${BillingTable.balance} - ${amountRefunded})`,
balance: sql`${BillingTable.balance} - ${payment.amount}`,
})
.where(eq(BillingTable.workspaceID, workspaceID))
}
+1 -1
View File
@@ -230,7 +230,7 @@ export function hoist<A, E, T extends Tag, const Items extends Replacements = re
if (existing && existing !== node) {
throw new Error(`Tag ${tag} has conflicting implementations for ${node.name}`)
}
hoisted.set(node.name, rewriteReplacementDependencies(node, replacementMap))
hoisted.set(node.name, node)
return group([])
}
if (node.kind === "unbound") {
-10
View File
@@ -38,7 +38,6 @@ export namespace FSUtil {
readonly ensureDir: (path: string) => Effect.Effect<void, Error>
readonly writeWithDirs: (path: string, content: string | Uint8Array, mode?: number) => Effect.Effect<void, Error>
readonly readDirectoryEntries: (path: string) => Effect.Effect<DirEntry[], Error>
readonly resolve: (path: string) => Effect.Effect<string>
readonly findUp: (target: string, start: string, stop?: string) => Effect.Effect<string[], Error>
readonly up: (options: { targets: string[]; start: string; stop?: string }) => Effect.Effect<string[], Error>
readonly globUp: (pattern: string, start: string, stop?: string) => Effect.Effect<string[], Error>
@@ -90,14 +89,6 @@ export namespace FSUtil {
})
})
const resolve = Effect.fn("FileSystem.resolve")(function* (path: string) {
const resolved = pathResolve(windowsPath(path))
return yield* fs.realPath(resolved).pipe(
Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(resolved)),
Effect.orDie,
)
})
const readJson = Effect.fn("FileSystem.readJson")(function* (path: string) {
const text = yield* fs.readFileString(path)
return yield* Effect.try({
@@ -196,7 +187,6 @@ export namespace FSUtil {
isDir,
isFile,
readDirectoryEntries,
resolve,
readJson,
writeJson,
ensureDir,
+11 -13
View File
@@ -38,24 +38,22 @@ const layer = Layer.effectDiscard(
})
const observe = Effect.fn("InstructionContext.observe")(function* () {
const start = yield* fs.resolve(location.directory)
const stop = yield* fs.resolve(location.project.directory)
const start = FSUtil.resolve(location.directory)
const stop = FSUtil.resolve(location.project.directory)
const fromProject = relative(stop, start)
const insideProject =
fromProject === "" || (fromProject !== ".." && !fromProject.startsWith(`..${sep}`) && !isAbsolute(fromProject))
const discovered = new Set(
yield* Effect.forEach(
Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
? []
: yield* fs.up({
targets: ["AGENTS.md"],
start,
stop,
}),
fs.resolve,
),
(Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
? []
: yield* fs.up({
targets: ["AGENTS.md"],
start,
stop,
})
).map(FSUtil.resolve),
)
const paths = Array.dedupe([yield* fs.resolve(join(global.config, "AGENTS.md")), ...discovered])
const paths = Array.dedupe([FSUtil.resolve(join(global.config, "AGENTS.md")), ...discovered])
const files = yield* Effect.forEach(
paths,
(path) =>
-4
View File
@@ -89,10 +89,6 @@ export function buildLocationServiceMap(
LayerMap.make(
(ref: Location.Ref) => {
const allReplacements = replacements.concat([[Location.node, Location.boundNode(ref)]])
// Apply replacements during hoist, not afterward: replacements can
// introduce new tagged dependencies (Location.boundNode depends on
// Project), and the hoist walk is the only pass that can still slice
// those back out.
const location = LayerNode.hoist(locationServices, Node.tags.values.global, allReplacements)
return LayerNode.compile(location.node).pipe(
+9 -10
View File
@@ -57,13 +57,13 @@ export type AskResult = typeof AskResult.Type
export const Event = Permission.Event
export class DeclinedError extends Schema.TaggedErrorClass<DeclinedError>()("PermissionV2.DeclinedError", {}) {}
export class RejectedError extends Schema.TaggedErrorClass<RejectedError>()("PermissionV2.RejectedError", {}) {}
export class CorrectedError extends Schema.TaggedErrorClass<CorrectedError>()("PermissionV2.CorrectedError", {
feedback: Schema.String,
}) {}
export class BlockedError extends Schema.TaggedErrorClass<BlockedError>()("PermissionV2.BlockedError", {
export class DeniedError extends Schema.TaggedErrorClass<DeniedError>()("PermissionV2.DeniedError", {
rules: Permission.Ruleset,
}) {}
@@ -71,7 +71,7 @@ export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Per
requestID: ID,
}) {}
export type Error = BlockedError | CorrectedError
export type Error = DeniedError | RejectedError | CorrectedError
export function evaluate(action: string, resource: string, ...rulesets: Permission.Ruleset[]): Permission.Rule {
return (
@@ -103,7 +103,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
interface Pending {
readonly request: Request
readonly agent?: AgentV2.ID
readonly deferred: Deferred.Deferred<void, DeclinedError | CorrectedError>
readonly deferred: Deferred.Deferred<void, RejectedError | CorrectedError>
}
const layer = Layer.effect(
@@ -117,7 +117,7 @@ const layer = Layer.effect(
const pending = new Map<ID, Pending>()
yield* EffectRuntime.addFinalizer(() =>
EffectRuntime.forEach(pending.values(), (item) => Deferred.fail(item.deferred, new DeclinedError()), {
EffectRuntime.forEach(pending.values(), (item) => Deferred.fail(item.deferred, new RejectedError()), {
discard: true,
}).pipe(
EffectRuntime.ensuring(
@@ -176,7 +176,7 @@ const layer = Layer.effect(
const create = (request: Request, agent?: AgentV2.ID) =>
EffectRuntime.uninterruptible(
EffectRuntime.gen(function* () {
const deferred = yield* Deferred.make<void, DeclinedError | CorrectedError>()
const deferred = yield* Deferred.make<void, RejectedError | CorrectedError>()
const item = { request, agent, deferred }
if (pending.has(request.id)) return yield* EffectRuntime.die(`Duplicate pending permission ID: ${request.id}`)
pending.set(request.id, item)
@@ -199,14 +199,13 @@ const layer = Layer.effect(
EffectRuntime.gen(function* () {
const result = yield* evaluateInput(input)
if (result.effect === "deny") {
return yield* new BlockedError({
return yield* new DeniedError({
rules: relevant(input, result.rules),
})
}
if (result.effect === "allow") return
const item = yield* create(request(input), input.agent)
return yield* restore(Deferred.await(item.deferred)).pipe(
EffectRuntime.catchTag("PermissionV2.DeclinedError", (error) => EffectRuntime.die(error)),
EffectRuntime.ensuring(
EffectRuntime.sync(() => {
pending.delete(item.request.id)
@@ -231,7 +230,7 @@ const layer = Layer.effect(
if (input.reply === "reject") {
yield* Deferred.fail(
existing.deferred,
input.message ? new CorrectedError({ feedback: input.message }) : new DeclinedError(),
input.message ? new CorrectedError({ feedback: input.message }) : new RejectedError(),
)
pending.delete(input.requestID)
for (const [id, item] of pending) {
@@ -241,7 +240,7 @@ const layer = Layer.effect(
requestID: item.request.id,
reply: "reject",
})
yield* Deferred.fail(item.deferred, new DeclinedError())
yield* Deferred.fail(item.deferred, new RejectedError())
pending.delete(id)
}
return
+1 -1
View File
@@ -139,7 +139,7 @@ const layer = Layer.effect(
})
const canonical = Effect.fnUntraced(function* (input: AbsolutePath) {
const resolved = AbsolutePath.make(yield* fs.resolve(input))
const resolved = AbsolutePath.make(FSUtil.resolve(input))
if (!(yield* fs.isDir(resolved))) return yield* new DirectoryUnavailableError({ directory: input })
return resolved
})
+25 -13
View File
@@ -15,27 +15,39 @@ const TOOL_OUTPUT_MAX_CHARS = 2_000
const SUMMARY_OUTPUT_TOKENS = 4_096
const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response.
<template>
## Objective
- [one or two brief sentences describing what the user is trying to accomplish]
## Goal
- [single-sentence task summary]
## Important Details
- [constraints/preferences, decisions and why, important facts/assumptions, exact context needed to continue, or "(none)"]
## Constraints & Preferences
- [user constraints, preferences, specs, or "(none)"]
## Work State
- Completed: [finished work, verified facts, or changes made; otherwise "(none)"]
- Active: [current work, partial changes, or investigation state; otherwise "(none)"]
- Blocked: [blockers, failing commands, or unknowns; otherwise "(none)"]
## Progress
### Done
- [completed work or "(none)"]
## Next Move
1. [immediate concrete action, or "(none)"]
2. [next action if known, or "(none)"]
### In Progress
- [current work or "(none)"]
### Blocked
- [blockers or "(none)"]
## Key Decisions
- [decision and why, or "(none)"]
## Next Steps
- [ordered next actions or "(none)"]
## Critical Context
- [important technical facts, errors, open questions, or "(none)"]
## Relevant Files
- [file or directory path: why it matters, or "(none)"]
</template>
Rules:
- Keep every section, even when empty.
- Use terse bullets, not prose paragraphs.
- Preserve exact file paths, symbols, commands, error strings, URLs, and identifiers when known.
- Put relevant files and symbols inside the section where they matter; do not add extra sections.
- Preserve exact file paths, commands, error strings, and identifiers when known.
- Do not mention the summary process or that context was compacted.`
type Entry = {
+4 -9
View File
@@ -15,7 +15,6 @@ import { Database } from "../../database/database"
import { EventV2 } from "../../event"
import { Location } from "../../location"
import { ModelV2 } from "../../model"
import { PermissionV2 } from "../../permission"
import { ProviderV2 } from "../../provider"
import { QuestionV2 } from "../../question"
import { SystemContext } from "../../system-context/index"
@@ -141,13 +140,9 @@ const layer = Layer.effect(
const awaitToolFibers = (fibers: FiberSet.FiberSet<void, ToolOutputStore.Error>) =>
Effect.raceFirst(FiberSet.join(fibers), FiberSet.awaitEmpty(fibers))
// Match V1: declining a user prompt halts the loop instead of becoming model-facing tool output.
const isUserDeclined = (cause: Cause.Cause<unknown>) =>
cause.reasons.some(
(reason) =>
Cause.isDieReason(reason) &&
(reason.defect instanceof PermissionV2.DeclinedError || reason.defect instanceof QuestionV2.RejectedError),
)
// Match V1: dismissing a question halts the loop instead of becoming model-facing tool output.
const isQuestionRejected = (cause: Cause.Cause<unknown>) =>
cause.reasons.some((reason) => Cause.isDieReason(reason) && reason.defect instanceof QuestionV2.RejectedError)
type TurnTransition =
// Automatic compaction completed; rebuild the request from compacted history.
@@ -294,7 +289,7 @@ const layer = Layer.effect(
}
if (stream._tag === "Failure" && Cause.hasInterrupts(stream.cause)) yield* FiberSet.clear(toolFibers)
const settled = yield* restore(awaitToolFibers(toolFibers)).pipe(Effect.exit)
if (settled._tag === "Failure" && isUserDeclined(settled.cause)) {
if (settled._tag === "Failure" && isQuestionRejected(settled.cause)) {
yield* FiberSet.clear(toolFibers)
yield* withPublication(publisher.failUnsettledTools("Tool execution interrupted"))
return yield* Effect.interrupt
+5 -9
View File
@@ -78,21 +78,17 @@ const isTimeout = (error: AppProcess.AppProcessError) =>
const shellTokens = (command: string) => command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? []
const unquote = (value: string) => value.replace(/^(['"])(.*)\1$/, "$2")
const externalCommandDirectories = Effect.fn("BashTool.externalCommandDirectories")(function* (
fs: FSUtil.Interface,
command: string,
cwd: string,
) {
const externalCommandDirectories = (command: string, cwd: string) => {
const directories = new Set<string>()
for (const token of shellTokens(command)) {
const value = unquote(token).replace(/[;,|&]+$/, "")
if (!path.isAbsolute(value)) continue
const resolved = yield* fs.resolve(value)
const resolved = FSUtil.resolve(value)
if (FSUtil.contains(cwd, resolved)) continue
directories.add(yield* fs.resolve(path.dirname(resolved)))
directories.add(FSUtil.resolve(path.dirname(resolved)))
}
return [...directories]
})
}
const layer = Layer.effectDiscard(
Effect.gen(function* () {
@@ -135,7 +131,7 @@ const layer = Layer.effectDiscard(
agent: context.agent,
source,
})
const warnings = (yield* externalCommandDirectories(fs, input.command, target.canonical)).map(
const warnings = externalCommandDirectories(input.command, target.canonical).map(
(directory) =>
`Command argument references external directory ${path.join(directory, "*").replaceAll("\\", "/")}. Bash runs with host-user filesystem, process, and network authority; this scan is advisory only.`,
)
+3 -21
View File
@@ -1,5 +1,5 @@
import { describe, expect } from "bun:test"
import { Cause, Deferred, Effect, Fiber, Layer } from "effect"
import { Deferred, Effect, Fiber, Layer } from "effect"
import { AgentV2 } from "@opencode-ai/core/agent"
import { Database } from "@opencode-ai/core/database/database"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
@@ -147,8 +147,8 @@ describe("PermissionV2", () => {
const service = yield* PermissionV2.Service
yield* service.assert(assertion())
yield* setRules([{ action: "read", resource: "*", effect: "deny" }])
const blocked = yield* service.assert(assertion()).pipe(Effect.flip)
expect(blocked).toBeInstanceOf(PermissionV2.BlockedError)
const denied = yield* service.assert(assertion()).pipe(Effect.flip)
expect(denied).toBeInstanceOf(PermissionV2.DeniedError)
expect(yield* service.list()).toEqual([])
}),
)
@@ -265,24 +265,6 @@ describe("PermissionV2", () => {
}),
)
it.effect("defects when an asked permission is declined", () =>
Effect.gen(function* () {
yield* setup()
const { service, fiber, request } = yield* waitForRequest()
yield* service.reply({ requestID: request.id, reply: "reject" })
const exit = yield* Fiber.await(fiber)
expect(exit._tag).toBe("Failure")
if (exit._tag === "Failure")
expect(
exit.cause.reasons.some(
(reason) => Cause.isDieReason(reason) && reason.defect instanceof PermissionV2.DeclinedError,
),
).toBe(true)
expect(yield* service.list()).toEqual([])
}),
)
it.effect("stores and removes saved resources for a project", () =>
Effect.gen(function* () {
yield* setup()
+15 -157
View File
@@ -1097,7 +1097,7 @@ describe("SessionRunnerLLM", () => {
currentModel = compactModel
requests.length = 0
responses = [
fragmentFixture("text", "text-summary", ["## Objective\n- Preserve the task"]).completeEvents,
fragmentFixture("text", "text-summary", ["## Goal\n- Preserve the task"]).completeEvents,
fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
]
yield* session.prompt({
@@ -1108,22 +1108,22 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
expect(requests).toHaveLength(2)
expect(userTexts(requests[0])[0]).toContain("## Objective")
expect(userTexts(requests[0])[0]).toContain("## Goal")
expect(userTexts(requests[1])).toHaveLength(1)
expect(userTexts(requests[1])[0]).toContain("<summary>\n## Objective\n- Preserve the task\n</summary>")
expect(userTexts(requests[1])[0]).toContain("<summary>\n## Goal\n- Preserve the task\n</summary>")
expect(userTexts(requests[1])[0]).toContain(`[User]: ${"Recent exact request ".repeat(180)}`)
const context = yield* (yield* SessionStore.Service).context(sessionID)
expect(context.map((message) => message.type)).toEqual(["compaction", "assistant"])
expect(context[0]).toMatchObject({
type: "compaction",
summary: "## Objective\n- Preserve the task",
summary: "## Goal\n- Preserve the task",
})
requests.length = 0
executions.length = 0
responses = [
fragmentFixture("text", "text-summary-2", ["## Objective\n- Preserve the updated task"]).completeEvents,
fragmentFixture("text", "text-summary-2", ["## Goal\n- Preserve the updated task"]).completeEvents,
fragmentFixture("text", "text-final-2", ["Continued again"]).completeEvents,
]
yield* session.prompt({
@@ -1135,12 +1135,12 @@ describe("SessionRunnerLLM", () => {
expect(requests).toHaveLength(2)
expect(userTexts(requests[0])[0]).toContain(
"<previous-summary>\n## Objective\n- Preserve the task\n</previous-summary>",
"<previous-summary>\n## Goal\n- Preserve the task\n</previous-summary>",
)
expect(userTexts(requests[0])[0]).toContain("Recent exact request")
expect((yield* (yield* SessionStore.Service).context(sessionID))[0]).toMatchObject({
type: "compaction",
summary: "## Objective\n- Preserve the updated task",
summary: "## Goal\n- Preserve the updated task",
})
}),
)
@@ -1153,17 +1153,17 @@ describe("SessionRunnerLLM", () => {
LLMEvent.stepStart({ index: 0 }),
LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" }),
],
fragmentFixture("text", "text-summary", ["## Objective\n- Recover overflow"]).completeEvents,
fragmentFixture("text", "text-summary", ["## Goal\n- Recover overflow"]).completeEvents,
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
]
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(3)
expect(userTexts(requests[1])[0]).toContain("## Objective")
expect(userTexts(requests[2])[0]).toContain("<summary>\n## Objective\n- Recover overflow\n</summary>")
expect(userTexts(requests[1])[0]).toContain("## Goal")
expect(userTexts(requests[2])[0]).toContain("<summary>\n## Goal\n- Recover overflow\n</summary>")
expect(yield* session.context(sessionID)).toMatchObject([
{ type: "compaction", summary: "## Objective\n- Recover overflow" },
{ type: "compaction", summary: "## Goal\n- Recover overflow" },
{ type: "assistant", finish: "stop" },
])
yield* replaySessionProjection(sessionID)
@@ -1183,7 +1183,7 @@ describe("SessionRunnerLLM", () => {
]
responses = [
overflow(),
fragmentFixture("text", "text-summary", ["## Objective\n- Recover once"]).completeEvents,
fragmentFixture("text", "text-summary", ["## Goal\n- Recover once"]).completeEvents,
overflow(),
]
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
@@ -1211,7 +1211,7 @@ describe("SessionRunnerLLM", () => {
}),
)
responses = [
fragmentFixture("text", "text-summary", ["## Objective\n- Recover raw overflow"]).completeEvents,
fragmentFixture("text", "text-summary", ["## Goal\n- Recover raw overflow"]).completeEvents,
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
]
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
@@ -1219,7 +1219,7 @@ describe("SessionRunnerLLM", () => {
expect(requests).toHaveLength(3)
expect(yield* session.context(sessionID)).toMatchObject([
{ type: "compaction", summary: "## Objective\n- Recover raw overflow" },
{ type: "compaction", summary: "## Goal\n- Recover raw overflow" },
{ type: "assistant", finish: "stop" },
])
}),
@@ -1250,7 +1250,7 @@ describe("SessionRunnerLLM", () => {
const session = yield* setupOverflowRecovery
responses = [
[LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" })],
fragmentFixture("text", "text-summary", ["## Objective\n- Interrupted"]).completeEvents,
fragmentFixture("text", "text-summary", ["## Goal\n- Interrupted"]).completeEvents,
]
const firstGate = yield* Deferred.make<void>()
const summaryGate = yield* Deferred.make<void>()
@@ -2609,148 +2609,6 @@ describe("SessionRunnerLLM", () => {
}),
)
it.effect("returns policy-blocked tools to the model and continues", () =>
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
const registry = yield* ToolRegistry.Service
yield* registry.register({
blocked: Tool.make({
description: "Fail because policy blocked execution",
input: Schema.Struct({}),
output: Schema.Struct({}),
execute: () =>
Effect.fail(new PermissionV2.BlockedError({ rules: [] })).pipe(
Effect.mapError(() => new Tool.Failure({ message: "Permission blocked" })),
),
}),
})
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call blocked" }), resume: false })
requests.length = 0
responses = [
[
LLMEvent.stepStart({ index: 0 }),
LLMEvent.toolCall({ id: "call-blocked", name: "blocked", input: {} }),
LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
LLMEvent.finish({ reason: "tool-calls" }),
],
[
LLMEvent.stepStart({ index: 0 }),
LLMEvent.stepFinish({ index: 0, reason: "stop" }),
LLMEvent.finish({ reason: "stop" }),
],
]
yield* session.resume(sessionID)
expect(requests).toHaveLength(2)
expect(yield* session.context(sessionID)).toMatchObject([
{ type: "user", text: "Call blocked" },
{
type: "assistant",
content: [
{ type: "tool", id: "call-blocked", state: { status: "error", error: { message: "Permission blocked" } } },
],
},
{ type: "assistant", finish: "stop" },
])
}),
)
it.effect("interrupts runner continuation when permission approval is declined", () =>
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
const registry = yield* ToolRegistry.Service
yield* registry.register({
declined: Tool.make({
description: "Fail because the user declined approval",
input: Schema.Struct({}),
output: Schema.Struct({}),
execute: () => Effect.die(new PermissionV2.DeclinedError()),
}),
})
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call declined" }), resume: false })
requests.length = 0
response = [
LLMEvent.stepStart({ index: 0 }),
LLMEvent.toolCall({ id: "call-declined", name: "declined", input: {} }),
LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
LLMEvent.finish({ reason: "tool-calls" }),
]
const exit = yield* session.resume(sessionID).pipe(Effect.exit)
expect(exit._tag).toBe("Failure")
if (exit._tag === "Failure") expect(Cause.hasInterruptsOnly(exit.cause)).toBe(true)
expect(requests).toHaveLength(1)
expect(yield* session.context(sessionID)).toMatchObject([
{ type: "user", text: "Call declined" },
{
type: "assistant",
content: [
{
type: "tool",
id: "call-declined",
state: { status: "error", error: { message: "Tool execution interrupted" } },
},
],
},
])
}),
)
it.effect("returns permission corrections to the model and continues", () =>
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
const registry = yield* ToolRegistry.Service
yield* registry.register({
corrected: Tool.make({
description: "Fail with user correction feedback",
input: Schema.Struct({}),
output: Schema.Struct({}),
execute: () =>
Effect.fail(new PermissionV2.CorrectedError({ feedback: "Use another tool" })).pipe(
Effect.mapError(() => new Tool.Failure({ message: "Use another tool" })),
),
}),
})
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call corrected" }), resume: false })
requests.length = 0
responses = [
[
LLMEvent.stepStart({ index: 0 }),
LLMEvent.toolCall({ id: "call-corrected", name: "corrected", input: {} }),
LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
LLMEvent.finish({ reason: "tool-calls" }),
],
[
LLMEvent.stepStart({ index: 0 }),
LLMEvent.stepFinish({ index: 0, reason: "stop" }),
LLMEvent.finish({ reason: "stop" }),
],
]
yield* session.resume(sessionID)
expect(requests).toHaveLength(2)
expect(yield* session.context(sessionID)).toMatchObject([
{ type: "user", text: "Call corrected" },
{
type: "assistant",
content: [
{ type: "tool", id: "call-corrected", state: { status: "error", error: { message: "Use another tool" } } },
],
},
{ type: "assistant", finish: "stop" },
])
}),
)
it.effect("interrupts runner continuation when a question is dismissed", () =>
Effect.gen(function* () {
yield* setup
+1 -1
View File
@@ -40,7 +40,7 @@ const permission = Layer.succeed(
}).pipe(
Effect.andThen(input.action === "edit" ? Effect.suspend(afterEditApproval) : Effect.void),
Effect.andThen(
input.action === denyAction ? Effect.fail(new PermissionV2.BlockedError({ rules: [] })) : Effect.void,
input.action === denyAction ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void,
),
),
ask: () => Effect.die("unused"),
+1 -1
View File
@@ -51,7 +51,7 @@ const permission = Layer.succeed(
Effect.sync(() => assertions.push(input)).pipe(
Effect.andThen(Effect.suspend(() => afterPermission(input))),
Effect.andThen(
input.action === denyAction ? Effect.fail(new PermissionV2.BlockedError({ rules: [] })) : Effect.void,
input.action === denyAction ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void,
),
),
ask: () => Effect.die("unused"),
+1 -1
View File
@@ -33,7 +33,7 @@ const permission = Layer.succeed(
assert: (input) =>
Effect.sync(() => assertions.push(input)).pipe(
Effect.andThen(
input.action === denyAction ? Effect.fail(new PermissionV2.BlockedError({ rules: [] })) : Effect.void,
input.action === denyAction ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void,
),
),
ask: () => Effect.die("unused"),
+1 -1
View File
@@ -22,7 +22,7 @@ const permission = Layer.succeed(
PermissionV2.Service.of({
assert: (input) =>
Effect.sync(() => assertions.push(input)).pipe(
Effect.andThen(deny ? Effect.fail(new PermissionV2.BlockedError({ rules: [] })) : Effect.void),
Effect.andThen(deny ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void),
),
ask: () => Effect.die("unused"),
reply: () => Effect.die("unused"),
+1 -1
View File
@@ -64,7 +64,7 @@ const permission = Layer.succeed(
assert: (input) =>
Effect.sync(() => {
assertions.push(input)
}).pipe(Effect.andThen(allow ? Effect.void : Effect.fail(new PermissionV2.BlockedError({ rules: [] })))),
}).pipe(Effect.andThen(allow ? Effect.void : Effect.fail(new PermissionV2.DeniedError({ rules: [] })))),
ask: () => Effect.die("unused"),
reply: () => Effect.die("unused"),
get: () => Effect.die("unused"),
+1 -1
View File
@@ -47,7 +47,7 @@ describe("SkillTool", () => {
PermissionV2.Service.of({
assert: (input) =>
Effect.sync(() => assertions.push(input)).pipe(
Effect.andThen(deny ? Effect.fail(new PermissionV2.BlockedError({ rules: [] })) : Effect.void),
Effect.andThen(deny ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void),
),
ask: () => Effect.die("unused"),
reply: () => Effect.die("unused"),
+1 -1
View File
@@ -26,7 +26,7 @@ const permission = Layer.succeed(
PermissionV2.Service.of({
assert: (input) =>
Effect.sync(() => assertions.push(input)).pipe(
Effect.andThen(deny ? Effect.fail(new PermissionV2.BlockedError({ rules: [] })) : Effect.void),
Effect.andThen(deny ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void),
),
ask: () => Effect.die("unused"),
reply: () => Effect.die("unused"),

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