diff --git a/apps/ade-cli/package-lock.json b/apps/ade-cli/package-lock.json index a45c86c48..81e71c474 100644 --- a/apps/ade-cli/package-lock.json +++ b/apps/ade-cli/package-lock.json @@ -8,10 +8,12 @@ "name": "ade-cli", "version": "0.0.0", "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.139", + "@anthropic-ai/claude-agent-sdk": "^0.3.170", + "@anthropic-ai/sdk": "^0.103.0", "@cursor/sdk": "^1.0.13", "@factory/droid-sdk": "^0.2.0", "@linear/sdk": "^84.0.0", + "@modelcontextprotocol/sdk": "^1.29.0", "@openai/codex": "0.133.0", "@opencode-ai/sdk": "^1.15.5", "@wize-logic/nodejs-rfb": "^4.2.0", @@ -90,35 +92,33 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.139.tgz", - "integrity": "sha512-9zmitYoxCQiQZsTUbm9IGC6VyZt70J3NLtkRQPQvFVfz7bKDrhlZZKzXmyl2XmqedXEIeQy2ACmwdjwzPIVIAw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.170.tgz", + "integrity": "sha512-pAvhfk+iTodXZ6RF18Kz7BEUWFjL7EcR3tKuhUNdPpE1NAYCR3mSHGbafi72JsrNwKEDIs7FU31z3fqhwy8QzA==", "license": "SEE LICENSE IN README.md", - "dependencies": { - "@anthropic-ai/sdk": "^0.81.0", - "@modelcontextprotocol/sdk": "^1.29.0" - }, "engines": { "node": ">=18.0.0" }, "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.139", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.139" + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.170", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.170" }, "peerDependencies": { + "@anthropic-ai/sdk": ">=0.93.0", + "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.139.tgz", - "integrity": "sha512-dnuO2E0x6o9GAk9iZZKlEd10h+0PQFdTfr5aQU4I0W+0ReKsFEoE9LAqfomS2EvLUQ9L62X0+n0iyZQmAVi1kw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.170.tgz", + "integrity": "sha512-rwfgArIa5WI0QPNqFsRBgvtSI0mrtpynUm0oK6+l6/KX4hcgnYGEzciZR1bOeD9/7sSZlTdIgt+T9alKeZmXcg==", "cpu": [ "arm64" ], @@ -129,9 +129,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.139.tgz", - "integrity": "sha512-SXyldBIwpMHDXppPGObXZ1wjSSWf/YPgD6vK4nssIXarC/DtMRnAQ419Hb3q5MaBB29vSjOPKmG0MOkMltFR/A==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.170.tgz", + "integrity": "sha512-0e58h8UQMtsQxLGIv9r4foxfBFWKZ7NeDtoplLhuD7EwQonehomw1sBXCch77t/IfUS+q5vQ5zv+fOGmap5nLQ==", "cpu": [ "x64" ], @@ -142,12 +142,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.139.tgz", - "integrity": "sha512-qfnQ4SjEcq//iGAJkk25J6j4Tq+dvQe9wHks0dcaSdGOs2D96Teqrb358YJe+nke2DBKVUa9Y4ComW3aUBM29w==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.170.tgz", + "integrity": "sha512-gLbaFqcGppFJQd4DLNV4IXoeahejT/p2/M8bSSvRDbla9GOsBr1AxV5XLRyBn1e7xFGozZIAIQr3+1chp7NJgQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -155,12 +158,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.139.tgz", - "integrity": "sha512-gzMfit9t7Fiy5taZ+miAaP8ZmOMc+hv8Ov3UOXGwJunK6H+0F88ctBSnolDPMPQaS6s2WoMD0o8fhUbBudtMVw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.170.tgz", + "integrity": "sha512-SRYfQcsXlOq+CD/FqkQBTSHbaD++w73GnnO+NUV9adLYrca3kfetRwWT1iguY1cNS0l34dCR3rlzCPq78vg1Jg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -168,12 +174,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.139.tgz", - "integrity": "sha512-2Gqy5hV/MyObbwSyNhj5ha2cY5EZnUfDLvpEwR1eeOaU1yqnxzsdNzXWgHIyWQGKGNE2ICwgLYtt6AtOJGWpPg==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.170.tgz", + "integrity": "sha512-Xl/m7TaSC3T5IDBdHrZQ9fCQYyDmPELN34CL+MoyPIf7uSmuZnjE9fUOqDh2Rv26JxWssi1M6X+BBvVuKd6Cpg==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -181,12 +190,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.139.tgz", - "integrity": "sha512-Fg/aQs1vdyqLrNXqGa1i7/ODpGxP6ud/K/2AgVarLteg2Z3ZnrHPvPQ6iQmTGI8+BhxAZ141t4Dg0CWz3CoqCQ==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.170.tgz", + "integrity": "sha512-m4+I0qBEk7cxRKS+pL+eoWXbXTFOAo83fQ0tQvap4z/mDMm06IWJtEPoYTaMBwsp32GJWLkHWKbZSBCHZnp2DQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -194,9 +206,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.139.tgz", - "integrity": "sha512-HusAU/gSQ0G0AHU+Hj/ps0Tl5JaUF2nxkp+G42tU6hpnwLMOQMdLx/yqvSQnz4WSxggxiDFmYvMDLYAmuE9Qdg==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.170.tgz", + "integrity": "sha512-IG+8isJNNJKbnnhO7m+PGhfVCg+XoQ/MDxGde5eigFI0WsEfitjuWSWwx82bT9ghxI1aa6qNvI+UPgPcZuo5Fg==", "cpu": [ "arm64" ], @@ -207,9 +219,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.139.tgz", - "integrity": "sha512-eJjbtLvEBJcTrl4WJhmhP7FYdTVvx/XtioifH7OEnCoxQozMHhOmA0X90csplIRpttX+jX2PqnE5j2FwU20eCw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.170.tgz", + "integrity": "sha512-7cuqSKbHVItPGVwRbd3A0BEJwcNtc7Fhoh6qHN4C6yrmjSrvdYYx3MLvq/VI768/RoG7mAMDxb+j7WfEfoP9BA==", "cpu": [ "x64" ], @@ -220,12 +232,13 @@ ] }, "node_modules/@anthropic-ai/sdk": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.81.0.tgz", - "integrity": "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw==", + "version": "0.103.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.103.0.tgz", + "integrity": "sha512-1uG7RNgoHTUxzOXqSCODKt0UTVlxWiHk/2Tt2/uQJiPW7XzBeKVuJyd3Aw6T3LPyvZV/jDTnPLX7SaM70WLLjA==", "license": "MIT", "dependencies": { - "json-schema-to-ts": "^3.1.1" + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" }, "bin": { "anthropic-ai-sdk": "bin/cli" @@ -240,9 +253,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1459,6 +1472,12 @@ "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", "dev": true }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, "node_modules/@statsig/client-core": { "version": "3.31.0", "resolved": "https://registry.npmjs.org/@statsig/client-core/-/client-core-3.31.0.tgz", @@ -2847,6 +2866,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fast-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", @@ -3123,9 +3148,9 @@ "optional": true }, "node_modules/graphql": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.0.tgz", - "integrity": "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==", + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", + "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", "license": "MIT", "peer": true, "engines": { @@ -5345,6 +5370,16 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -6680,82 +6715,81 @@ } }, "@anthropic-ai/claude-agent-sdk": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.139.tgz", - "integrity": "sha512-9zmitYoxCQiQZsTUbm9IGC6VyZt70J3NLtkRQPQvFVfz7bKDrhlZZKzXmyl2XmqedXEIeQy2ACmwdjwzPIVIAw==", - "requires": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.139", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.139", - "@anthropic-ai/sdk": "^0.81.0", - "@modelcontextprotocol/sdk": "^1.29.0" + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.170.tgz", + "integrity": "sha512-pAvhfk+iTodXZ6RF18Kz7BEUWFjL7EcR3tKuhUNdPpE1NAYCR3mSHGbafi72JsrNwKEDIs7FU31z3fqhwy8QzA==", + "requires": { + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.170", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.170" } }, "@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.139.tgz", - "integrity": "sha512-dnuO2E0x6o9GAk9iZZKlEd10h+0PQFdTfr5aQU4I0W+0ReKsFEoE9LAqfomS2EvLUQ9L62X0+n0iyZQmAVi1kw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.170.tgz", + "integrity": "sha512-rwfgArIa5WI0QPNqFsRBgvtSI0mrtpynUm0oK6+l6/KX4hcgnYGEzciZR1bOeD9/7sSZlTdIgt+T9alKeZmXcg==", "optional": true }, "@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.139.tgz", - "integrity": "sha512-SXyldBIwpMHDXppPGObXZ1wjSSWf/YPgD6vK4nssIXarC/DtMRnAQ419Hb3q5MaBB29vSjOPKmG0MOkMltFR/A==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.170.tgz", + "integrity": "sha512-0e58h8UQMtsQxLGIv9r4foxfBFWKZ7NeDtoplLhuD7EwQonehomw1sBXCch77t/IfUS+q5vQ5zv+fOGmap5nLQ==", "optional": true }, "@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.139.tgz", - "integrity": "sha512-qfnQ4SjEcq//iGAJkk25J6j4Tq+dvQe9wHks0dcaSdGOs2D96Teqrb358YJe+nke2DBKVUa9Y4ComW3aUBM29w==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.170.tgz", + "integrity": "sha512-gLbaFqcGppFJQd4DLNV4IXoeahejT/p2/M8bSSvRDbla9GOsBr1AxV5XLRyBn1e7xFGozZIAIQr3+1chp7NJgQ==", "optional": true }, "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.139.tgz", - "integrity": "sha512-gzMfit9t7Fiy5taZ+miAaP8ZmOMc+hv8Ov3UOXGwJunK6H+0F88ctBSnolDPMPQaS6s2WoMD0o8fhUbBudtMVw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.170.tgz", + "integrity": "sha512-SRYfQcsXlOq+CD/FqkQBTSHbaD++w73GnnO+NUV9adLYrca3kfetRwWT1iguY1cNS0l34dCR3rlzCPq78vg1Jg==", "optional": true }, "@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.139.tgz", - "integrity": "sha512-2Gqy5hV/MyObbwSyNhj5ha2cY5EZnUfDLvpEwR1eeOaU1yqnxzsdNzXWgHIyWQGKGNE2ICwgLYtt6AtOJGWpPg==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.170.tgz", + "integrity": "sha512-Xl/m7TaSC3T5IDBdHrZQ9fCQYyDmPELN34CL+MoyPIf7uSmuZnjE9fUOqDh2Rv26JxWssi1M6X+BBvVuKd6Cpg==", "optional": true }, "@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.139.tgz", - "integrity": "sha512-Fg/aQs1vdyqLrNXqGa1i7/ODpGxP6ud/K/2AgVarLteg2Z3ZnrHPvPQ6iQmTGI8+BhxAZ141t4Dg0CWz3CoqCQ==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.170.tgz", + "integrity": "sha512-m4+I0qBEk7cxRKS+pL+eoWXbXTFOAo83fQ0tQvap4z/mDMm06IWJtEPoYTaMBwsp32GJWLkHWKbZSBCHZnp2DQ==", "optional": true }, "@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.139.tgz", - "integrity": "sha512-HusAU/gSQ0G0AHU+Hj/ps0Tl5JaUF2nxkp+G42tU6hpnwLMOQMdLx/yqvSQnz4WSxggxiDFmYvMDLYAmuE9Qdg==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.170.tgz", + "integrity": "sha512-IG+8isJNNJKbnnhO7m+PGhfVCg+XoQ/MDxGde5eigFI0WsEfitjuWSWwx82bT9ghxI1aa6qNvI+UPgPcZuo5Fg==", "optional": true }, "@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.139.tgz", - "integrity": "sha512-eJjbtLvEBJcTrl4WJhmhP7FYdTVvx/XtioifH7OEnCoxQozMHhOmA0X90csplIRpttX+jX2PqnE5j2FwU20eCw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.170.tgz", + "integrity": "sha512-7cuqSKbHVItPGVwRbd3A0BEJwcNtc7Fhoh6qHN4C6yrmjSrvdYYx3MLvq/VI768/RoG7mAMDxb+j7WfEfoP9BA==", "optional": true }, "@anthropic-ai/sdk": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.81.0.tgz", - "integrity": "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw==", + "version": "0.103.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.103.0.tgz", + "integrity": "sha512-1uG7RNgoHTUxzOXqSCODKt0UTVlxWiHk/2Tt2/uQJiPW7XzBeKVuJyd3Aw6T3LPyvZV/jDTnPLX7SaM70WLLjA==", "requires": { - "json-schema-to-ts": "^3.1.1" + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" } }, "@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==" + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==" }, "@bufbuild/protobuf": { "version": "1.10.0", @@ -7394,6 +7428,11 @@ "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", "dev": true }, + "@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" + }, "@statsig/client-core": { "version": "3.31.0", "resolved": "https://registry.npmjs.org/@statsig/client-core/-/client-core-3.31.0.tgz", @@ -8328,6 +8367,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, "fast-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", @@ -8503,9 +8547,9 @@ "optional": true }, "graphql": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.0.tgz", - "integrity": "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==", + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", + "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", "peer": true }, "has-symbols": { @@ -9879,6 +9923,15 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "requires": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", diff --git a/apps/ade-cli/package.json b/apps/ade-cli/package.json index 41e09aa7e..a2b41a72d 100644 --- a/apps/ade-cli/package.json +++ b/apps/ade-cli/package.json @@ -24,10 +24,12 @@ "test": "vitest run" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.139", + "@anthropic-ai/claude-agent-sdk": "^0.3.170", + "@anthropic-ai/sdk": "^0.103.0", "@cursor/sdk": "^1.0.13", "@factory/droid-sdk": "^0.2.0", "@linear/sdk": "^84.0.0", + "@modelcontextprotocol/sdk": "^1.29.0", "@openai/codex": "0.133.0", "@opencode-ai/sdk": "^1.15.5", "@wize-logic/nodejs-rfb": "^4.2.0", diff --git a/apps/ade-cli/src/tuiClient/app.tsx b/apps/ade-cli/src/tuiClient/app.tsx index 6ceb1d4c1..76a596034 100644 --- a/apps/ade-cli/src/tuiClient/app.tsx +++ b/apps/ade-cli/src/tuiClient/app.tsx @@ -209,7 +209,7 @@ import type { } from "./types"; const PURPLE = theme.color.accent; -const EFFORTS = ["low", "medium", "high", "xhigh", "max"]; +const EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultracode"]; const PROVIDER_OPTIONS: Array<{ value: AdeCodeProvider; label: string }> = [ { value: "claude", label: "Claude" }, { value: "codex", label: "Codex" }, @@ -722,10 +722,14 @@ function modelCatalogClientRefreshTtlMs(provider?: AgentChatModelCatalogRefreshP function firstReasoningEffortForModel(model: AgentChatModelInfo | null | undefined, provider: AdeCodeProvider): string | null { const efforts = model?.reasoningEfforts?.map((entry) => entry.effort).filter(Boolean) ?? []; + const modelId = `${model?.modelId ?? ""} ${model?.id ?? ""} ${model?.displayName ?? ""}`.toLowerCase(); + if (modelId.includes("fable") && efforts.includes("high")) return "high"; if (efforts.includes(DEFAULT_CODEX_REASONING_EFFORT)) return DEFAULT_CODEX_REASONING_EFFORT; if (efforts.length) return efforts[0] ?? null; const descriptor = model?.modelId || model?.id ? getModelById(model.modelId ?? model.id) : undefined; const descriptorEfforts = descriptor?.reasoningTiers ?? []; + const descriptorId = `${descriptor?.id ?? ""} ${descriptor?.providerModelId ?? ""} ${descriptor?.displayName ?? ""}`.toLowerCase(); + if (descriptorId.includes("fable") && descriptorEfforts.includes("high")) return "high"; if (descriptorEfforts.includes(DEFAULT_CODEX_REASONING_EFFORT)) return DEFAULT_CODEX_REASONING_EFFORT; if (descriptorEfforts.length) return descriptorEfforts[0] ?? null; return provider === "codex" ? DEFAULT_CODEX_REASONING_EFFORT : null; @@ -761,7 +765,9 @@ function fallbackModelStatePatch(provider: AdeCodeProvider): Pick=18.0.0" }, "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.139", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.139", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.139" + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.170", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.170", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.170" }, "peerDependencies": { + "@anthropic-ai/sdk": ">=0.93.0", + "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.139.tgz", - "integrity": "sha512-dnuO2E0x6o9GAk9iZZKlEd10h+0PQFdTfr5aQU4I0W+0ReKsFEoE9LAqfomS2EvLUQ9L62X0+n0iyZQmAVi1kw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.170.tgz", + "integrity": "sha512-rwfgArIa5WI0QPNqFsRBgvtSI0mrtpynUm0oK6+l6/KX4hcgnYGEzciZR1bOeD9/7sSZlTdIgt+T9alKeZmXcg==", "cpu": [ "arm64" ], @@ -284,9 +284,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.139.tgz", - "integrity": "sha512-SXyldBIwpMHDXppPGObXZ1wjSSWf/YPgD6vK4nssIXarC/DtMRnAQ419Hb3q5MaBB29vSjOPKmG0MOkMltFR/A==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.170.tgz", + "integrity": "sha512-0e58h8UQMtsQxLGIv9r4foxfBFWKZ7NeDtoplLhuD7EwQonehomw1sBXCch77t/IfUS+q5vQ5zv+fOGmap5nLQ==", "cpu": [ "x64" ], @@ -297,12 +297,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.139.tgz", - "integrity": "sha512-qfnQ4SjEcq//iGAJkk25J6j4Tq+dvQe9wHks0dcaSdGOs2D96Teqrb358YJe+nke2DBKVUa9Y4ComW3aUBM29w==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.170.tgz", + "integrity": "sha512-gLbaFqcGppFJQd4DLNV4IXoeahejT/p2/M8bSSvRDbla9GOsBr1AxV5XLRyBn1e7xFGozZIAIQr3+1chp7NJgQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -310,12 +313,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.139.tgz", - "integrity": "sha512-gzMfit9t7Fiy5taZ+miAaP8ZmOMc+hv8Ov3UOXGwJunK6H+0F88ctBSnolDPMPQaS6s2WoMD0o8fhUbBudtMVw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.170.tgz", + "integrity": "sha512-SRYfQcsXlOq+CD/FqkQBTSHbaD++w73GnnO+NUV9adLYrca3kfetRwWT1iguY1cNS0l34dCR3rlzCPq78vg1Jg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -323,12 +329,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.139.tgz", - "integrity": "sha512-2Gqy5hV/MyObbwSyNhj5ha2cY5EZnUfDLvpEwR1eeOaU1yqnxzsdNzXWgHIyWQGKGNE2ICwgLYtt6AtOJGWpPg==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.170.tgz", + "integrity": "sha512-Xl/m7TaSC3T5IDBdHrZQ9fCQYyDmPELN34CL+MoyPIf7uSmuZnjE9fUOqDh2Rv26JxWssi1M6X+BBvVuKd6Cpg==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -336,12 +345,15 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.139.tgz", - "integrity": "sha512-Fg/aQs1vdyqLrNXqGa1i7/ODpGxP6ud/K/2AgVarLteg2Z3ZnrHPvPQ6iQmTGI8+BhxAZ141t4Dg0CWz3CoqCQ==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.170.tgz", + "integrity": "sha512-m4+I0qBEk7cxRKS+pL+eoWXbXTFOAo83fQ0tQvap4z/mDMm06IWJtEPoYTaMBwsp32GJWLkHWKbZSBCHZnp2DQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -349,9 +361,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.139.tgz", - "integrity": "sha512-HusAU/gSQ0G0AHU+Hj/ps0Tl5JaUF2nxkp+G42tU6hpnwLMOQMdLx/yqvSQnz4WSxggxiDFmYvMDLYAmuE9Qdg==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.170.tgz", + "integrity": "sha512-IG+8isJNNJKbnnhO7m+PGhfVCg+XoQ/MDxGde5eigFI0WsEfitjuWSWwx82bT9ghxI1aa6qNvI+UPgPcZuo5Fg==", "cpu": [ "arm64" ], @@ -362,9 +374,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.2.139", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.139.tgz", - "integrity": "sha512-eJjbtLvEBJcTrl4WJhmhP7FYdTVvx/XtioifH7OEnCoxQozMHhOmA0X90csplIRpttX+jX2PqnE5j2FwU20eCw==", + "version": "0.3.170", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.170.tgz", + "integrity": "sha512-7cuqSKbHVItPGVwRbd3A0BEJwcNtc7Fhoh6qHN4C6yrmjSrvdYYx3MLvq/VI768/RoG7mAMDxb+j7WfEfoP9BA==", "cpu": [ "x64" ], @@ -375,12 +387,13 @@ ] }, "node_modules/@anthropic-ai/sdk": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.81.0.tgz", - "integrity": "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw==", + "version": "0.103.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.103.0.tgz", + "integrity": "sha512-1uG7RNgoHTUxzOXqSCODKt0UTVlxWiHk/2Tt2/uQJiPW7XzBeKVuJyd3Aw6T3LPyvZV/jDTnPLX7SaM70WLLjA==", "license": "MIT", "dependencies": { - "json-schema-to-ts": "^3.1.1" + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" }, "bin": { "anthropic-ai-sdk": "bin/cli" @@ -5848,6 +5861,12 @@ "semver-compare": "^1.0.0" } }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, "node_modules/@statsig/client-core": { "version": "3.31.0", "resolved": "https://registry.npmjs.org/@statsig/client-core/-/client-core-3.31.0.tgz", @@ -11609,6 +11628,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -19630,6 +19655,16 @@ "dev": true, "license": "MIT" }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e5e965828..8ad96851b 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -51,7 +51,8 @@ "version:release": "node ./scripts/set-release-version.mjs" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.139", + "@anthropic-ai/claude-agent-sdk": "^0.3.170", + "@anthropic-ai/sdk": "^0.103.0", "@cursor/sdk": "^1.0.13", "@factory/droid-sdk": "^0.2.0", "@floating-ui/react": "^0.27.19", @@ -62,6 +63,7 @@ "@lobehub/icons": "^5.2.0", "@lobehub/icons-static-svg": "^1.84.0", "@lobehub/ui": "^5.6.3", + "@modelcontextprotocol/sdk": "^1.29.0", "@novnc/novnc": "^1.7.0", "@openai/codex": "0.133.0", "@opencode-ai/sdk": "^1.15.5", diff --git a/apps/desktop/src/main/services/chat/agentChatService.test.ts b/apps/desktop/src/main/services/chat/agentChatService.test.ts index 215a66108..3d2a12108 100644 --- a/apps/desktop/src/main/services/chat/agentChatService.test.ts +++ b/apps/desktop/src/main/services/chat/agentChatService.test.ts @@ -10737,6 +10737,208 @@ describe("createAgentChatService", () => { }), ])); }); + + it("emits todo_update events for Claude TaskCreate and TaskUpdate tool uses", async () => { + const events: AgentChatEventEnvelope[] = []; + const setPermissionMode = vi.fn().mockResolvedValue(undefined); + const send = vi.fn().mockResolvedValue(undefined); + let streamCall = 0; + const stream = vi.fn(() => (async function* () { + streamCall += 1; + if (streamCall === 1) { + yield { + type: "system", + subtype: "init", + session_id: "sdk-session-1", + slash_commands: [], + }; + return; + } + + yield { + type: "assistant", + message: { + content: [ + { + type: "tool_use", + id: "task-create-1", + name: "TaskCreate", + input: { + subject: "Inspect SDK changes", + description: "Inspect the latest Claude Agent SDK changes", + activeForm: "Inspecting SDK changes", + }, + }, + ], + usage: { input_tokens: 1, output_tokens: 1 }, + }, + }; + yield { + type: "system", + subtype: "task_started", + task_id: "task-1", + parent_tool_use_id: "task-create-1", + description: "Inspect SDK changes", + task_type: "other", + }; + yield { + type: "assistant", + message: { + content: [ + { + type: "tool_use", + id: "task-update-1", + name: "TaskUpdate", + input: { + taskId: "task-1", + status: "in_progress", + activeForm: "Applying SDK changes", + }, + }, + ], + usage: { input_tokens: 1, output_tokens: 1 }, + }, + }; + yield { + type: "result", + usage: { input_tokens: 1, output_tokens: 1 }, + }; + })()); + vi.mocked(claudeSdkCreateSessionCompat).mockReturnValue({ + send, + stream, + close: vi.fn(), + sessionId: "sdk-session-1", + setPermissionMode, + } as any); + + const { service } = createService({ + onEvent: (event: AgentChatEventEnvelope) => events.push(event), + }); + const session = await service.createSession({ + laneId: "lane-1", + provider: "claude", + model: "sonnet", + }); + + await service.runSessionTurn({ + sessionId: session.id, + text: "Track the SDK task list.", + }); + + const todoEvents = events + .map((event) => event.event) + .filter((event): event is Extract => + event.type === "todo_update", + ); + expect(todoEvents.length).toBeGreaterThanOrEqual(3); + expect(todoEvents.at(-1)).toMatchObject({ + type: "todo_update", + items: [ + { + id: "task-1", + description: "Applying SDK changes", + status: "in_progress", + }, + ], + }); + + expect(events).toEqual(expect.arrayContaining([ + expect.objectContaining({ + event: expect.objectContaining({ + type: "tool_call", + tool: "TaskCreate", + itemId: "task-create-1", + }), + }), + expect.objectContaining({ + event: expect.objectContaining({ + type: "tool_call", + tool: "TaskUpdate", + itemId: "task-update-1", + }), + }), + ])); + }); + + it("applies Claude task_started updates when the SDK task id matches the tool use id", async () => { + const events: AgentChatEventEnvelope[] = []; + const stream = vi.fn(() => (async function* () { + yield { + type: "system", + subtype: "init", + session_id: "sdk-session-1", + slash_commands: [], + }; + yield { + type: "assistant", + message: { + content: [ + { + type: "tool_use", + id: "task-1", + name: "TaskCreate", + input: { + subject: "Inspect SDK changes", + activeForm: "Inspecting SDK changes", + }, + }, + ], + usage: { input_tokens: 1, output_tokens: 1 }, + }, + }; + yield { + type: "system", + subtype: "task_started", + task_id: "task-1", + parent_tool_use_id: "task-1", + description: "Inspect SDK changes", + task_type: "other", + }; + yield { + type: "result", + usage: { input_tokens: 1, output_tokens: 1 }, + }; + })()); + vi.mocked(claudeSdkCreateSessionCompat).mockReturnValue({ + send: vi.fn().mockResolvedValue(undefined), + stream, + close: vi.fn(), + sessionId: "sdk-session-1", + setPermissionMode: vi.fn().mockResolvedValue(undefined), + } as any); + + const { service } = createService({ + onEvent: (event: AgentChatEventEnvelope) => events.push(event), + }); + const session = await service.createSession({ + laneId: "lane-1", + provider: "claude", + model: "sonnet", + }); + + await service.runSessionTurn({ + sessionId: session.id, + text: "Track the SDK task list.", + }); + + const todoEvents = events + .map((event) => event.event) + .filter((event): event is Extract => + event.type === "todo_update", + ); + + expect(todoEvents.at(-1)).toMatchObject({ + type: "todo_update", + items: [ + { + id: "task-1", + description: "Inspect SDK changes", + status: "in_progress", + }, + ], + }); + }); }); // -------------------------------------------------------------------------- diff --git a/apps/desktop/src/main/services/chat/agentChatService.ts b/apps/desktop/src/main/services/chat/agentChatService.ts index 3020fe194..5a19dc9df 100644 --- a/apps/desktop/src/main/services/chat/agentChatService.ts +++ b/apps/desktop/src/main/services/chat/agentChatService.ts @@ -369,7 +369,7 @@ import { } from "../../../shared/orchestrationRuntimePolicy"; import type { ProcessRegistryService } from "../runtime/processRegistryService"; -const CLAUDE_AGENT_SDK_VERSION = "0.2.139"; +const CLAUDE_AGENT_SDK_VERSION = "0.3.170"; const CLAUDE_AGENT_SDK_API = "v1_query"; const CLAUDE_AGENT_SDK_TELEMETRY_TAGS = { "claude_sdk.version": CLAUDE_AGENT_SDK_VERSION, @@ -1995,8 +1995,9 @@ const CLAUDE_REASONING_EFFORTS: Array<{ effort: string; description: string }> = { effort: "low", description: "Quick responses with minimal reasoning." }, { effort: "medium", description: "Balanced reasoning depth and speed." }, { effort: "high", description: "Deep reasoning for complex tasks." }, - { effort: "xhigh", description: "Extra-high reasoning depth for Opus 4.7." }, - { effort: "max", description: "Maximum reasoning depth. Best for Opus on hard problems." }, + { effort: "xhigh", description: "Extra-high reasoning depth for complex Claude tasks." }, + { effort: "max", description: "Maximum reasoning depth. Best for hard problems." }, + { effort: "ultracode", description: "Ultracode orchestration for the hardest coding tasks." }, ]; const KNOWN_CLAUDE_EFFORTS = new Set(CLAUDE_REASONING_EFFORTS.map((e) => e.effort)); @@ -2053,6 +2054,16 @@ function normalizeReasoningEffort(value: unknown): string | null { return normalized.length > 0 ? normalized : null; } +function claudeRuntimeEffortForReasoningEffort(effort: string | null | undefined): "low" | "medium" | "high" | "xhigh" | "max" | null { + if (effort === "low" || effort === "medium" || effort === "high" || effort === "xhigh" || effort === "max") return effort; + if (effort === "ultracode") return "xhigh"; + return null; +} + +function isClaudeUltracodeEffort(effort: string | null | undefined): boolean { + return effort === "ultracode"; +} + type CodexServiceTier = "fast"; function normalizeFastMode(value: unknown): boolean { @@ -3417,6 +3428,88 @@ function normalizeClaudeTodoItems( return items.length ? items : null; } +type ClaudeTodoItems = Extract["items"]; +type ClaudeTaskTodoState = ClaudeTodoItems[number]; + +function normalizeClaudeTaskTodoStatus(value: unknown): ClaudeTaskTodoState["status"] { + const normalized = typeof value === "string" ? value.trim().toLowerCase() : ""; + if (normalized === "completed") return "completed"; + if (normalized === "in_progress" || normalized === "inprogress" || normalized === "running") return "in_progress"; + return "pending"; +} + +function firstNonEmptyString(...values: unknown[]): string | null { + for (const value of values) { + if (typeof value === "string" && value.trim().length) return value.trim(); + } + return null; +} + +function updateClaudeTaskTodosFromToolInput( + tasksById: Map, + toolName: string, + input: unknown, + fallbackId: string, +): ClaudeTodoItems | null { + if (!input || typeof input !== "object") return null; + const record = input as Record; + const normalizedToolName = toolName.trim(); + if (normalizedToolName === "TaskCreate") { + const description = firstNonEmptyString(record.subject, record.description, record.activeForm); + if (!description) return null; + const id = firstNonEmptyString(record.taskId, record.id, record.metadata && typeof record.metadata === "object" + ? (record.metadata as Record).taskId + : null) ?? fallbackId; + tasksById.set(id, { + id, + description, + status: normalizeClaudeTaskTodoStatus(record.status), + }); + } else if (normalizedToolName === "TaskUpdate") { + const id = firstNonEmptyString(record.taskId, record.id); + if (!id) return null; + const rawStatus = typeof record.status === "string" ? record.status.trim().toLowerCase() : ""; + if (rawStatus === "deleted") { + tasksById.delete(id); + } else { + const existing = tasksById.get(id); + const description = firstNonEmptyString(record.subject, record.description, record.activeForm, existing?.description) + ?? id; + tasksById.set(id, { + id, + description, + status: normalizeClaudeTaskTodoStatus(record.status ?? existing?.status), + }); + } + } else { + return null; + } + return [...tasksById.values()]; +} + +function remapClaudeTaskTodoFromRuntimeEvent( + tasksById: Map, + previousId: string | null | undefined, + nextId: string | null | undefined, + updates?: { description?: string | null; status?: ClaudeTaskTodoState["status"] }, +): ClaudeTodoItems | null { + const fromId = previousId?.trim(); + const toId = nextId?.trim(); + if (!fromId || !toId) return null; + const existing = tasksById.get(fromId); + if (!existing) return null; + if (fromId !== toId) { + tasksById.delete(fromId); + } + tasksById.set(toId, { + ...existing, + id: toId, + description: firstNonEmptyString(updates?.description, existing.description) ?? existing.description, + status: updates?.status ?? existing.status, + }); + return [...tasksById.values()]; +} + async function buildStreamingUserContent( args: { baseText: string; @@ -10903,6 +10996,7 @@ export function createAgentChatService(args: { const toolInputJsonByContentIndex = new Map(); const toolUseMetaByContentIndex = new Map(); const emittedClaudeTodoIds = new Set(); + const claudeTaskTodosById = new Map(); const emitClaudeToolCompletion = ( itemId: string, result: Record, @@ -10955,9 +11049,10 @@ export function createAgentChatService(args: { } }; const maybeEmitTodoUpdate = (toolName: string, input: unknown, itemId: string): void => { - if (toolName !== "TodoWrite") return; if (emittedClaudeTodoIds.has(itemId)) return; - const todoItems = normalizeClaudeTodoItems(input ?? {}); + const todoItems = toolName === "TodoWrite" + ? normalizeClaudeTodoItems(input ?? {}) + : updateClaudeTaskTodosFromToolInput(claudeTaskTodosById, toolName, input ?? {}, itemId); if (!todoItems) return; emittedClaudeTodoIds.add(itemId); emitChatEvent(managed, { type: "todo_update", items: todoItems, turnId }); @@ -11405,6 +11500,18 @@ export function createAgentChatService(args: { ...(taskType ? { taskType } : {}), ...(workflowName ? { workflowName } : {}), }); + const remappedTodoItems = remapClaudeTaskTodoFromRuntimeEvent( + claudeTaskTodosById, + parentToolUseId, + taskId, + { + description, + status: "in_progress", + }, + ); + if (remappedTodoItems) { + emitChatEvent(managed, { type: "todo_update", items: remappedTodoItems, turnId }); + } emitChatEvent(managed, { type: "subagent_started", taskId, @@ -11694,15 +11801,7 @@ export function createAgentChatService(args: { itemId, turnId, }); - const todoItems = toolName === "TodoWrite" ? normalizeClaudeTodoItems(block.input ?? {}) : null; - if (todoItems && !emittedClaudeTodoIds.has(itemId)) { - emittedClaudeTodoIds.add(itemId); - emitChatEvent(managed, { - type: "todo_update", - items: todoItems, - turnId, - }); - } + maybeEmitTodoUpdate(toolName, block.input, itemId); if (typeof contentIndex === "number") { const initial = block.input != null && typeof block.input === "object" && Object.keys(block.input as object).length @@ -11741,6 +11840,7 @@ export function createAgentChatService(args: { const taskInput = extractTaskToolInput(parsed); if (taskInput) runtime.taskToolInputByToolUseId.set(meta.toolUseId, taskInput); } + maybeEmitTodoUpdate(meta.toolName, parsed, meta.itemId); const syntheticResult = maybeSyntheticToolResult(meta.toolName, parsed, meta.itemId, turnId); if (syntheticResult && !emittedSyntheticItemIds.has(meta.itemId)) { emittedSyntheticItemIds.add(meta.itemId); @@ -12238,8 +12338,12 @@ export function createAgentChatService(args: { const claudeSupportsReasoning = claudeDescriptor?.capabilities.reasoning ?? true; if (claudeSupportsReasoning) { const effort = managed.session.reasoningEffort; - if (effort === "low" || effort === "medium" || effort === "high" || effort === "xhigh" || effort === "max") { - cliArgs.push("--effort", effort); + const runtimeEffort = claudeRuntimeEffortForReasoningEffort(effort); + if (runtimeEffort) { + cliArgs.push("--effort", runtimeEffort); + } + if (isClaudeUltracodeEffort(effort)) { + cliArgs.push("--settings", JSON.stringify({ ultracode: true })); } } cliArgs.push(promptText); @@ -16712,8 +16816,15 @@ export function createAgentChatService(args: { const claudeSupportsReasoning = claudeDescriptor?.capabilities.reasoning ?? true; if (claudeSupportsReasoning) { const effort = managed.session.reasoningEffort; - if (effort === "low" || effort === "medium" || effort === "high" || effort === "xhigh" || effort === "max") { - opts.effort = effort as any; + const runtimeEffort = claudeRuntimeEffortForReasoningEffort(effort); + if (runtimeEffort) { + opts.effort = runtimeEffort as any; + } + if (isClaudeUltracodeEffort(effort)) { + opts.settings = { + ...((opts.settings ?? {}) as Record), + ultracode: true, + } as any; } } const model = opts.model ?? resolveClaudeCliModel(managed.session.model) ?? DEFAULT_CLAUDE_MODEL; diff --git a/apps/desktop/src/main/services/chat/cursorModelsDiscovery.ts b/apps/desktop/src/main/services/chat/cursorModelsDiscovery.ts index 0907f1e9c..f2659b972 100644 --- a/apps/desktop/src/main/services/chat/cursorModelsDiscovery.ts +++ b/apps/desktop/src/main/services/chat/cursorModelsDiscovery.ts @@ -151,6 +151,8 @@ type ParsedCursorCliVariant = { }; const CURSOR_CLI_REASONING_SUFFIXES = [ + ["ultra-code", "ultracode"], + ["ultracode", "ultracode"], ["extra-high", "xhigh"], ["xhigh", "xhigh"], ["minimal", "minimal"], @@ -182,7 +184,7 @@ function parseCursorCliVariantId(id: string): ParsedCursorCliVariant | null { } } - const thinkingMiddle = base.match(/^(.*)-(none|minimal|low|medium|high|xhigh|extra-high|max)-thinking$/i); + const thinkingMiddle = base.match(/^(.*)-(none|minimal|low|medium|high|xhigh|extra-high|max|ultracode|ultra-code)-thinking$/i); if (thinkingMiddle) { const reasoningEffort = normalizeCursorReasoningValue(thinkingMiddle[2]); if (reasoningEffort) { @@ -355,6 +357,7 @@ function normalizeCursorReasoningValue(value: unknown): string | null { const normalized = normalizeCursorMetadataText(value); if (!normalized) return null; if (normalized === "extra-high" || normalized === "extra_high") return "xhigh"; + if (normalized === "ultra-code" || normalized === "ultra_code") return "ultracode"; if ([ "none", "dynamic", @@ -365,6 +368,7 @@ function normalizeCursorReasoningValue(value: unknown): string | null { "high", "xhigh", "max", + "ultracode", "thinking", ].includes(normalized)) { return normalized; diff --git a/apps/desktop/src/main/services/chat/droidModelsDiscovery.ts b/apps/desktop/src/main/services/chat/droidModelsDiscovery.ts index 1ef90baa4..a678cec86 100644 --- a/apps/desktop/src/main/services/chat/droidModelsDiscovery.ts +++ b/apps/desktop/src/main/services/chat/droidModelsDiscovery.ts @@ -179,10 +179,14 @@ function normalizeDroidReasoningEffort(value: unknown): string | null { case "high": case "xhigh": case "max": + case "ultracode": return normalized; case "extra-high": case "extra_high": return "xhigh"; + case "ultra-code": + case "ultra_code": + return "ultracode"; default: return null; } diff --git a/apps/desktop/src/main/services/opencode/openCodeInventory.ts b/apps/desktop/src/main/services/opencode/openCodeInventory.ts index cf53a1cf3..89f74c1e5 100644 --- a/apps/desktop/src/main/services/opencode/openCodeInventory.ts +++ b/apps/desktop/src/main/services/opencode/openCodeInventory.ts @@ -89,6 +89,9 @@ const OPENCODE_REASONING_VARIANT_ALIASES: Record = { "extra-high": "xhigh", extra_high: "xhigh", max: "max", + ultracode: "ultracode", + ultra_code: "ultracode", + "ultra-code": "ultracode", }; const OPENCODE_SERVICE_VARIANT_ALIASES: Record = { diff --git a/apps/desktop/src/main/services/usage/usagePricing.ts b/apps/desktop/src/main/services/usage/usagePricing.ts index d41354557..c6346b17c 100644 --- a/apps/desktop/src/main/services/usage/usagePricing.ts +++ b/apps/desktop/src/main/services/usage/usagePricing.ts @@ -40,6 +40,7 @@ const STATIC_TOKEN_PRICES: Record = { "claude-3-5-sonnet": tokenPrice(3, 15, 0.3, 3.75), "claude-3-7-sonnet": tokenPrice(3, 15, 0.3, 3.75), "claude-3-haiku": tokenPrice(0.25, 1.25, 0.03, 0.3), + "claude-fable-5": tokenPrice(10, 50, 1, 12.5), "claude-opus-4-1": tokenPrice(15, 75, 1.5, 18.75), "claude-opus-4": tokenPrice(15, 75, 1.5, 18.75), "claude-opus-4-8": tokenPrice(5, 25, 0.5, 6.25), @@ -94,6 +95,7 @@ const STATIC_TOKEN_PRICES: Record = { const BUILTIN_PRICING_ALIASES: Record = { auto: "claude-sonnet-4-5", + fable: "claude-fable-5", "anthropic--claude-4.6-opus": "claude-opus-4-6", "anthropic--claude-4.6-sonnet": "claude-sonnet-4-6", "anthropic--claude-4.5-opus": "claude-opus-4-5", diff --git a/apps/desktop/src/renderer/components/chat/AgentChatPane.tsx b/apps/desktop/src/renderer/components/chat/AgentChatPane.tsx index 33745eff7..1c12df43e 100644 --- a/apps/desktop/src/renderer/components/chat/AgentChatPane.tsx +++ b/apps/desktop/src/renderer/components/chat/AgentChatPane.tsx @@ -106,7 +106,6 @@ import { ChatComputerUsePanel } from "./ChatComputerUsePanel"; import { ChatIosSimulatorPanel } from "./ChatIosSimulatorPanel"; import { ChatAppControlPanel } from "./ChatAppControlPanel"; import { ChatSubagentsPanel } from "./ChatSubagentsPanel"; -import { ChatTasksPanel } from "./ChatTasksPanel"; import { ChatFileChangesPanel } from "./ChatFileChangesPanel"; import { RewindFilesConfirmDialog, type RewindFilesConfirmDialogState } from "./RewindFilesConfirmDialog"; import { buildRewindPreviewFiles, deriveRewindDiffSummaries } from "./rewindFilesPreview"; @@ -1534,11 +1533,13 @@ function writeLastUsedReasoningEffort(args: { function selectReasoningEffort(args: { tiers: string[]; preferred: string | null; + modelId?: string | null; }): string | null { if (!args.tiers.length) return null; if (args.preferred && args.tiers.includes(args.preferred)) { return args.preferred; } + if (args.modelId?.toLowerCase().includes("fable") && args.tiers.includes("high")) return "high"; return args.tiers.includes("medium") ? "medium" : args.tiers[0]!; } @@ -4285,6 +4286,7 @@ export function AgentChatPane({ setReasoningEffort(selectReasoningEffort({ tiers, preferred: config.reasoningEffort, + modelId: config.modelId, })); setFastMode(modelSupportsFastMode(desc) && config.fastMode); setExecutionMode(config.executionMode); @@ -5213,7 +5215,7 @@ export function AgentChatPane({ } if (reasoningEffort && reasoningTiers.includes(reasoningEffort)) return; const preferred = readLastUsedReasoningEffort({ laneId, modelId }); - setReasoningEffort(selectReasoningEffort({ tiers: reasoningTiers, preferred })); + setReasoningEffort(selectReasoningEffort({ tiers: reasoningTiers, preferred, modelId })); }, [laneId, modelId, reasoningEffort, reasoningTiers]); useEffect(() => { @@ -6325,7 +6327,7 @@ export function AgentChatPane({ const nextModel = nextProvider === "opencode" ? nextModelId : runtimeFacingModelId(nextDesc, nextModelId); const tiers = nextDesc?.reasoningTiers ?? []; const preferred = readLastUsedReasoningEffort({ laneId, modelId: nextModelId }); - const nextReasoningEffort = selectReasoningEffort({ tiers, preferred }); + const nextReasoningEffort = selectReasoningEffort({ tiers, preferred, modelId: nextModelId }); const nextRec = recommendedOpenCodePermissionModeForModel(nextPermissionDesc); return { nextDesc, @@ -8339,10 +8341,11 @@ export function AgentChatPane({ const ChatActionsToolbarIcon = chatActionsToolbarIcon; const proofArtifactCount = computerUseSnapshot?.artifacts?.length ?? 0; const proofSessionId = selectedSessionId ?? ""; - const agentsTabContent = selectedSubagentPaneAvailable ? ( + const agentsTabContent = selectedSubagentPaneAvailable || selectedTodoItems.length > 0 ? ( { setSubagentView({ @@ -8376,7 +8379,7 @@ export function AgentChatPane({ /> ) : (
-

No subagents detected

+

No agent activity detected

); const proofTabContent = ( @@ -9399,7 +9402,7 @@ export function AgentChatPane({ const desc = resolveModelDescriptorWithRuntimeCatalog(nextModelId) ?? getModelById(nextModelId); const tiers = desc?.reasoningTiers ?? []; const preferred = readLastUsedReasoningEffort({ laneId, modelId: nextModelId }); - const nextEffort = selectReasoningEffort({ tiers, preferred }); + const nextEffort = selectReasoningEffort({ tiers, preferred, modelId: nextModelId }); const previousPermissionDesc = getModelDescriptorForPermissionMode(parallelModelSlots[index]?.modelId ?? ""); const nextPermissionDesc = getModelDescriptorForPermissionMode(nextModelId); const nextRecommendedOpenCodeMode = recommendedOpenCodePermissionModeForModel(nextPermissionDesc); @@ -9853,9 +9856,6 @@ export function AgentChatPane({ -{sessionDelta.deletions} ) : null} - {selectedTodoItems.length ? ( - - ) : null} {selectedTurnDiffSummaries.length && selectedSessionId ? ( { ).toBeTruthy(); }); + it("renders task snapshots in the agents pane even when no subagents exist", () => { + render( + , + ); + + expect(screen.getByText("Tasks")).toBeTruthy(); + expect(screen.getByText("1/3 complete · 1 active")).toBeTruthy(); + expect(screen.getByText("Wire task pane")).toBeTruthy(); + expect(screen.getByText("Run focused checks")).toBeTruthy(); + expect(screen.queryByText(/No agent activity/i)).toBeNull(); + }); + it("toggles the inline drawer closed on a second click of the same row", async () => { const probeSubagentTranscript = vi.fn().mockResolvedValue(false); render( diff --git a/apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx b/apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx index 64a4e780e..ed0d5bdcc 100644 --- a/apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx +++ b/apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx @@ -10,6 +10,8 @@ import { import { cn } from "../ui/cn"; import type { ChatSubagentSnapshot } from "./chatExecutionSummary"; import { derivePlan } from "./chatExecutionSummary"; +import type { TodoItemSnapshot } from "./chatExecutionSummary"; +import { ChatTaskList } from "./ChatTasksPanel"; import type { ChatInfoPlanStep } from "../../../shared/chatSubagents"; import type { AgentChatEventEnvelope, CodexThreadGoal } from "../../../shared/types"; import type { SubagentCapability } from "../../../shared/subagentCapabilities"; @@ -450,6 +452,7 @@ export function ChatSubagentsPanel({ onEditGoal, onClearGoal, goalPending = false, + todoItems = [], }: { snapshots: ChatSubagentSnapshot[]; events: AgentChatEventEnvelope[]; @@ -471,6 +474,7 @@ export function ChatSubagentsPanel({ onEditGoal?: (nextObjective: string) => void; onClearGoal?: () => void; goalPending?: boolean; + todoItems?: TodoItemSnapshot[]; }) { const [expanded, setExpanded] = useState(false); // Which agent's inline details drawer is open (agents with no transcript). @@ -599,9 +603,18 @@ export function ChatSubagentsPanel({ const planComplete = plan?.steps.filter((step) => step.status === "completed").length ?? 0; const planTotal = plan?.steps.length ?? 0; const planPercent = planTotal > 0 ? Math.round((planComplete / planTotal) * 100) : 0; + const taskComplete = todoItems.filter((item) => item.status === "completed").length; + const taskActive = todoItems.filter((item) => item.status === "in_progress").length; + const taskHint = todoItems.length + ? [ + `${taskComplete}/${todoItems.length} complete`, + ...(taskActive ? [`${taskActive} active`] : []), + ].join(" · ") + : undefined; const hasGoal = Boolean(goal?.objective?.trim()); - const hasAnything = hasGoal || Boolean(plan) || foreground.length > 0 || background.length > 0; + const hasTasks = todoItems.length > 0; + const hasAnything = hasGoal || Boolean(plan) || hasTasks || foreground.length > 0 || background.length > 0; const body = (
@@ -651,11 +664,29 @@ export function ChatSubagentsPanel({ ) : null} + {/* ── Tasks ───────────────────────────────────────────────── */} + {hasTasks ? ( +
+ + +
+ ) : null} + {/* ── Subagents ────────────────────────────────────────────── */}
setExpanded((v) => !v)} > -
- {sortedItems.map((item) => ( -
- - {statusIcon(item.status)} - - - {item.description} - -
- ))} -
+ ); }); + +export const ChatTaskList = React.memo(function ChatTaskList({ + items, + className, +}: { + items: TodoItemSnapshot[]; + className?: string; +}) { + const sortedItems = useMemo( + () => [...items].sort((a, b) => STATUS_SORT_ORDER[a.status] - STATUS_SORT_ORDER[b.status]), + [items], + ); + + if (!items.length) return null; + + return ( +
+ {sortedItems.map((item) => ( +
+ + {statusIcon(item.status)} + + + {item.description} + +
+ ))} +
+ ); +}); diff --git a/apps/desktop/src/renderer/components/shared/ModelPicker/ModelListRow.tsx b/apps/desktop/src/renderer/components/shared/ModelPicker/ModelListRow.tsx index 63f69b64c..2393215ed 100644 --- a/apps/desktop/src/renderer/components/shared/ModelPicker/ModelListRow.tsx +++ b/apps/desktop/src/renderer/components/shared/ModelPicker/ModelListRow.tsx @@ -50,6 +50,7 @@ const REASONING_LABELS: Record = { high: "High", xhigh: "Extra High", max: "Max", + ultracode: "Ultracode", }; function reasoningChipLabel(effort: string | null): string { diff --git a/apps/desktop/src/renderer/components/shared/ModelPicker/ReasoningEffortPicker.tsx b/apps/desktop/src/renderer/components/shared/ModelPicker/ReasoningEffortPicker.tsx index 9c05e4610..2e27d0e60 100644 --- a/apps/desktop/src/renderer/components/shared/ModelPicker/ReasoningEffortPicker.tsx +++ b/apps/desktop/src/renderer/components/shared/ModelPicker/ReasoningEffortPicker.tsx @@ -26,12 +26,14 @@ export function reasoningChipLabel(effort: string | null | undefined): string | if (lower === "high") return "HI"; if (lower === "xhigh") return "XH"; if (lower === "max") return "MAX"; + if (lower === "ultracode") return "ULTRA"; return lower.slice(0, 3).toUpperCase(); } function tierLabel(tier: string): string { if (tier === "xhigh") return "Extra High"; if (tier === "max") return "Max"; + if (tier === "ultracode") return "Ultracode"; return tier.charAt(0).toUpperCase() + tier.slice(1); } diff --git a/apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts b/apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts index 79138d09b..f7a960338 100644 --- a/apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts +++ b/apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts @@ -254,6 +254,30 @@ describe("buildTrackedCliStartupCommand", () => { expect(standardLaunch.startupCommand).toContain("fastMode"); expect(standardLaunch.startupCommand).toContain("false"); }); + + it("translates Claude ultracode to xhigh effort plus per-session settings", () => { + const launch = buildTrackedCliLaunchCommand({ + provider: "claude", + permissionMode: "default", + sessionId: "00000000-0000-0000-0000-000000000001", + model: "anthropic/claude-fable-5", + reasoningEffort: "ultracode", + fastMode: true, + }); + + expect(launch.args).toEqual(expect.arrayContaining([ + "--model", + "fable", + "--effort", + "xhigh", + "--settings", + JSON.stringify({ fastMode: true, ultracode: true }), + ])); + expect(launch.args).not.toEqual(expect.arrayContaining(["--effort", "ultracode"])); + expect(launch.startupCommand).toContain("--model fable"); + expect(launch.startupCommand).toContain("--effort xhigh"); + expect(launch.startupCommand).toContain("ultracode"); + }); }); describe("codex provider", () => { @@ -663,6 +687,19 @@ describe("tracked CLI resume helpers", () => { "claude --permission-mode default --model claude-opus-4-8 --settings \"{\\\"fastMode\\\":false}\" --resume claude-session-1", ); + expect(buildTrackedCliResumeCommand({ + provider: "claude", + targetKind: "session", + targetId: "claude-session-1", + launch: { + permissionMode: "default", + model: "anthropic/claude-fable-5", + reasoningEffort: "ultracode", + }, + })).toBe( + "claude --permission-mode default --model fable --effort xhigh --settings \"{\\\"ultracode\\\":true}\" --resume claude-session-1", + ); + expect(buildTrackedCliResumeCommand({ provider: "codex", targetKind: "thread", diff --git a/apps/desktop/src/shared/claudeCliModels.ts b/apps/desktop/src/shared/claudeCliModels.ts index 0ddd98096..22edb612d 100644 --- a/apps/desktop/src/shared/claudeCliModels.ts +++ b/apps/desktop/src/shared/claudeCliModels.ts @@ -1,6 +1,10 @@ -export type ClaudeCliModelAlias = "opus" | "opus[1m]" | "claude-opus-4-8" | "sonnet" | "haiku"; +export type ClaudeCliModelAlias = "fable" | "opus" | "opus[1m]" | "claude-opus-4-8" | "sonnet" | "haiku"; export const CLAUDE_CLI_MODEL_ALIAS_MAP: Readonly> = { + fable: "fable", + "claude-fable-5": "fable", + "anthropic/claude-fable-5": "fable", + "anthropic/claude-fable-5-api": "fable", opus: "opus", "opus-4.8": "claude-opus-4-8", "opus-4-8": "claude-opus-4-8", @@ -55,6 +59,7 @@ export function resolveClaudeCliModelAlias( if (normalized.includes("opus") && hasOpus1mToken) { return /4[-.]8/.test(normalized) ? "claude-opus-4-8" : "opus[1m]"; } + if (normalized.includes("fable")) return "fable"; if (normalized.includes("sonnet")) return "sonnet"; if (normalized.includes("opus")) return "opus"; if (normalized.includes("haiku")) return "haiku"; diff --git a/apps/desktop/src/shared/cliLaunch.ts b/apps/desktop/src/shared/cliLaunch.ts index c5bf72d17..c89fc3952 100644 --- a/apps/desktop/src/shared/cliLaunch.ts +++ b/apps/desktop/src/shared/cliLaunch.ts @@ -333,11 +333,8 @@ export function buildTrackedCliLaunchCommand(args: { if (model) { commandArgs.push("--model", model); } - const reasoningEffort = normalizeCliFlagValue(args.reasoningEffort); - if (reasoningEffort) { - commandArgs.push("--effort", reasoningEffort); - } - commandArgs.push(...claudeFastModeSettingsFlags(args.fastMode)); + commandArgs.push(...claudeRuntimeEffortFlags(args.reasoningEffort)); + commandArgs.push(...claudeSessionSettingsFlags(args.fastMode, args.reasoningEffort)); const guidance = buildAdeCliAgentGuidance(skillRoots); commandArgs.push("--append-system-prompt", guidance); commandArgs.push(...permissionModeToClaudeFlag(permissionMode)); @@ -508,6 +505,24 @@ export function claudeFastModeSettingsFlags(fastMode: boolean | null | undefined return []; } +function claudeRuntimeEffortFlags(reasoningEffort: string | null | undefined): string[] { + const effort = normalizeCliFlagValue(reasoningEffort); + if (!effort) return []; + if (effort === "ultracode") return ["--effort", "xhigh"]; + return ["--effort", effort]; +} + +function claudeSessionSettingsFlags( + fastMode: boolean | null | undefined, + reasoningEffort: string | null | undefined, +): string[] { + const settings: Record = {}; + if (fastMode === true) settings.fastMode = true; + if (fastMode === false) settings.fastMode = false; + if (normalizeCliFlagValue(reasoningEffort) === "ultracode") settings.ultracode = true; + return Object.keys(settings).length ? ["--settings", JSON.stringify(settings)] : []; +} + function workTabCliPrompt(initialPrompt: string | null, skillRoots: readonly string[]): string { const preamble = workTabCliPreamblePrompt(skillRoots); if (!initialPrompt) return preamble; @@ -762,9 +777,8 @@ export function buildTrackedCliResumeCommand( const parts = ["claude", ...permissionModeToClaudeFlag(permissionMode)]; const claudeModel = resolveClaudeCliModelForLaunch(model); if (claudeModel) parts.push("--model", claudeModel); - const claudeReasoningEffort = normalizeCliFlagValue(reasoningEffort); - if (claudeReasoningEffort) parts.push("--effort", claudeReasoningEffort); - parts.push(...claudeFastModeSettingsFlags(fastMode)); + parts.push(...claudeRuntimeEffortFlags(reasoningEffort)); + parts.push(...claudeSessionSettingsFlags(fastMode, reasoningEffort)); parts.push("--resume"); if (targetId) parts.push(targetId); if (prompt) parts.push(prompt); diff --git a/apps/desktop/src/shared/modelProfiles.test.ts b/apps/desktop/src/shared/modelProfiles.test.ts index 79636f9fc..3f747dada 100644 --- a/apps/desktop/src/shared/modelProfiles.test.ts +++ b/apps/desktop/src/shared/modelProfiles.test.ts @@ -114,9 +114,16 @@ describe("getModelsForProvider", () => { // --------------------------------------------------------------------------- describe("thinking levels", () => { - it("CLAUDE_THINKING_LEVELS exposes the Opus-capable effort ladder", () => { - expect(CLAUDE_THINKING_LEVELS).toHaveLength(4); - expect(CLAUDE_THINKING_LEVELS.map((t) => t.value)).toEqual(["low", "medium", "high", "max"]); + it("CLAUDE_THINKING_LEVELS exposes the Claude Code effort ladder", () => { + expect(CLAUDE_THINKING_LEVELS).toHaveLength(6); + expect(CLAUDE_THINKING_LEVELS.map((t) => t.value)).toEqual([ + "low", + "medium", + "high", + "xhigh", + "max", + "ultracode", + ]); }); it("CODEX_THINKING_LEVELS has four levels including xhigh", () => { @@ -195,6 +202,10 @@ describe("thinkingLevelToReasoningEffort", () => { expect(thinkingLevelToReasoningEffort("max")).toBe("max"); }); + it("maps 'ultracode' to 'ultracode'", () => { + expect(thinkingLevelToReasoningEffort("ultracode")).toBe("ultracode"); + }); + it("returns 'low' for null", () => { expect(thinkingLevelToReasoningEffort(null)).toBe("low"); }); diff --git a/apps/desktop/src/shared/modelProfiles.ts b/apps/desktop/src/shared/modelProfiles.ts index cd49ef92a..271b81b6c 100644 --- a/apps/desktop/src/shared/modelProfiles.ts +++ b/apps/desktop/src/shared/modelProfiles.ts @@ -83,7 +83,9 @@ export const CLAUDE_THINKING_LEVELS: ThinkingOption[] = [ { value: "low", label: "Low" }, { value: "medium", label: "Medium" }, { value: "high", label: "High" }, + { value: "xhigh", label: "Extra High" }, { value: "max", label: "Max" }, + { value: "ultracode", label: "Ultracode" }, ]; export const CODEX_THINKING_LEVELS: ThinkingOption[] = [ diff --git a/apps/desktop/src/shared/modelRegistry.test.ts b/apps/desktop/src/shared/modelRegistry.test.ts index 0e6a842bf..493e71ccb 100644 --- a/apps/desktop/src/shared/modelRegistry.test.ts +++ b/apps/desktop/src/shared/modelRegistry.test.ts @@ -235,14 +235,30 @@ describe("modelRegistry", () => { })).toBe("claude-opus-4-7-thinking-low-fast"); }); - describe("Claude Opus descriptors", () => { - it("adds Opus 4.8 1M at the top of the Claude model registry", () => { + describe("Claude descriptors", () => { + it("adds Fable 5 above Opus 4.8 in the Claude model registry", () => { expect(MODEL_REGISTRY.filter((model) => model.family === "anthropic").slice(0, 4).map((model) => model.id)).toEqual([ + "anthropic/claude-fable-5", "anthropic/claude-opus-4-8", "anthropic/claude-opus-4-7", "anthropic/claude-opus-4-7-1m", - "anthropic/claude-sonnet-4-6", ]); + const fable = getModelById("anthropic/claude-fable-5"); + expect(fable).toBeTruthy(); + expect(fable).toMatchObject({ + displayName: "Claude Fable 5", + shortId: "fable", + family: "anthropic", + providerRoute: "claude-cli", + providerModelId: "claude-fable-5", + contextWindow: 1_000_000, + maxOutputTokens: 128_000, + inputPricePer1M: 10, + outputPricePer1M: 50, + }); + expect(fable?.reasoningTiers).toEqual(["low", "medium", "high", "xhigh", "max", "ultracode"]); + expect(resolveModelAlias("fable")?.id).toBe("anthropic/claude-fable-5"); + const opus = getModelById("anthropic/claude-opus-4-8"); expect(opus).toBeTruthy(); expect(opus).toMatchObject({ @@ -256,8 +272,9 @@ describe("modelRegistry", () => { inputPricePer1M: 5, outputPricePer1M: 25, }); - expect(opus?.reasoningTiers).toEqual(["low", "medium", "high", "xhigh", "max"]); + expect(opus?.reasoningTiers).toEqual(["low", "medium", "high", "xhigh", "max", "ultracode"]); expect(opus?.serviceTiers).toEqual(["fast"]); + expect(getDefaultModelDescriptor("claude")?.id).toBe("anthropic/claude-fable-5"); }); it("keeps Opus 4.7 and Opus 4.7 1M as distinct selectable models", () => { diff --git a/apps/desktop/src/shared/modelRegistry.ts b/apps/desktop/src/shared/modelRegistry.ts index b9122899f..4b42d3bfc 100644 --- a/apps/desktop/src/shared/modelRegistry.ts +++ b/apps/desktop/src/shared/modelRegistry.ts @@ -226,6 +226,31 @@ export const MODEL_REGISTRY: ModelDescriptor[] = [ // ---- Anthropic (CLI-wrapped via claude) ---- // Claude chat surfaces use the native Agent SDK effort ladder. Keep these // tiers aligned with the launch validation path. + { + id: "anthropic/claude-fable-5", + shortId: "fable", + aliases: [ + "fable", + "claude-fable-5", + "anthropic/claude-fable-5-api", + ], + displayName: "Claude Fable 5", + family: "anthropic", + authTypes: ["cli-subscription"], + contextWindow: 1_000_000, + maxOutputTokens: 128_000, + capabilities: ALL_CAPS, + reasoningTiers: ["low", "medium", "high", "xhigh", "max", "ultracode"], + serviceTiers: ["fast"], + color: "#D97706", + providerRoute: "claude-cli", + providerModelId: "claude-fable-5", + cliCommand: "claude", + isCliWrapped: true, + inputPricePer1M: 10, + outputPricePer1M: 50, + costTier: "very_high", + }, { id: "anthropic/claude-opus-4-8", shortId: "opus-4.8-1m", @@ -247,7 +272,7 @@ export const MODEL_REGISTRY: ModelDescriptor[] = [ contextWindow: 1_000_000, maxOutputTokens: 128_000, capabilities: ALL_CAPS, - reasoningTiers: ["low", "medium", "high", "xhigh", "max"], + reasoningTiers: ["low", "medium", "high", "xhigh", "max", "ultracode"], serviceTiers: ["fast"], color: "#D97706", providerRoute: "claude-cli", @@ -315,7 +340,7 @@ export const MODEL_REGISTRY: ModelDescriptor[] = [ contextWindow: 200_000, maxOutputTokens: 32_000, capabilities: ALL_CAPS, - reasoningTiers: ["low", "medium", "high"], + reasoningTiers: ["low", "medium", "high", "max"], color: "#8B5CF6", providerRoute: "claude-cli", providerModelId: "sonnet", @@ -833,7 +858,7 @@ export function cursorCliLineGroupFromSdkId(providerModelId: string): CursorCliL const s = providerModelId.trim().toLowerCase(); if (s === "auto") return "auto"; if (s.includes("composer")) return "composer"; - if (/claude|sonnet|opus|haiku/.test(s)) return "anthropic"; + if (/claude|fable|sonnet|opus|haiku/.test(s)) return "anthropic"; if (/gemini/.test(s)) return "google"; if (/grok/.test(s)) return "grok"; if (/gpt|(?:^|[:/])o\d|codex/.test(s)) return "openai"; @@ -864,7 +889,7 @@ function formatCursorSdkFallbackDisplayName(providerModelId: string): string { function colorForCursorSdkId(providerModelId: string): string { const s = providerModelId.toLowerCase(); if (s === "auto") return "#A78BFA"; - if (/claude|sonnet|opus|haiku/.test(s)) return "#D97706"; + if (/claude|fable|sonnet|opus|haiku/.test(s)) return "#D97706"; if (/composer/.test(s)) return "#8B5CF6"; if (/gemini/.test(s)) return "#4285F4"; if (/grok/.test(s)) return "#1DA1F2"; @@ -951,7 +976,7 @@ export const DROID_CLI_LINE_ORDER: DroidCliLineGroup[] = ["anthropic", "openai", export function droidCliLineGroupFromModelId(providerModelId: string): DroidCliLineGroup { const s = providerModelId.trim().toLowerCase(); if (s.startsWith("custom:")) return "custom"; - if (/claude|sonnet|opus|haiku/.test(s)) return "anthropic"; + if (/claude|fable|sonnet|opus|haiku/.test(s)) return "anthropic"; if (/gpt|(?:^|[:/])o\d|codex/.test(s)) return "openai"; if (/gemini/.test(s)) return "google"; return "other"; @@ -970,7 +995,7 @@ export function droidCliLineGroupLabel(group: DroidCliLineGroup): string { function colorForDroidModelId(providerModelId: string): string { const s = providerModelId.toLowerCase(); - if (/claude|sonnet|opus|haiku/.test(s)) return "#D97706"; + if (/claude|fable|sonnet|opus|haiku/.test(s)) return "#D97706"; if (/gemini/.test(s)) return "#4285F4"; if (/gpt|(?:^|[:/])o\d|codex/.test(s)) return "#10A37F"; return "#71717A"; @@ -1037,6 +1062,7 @@ function normalizeDroidEffortLabel(value: string): string { } const KNOWN_DROID_COMPACT_DISPLAY_NAMES: Record = { + "claude-fable-5": "Fable 5", "claude-opus-4-5-20251101": "Opus 4.5 (2x)", "claude-opus-4-6": "Opus 4.6 (2x)", "claude-opus-4-6-fast": "Opus 4.6 Fast Mode (12x)", @@ -1449,6 +1475,7 @@ function pickPreferredModel( function pickDefaultClaudeModel(models: ModelDescriptor[]): ModelDescriptor | undefined { return pickPreferredModel(models, [ + (model) => /\bfable\b/i.test(model.displayName) || /\bfable\b/i.test(model.providerModelId), (model) => /\bsonnet\b/i.test(model.displayName) || /\bsonnet\b/i.test(model.providerModelId), (model) => /\bopus\b/i.test(model.displayName) || /\bopus\b/i.test(model.providerModelId), (model) => /\bhaiku\b/i.test(model.displayName) || /\bhaiku\b/i.test(model.providerModelId), @@ -1476,6 +1503,7 @@ function pickDefaultCodexModel(models: ModelDescriptor[]): ModelDescriptor | und function pickDefaultOpenCodeModel(models: ModelDescriptor[]): ModelDescriptor | undefined { return pickPreferredModel(models, [ (model) => model.family === "openai" && /\bgpt-5\.4\b/i.test(`${model.displayName} ${model.providerModelId}`), + (model) => model.id === "opencode/anthropic/claude-fable-5" || (model.family === "anthropic" && /\bfable\b/i.test(`${model.displayName} ${model.providerModelId}`)), (model) => model.id === "opencode/anthropic/claude-sonnet-4-6" || (model.family === "anthropic" && model.providerRoute === "opencode"), (model) => model.family === "anthropic" && /\bsonnet\b/i.test(model.displayName), (model) => model.family === "anthropic", @@ -1487,6 +1515,7 @@ function pickDefaultOpenCodeModel(models: ModelDescriptor[]): ModelDescriptor | export function pickDefaultCursorDescriptorFromCliList(models: ModelDescriptor[]): ModelDescriptor | undefined { return pickPreferredModel(models, [ (m) => m.providerModelId === "auto", + (m) => /fable/i.test(m.providerModelId) || /fable/i.test(m.displayName), (m) => /sonnet/i.test(m.providerModelId) || /sonnet/i.test(m.displayName), (m) => /composer/i.test(m.providerModelId), (m) => /gpt-5\.4/i.test(m.providerModelId), @@ -1495,6 +1524,7 @@ export function pickDefaultCursorDescriptorFromCliList(models: ModelDescriptor[] export function pickDefaultDroidDescriptorFromCliList(models: ModelDescriptor[]): ModelDescriptor | undefined { return pickPreferredModel(models, [ + (m) => /fable/i.test(m.providerModelId) || /fable/i.test(m.displayName), (m) => /sonnet/i.test(m.providerModelId) || /sonnet/i.test(m.displayName), (m) => /opus/i.test(m.providerModelId), (m) => /gpt-5\.1-codex/i.test(m.providerModelId), diff --git a/apps/desktop/src/shared/types/models.ts b/apps/desktop/src/shared/types/models.ts index 57f44fefc..f2984b549 100644 --- a/apps/desktop/src/shared/types/models.ts +++ b/apps/desktop/src/shared/types/models.ts @@ -4,7 +4,7 @@ export type ModelProvider = "claude" | "codex" | "cursor" | "droid" | "opencode" | (string & {}); -export type ThinkingLevel = "none" | "minimal" | "low" | "medium" | "high" | "max" | "xhigh"; +export type ThinkingLevel = "none" | "minimal" | "low" | "medium" | "high" | "max" | "xhigh" | "ultracode"; export type ModelConfig = { /** Optional provider hint; routing is resolved from modelId via model registry. */ diff --git a/apps/ios/ADE/Views/Components/ADEDesignSystem.swift b/apps/ios/ADE/Views/Components/ADEDesignSystem.swift index d45462e5e..05f927d16 100644 --- a/apps/ios/ADE/Views/Components/ADEDesignSystem.swift +++ b/apps/ios/ADE/Views/Components/ADEDesignSystem.swift @@ -117,6 +117,10 @@ enum ADEColor { /// Keys cover both the registry id ("anthropic/claude-opus-4-7") and shortId ("opus"). private static let modelColors: [String: UInt32] = [ // Anthropic + "anthropic/claude-fable-5": 0xD97706, + "anthropic/claude-fable-5-api": 0xD97706, + "claude-fable-5": 0xD97706, + "fable": 0xD97706, "anthropic/claude-opus-4-8": 0xD97706, "claude-opus-4-8": 0xD97706, "anthropic/claude-opus-4-7": 0xD97706, @@ -174,6 +178,11 @@ enum ADEColor { case "gpt-5.4-mini", "gpt-5.4-mini-codex", "openai/gpt-5.4-mini", "openai/gpt-5.4-mini-codex": append("openai/gpt-5.4-mini") append("gpt-5.4-mini") + case "fable", "anthropic/claude-fable-5", "anthropic/claude-fable-5-api", "claude-fable-5": + append("anthropic/claude-fable-5") + append("anthropic/claude-fable-5-api") + append("claude-fable-5") + append("fable") case "anthropic/claude-opus-4-8", "claude-opus-4-8", "opus-4.8", "opus-4-8", "opus-4.8-1m", "opus-4.8[1m]", "opus-4-8-1m", "anthropic/claude-opus-4-8-1m", "claude-opus-4-8-1m", "claude-opus-4-8[1m]": @@ -212,7 +221,7 @@ enum ADEColor { private static func cursorSdkHex(for sdkId: String) -> UInt32 { if sdkId == "auto" { return 0xA78BFA } - if sdkId.range(of: "claude|sonnet|opus|haiku", options: .regularExpression) != nil { return 0xD97706 } + if sdkId.range(of: "claude|fable|sonnet|opus|haiku", options: .regularExpression) != nil { return 0xD97706 } if sdkId.contains("composer") { return 0x8B5CF6 } if sdkId.contains("gemini") { return 0x4285F4 } if sdkId.contains("grok") { return 0x1DA1F2 } @@ -228,8 +237,12 @@ enum ADEColor { /// the same effort actions as desktop and the ADE TUI. private static let modelReasoningTiers: [String: [String]] = [ // Claude - "anthropic/claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"], - "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"], + "anthropic/claude-fable-5": ["low", "medium", "high", "xhigh", "max", "ultracode"], + "anthropic/claude-fable-5-api": ["low", "medium", "high", "xhigh", "max", "ultracode"], + "claude-fable-5": ["low", "medium", "high", "xhigh", "max", "ultracode"], + "fable": ["low", "medium", "high", "xhigh", "max", "ultracode"], + "anthropic/claude-opus-4-8": ["low", "medium", "high", "xhigh", "max", "ultracode"], + "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max", "ultracode"], "anthropic/claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], "opus": ["low", "medium", "high", "xhigh", "max"], @@ -238,8 +251,8 @@ enum ADEColor { "opus-1m": ["low", "medium", "high", "xhigh", "max"], "opus[1m]": ["low", "medium", "high", "xhigh", "max"], "claude-opus-4-7[1m]": ["low", "medium", "high", "xhigh", "max"], - "anthropic/claude-sonnet-4-6": ["low", "medium", "high"], - "sonnet": ["low", "medium", "high"], + "anthropic/claude-sonnet-4-6": ["low", "medium", "high", "max"], + "sonnet": ["low", "medium", "high", "max"], // Claude Haiku intentionally absent — no reasoning tiers. // OpenAI / Codex "openai/gpt-5.5": ["low", "medium", "high", "xhigh"], diff --git a/apps/ios/ADE/Views/Work/WorkModelCatalog.swift b/apps/ios/ADE/Views/Work/WorkModelCatalog.swift index bdf7dc67c..70480a2b1 100644 --- a/apps/ios/ADE/Views/Work/WorkModelCatalog.swift +++ b/apps/ios/ADE/Views/Work/WorkModelCatalog.swift @@ -194,6 +194,7 @@ private func workCuratedModelCatalogGroups() -> [WorkModelCatalogGroup] { key: "anthropic", displayName: "Anthropic", models: [ + WorkModelOption(id: "claude-fable-5", displayName: "Claude Fable 5", tier: .flagship, tagline: "Flagship · 1M context", provider: "claude"), WorkModelOption(id: "claude-opus-4-8", displayName: "Claude Opus 4.8 1M", tier: .flagship, tagline: "Flagship · 1M context", provider: "claude"), WorkModelOption(id: "claude-opus-4-7", displayName: "Claude Opus 4.7", tier: .flagship, tagline: "Flagship · best for complex reasoning", provider: "claude"), WorkModelOption(id: "claude-opus-4-7-1m", displayName: "Claude Opus 4.7 1M", tier: .flagship, tagline: "1M-token context window", provider: "claude"), @@ -231,6 +232,8 @@ private func workCuratedModelCatalogGroups() -> [WorkModelCatalogGroup] { key: "anthropic", displayName: "Anthropic", models: [ + WorkModelOption(id: "claude-fable-5-thinking-high", displayName: "Claude Fable 5 Thinking High", tier: .flagship, tagline: "Cursor-routed Fable", provider: "claude"), + WorkModelOption(id: "claude-fable-5-thinking-xhigh", displayName: "Claude Fable 5 Thinking XHigh", tier: .flagship, tagline: "Cursor-routed Fable", provider: "claude"), WorkModelOption(id: "claude-4.6-sonnet-thinking", displayName: "Sonnet 4.6 · Thinking", tier: .reasoning, tagline: "Extended reasoning", provider: "claude"), WorkModelOption(id: "claude-4.6-sonnet", displayName: "Sonnet 4.6", tier: .balanced, tagline: "Fast coding default", provider: "claude"), ] @@ -312,6 +315,7 @@ private func workCuratedModelCatalogGroups() -> [WorkModelCatalogGroup] { key: "anthropic", displayName: "Anthropic", models: [ + WorkModelOption(id: "opencode/anthropic/claude-fable-5", displayName: "Claude Fable 5", tier: .flagship, tagline: "Flagship · 1M context", provider: "claude"), WorkModelOption(id: "opencode/anthropic/claude-sonnet-4-6", displayName: "Claude Sonnet 4.6", tier: .balanced, tagline: "Balanced coder", provider: "claude"), WorkModelOption(id: "opencode/anthropic/claude-opus-4-8", displayName: "Claude Opus 4.8 1M", tier: .flagship, tagline: "Flagship reasoning · 1M context", provider: "claude"), WorkModelOption(id: "opencode/anthropic/claude-opus-4-7", displayName: "Claude Opus 4.7", tier: .flagship, tagline: "Flagship reasoning", provider: "claude"), @@ -570,6 +574,8 @@ private func workModelLookupKeys(_ raw: String?) -> [String] { private func workCanonicalClaudeRegistryId(for raw: String) -> String? { switch raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() { + case "fable", "claude-fable-5", "anthropic/claude-fable-5", "anthropic/claude-fable-5-api": + return "anthropic/claude-fable-5" case "claude-opus-4-8", "anthropic/claude-opus-4-8", "opus-4.8", "opus-4-8", "opus-4.8-1m", "opus-4.8[1m]", "opus-4-8-1m", "claude-opus-4-8-1m", "claude-opus-4-8[1m]", "anthropic/claude-opus-4-8-1m": @@ -589,6 +595,8 @@ private func workCanonicalClaudeRegistryId(for raw: String) -> String? { private func workClaudeRuntimeModelId(for raw: String) -> String? { switch raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() { + case "fable", "claude-fable-5", "anthropic/claude-fable-5", "anthropic/claude-fable-5-api": + return "claude-fable-5" case "claude-opus-4-8", "anthropic/claude-opus-4-8", "opus-4.8", "opus-4-8", "opus-4.8-1m", "opus-4.8[1m]", "opus-4-8-1m", "claude-opus-4-8-1m", "claude-opus-4-8[1m]", "anthropic/claude-opus-4-8-1m": @@ -652,6 +660,8 @@ func workModelIdsEquivalent(_ lhs: String?, _ rhs: String?) -> Bool { func workKnownModelDisplayName(_ raw: String?) -> String? { switch raw?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() ?? "" { + case "fable", "anthropic/claude-fable-5", "claude-fable-5", "opencode/anthropic/claude-fable-5": + return "Claude Fable 5" case "anthropic/claude-opus-4-8", "claude-opus-4-8", "opus-4.8", "opus-4-8", "opus-4.8-1m", "opus-4.8[1m]", "opus-4-8-1m", "anthropic/claude-opus-4-8-1m", "claude-opus-4-8-1m", "claude-opus-4-8[1m]": @@ -757,7 +767,7 @@ private func workModelProviderKey(for model: AgentChatModelInfo, topLevelProvide if normalizedId.hasPrefix("glm-") || normalizedId.hasPrefix("kimi-") || normalizedId.hasPrefix("minimax-") || normalizedId.hasPrefix("custom:") { return "factory" } - if normalizedId.contains("claude") || normalizedId.contains("sonnet") || normalizedId.contains("opus") || normalizedId.contains("haiku") { + if normalizedId.contains("claude") || normalizedId.contains("fable") || normalizedId.contains("sonnet") || normalizedId.contains("opus") || normalizedId.contains("haiku") { return "anthropic" } if normalizedId.contains("gpt") || normalizedId.contains("codex") { @@ -782,7 +792,7 @@ private func workModelProviderKey(for model: AgentChatModelInfo, topLevelProvide if normalizedFamily == "cursor" { return "cursor" } - if normalizedFamily == "anthropic" || normalizedId.contains("claude") || normalizedId.contains("sonnet") || normalizedId.contains("opus") || normalizedId.contains("haiku") { + if normalizedFamily == "anthropic" || normalizedId.contains("claude") || normalizedId.contains("fable") || normalizedId.contains("sonnet") || normalizedId.contains("opus") || normalizedId.contains("haiku") { return "anthropic" } if normalizedFamily == "openai" || normalizedFamily == "codex" || normalizedId.contains("gpt") || normalizedId.contains("codex") { @@ -861,7 +871,7 @@ private func workDynamicModelTier(for modelId: String, curated: WorkModelOption? if normalized.contains("mini") || normalized.contains("spark") || normalized.contains("flash") || normalized == "auto" || normalized.contains("haiku") { return .fast } - if normalized.contains("opus") || normalized.contains("gpt-5.5") || normalized == "gpt-5" { + if normalized.contains("fable") || normalized.contains("opus") || normalized.contains("gpt-5.5") || normalized == "gpt-5" { return .flagship } return .balanced @@ -966,7 +976,7 @@ func workModelCatalogGroupKey(for currentModelId: String, currentProvider: Strin if modelId.hasPrefix("opencode/") || provider == "opencode" { return "opencode" } - if provider == "anthropic" || provider == "claude" || modelId.hasPrefix("anthropic/") || modelId.contains("claude") || modelId.contains("sonnet") || modelId.contains("opus") || modelId.contains("haiku") { + if provider == "anthropic" || provider == "claude" || modelId.hasPrefix("anthropic/") || modelId.contains("claude") || modelId.contains("fable") || modelId.contains("sonnet") || modelId.contains("opus") || modelId.contains("haiku") { return "claude" } if provider == "openai" || provider == "codex" || modelId.hasPrefix("openai/") || modelId.contains("gpt") || modelId.contains("codex") { diff --git a/apps/ios/ADE/Views/Work/WorkModelPickerSheet.swift b/apps/ios/ADE/Views/Work/WorkModelPickerSheet.swift index f2867cb63..f694a7c8c 100644 --- a/apps/ios/ADE/Views/Work/WorkModelPickerSheet.swift +++ b/apps/ios/ADE/Views/Work/WorkModelPickerSheet.swift @@ -1001,6 +1001,7 @@ struct ModelPickerListRow: View { switch tier.lowercased() { case "xhigh": return "XHigh" case "max": return "Max" + case "ultracode": return "Ultracode" default: return tier.capitalized } } diff --git a/apps/ios/ADETests/ADETests.swift b/apps/ios/ADETests/ADETests.swift index f07d08a28..e64ddfa7e 100644 --- a/apps/ios/ADETests/ADETests.swift +++ b/apps/ios/ADETests/ADETests.swift @@ -7768,11 +7768,17 @@ final class ADETests: XCTestCase { let groups = workModelCatalogGroups(currentModelId: "", currentProvider: "codex") let claudeGroup = groups.first(where: { $0.key == "claude" }) let anthropicProvider = claudeGroup?.providers.first(where: { $0.key == "anthropic" }) + let fable = anthropicProvider?.models.first(where: { $0.id == "claude-fable-5" }) let opus48 = anthropicProvider?.models.first(where: { $0.id == "claude-opus-4-8" }) let codexGroup = groups.first(where: { $0.key == "codex" }) let openAIProvider = codexGroup?.providers.first(where: { $0.key == "openai" }) let gpt55 = openAIProvider?.models.first(where: { $0.id == "gpt-5.5" }) + XCTAssertEqual(anthropicProvider?.models.first?.id, "claude-fable-5") + XCTAssertEqual(fable?.displayName, "Claude Fable 5") + XCTAssertEqual(fable?.tier, .flagship) + XCTAssertEqual(fable?.tagline, "Flagship · 1M context") + XCTAssertNotNil(ADEColor.modelBrand(for: "claude-fable-5")) XCTAssertEqual(opus48?.displayName, "Claude Opus 4.8 1M") XCTAssertEqual(opus48?.tier, .flagship) XCTAssertEqual(opus48?.tagline, "Flagship · 1M context") @@ -7784,12 +7790,16 @@ final class ADETests: XCTestCase { } func testMobileComposerReasoningTiersMirrorDesktopRegistry() { - XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-opus-4-8"), ["low", "medium", "high", "xhigh", "max"]) - XCTAssertEqual(ADEColor.reasoningTiers(for: "claude-opus-4-8"), ["low", "medium", "high", "xhigh", "max"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-fable-5"), ["low", "medium", "high", "xhigh", "max", "ultracode"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-fable-5-api"), ["low", "medium", "high", "xhigh", "max", "ultracode"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "claude-fable-5"), ["low", "medium", "high", "xhigh", "max", "ultracode"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "fable"), ["low", "medium", "high", "xhigh", "max", "ultracode"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-opus-4-8"), ["low", "medium", "high", "xhigh", "max", "ultracode"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "claude-opus-4-8"), ["low", "medium", "high", "xhigh", "max", "ultracode"]) XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-opus-4-7"), ["low", "medium", "high", "xhigh", "max"]) XCTAssertEqual(ADEColor.reasoningTiers(for: "claude-opus-4-7"), ["low", "medium", "high", "xhigh", "max"]) XCTAssertEqual(ADEColor.reasoningTiers(for: "opus"), ["low", "medium", "high", "xhigh", "max"]) - XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-sonnet-4-6"), ["low", "medium", "high"]) + XCTAssertEqual(ADEColor.reasoningTiers(for: "anthropic/claude-sonnet-4-6"), ["low", "medium", "high", "max"]) XCTAssertNil(ADEColor.reasoningTiers(for: "claude-haiku-4-5")) XCTAssertEqual(ADEColor.reasoningTiers(for: "openai/gpt-5.3-codex-spark"), ["low", "medium", "high", "xhigh"]) XCTAssertEqual(ADEColor.reasoningTiers(for: "gpt-5.2"), ["low", "medium", "high", "xhigh"])