diff --git a/README.md b/README.md index 22cff7b..8bdefdf 100644 --- a/README.md +++ b/README.md @@ -392,7 +392,7 @@ Statuses: `done` (agent chose to stop), `goal_achieved` (the goal watcher fired) ## The typing model -Jev never generates text. It returns typed decisions only: which option, with what probabilities. So when a task needs a string, typing a search query or filling a field, that string comes from a small model you choose. This is the only place a second model is involved, and it runs once per typed field, so a multi-field form makes one call per field. Each call is capped at 256 output tokens on OpenRouter (reasoning is disabled there, since reasoning models can burn the whole budget on hidden tokens and return empty text) and 48 tokens on every other provider. +Jev never generates text. It returns typed decisions only: which option, with what probabilities. So when a task needs a string, typing a search query or filling a field, that string comes from a small model you choose. This is the only place a second model is involved, and it runs once per typed field, so a multi-field form makes one call per field. Each call is capped at 256 output tokens on OpenRouter and Google and 48 tokens on every other provider. Reasoning is disabled on OpenRouter and on Gemini Flash models (reasoning models can burn the whole budget on hidden tokens and return empty text; Gemini 3 Flash keeps its minimum thinking level). Gemini Pro models cannot turn thinking that low, so they keep their default thinking. `JEV_PROVIDER` is not involved here at all: it selects the transport for the Jev judgments and has nothing to do with typing. Typing configuration is a separate set of variables. diff --git a/package-lock.json b/package-lock.json index 635b442..559e012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,18 +10,18 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@ai-sdk/anthropic": "^2.0.0", - "@ai-sdk/google": "2.0.9", - "@ai-sdk/openai": "^2.0.0", - "@ai-sdk/openai-compatible": "^1.0.0", + "@ai-sdk/anthropic": "^4.0.63", + "@ai-sdk/google": "^4.0.80", + "@ai-sdk/openai": "^4.0.75", + "@ai-sdk/openai-compatible": "^3.0.55", "@jkudish/jev-agent-tools": "^0.1.0", "@modelcontextprotocol/sdk": "^1.17.0", "@typesafe-ai/sdk": "^0.6.0", - "ai": "^7.0.105", + "ai": "^7.0.114", "playwright": "^1.55.0", "turndown": "^7.2.0", "turndown-plugin-gfm": "^1.0.2", - "zod": "^3.25.0" + "zod": "^4.6.5" }, "bin": { "jev-browser": "dist/index.js" @@ -29,23 +29,23 @@ "devDependencies": { "@types/node": "^22.0.0", "@types/turndown": "^5.0.5", - "typescript": "^5.6.0" + "typescript": "^7.0.2" }, "engines": { "node": ">=22" } }, "node_modules/@ai-sdk/anthropic": { - "version": "2.0.105", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.105.tgz", - "integrity": "sha512-yz+RATWLmsvxKuswRxuvasUlrhxgwCY9RrafM2q2ZBax7XFDuiMR7fv/syMTNRtLo4Y3Oclz8x89V28TiAvARA==", + "version": "4.0.65", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.65.tgz", + "integrity": "sha512-3fmxxn5xWGOPU1wGrUCI30YFW5r6h2WrY+sz3rokkVlBHrDr889hPYP1yNoSmOVvtAwCFWlm79P2lRngIPOObg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.5", - "@ai-sdk/provider-utils": "3.0.39" + "@ai-sdk/provider": "4.0.18", + "@ai-sdk/provider-utils": "5.0.49" }, "engines": { - "node": ">=18" + "node": ">=22" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" @@ -68,18 +68,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.18.tgz", - "integrity": "sha512-+GZJIgz1jk86pwEbb3f1BD2bdoSKyWE4Jg4YUc7NMnMozbWemSKYZcw2F4nMaO5qwsL5A8RmioAKW81YktRpIQ==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=22" - } - }, "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider-utils": { "version": "5.0.47", "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.47.tgz", @@ -99,131 +87,85 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/gateway/node_modules/undici": { - "version": "7.30.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", - "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, "node_modules/@ai-sdk/google": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-2.0.9.tgz", - "integrity": "sha512-sWhuiRzsVEBS3HOyvqgRiMahLxK2OEBs+x6RZMnibHt4nwHOl8AjJ7UVstJtJHo4U2YX8iaMSJMU+qhX1wRPTA==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@ai-sdk/provider-utils": "3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4" - } - }, - "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", - "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "version": "4.0.82", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.82.tgz", + "integrity": "sha512-2zPYyBSyIcywLtOEJRxlrtXjBjz4KxEU19Qqg/Dk8I9BrsB08+ghG2jv155dtuDZj8qZMrh6LO8J+Wt5O4xVhg==", "license": "Apache-2.0", "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider-utils": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.6.tgz", - "integrity": "sha512-s1+9okDSqbxKvwf1mqyyqtOY27/RV9O+XTzaRKEamVKbmVBM7BiCSfui7vH7A/1EETECtTJkS2MUL5D3Pw5GFw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@standard-schema/spec": "^1.0.0", - "eventsource-parser": "^3.0.3" + "@ai-sdk/provider": "4.0.18", + "@ai-sdk/provider-utils": "5.0.49" }, "engines": { - "node": ">=18" + "node": ">=22" }, "peerDependencies": { - "zod": "^3.25.76 || ^4" + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@ai-sdk/openai": { - "version": "2.0.130", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.130.tgz", - "integrity": "sha512-Amwroc0eHCr03Kcc6BEHz1z7SDEDloj9+9ORFT/Hf8FKpKZZauEhohglt3TPFBuD3lqUTQbVrzKyF5fvG8s16Q==", + "version": "4.0.77", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.77.tgz", + "integrity": "sha512-KUCeRneOIww8az/mH3dy68csmOTqa7s0F6fiHEkdXtr1u0xUoQTIU4y7k0YrjjBuyybpX2yN94rf9elhPu4zFg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.5", - "@ai-sdk/provider-utils": "3.0.39" + "@ai-sdk/provider": "4.0.18", + "@ai-sdk/provider-utils": "5.0.49" }, "engines": { - "node": ">=18" + "node": ">=22" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "1.0.56", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-1.0.56.tgz", - "integrity": "sha512-S84f0SC506XBLEKnMq/Ldinm240z6hOHbl+vHqkdNQ965JqJLWV5D+WBWQ1EriDkbvfoXeJpzckc9ktSOt6nqQ==", + "version": "3.0.57", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.57.tgz", + "integrity": "sha512-TEY5Ng/bTJXrYy9wpdxK70gABdYejYedaKbmPCZLhBkPe21rmLzkGxAGkfPYgAfCKPaoAO9THrEhmwUZ4Xhg2Q==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.5", - "@ai-sdk/provider-utils": "3.0.39" + "@ai-sdk/provider": "4.0.18", + "@ai-sdk/provider-utils": "5.0.49" }, "engines": { - "node": ">=18" + "node": ">=22" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@ai-sdk/provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.5.tgz", - "integrity": "sha512-cmG40u10QFEUrYXWYom6Lo6DLR+pND3wWABTTT9Nf7zg9F7BD46KNj+STekndp9Kg5ZAIPA0WxmTI0IwRvKvvQ==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.18.tgz", + "integrity": "sha512-+GZJIgz1jk86pwEbb3f1BD2bdoSKyWE4Jg4YUc7NMnMozbWemSKYZcw2F4nMaO5qwsL5A8RmioAKW81YktRpIQ==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" }, "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/@ai-sdk/provider-utils": { - "version": "3.0.39", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.39.tgz", - "integrity": "sha512-Cb1GE1UKcZxouWhV7weIQoJTgyz4WMswQfapPClN4QQP1pO01ZNSbG9EVN5C0gCsikA8vM0wrJZIT7HiZmH3wA==", + "version": "5.0.49", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.49.tgz", + "integrity": "sha512-T+/H8DCvqJoCqLhltVatS7ffA779Iyk/ZZrgNXkrgv51E0n2Cd6eIdCmtwhCGIKqtbTtwCvTmQdAMv5Z8rOGHQ==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.5", - "@standard-schema/spec": "^1.0.0", - "eventsource-parser": "^3.0.6", - "undici": "^5.29.0" + "@ai-sdk/provider": "4.0.18", + "@standard-schema/spec": "^1.1.0", + "@workflow/serde": "4.1.0", + "eventsource-parser": "^3.0.8", + "undici": "^7.29.0" }, "engines": { - "node": ">=18" + "node": ">=22" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, "node_modules/@hono/node-server": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", @@ -323,6 +265,346 @@ "node": ">=20" } }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@vercel/oidc": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz", @@ -368,18 +650,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/ai/node_modules/@ai-sdk/provider": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.18.tgz", - "integrity": "sha512-+GZJIgz1jk86pwEbb3f1BD2bdoSKyWE4Jg4YUc7NMnMozbWemSKYZcw2F4nMaO5qwsL5A8RmioAKW81YktRpIQ==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=22" - } - }, "node_modules/ai/node_modules/@ai-sdk/provider-utils": { "version": "5.0.47", "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.47.tgz", @@ -399,15 +669,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/ai/node_modules/undici": { - "version": "7.30.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", - "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -1526,29 +1787,47 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", "dev": true, "license": "Apache-2.0", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "tsc": "bin/tsc" }, "engines": { - "node": ">=14.17" + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" } }, "node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", + "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, "engines": { - "node": ">=14.0" + "node": ">=20.18.1" } }, "node_modules/undici-types": { @@ -1598,9 +1877,9 @@ "license": "ISC" }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 32d06e8..efbd07b 100644 --- a/package.json +++ b/package.json @@ -54,23 +54,23 @@ "automation" ], "dependencies": { - "@ai-sdk/anthropic": "^2.0.0", - "@ai-sdk/google": "2.0.9", - "@ai-sdk/openai": "^2.0.0", - "@ai-sdk/openai-compatible": "^1.0.0", + "@ai-sdk/anthropic": "^4.0.63", + "@ai-sdk/google": "^4.0.80", + "@ai-sdk/openai": "^4.0.75", + "@ai-sdk/openai-compatible": "^3.0.55", "@jkudish/jev-agent-tools": "^0.1.0", "@modelcontextprotocol/sdk": "^1.17.0", "@typesafe-ai/sdk": "^0.6.0", - "ai": "^7.0.105", + "ai": "^7.0.114", "playwright": "^1.55.0", "turndown": "^7.2.0", "turndown-plugin-gfm": "^1.0.2", - "zod": "^3.25.0" + "zod": "^4.6.5" }, "devDependencies": { "@types/node": "^22.0.0", "@types/turndown": "^5.0.5", - "typescript": "^5.6.0" + "typescript": "^7.0.2" }, "publishConfig": { "access": "public" diff --git a/src/navigate.ts b/src/navigate.ts index 3744642..7c78e0a 100644 --- a/src/navigate.ts +++ b/src/navigate.ts @@ -176,8 +176,8 @@ export function createTypingGenerator(env: NodeJS.ProcessEnv = process.env): Typ return { provider: "google", modelId: selection.modelId, - // @ai-sdk/google@2 speaks model spec v2, matching the ai@7 core; no - // compatibility cast is needed or wanted here. + // Every @ai-sdk provider here implements model spec v4, the native + // spec of the ai@7 core, so no compatibility cast is needed or wanted. model: createGoogleGenerativeAI({ apiKey: (env.GOOGLE_GENERATIVE_AI_API_KEY ?? env.GEMINI_API_KEY)!, ...(selection.baseUrl ? { baseURL: selection.baseUrl } : {}), @@ -194,6 +194,11 @@ export function createTypingGenerator(env: NodeJS.ProcessEnv = process.env): Typ } } +// Gemini Flash families whose thinking can be switched off (2.5) or dropped to +// a minimum level (3+) through the AI SDK's reasoning: "none". Pro models +// cannot go that low and pre-2.5 models have no thinking configuration. +const GEMINI_FLASH_THINKING_OFF = /^(models\/)?gemini-(2\.5|[3-9](\.\d+)?)-flash|^(models\/)?gemini-flash(-lite)?-latest$/; + export type TypingTextResult = | { ok: true; text: string; via: string } | { ok: false; code: TypingWarningCode; message: string; finishReason?: string }; @@ -211,17 +216,24 @@ export async function generateTextToType( elementDescription: string, url: string, ): Promise { - // OpenRouter reasoning models can burn the whole budget on hidden reasoning - // tokens and return an empty message, so reasoning is disabled there and - // the output budget raised; other providers keep the tight cap. - const openrouter = generator.provider === "openrouter"; + // Reasoning models (OpenRouter's, and Gemini, which thinks by default) can + // burn the whole budget on hidden reasoning tokens and return an empty + // message, so reasoning is disabled there and the output budget raised; + // other providers keep the tight cap. On Google only Gemini Flash models + // accept the "none" mapping (budget 0 on 2.5, minimum level on 3+, which + // still thinks a little, hence the headroom); Pro and older models reject + // it, so they keep their default thinking and just get the larger cap. + const generationLimits = + generator.provider === "openrouter" + ? { maxOutputTokens: 256, providerOptions: { openrouter: { reasoning: { enabled: false } } } } + : generator.provider === "google" + ? { maxOutputTokens: 256, ...(GEMINI_FLASH_THINKING_OFF.test(generator.modelId) ? { reasoning: "none" as const } : {}) } + : { maxOutputTokens: 48 }; try { const { text, finishReason } = await generateText({ model: generator.model, prompt: `A browser agent is performing this task: "${task}". It must type into the ${elementDescription} on ${url}. Reply with ONLY the exact text to type (for a search box: a short search query; no quotes, no explanation).`, - ...(openrouter - ? { maxOutputTokens: 256, providerOptions: { openrouter: { reasoning: { enabled: false } } } } - : { maxOutputTokens: 48 }), + ...generationLimits, abortSignal: signal, }); const cleaned = text.trim().replace(/^["']|["']$/g, ""); diff --git a/test/unit.test.mjs b/test/unit.test.mjs index 631d369..651a6f6 100644 --- a/test/unit.test.mjs +++ b/test/unit.test.mjs @@ -970,7 +970,7 @@ test("generateTextToType: BASE_URL routes every named provider to the configured } }); -test("generateTextToType: the google provider generates without a compatibility cast", async () => { +test("generateTextToType: google switches thinking off and raises the cap", async () => { const realFetch = globalThis.fetch; let seen; globalThis.fetch = async (url, init) => { @@ -986,16 +986,57 @@ test("generateTextToType: the google provider generates without a compatibility const out = await generateTextToType(new AbortController().signal, generator, "task", "the search box", "https://x.test/"); assert.deepEqual(out, { ok: true, text: "ristretto", via: "google" }); assert.match(seen.url, /generativelanguage\.googleapis\.com/); - assert.equal(seen.body.generationConfig.maxOutputTokens, 48); // tight cap, no openrouter options - // google models reasoning controls via generationConfig.thinkingConfig; the - // openrouter reasoning namespace must not leak into any google request - assert.equal(seen.body.generationConfig.thinkingConfig, undefined); + // Gemini thinks by default and can spend a 48-token cap on hidden thoughts, + // returning nothing: thinking is switched off (budget 0 on 2.5) and the + // cap raised, via generationConfig.thinkingConfig. The openrouter + // reasoning namespace must not leak into any google request. + assert.equal(seen.body.generationConfig.maxOutputTokens, 256); + assert.deepEqual(seen.body.generationConfig.thinkingConfig, { thinkingBudget: 0 }); assert.equal(seen.body.generationConfig.reasoning, undefined); + assert.equal(seen.body.reasoning, undefined); + + // Gemini 3 cannot disable thinking; "none" maps to its minimum level + const gemini3 = createTypingGenerator({ GEMINI_API_KEY: "AIza-google-key-0123456789", JEV_BROWSER_TYPE_MODEL: "gemini-3-flash" }); + await generateTextToType(new AbortController().signal, gemini3, "task", "the search box", "https://x.test/"); + assert.deepEqual(seen.body.generationConfig.thinkingConfig, { thinkingLevel: "minimal" }); + + // Only Flash families get the override: Pro models reject budget 0 (2.5) + // and "minimal" (3.x), and pre-2.5 models have no thinking config at all. + // They keep their default thinking and only take the raised cap. + const cases = [ + ["gemini-2.5-flash-lite", { thinkingBudget: 0 }], + ["gemini-2.5-pro", undefined], + ["gemini-3.1-pro-preview", undefined], + ["gemini-2.0-flash", undefined], + ]; + for (const [model, thinkingConfig] of cases) { + const g = createTypingGenerator({ GEMINI_API_KEY: "AIza-google-key-0123456789", JEV_BROWSER_TYPE_MODEL: model }); + await generateTextToType(new AbortController().signal, g, "task", "the search box", "https://x.test/"); + assert.deepEqual(seen.body.generationConfig.thinkingConfig, thinkingConfig, model); + assert.equal(seen.body.generationConfig.maxOutputTokens, 256, model); + } } finally { globalThis.fetch = realFetch; } }); +test("createTypingGenerator: every provider builds a native spec-v4 model (no ai@7 compatibility mode)", () => { + const cases = [ + { OPENAI_API_KEY: "sk-openai-key-0123456789" }, + { OPENROUTER_API_KEY: "sk-or-v1-openrouter-key-0123456789" }, + { JEV_BROWSER_TYPE_PROVIDER: "anthropic", ANTHROPIC_API_KEY: "sk-ant-anthropic-key-0123456789" }, + { GEMINI_API_KEY: "AIza-google-key-0123456789" }, + { JEV_BROWSER_TYPE_BASE_URL: "http://typing.test/v1", JEV_BROWSER_TYPE_MODEL: "local-model" }, + ]; + const seen = new Set(); + for (const env of cases) { + const generator = createTypingGenerator(env); + seen.add(generator.provider); + assert.equal(generator.model.specificationVersion, "v4", `${generator.provider} model is not spec v4`); + } + assert.equal(seen.size, cases.length); // each case reached a distinct provider branch +}); + // ── detectBotProtection ───────────────────────────────────────────────────── // The scoring rules: a cf-mitigated header or a known challenge title is // decisive on its own; body markers need three to stand alone, so an article diff --git a/tsconfig.json b/tsconfig.json index 34f8c08..9681abd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "outDir": "dist", "rootDir": "src", "strict": true, + "types": ["node"], "declaration": true, "sourceMap": true, "esModuleInterop": true,