@bitkyc08/opencodex 2.62.0-preview.20260923 → 2.63.0-preview.20260923
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/dist/assets/App-EJxiUFMq.js +50 -0
- package/gui/dist/assets/{Tray-DdvPBVLi.js → Tray-B03pW-Uf.js} +1 -1
- package/gui/dist/assets/index-BmJwNBHL.js +86 -0
- package/gui/dist/assets/index-DdDunwDb.css +1 -0
- package/gui/dist/assets/{usage-companion-chart-BIhvlbQW.js → usage-companion-chart-IftE60UK.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/cursor/catalog.ts +15 -0
- package/src/adapters/cursor/effort-map.ts +7 -0
- package/src/adapters/cursor/envelope-echo.ts +51 -25
- package/src/adapters/cursor/protobuf-request.ts +39 -13
- package/src/adapters/cursor.ts +52 -4
- package/src/adapters/devin/cloud-direct/chat.ts +50 -16
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +4 -1
- package/src/adapters/devin/live-models.ts +7 -0
- package/src/adapters/devin.ts +7 -5
- package/src/adapters/kiro/reasoning.ts +5 -0
- package/src/adapters/openai-responses/passthrough.ts +4 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +5 -3
- package/src/claude/desktop-gateway-state.ts +7 -2
- package/src/claude/desktop-policy.ts +105 -1
- package/src/cli/config-command.ts +22 -7
- package/src/cli/doctor.ts +14 -4
- package/src/codex/catalog/effort.ts +35 -4
- package/src/codex/catalog/metadata.ts +33 -5
- package/src/codex/catalog/native-models.ts +43 -2
- package/src/codex/catalog/pinned-models.ts +37 -0
- package/src/codex/catalog-auto-refresh.ts +6 -0
- package/src/codex/data/roster-pinned-models.json +359 -0
- package/src/codex/data/upstream-models.json +365 -271
- package/src/codex/inject/provider-table.ts +108 -0
- package/src/codex/inject/remove.ts +2 -114
- package/src/codex/model-entitlements.ts +14 -10
- package/src/codex/subagent-defaults.ts +2 -109
- package/src/codex/toml-source-lines.ts +112 -0
- package/src/config/live-reconcile.ts +145 -27
- package/src/config/load-degrade.ts +3 -4
- package/src/config.ts +2 -2
- package/src/generated/compatibility-version.json +83 -67
- package/src/generated/model-metadata.ts +5 -5
- package/src/lab/artifacts/sanitize.ts +60 -19
- package/src/lib/app-owned-memory-stores.ts +7 -0
- package/src/lib/app-owned-memory.ts +13 -2
- package/src/lib/errors.ts +6 -4
- package/src/lib/upstream-retry.ts +35 -5
- package/src/oauth/devin.ts +43 -37
- package/src/providers/codebuddy-models.ts +11 -0
- package/src/providers/kiro-models.ts +9 -0
- package/src/providers/quota/vendor-probes-oauth.ts +9 -2
- package/src/providers/registry/entries-core.ts +19 -8
- package/src/providers/registry/entries-extended.ts +4 -1
- package/src/providers/registry/model-seeds.ts +22 -2
- package/src/responses/bridge-search-replay-cache.ts +20 -10
- package/src/responses/plaintext-v2-agent-messages.ts +10 -1
- package/src/routing/identity-domains.ts +22 -15
- package/src/server/index/websocket-handler.ts +22 -2
- package/src/server/management/agent-settings-routes.ts +19 -10
- package/src/server/management/context.ts +4 -2
- package/src/server/responses/codex-ws-exchange.ts +24 -8
- package/src/server/responses/core-codex-account.ts +4 -0
- package/src/server/responses/core-combo-failure.ts +16 -9
- package/src/server/responses/core-combo.ts +7 -3
- package/src/server/responses/core-options.ts +6 -0
- package/src/server/responses/native-injection-replay.ts +13 -1
- package/src/server/responses/native-injection.ts +66 -7
- package/src/server/responses/native-response-control.ts +6 -2
- package/src/server/responses/native-steering-replay.ts +60 -0
- package/src/server/responses/native-steering.ts +9 -1
- package/src/server/responses/passthrough-delivery.ts +3 -4
- package/src/server/responses/passthrough-dispatch.ts +7 -0
- package/src/server/responses/request-prepare.ts +12 -0
- package/src/server/responses/request-transport.ts +5 -2
- package/src/server/responses/ws-upstream.ts +1 -1
- package/src/server/ws-bridge.ts +17 -1
- package/src/types/request.ts +5 -0
- package/src/usage/expected-prices.ts +38 -8
- package/src/web-search/executor.ts +38 -13
- package/gui/dist/assets/App-CANWata-.js +0 -50
- package/gui/dist/assets/index-BIS5HXEP.js +0 -86
- package/gui/dist/assets/index-_bpvxJu0.css +0 -1
package/gui/dist/assets/{usage-companion-chart-BIhvlbQW.js → usage-companion-chart-IftE60UK.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./index-BIS5HXEP.js";var t={anthropic:`claude-color.svg`,"anthropic-apikey":`claude-color.svg`,"azure-openai":`openai.svg`,chatgpt:`openai.svg`,"cloudflare-ai-gateway":`cloudflare-ai-gateway-color.svg`,"cloudflare-workers-ai":`cloudflare-ai-gateway-color.svg`,cline:`cline-color.svg`,"cline-pass":`cline-color.svg`,"command-code":`commandcode-color.svg`,commandcode:`commandcode-color.svg`,cursor:`cursor-color.svg`,deepseek:`deepseek-color.svg`,devin:`devin.svg`,firepass:`firepass-color.svg`,fireworks:`fireworks-color.svg`,github:`github-copilot-color.svg`,"github-copilot":`copilot-color.svg`,"gitlab-duo":`gitlab-duo-color.svg`,google:`gemini-color.svg`,"google-antigravity":`antigravity-color.svg`,"google-vertex":`gemini-color.svg`,groq:`groq-color.svg`,huggingface:`huggingface-color.svg`,kimi:`kimi-color.svg`,"kimi-code":`kimi-color.svg`,kiro:`kiro-color.svg`,"lm-studio":`lm-studio-color.svg`,"meta-model":`meta.svg`,"meta-muse":`meta.svg`,mistral:`mistral-color.svg`,minimax:`minimax.svg`,"minimax-cn":`minimax.svg`,moonshot:`moonshot-color.svg`,nvidia:`nvidia-color.svg`,ollama:`ollama-color.svg`,"ollama-cloud":`ollama-color.svg`,openai:`openai.svg`,"openai-apikey":`openai.svg`,"opencode-free":`opencode.svg`,"opencode-go":`opencode.svg`,"opencode-zen":`opencode.svg`,openrouter:`openrouter-color.svg`,opper:`opper.svg`,qianfan:`qianfan-color.svg`,qoder:`qoder.svg`,"qoder-cn":`qoder.svg`,alibaba:`alibaba-color.svg`,"alibaba-token-plan":`alibaba-color.svg`,"alibaba-token-plan-intl":`alibaba-color.svg`,baseten:`baseten.svg`,bizrouter:`bizrouter.svg`,cerebras:`cerebras.svg`,crusoe:`crusoe.svg`,deepinfra:`deepinfra.svg`,digitalocean:`digitalocean.svg`,featherless:`featherless.svg`,hyperbolic:`hyperbolic.svg`,kilo:`kilo.svg`,nanogpt:`nanogpt.svg`,nebius:`nebius.svg`,neuralwatt:`neuralwatt.svg`,nous:`nous.svg`,novita:`novita.svg`,orcarouter:`orcarouter.svg`,"orcarouter-oauth":`orcarouter.svg`,packycode:`packycode.svg`,parallel:`parallel.svg`,sambanova:`sambanova.svg`,scaleway:`scaleway.svg`,stepfun:`stepfun-color.svg`,siliconflow:`siliconflow.svg`,synthetic:`synthetic.svg`,together:`together.svg`,umans:`umans.svg`,venice:`venice.svg`,vultr:`vultr.svg`,litellm:`litellm.svg`,zenmux:`zenmux.svg`,zai:`zai.svg`,"zhipu-bigmodel":`zai.svg`,"zhipu-bigmodel-coding":`zai.svg`,"qwen-cloud":`qwen-portal-color.svg`,"vercel-ai-gateway":`vercel-ai-gateway-color.svg`,vllm:`vllm-color.svg`,xai:`grok.svg`,"mimo-free":`xiaomi-color.svg`,mimo:`xiaomi-color.svg`,xiaomi:`xiaomi-color.svg`,"xiaomi-mimo":`xiaomi-color.svg`},n={anthropic:`Anthropic Claude`,"anthropic-apikey":`Anthropic Claude`,chatgpt:`ChatGPT`,openai:`OpenAI (Codex login)`,"openai-apikey":`OpenAI API`,"azure-openai":`Azure OpenAI`,"cloudflare-ai-gateway":`Cloudflare AI Gateway`,"cloudflare-workers-ai":`Cloudflare Workers AI`,cline:`Cline`,"cline-pass":`ClinePass`,nvidia:`NVIDIA NIM`,ollama:`Ollama`,"ollama-cloud":`Ollama Cloud`,xai:`xAI Grok`,"mimo-free":`MiMo Free`,xiaomi:`Xiaomi`,cursor:`Cursor`,deepseek:`DeepSeek`,devin:`Devin`,github:`GitHub`,"github-copilot":`GitHub Copilot`,"gitlab-duo":`GitLab Duo`,openrouter:`OpenRouter`,opper:`Opper`,"opencode-go":`OpenCode Go`,"opencode-free":`OpenCode Free`,"opencode-zen":`OpenCode Zen`,orcarouter:`OrcaRouter - API`,"orcarouter-oauth":`OrcaRouter - Auth`,packycode:`PackyCode`,mistral:`Mistral`,groq:`Groq`,"meta-model":`Meta Model API`,"meta-muse":`Muse Code`,alibaba:`Alibaba Coding Plan`,"alibaba-token-plan":`Alibaba Token Plan`,"alibaba-token-plan-intl":`Alibaba Token Plan (Intl)`,kimi:`Kimi`,"kimi-code":`Kimi`,moonshot:`Moonshot`,google:`Google`,"google-vertex":`Google Vertex`,"lm-studio":`LM Studio`,huggingface:`Hugging Face`,"qwen-cloud":`Qwen Cloud`,siliconflow:`SiliconFlow`,stepfun:`StepFun`,"tencent-coding-plan":`Tencent Cloud Coding Plan`,codebuddy:`CodeBuddy`,"codebuddy-cn":`CodeBuddy CN`,qoder:`Qoder`,"qoder-cn":`Qoder CN`,"vercel-ai-gateway":`Vercel AI Gateway`,vllm:`vLLM`,litellm:`LiteLLM`},r={"command-code":`provider.name.commandCodeAuth`,commandcode:`provider.name.commandCodeApi`,orcarouter:`provider.name.orcaRouterApi`,"orcarouter-oauth":`provider.name.orcaRouterAuth`,volcengine:`provider.name.volcengine`,"volcengine-coding-plan":`provider.name.volcengineCodingPlan`,"volcengine-agent-plan":`provider.name.volcengineAgentPlan`},i=new Set([...Object.keys(n),...Object.keys(r)]);function a(e){let n=e.toLowerCase();return Object.hasOwn(t,n)?t[n]:void 0}function o(e,t){let n=a(e);return n?`/provider-icons/${n}`:void 0}var s=new Set([`cerebras.svg`,`deepinfra.svg`,`neuralwatt.svg`,`nous.svg`,`novita.svg`,`packycode.svg`,`opper.svg`,`siliconflow.svg`,`synthetic.svg`,`zenmux.svg`,`grok.svg`,`kimi-color.svg`,`ollama-color.svg`,`opencode.svg`,`vercel-ai-gateway-color.svg`]),c=new Set([`baseten.svg`,`kilo.svg`,`sambanova.svg`,`venice.svg`,`zai.svg`]),l=new Set([`bizrouter.svg`,`featherless.svg`,`hyperbolic.svg`,`nebius.svg`,`parallel.svg`,`umans.svg`]);function u(e){if(!e)return`image`;let t=e.split(`/`).pop()??``;return s.has(t)?`mask`:c.has(t)?`plate`:l.has(t)?`dark-plate`:`image`}function d(e,t){let i=e.toLowerCase(),a=Object.hasOwn(r,i)?r[i]:void 0;return a?t(a):(Object.hasOwn(n,i)?n[i]:void 0)||(e===i&&/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e)?e.split(`-`).map(e=>e&&e[0].toUpperCase()+e.slice(1)).join(` `):e)}function f(e){return i.has(e.toLowerCase())}function p(e){return e===`command-code`?`commandcode-auth`:e===`commandcode`?`commandcode-api`:e}function m(e,t){let n=e.indexOf(`/`);if(n<=0)return e;let r=e.slice(0,n),i=e.slice(n+1);if(r===`command-code`||r===`commandcode`){let e=i.match(/^([a-z0-9]+)-([a-z0-9]+(?:-[a-z0-9]+)+)$/i);return e&&i.startsWith(`${e[1]}-${e[1]}-`)&&(i=i.slice(e[1].length+1)),`${r===`command-code`?`commandcode-auth`:`commandcode-api`}/${i}`}return e}var h={ko:[{v:0x2386f26fc10000,s:`경`},{v:0xe8d4a51000,s:`조`},{v:1e8,s:`억`},{v:1e4,s:`만`}],zh:[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`亿`},{v:1e4,s:`万`}],"zh-TW":[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`億`},{v:1e4,s:`萬`}]};function g(e){return e.replace(/\.0+$/,``).replace(/(\.\d*?)0+$/,`$1`)}function _(e,t){let n=h[t];if(n){for(let t of n)if(e>=t.v)return`${g((e/t.v).toFixed(1))}${t.s}`;return String(e)}return e<1e4?String(e):e<1e6?`${g((e/1e3).toFixed(1))}K`:e<1e9?`${g((e/1e6).toFixed(1))}M`:e<0xe8d4a51000?`${g((e/1e9).toFixed(1))}B`:`${g((e/0xe8d4a51000).toFixed(1))}T`}var v=1e10;function y(e){return e<v?e*1e3:e}function b(e,t){let n=e=>typeof e==`number`&&Number.isFinite(e);if(!n(e.shortPercent)||e.shortPercent<100)return!1;let r=e.shortResetAt;if(!n(r)||r<=0){let r=e.shortObservedAt;if(!n(r))return!1;let i=t-r;return i>=0&&i<=3e5}return y(r)>t}function x(e){return{fiveHourAvailable:e?.shortWindowSeconds===18e3&&typeof e.shortResetAt==`number`,weeklyAvailable:typeof e?.weeklyResetAt==`number`}}function S(e){let t=e?.trim().toLowerCase();return t===`go`||t===`free`}function C(e,t){if(!e)return null;let n=e.shortPercent===void 0&&e.shortResetAt===void 0?e:{...e,fiveHourPercent:e.fiveHourPercent??e.shortPercent,fiveHourResetAt:e.fiveHourResetAt??e.shortResetAt};return S(t)?{...n.monthlyPercent===void 0?{}:{monthlyPercent:n.monthlyPercent},...n.monthlyResetAt===void 0?{}:{monthlyResetAt:n.monthlyResetAt},...n.creditsUsd===void 0?{}:{creditsUsd:n.creditsUsd},...n.resetCredits===void 0?{}:{resetCredits:n.resetCredits},updatedAt:n.updatedAt}:n}function w(e,t,n=Date.now()){if(!e)return null;let r=e=>typeof e==`number`&&Number.isFinite(e),i=r(e.fiveHourPercent)?e.fiveHourPercent:r(e.shortPercent)?e.shortPercent:void 0,a=(S(t)?[e.monthlyPercent]:[e.weeklyPercent,e.monthlyPercent]).filter(r);if(a.length===0)return b({...r(i)?{shortPercent:i}:{},...r(e.fiveHourResetAt??e.shortResetAt)?{shortResetAt:e.fiveHourResetAt??e.shortResetAt}:{},...r(e.shortObservedAt)?{shortObservedAt:e.shortObservedAt}:{}},n)?100:null;let o=r(i)?[...a,i]:a;return o.length?Math.max(...o):null}function T(e){let t=new URLSearchParams({hours:String(e.chartHours),bucketMinutes:String(e.bucketMinutes),metric:e.tokenMetric,aggregation:e.aggregation,grouping:e.chartGrouping});e.models?.length&&t.set(`models`,e.models.join(`,`));for(let n of e.hiddenProviders)t.append(`hiddenProvider`,n);return t}function E(e){return Array.isArray(e)&&e.length<=100&&e.every(e=>typeof e==`string`)?JSON.stringify([...new Set(e)].sort()):void 0}function D(e,t){let n=new Set(t.hiddenProviders),r=e.availableModels.filter(e=>!n.has(e.slice(0,e.indexOf(`/`))));if(t.models?.length===0)return{...e,series:[],availableModels:r};let i=t.models===null?null:new Set(t.models),a=i!==null||n.size>0,o=e.appliedFilters,s=!!o&&E(o.hiddenProviders)===E(t.hiddenProviders)&&(t.models===null?o.models===null:E(o.models)===E(t.models)),c=e.series.filter(e=>e.id===`other`&&e.provider===``?!a||s:!n.has(e.provider)&&(i===null||i.has(`${e.provider}/${e.model}`)||i.has(e.model)));return{...e,series:c,availableModels:r,truncated:e.truncated||(a||o!==void 0)&&!s}}var O={6:15,24:60,72:180,168:360};function k(e){return O[e]}function A(e){if(e<1e3)return String(Math.round(e));let t=[[0xe8d4a51000,`T`],[1e9,`B`],[1e6,`M`],[1e3,`K`]];for(let n=0;n<t.length;n+=1){let[r,i]=t[n];if(e>=r){let a=Math.round(e/r);if(a>=1e3&&n>0){let[r,i]=t[n-1];return`${Math.round(e/r)}${i}`}return`${a}${i}`}}return String(Math.round(e))}function j(e,t){let n=new Map;for(let r of e){let e=r.includes(`/`)?r.slice(0,r.indexOf(`/`)):r,i=n.get(e)??{provider:e,models:[],total:0},a=t.get(r)??0;i.models.push({id:r,total:a}),i.total+=a,n.set(e,i)}return Array.from(n.values()).map(e=>({...e,models:e.models.toSorted((e,t)=>t.total-e.total||e.id.localeCompare(t.id))})).toSorted((e,t)=>t.total-e.total||e.provider.localeCompare(t.provider))}function M(e,t,n,r){let i=new Set(t),a=new Set((e??t).filter(e=>i.has(e)));for(let e of n)r?a.add(e):a.delete(e);return t.length>0&&t.every(e=>a.has(e))?null:t.filter(e=>a.has(e))}function N(e,t=[]){let n={...e};if(typeof n.menuBarTemplate==`string`&&n.menuBarTemplate.trim()===``&&(n.menuBarTemplate=null),n.models!==void 0&&t.length>0){let e=n.models??[],r=new Set(e);e.length===t.length&&t.every(e=>r.has(e))&&(n.models=null)}return n}function P(e,t,n,r,i=8){let a=Math.max(0,t-i*2),o=Math.max(0,n-i*2),s=Math.max(r,1),c=Math.max(e.length-1,1);return e.map((e,t)=>`${i+a*t/c},${i+o*(1-Math.max(0,e)/s)}`).join(` `)}function F(e,t,n,r,i=8){let a=e[0]?.points.length??0;if(a===0)return[];let o=Math.max(0,t-i*2),s=Math.max(0,n-i*2),c=Math.max(r,1),l=Math.min(3,o/Math.max(a*8,1)),u=Math.max(0,o/a-l),d=[];for(let t=0;t<a;t+=1){let n=0;for(let r=0;r<e.length;r+=1){let f=s*Math.max(0,e[r]?.points[t]??0)/c;f>0&&d.push({x:i+o/a*t+l/2,y:i+s-n-f,width:u,height:f,seriesIndex:r,bucketIndex:t}),n+=f}}return d}var I=e(),L=[`#0A84FF`,`#FF9F0A`,`#30D158`,`#BF5AF2`,`#FF453A`,`#64D2FF`],R=640,z=160,B=28;function V(e,t){return Math.max(...t===`stackedBar`?Array.from({length:e.buckets},(t,n)=>e.series.reduce((e,t)=>e+(t.points[n]??0),0)):e.series.flatMap(e=>e.points),0)}function H(e,t){let n=new Intl.DateTimeFormat(t,{month:`short`,day:`numeric`}),r=Math.max(1,Math.floor((e.buckets-1)/3));return[0,1,2,3].map(t=>{let i=Math.min(e.buckets-1,t*r);return n.format(new Date((e.start+i*e.bucketSeconds)*1e3))})}function U({timeline:e,chartStyle:t,hours:n,loading:r,error:i,onRetry:a,locale:o,t:s}){if(r)return(0,I.jsx)(`div`,{className:`usage-companion-chart-skeleton`,"aria-busy":`true`,"aria-label":s(`usage.companion.loading`)});if(i)return(0,I.jsxs)(`div`,{className:`usage-companion-chart-state`,role:`alert`,children:[(0,I.jsx)(`span`,{children:s(`usage.companion.timelineUnavailable`)}),(0,I.jsx)(`button`,{type:`button`,className:`btn btn-ghost btn-sm`,onClick:a,children:s(`common.retry`)})]});if(!e||e.series.length===0)return(0,I.jsx)(`div`,{className:`usage-companion-chart-state`,children:s(`usage.companion.empty`,{hours:e?.buckets?Math.round(e.buckets*e.bucketSeconds/3600):n})});let c=V(e,t),l=H(e,o),u=B,d=l.map((e,t)=>(0,I.jsx)(`text`,{x:B+584*t/3,y:156,textAnchor:t===0?`start`:t===3?`end`:`middle`,className:`usage-companion-axis-label`,children:e},`${e}-${t}`)),f=t===`line`?e.series.map((e,t)=>(0,I.jsx)(`polyline`,{points:P(e.points,584,104,c,0),transform:`translate(${B},${B})`,fill:`none`,stroke:L[t%L.length],strokeWidth:`2`,strokeLinejoin:`round`,strokeLinecap:`round`},e.id)):F(e.series,584,104,c,0).map(e=>(0,I.jsx)(`rect`,{x:B+e.x,y:B+e.y,width:e.width,height:e.height,fill:L[e.seriesIndex%L.length],rx:`1`},`${e.bucketIndex}-${e.seriesIndex}`));return(0,I.jsxs)(`div`,{className:`usage-companion-chart`,children:[(0,I.jsxs)(`svg`,{viewBox:`0 0 ${R} ${z}`,role:`img`,"aria-label":s(`usage.companion.chartLabel`),children:[(0,I.jsx)(`line`,{x1:B,y1:u,x2:B,y2:132,className:`usage-companion-axis`}),(0,I.jsx)(`line`,{x1:B,y1:132,x2:612,y2:132,className:`usage-companion-axis`}),(0,I.jsx)(`text`,{x:24,y:32,textAnchor:`end`,className:`usage-companion-axis-label`,children:A(c)}),f,d]}),(0,I.jsx)(`div`,{className:`usage-companion-legend`,children:e.series.map((e,t)=>(0,I.jsxs)(`span`,{className:`usage-companion-legend-item`,children:[(0,I.jsx)(`span`,{className:`usage-companion-swatch`,style:{background:L[t%L.length]},"aria-hidden":`true`}),(0,I.jsx)(`span`,{children:e.id})]},e.id))}),e.truncated&&(0,I.jsx)(`p`,{className:`muted text-caption`,children:s(`usage.companion.olderRecordsSkipped`)})]})}export{u as _,T as a,M as c,x as d,_ as f,p as g,f as h,D as i,w as l,d as m,k as n,A as o,m as p,N as r,j as s,U as t,C as u,o as v};
|
|
1
|
+
import{n as e}from"./index-BmJwNBHL.js";var t={anthropic:`claude-color.svg`,"anthropic-apikey":`claude-color.svg`,"azure-openai":`openai.svg`,chatgpt:`openai.svg`,"cloudflare-ai-gateway":`cloudflare-ai-gateway-color.svg`,"cloudflare-workers-ai":`cloudflare-ai-gateway-color.svg`,cline:`cline-color.svg`,"cline-pass":`cline-color.svg`,"command-code":`commandcode-color.svg`,commandcode:`commandcode-color.svg`,cursor:`cursor-color.svg`,deepseek:`deepseek-color.svg`,devin:`devin.svg`,firepass:`firepass-color.svg`,fireworks:`fireworks-color.svg`,github:`github-copilot-color.svg`,"github-copilot":`copilot-color.svg`,"gitlab-duo":`gitlab-duo-color.svg`,google:`gemini-color.svg`,"google-antigravity":`antigravity-color.svg`,"google-vertex":`gemini-color.svg`,groq:`groq-color.svg`,huggingface:`huggingface-color.svg`,kimi:`kimi-color.svg`,"kimi-code":`kimi-color.svg`,kiro:`kiro-color.svg`,"lm-studio":`lm-studio-color.svg`,"meta-model":`meta.svg`,"meta-muse":`meta.svg`,mistral:`mistral-color.svg`,minimax:`minimax.svg`,"minimax-cn":`minimax.svg`,moonshot:`moonshot-color.svg`,nvidia:`nvidia-color.svg`,ollama:`ollama-color.svg`,"ollama-cloud":`ollama-color.svg`,openai:`openai.svg`,"openai-apikey":`openai.svg`,"opencode-free":`opencode.svg`,"opencode-go":`opencode.svg`,"opencode-zen":`opencode.svg`,openrouter:`openrouter-color.svg`,opper:`opper.svg`,qianfan:`qianfan-color.svg`,qoder:`qoder.svg`,"qoder-cn":`qoder.svg`,alibaba:`alibaba-color.svg`,"alibaba-token-plan":`alibaba-color.svg`,"alibaba-token-plan-intl":`alibaba-color.svg`,baseten:`baseten.svg`,bizrouter:`bizrouter.svg`,cerebras:`cerebras.svg`,crusoe:`crusoe.svg`,deepinfra:`deepinfra.svg`,digitalocean:`digitalocean.svg`,featherless:`featherless.svg`,hyperbolic:`hyperbolic.svg`,kilo:`kilo.svg`,nanogpt:`nanogpt.svg`,nebius:`nebius.svg`,neuralwatt:`neuralwatt.svg`,nous:`nous.svg`,novita:`novita.svg`,orcarouter:`orcarouter.svg`,"orcarouter-oauth":`orcarouter.svg`,packycode:`packycode.svg`,parallel:`parallel.svg`,sambanova:`sambanova.svg`,scaleway:`scaleway.svg`,stepfun:`stepfun-color.svg`,siliconflow:`siliconflow.svg`,synthetic:`synthetic.svg`,together:`together.svg`,umans:`umans.svg`,venice:`venice.svg`,vultr:`vultr.svg`,litellm:`litellm.svg`,zenmux:`zenmux.svg`,zai:`zai.svg`,"zhipu-bigmodel":`zai.svg`,"zhipu-bigmodel-coding":`zai.svg`,"qwen-cloud":`qwen-portal-color.svg`,"vercel-ai-gateway":`vercel-ai-gateway-color.svg`,vllm:`vllm-color.svg`,xai:`grok.svg`,"mimo-free":`xiaomi-color.svg`,mimo:`xiaomi-color.svg`,xiaomi:`xiaomi-color.svg`,"xiaomi-mimo":`xiaomi-color.svg`},n={anthropic:`Anthropic Claude`,"anthropic-apikey":`Anthropic Claude`,chatgpt:`ChatGPT`,openai:`OpenAI (Codex login)`,"openai-apikey":`OpenAI API`,"azure-openai":`Azure OpenAI`,"cloudflare-ai-gateway":`Cloudflare AI Gateway`,"cloudflare-workers-ai":`Cloudflare Workers AI`,cline:`Cline`,"cline-pass":`ClinePass`,nvidia:`NVIDIA NIM`,ollama:`Ollama`,"ollama-cloud":`Ollama Cloud`,xai:`xAI Grok`,"mimo-free":`MiMo Free`,xiaomi:`Xiaomi`,cursor:`Cursor`,deepseek:`DeepSeek`,devin:`Devin`,github:`GitHub`,"github-copilot":`GitHub Copilot`,"gitlab-duo":`GitLab Duo`,openrouter:`OpenRouter`,opper:`Opper`,"opencode-go":`OpenCode Go`,"opencode-free":`OpenCode Free`,"opencode-zen":`OpenCode Zen`,orcarouter:`OrcaRouter - API`,"orcarouter-oauth":`OrcaRouter - Auth`,packycode:`PackyCode`,mistral:`Mistral`,groq:`Groq`,"meta-model":`Meta Model API`,"meta-muse":`Muse Code`,alibaba:`Alibaba Coding Plan`,"alibaba-token-plan":`Alibaba Token Plan`,"alibaba-token-plan-intl":`Alibaba Token Plan (Intl)`,kimi:`Kimi`,"kimi-code":`Kimi`,moonshot:`Moonshot`,google:`Google`,"google-vertex":`Google Vertex`,"lm-studio":`LM Studio`,huggingface:`Hugging Face`,"qwen-cloud":`Qwen Cloud`,siliconflow:`SiliconFlow`,stepfun:`StepFun`,"tencent-coding-plan":`Tencent Cloud Coding Plan`,codebuddy:`CodeBuddy`,"codebuddy-cn":`CodeBuddy CN`,qoder:`Qoder`,"qoder-cn":`Qoder CN`,"vercel-ai-gateway":`Vercel AI Gateway`,vllm:`vLLM`,litellm:`LiteLLM`},r={"command-code":`provider.name.commandCodeAuth`,commandcode:`provider.name.commandCodeApi`,orcarouter:`provider.name.orcaRouterApi`,"orcarouter-oauth":`provider.name.orcaRouterAuth`,volcengine:`provider.name.volcengine`,"volcengine-coding-plan":`provider.name.volcengineCodingPlan`,"volcengine-agent-plan":`provider.name.volcengineAgentPlan`},i=new Set([...Object.keys(n),...Object.keys(r)]);function a(e){let n=e.toLowerCase();return Object.hasOwn(t,n)?t[n]:void 0}function o(e,t){let n=a(e);return n?`/provider-icons/${n}`:void 0}var s=new Set([`cerebras.svg`,`deepinfra.svg`,`neuralwatt.svg`,`nous.svg`,`novita.svg`,`packycode.svg`,`opper.svg`,`siliconflow.svg`,`synthetic.svg`,`zenmux.svg`,`grok.svg`,`kimi-color.svg`,`ollama-color.svg`,`opencode.svg`,`vercel-ai-gateway-color.svg`]),c=new Set([`baseten.svg`,`kilo.svg`,`sambanova.svg`,`venice.svg`,`zai.svg`]),l=new Set([`bizrouter.svg`,`featherless.svg`,`hyperbolic.svg`,`nebius.svg`,`parallel.svg`,`umans.svg`]);function u(e){if(!e)return`image`;let t=e.split(`/`).pop()??``;return s.has(t)?`mask`:c.has(t)?`plate`:l.has(t)?`dark-plate`:`image`}function d(e,t){let i=e.toLowerCase(),a=Object.hasOwn(r,i)?r[i]:void 0;return a?t(a):(Object.hasOwn(n,i)?n[i]:void 0)||(e===i&&/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e)?e.split(`-`).map(e=>e&&e[0].toUpperCase()+e.slice(1)).join(` `):e)}function f(e){return i.has(e.toLowerCase())}function p(e){return e===`command-code`?`commandcode-auth`:e===`commandcode`?`commandcode-api`:e}function m(e,t){let n=e.indexOf(`/`);if(n<=0)return e;let r=e.slice(0,n),i=e.slice(n+1);if(r===`command-code`||r===`commandcode`){let e=i.match(/^([a-z0-9]+)-([a-z0-9]+(?:-[a-z0-9]+)+)$/i);return e&&i.startsWith(`${e[1]}-${e[1]}-`)&&(i=i.slice(e[1].length+1)),`${r===`command-code`?`commandcode-auth`:`commandcode-api`}/${i}`}return e}var h={ko:[{v:0x2386f26fc10000,s:`경`},{v:0xe8d4a51000,s:`조`},{v:1e8,s:`억`},{v:1e4,s:`만`}],zh:[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`亿`},{v:1e4,s:`万`}],"zh-TW":[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`億`},{v:1e4,s:`萬`}]};function g(e){return e.replace(/\.0+$/,``).replace(/(\.\d*?)0+$/,`$1`)}function _(e,t){let n=h[t];if(n){for(let t of n)if(e>=t.v)return`${g((e/t.v).toFixed(1))}${t.s}`;return String(e)}return e<1e4?String(e):e<1e6?`${g((e/1e3).toFixed(1))}K`:e<1e9?`${g((e/1e6).toFixed(1))}M`:e<0xe8d4a51000?`${g((e/1e9).toFixed(1))}B`:`${g((e/0xe8d4a51000).toFixed(1))}T`}var v=1e10;function y(e){return e<v?e*1e3:e}function b(e,t){let n=e=>typeof e==`number`&&Number.isFinite(e);if(!n(e.shortPercent)||e.shortPercent<100)return!1;let r=e.shortResetAt;if(!n(r)||r<=0){let r=e.shortObservedAt;if(!n(r))return!1;let i=t-r;return i>=0&&i<=3e5}return y(r)>t}function x(e){return{fiveHourAvailable:e?.shortWindowSeconds===18e3&&typeof e.shortResetAt==`number`,weeklyAvailable:typeof e?.weeklyResetAt==`number`}}function S(e){let t=e?.trim().toLowerCase();return t===`go`||t===`free`}function C(e,t){if(!e)return null;let n=e.shortPercent===void 0&&e.shortResetAt===void 0?e:{...e,fiveHourPercent:e.fiveHourPercent??e.shortPercent,fiveHourResetAt:e.fiveHourResetAt??e.shortResetAt};return S(t)?{...n.monthlyPercent===void 0?{}:{monthlyPercent:n.monthlyPercent},...n.monthlyResetAt===void 0?{}:{monthlyResetAt:n.monthlyResetAt},...n.creditsUsd===void 0?{}:{creditsUsd:n.creditsUsd},...n.resetCredits===void 0?{}:{resetCredits:n.resetCredits},updatedAt:n.updatedAt}:n}function w(e,t,n=Date.now()){if(!e)return null;let r=e=>typeof e==`number`&&Number.isFinite(e),i=r(e.fiveHourPercent)?e.fiveHourPercent:r(e.shortPercent)?e.shortPercent:void 0,a=(S(t)?[e.monthlyPercent]:[e.weeklyPercent,e.monthlyPercent]).filter(r);if(a.length===0)return b({...r(i)?{shortPercent:i}:{},...r(e.fiveHourResetAt??e.shortResetAt)?{shortResetAt:e.fiveHourResetAt??e.shortResetAt}:{},...r(e.shortObservedAt)?{shortObservedAt:e.shortObservedAt}:{}},n)?100:null;let o=r(i)?[...a,i]:a;return o.length?Math.max(...o):null}function T(e){let t=new URLSearchParams({hours:String(e.chartHours),bucketMinutes:String(e.bucketMinutes),metric:e.tokenMetric,aggregation:e.aggregation,grouping:e.chartGrouping});e.models?.length&&t.set(`models`,e.models.join(`,`));for(let n of e.hiddenProviders)t.append(`hiddenProvider`,n);return t}function E(e){return Array.isArray(e)&&e.length<=100&&e.every(e=>typeof e==`string`)?JSON.stringify([...new Set(e)].sort()):void 0}function D(e,t){let n=new Set(t.hiddenProviders),r=e.availableModels.filter(e=>!n.has(e.slice(0,e.indexOf(`/`))));if(t.models?.length===0)return{...e,series:[],availableModels:r};let i=t.models===null?null:new Set(t.models),a=i!==null||n.size>0,o=e.appliedFilters,s=!!o&&E(o.hiddenProviders)===E(t.hiddenProviders)&&(t.models===null?o.models===null:E(o.models)===E(t.models)),c=e.series.filter(e=>e.id===`other`&&e.provider===``?!a||s:!n.has(e.provider)&&(i===null||i.has(`${e.provider}/${e.model}`)||i.has(e.model)));return{...e,series:c,availableModels:r,truncated:e.truncated||(a||o!==void 0)&&!s}}var O={6:15,24:60,72:180,168:360};function k(e){return O[e]}function A(e){if(e<1e3)return String(Math.round(e));let t=[[0xe8d4a51000,`T`],[1e9,`B`],[1e6,`M`],[1e3,`K`]];for(let n=0;n<t.length;n+=1){let[r,i]=t[n];if(e>=r){let a=Math.round(e/r);if(a>=1e3&&n>0){let[r,i]=t[n-1];return`${Math.round(e/r)}${i}`}return`${a}${i}`}}return String(Math.round(e))}function j(e,t){let n=new Map;for(let r of e){let e=r.includes(`/`)?r.slice(0,r.indexOf(`/`)):r,i=n.get(e)??{provider:e,models:[],total:0},a=t.get(r)??0;i.models.push({id:r,total:a}),i.total+=a,n.set(e,i)}return Array.from(n.values()).map(e=>({...e,models:e.models.toSorted((e,t)=>t.total-e.total||e.id.localeCompare(t.id))})).toSorted((e,t)=>t.total-e.total||e.provider.localeCompare(t.provider))}function M(e,t,n,r){let i=new Set(t),a=new Set((e??t).filter(e=>i.has(e)));for(let e of n)r?a.add(e):a.delete(e);return t.length>0&&t.every(e=>a.has(e))?null:t.filter(e=>a.has(e))}function N(e,t=[]){let n={...e};if(typeof n.menuBarTemplate==`string`&&n.menuBarTemplate.trim()===``&&(n.menuBarTemplate=null),n.models!==void 0&&t.length>0){let e=n.models??[],r=new Set(e);e.length===t.length&&t.every(e=>r.has(e))&&(n.models=null)}return n}function P(e,t,n,r,i=8){let a=Math.max(0,t-i*2),o=Math.max(0,n-i*2),s=Math.max(r,1),c=Math.max(e.length-1,1);return e.map((e,t)=>`${i+a*t/c},${i+o*(1-Math.max(0,e)/s)}`).join(` `)}function F(e,t,n,r,i=8){let a=e[0]?.points.length??0;if(a===0)return[];let o=Math.max(0,t-i*2),s=Math.max(0,n-i*2),c=Math.max(r,1),l=Math.min(3,o/Math.max(a*8,1)),u=Math.max(0,o/a-l),d=[];for(let t=0;t<a;t+=1){let n=0;for(let r=0;r<e.length;r+=1){let f=s*Math.max(0,e[r]?.points[t]??0)/c;f>0&&d.push({x:i+o/a*t+l/2,y:i+s-n-f,width:u,height:f,seriesIndex:r,bucketIndex:t}),n+=f}}return d}var I=e(),L=[`#0A84FF`,`#FF9F0A`,`#30D158`,`#BF5AF2`,`#FF453A`,`#64D2FF`],R=640,z=160,B=28;function V(e,t){return Math.max(...t===`stackedBar`?Array.from({length:e.buckets},(t,n)=>e.series.reduce((e,t)=>e+(t.points[n]??0),0)):e.series.flatMap(e=>e.points),0)}function H(e,t){let n=new Intl.DateTimeFormat(t,{month:`short`,day:`numeric`}),r=Math.max(1,Math.floor((e.buckets-1)/3));return[0,1,2,3].map(t=>{let i=Math.min(e.buckets-1,t*r);return n.format(new Date((e.start+i*e.bucketSeconds)*1e3))})}function U({timeline:e,chartStyle:t,hours:n,loading:r,error:i,onRetry:a,locale:o,t:s}){if(r)return(0,I.jsx)(`div`,{className:`usage-companion-chart-skeleton`,"aria-busy":`true`,"aria-label":s(`usage.companion.loading`)});if(i)return(0,I.jsxs)(`div`,{className:`usage-companion-chart-state`,role:`alert`,children:[(0,I.jsx)(`span`,{children:s(`usage.companion.timelineUnavailable`)}),(0,I.jsx)(`button`,{type:`button`,className:`btn btn-ghost btn-sm`,onClick:a,children:s(`common.retry`)})]});if(!e||e.series.length===0)return(0,I.jsx)(`div`,{className:`usage-companion-chart-state`,children:s(`usage.companion.empty`,{hours:e?.buckets?Math.round(e.buckets*e.bucketSeconds/3600):n})});let c=V(e,t),l=H(e,o),u=B,d=l.map((e,t)=>(0,I.jsx)(`text`,{x:B+584*t/3,y:156,textAnchor:t===0?`start`:t===3?`end`:`middle`,className:`usage-companion-axis-label`,children:e},`${e}-${t}`)),f=t===`line`?e.series.map((e,t)=>(0,I.jsx)(`polyline`,{points:P(e.points,584,104,c,0),transform:`translate(${B},${B})`,fill:`none`,stroke:L[t%L.length],strokeWidth:`2`,strokeLinejoin:`round`,strokeLinecap:`round`},e.id)):F(e.series,584,104,c,0).map(e=>(0,I.jsx)(`rect`,{x:B+e.x,y:B+e.y,width:e.width,height:e.height,fill:L[e.seriesIndex%L.length],rx:`1`},`${e.bucketIndex}-${e.seriesIndex}`));return(0,I.jsxs)(`div`,{className:`usage-companion-chart`,children:[(0,I.jsxs)(`svg`,{viewBox:`0 0 ${R} ${z}`,role:`img`,"aria-label":s(`usage.companion.chartLabel`),children:[(0,I.jsx)(`line`,{x1:B,y1:u,x2:B,y2:132,className:`usage-companion-axis`}),(0,I.jsx)(`line`,{x1:B,y1:132,x2:612,y2:132,className:`usage-companion-axis`}),(0,I.jsx)(`text`,{x:24,y:32,textAnchor:`end`,className:`usage-companion-axis-label`,children:A(c)}),f,d]}),(0,I.jsx)(`div`,{className:`usage-companion-legend`,children:e.series.map((e,t)=>(0,I.jsxs)(`span`,{className:`usage-companion-legend-item`,children:[(0,I.jsx)(`span`,{className:`usage-companion-swatch`,style:{background:L[t%L.length]},"aria-hidden":`true`}),(0,I.jsx)(`span`,{children:e.id})]},e.id))}),e.truncated&&(0,I.jsx)(`p`,{className:`muted text-caption`,children:s(`usage.companion.olderRecordsSkipped`)})]})}export{u as _,T as a,M as c,x as d,_ as f,p as g,f as h,D as i,w as l,d as m,k as n,A as o,m as p,N as r,j as s,U as t,C as u,o as v};
|
package/gui/dist/index.html
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-BmJwNBHL.js"></script>
|
|
20
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DdDunwDb.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitkyc08/opencodex",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.63.0-preview.20260923",
|
|
4
4
|
"description": "Universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/package-main.mjs",
|
|
@@ -184,6 +184,21 @@ export const CURSOR_CAPABILITIES: Record<string, CursorCapability> = {
|
|
|
184
184
|
thinkingFast: { levels: FULL, order: T },
|
|
185
185
|
},
|
|
186
186
|
},
|
|
187
|
+
// 260923 Claude Opus 5.5: cursor.com/docs/models/claude-opus-5-5 publishes the id
|
|
188
|
+
// `claude-opus-5-5`, a thinking variant, a `claude-opus-5-5-fast` tier and a 1M max context.
|
|
189
|
+
// The ladders mirror the measured claude-opus-5 rows (fast stops at high) until the live
|
|
190
|
+
// GetUsableModels roster is dumped; the live filter drops any id the account cannot use.
|
|
191
|
+
"claude-opus-5-5": {
|
|
192
|
+
displayName: "Claude Opus 5.5",
|
|
193
|
+
window: CONTEXT_1M,
|
|
194
|
+
defaultVariant: "thinking",
|
|
195
|
+
variants: {
|
|
196
|
+
regular: { levels: FULL },
|
|
197
|
+
thinking: { levels: FULL, order: T },
|
|
198
|
+
fast: { levels: ["low", "medium", "high"] },
|
|
199
|
+
thinkingFast: { levels: FULL, order: T },
|
|
200
|
+
},
|
|
201
|
+
},
|
|
187
202
|
"glm-5.2": {
|
|
188
203
|
displayName: "GLM 5.2",
|
|
189
204
|
window: CONTEXT_1M,
|
|
@@ -36,6 +36,9 @@ const CURSOR_MODEL_EFFORT_TIERS: Record<string, readonly string[]> = {
|
|
|
36
36
|
"claude-opus-4-8-fast": ["low", "medium", "high", "xhigh", "max"],
|
|
37
37
|
"claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
|
|
38
38
|
"claude-opus-5-fast": ["low", "medium", "high"],
|
|
39
|
+
// 260923 Opus 5.5 (cursor.com/docs/models/claude-opus-5-5), mirroring the measured opus-5 rows.
|
|
40
|
+
"claude-opus-5-5": ["low", "medium", "high", "xhigh", "max"],
|
|
41
|
+
"claude-opus-5-5-fast": ["low", "medium", "high"],
|
|
39
42
|
"claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
|
|
40
43
|
"glm-5.2": ["high", "max"],
|
|
41
44
|
// 260825 live GetUsableModels. gemini-3.6-flash was the first Cursor model exposing
|
|
@@ -53,6 +56,8 @@ const CURSOR_MODEL_EFFORT_TIERS: Record<string, readonly string[]> = {
|
|
|
53
56
|
// 4.6-opus thinks only at high/max, 4.5-opus only at high, 4.6-sonnet only at medium.
|
|
54
57
|
"claude-opus-5-thinking": ["low", "medium", "high", "xhigh", "max"],
|
|
55
58
|
"claude-opus-5-thinking-fast": ["low", "medium", "high", "xhigh", "max"],
|
|
59
|
+
"claude-opus-5-5-thinking": ["low", "medium", "high", "xhigh", "max"],
|
|
60
|
+
"claude-opus-5-5-thinking-fast": ["low", "medium", "high", "xhigh", "max"],
|
|
56
61
|
"claude-opus-4-8-thinking": ["low", "medium", "high", "xhigh", "max"],
|
|
57
62
|
"claude-opus-4-8-thinking-fast": ["low", "medium", "high", "xhigh", "max"],
|
|
58
63
|
"claude-opus-4-7-thinking": ["low", "medium", "high", "xhigh", "max"],
|
|
@@ -130,6 +135,8 @@ const CANONICAL_CODEX_EFFORT_ORDER = ["low", "medium", "high", "xhigh", "max"] a
|
|
|
130
135
|
const CURSOR_THINKING_FAMILIES: Readonly<Record<string, { source: string; order: "thinking-then-effort" | "effort-then-thinking" | "bare" }>> = {
|
|
131
136
|
"claude-opus-5-thinking": { source: "claude-opus-5", order: "thinking-then-effort" },
|
|
132
137
|
"claude-opus-5-thinking-fast": { source: "claude-opus-5-fast", order: "thinking-then-effort" },
|
|
138
|
+
"claude-opus-5-5-thinking": { source: "claude-opus-5-5", order: "thinking-then-effort" },
|
|
139
|
+
"claude-opus-5-5-thinking-fast": { source: "claude-opus-5-5-fast", order: "thinking-then-effort" },
|
|
133
140
|
"claude-opus-4-8-thinking": { source: "claude-opus-4-8", order: "thinking-then-effort" },
|
|
134
141
|
"claude-opus-4-8-thinking-fast": { source: "claude-opus-4-8-fast", order: "thinking-then-effort" },
|
|
135
142
|
"claude-opus-4-7-thinking": { source: "claude-opus-4-7", order: "thinking-then-effort" },
|
|
@@ -76,7 +76,7 @@ export const MAX_MIDSTREAM_SCAN_LENGTH = 512 * 1024;
|
|
|
76
76
|
const MAX_MIDSTREAM_FINDINGS = 8;
|
|
77
77
|
const MAX_ROUTING_COMMENTARY_BYTES = 512;
|
|
78
78
|
/** Aggregate quarantine cap: past this, flush and disarm. */
|
|
79
|
-
const
|
|
79
|
+
export const CURSOR_OUTPUT_GUARD_MAX_HOLD_BYTES = 8 * 1024;
|
|
80
80
|
const encoder = new TextEncoder();
|
|
81
81
|
|
|
82
82
|
export class CursorToolResultEchoError extends Error {
|
|
@@ -130,16 +130,19 @@ export class CursorMidstreamEchoObserver {
|
|
|
130
130
|
private totalLength = 0;
|
|
131
131
|
private disarmed = false;
|
|
132
132
|
private lineDisarmed = false;
|
|
133
|
-
private
|
|
133
|
+
private readonly corruptionWatches: Array<{
|
|
134
|
+
finding: MidstreamEchoFinding;
|
|
135
|
+
remaining: number;
|
|
136
|
+
window: string;
|
|
137
|
+
}> = [];
|
|
134
138
|
private readonly recorded: MidstreamEchoFinding[] = [];
|
|
135
139
|
|
|
136
140
|
feed(textDelta: string): void {
|
|
137
|
-
if (this.disarmed &&
|
|
141
|
+
if (this.disarmed && this.corruptionWatches.length === 0) return;
|
|
138
142
|
let index = 0;
|
|
139
143
|
while (index < textDelta.length) {
|
|
140
144
|
const newline = textDelta.indexOf("\n", index);
|
|
141
145
|
const segment = newline === -1 ? textDelta.slice(index) : textDelta.slice(index, newline);
|
|
142
|
-
if (this.corruptionWatch) this.watchCorruption(segment + (newline === -1 ? "" : "\n"));
|
|
143
146
|
if (!this.disarmed && !this.lineDisarmed && segment.length > 0) {
|
|
144
147
|
this.lineBuffer += segment;
|
|
145
148
|
if (this.lineBuffer.length > MAX_MIDSTREAM_LINE_INDENT + 32) {
|
|
@@ -149,6 +152,11 @@ export class CursorMidstreamEchoObserver {
|
|
|
149
152
|
}
|
|
150
153
|
this.checkLine();
|
|
151
154
|
}
|
|
155
|
+
// Recognize the next marker before charging its line to a corruption window.
|
|
156
|
+
// A call-id on the marker's own line belongs to the new finding as well.
|
|
157
|
+
if (this.corruptionWatches.length > 0) {
|
|
158
|
+
this.watchCorruption(segment + (newline === -1 ? "" : "\n"));
|
|
159
|
+
}
|
|
152
160
|
if (newline === -1) break;
|
|
153
161
|
this.lineBuffer = "";
|
|
154
162
|
this.lineDisarmed = false;
|
|
@@ -160,9 +168,7 @@ export class CursorMidstreamEchoObserver {
|
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
findings(): readonly MidstreamEchoFinding[] {
|
|
163
|
-
|
|
164
|
-
this.settleCorruption();
|
|
165
|
-
}
|
|
171
|
+
while (this.corruptionWatches.length > 0) this.settleCorruption(0);
|
|
166
172
|
return this.recorded;
|
|
167
173
|
}
|
|
168
174
|
|
|
@@ -187,12 +193,24 @@ export class CursorMidstreamEchoObserver {
|
|
|
187
193
|
this.lineDisarmed = true;
|
|
188
194
|
return;
|
|
189
195
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.
|
|
196
|
+
// A new marker ends the previous marker's corruption window: the text
|
|
197
|
+
// between two markers belongs to the earlier finding only. Without this,
|
|
198
|
+
// every open watch consumed the same following text, so one corrupt
|
|
199
|
+
// call-id after a second marker also marked the first, clean finding
|
|
200
|
+
// corrupt (clean-then-corrupt cross-contamination).
|
|
201
|
+
while (this.corruptionWatches.length > 0) this.settleCorruption(0);
|
|
202
|
+
if (this.recorded.length + this.corruptionWatches.length < MAX_MIDSTREAM_FINDINGS) {
|
|
203
|
+
const finding: MidstreamEchoFinding = {
|
|
204
|
+
marker,
|
|
205
|
+
offset: this.lineStartOffset,
|
|
206
|
+
callIdCorrupt: false,
|
|
207
|
+
};
|
|
208
|
+
this.corruptionWatches.push({
|
|
209
|
+
finding,
|
|
210
|
+
remaining: MIDSTREAM_CORRUPTION_WINDOW,
|
|
211
|
+
window: "",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
196
214
|
this.lineDisarmed = true;
|
|
197
215
|
return;
|
|
198
216
|
}
|
|
@@ -203,24 +221,28 @@ export class CursorMidstreamEchoObserver {
|
|
|
203
221
|
}
|
|
204
222
|
|
|
205
223
|
private watchCorruption(text: string): void {
|
|
206
|
-
const watch
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
224
|
+
for (const watch of this.corruptionWatches) {
|
|
225
|
+
const take = Math.min(watch.remaining, text.length);
|
|
226
|
+
watch.window += text.slice(0, take);
|
|
227
|
+
watch.remaining -= take;
|
|
228
|
+
}
|
|
229
|
+
let index = 0;
|
|
230
|
+
while (index < this.corruptionWatches.length) {
|
|
231
|
+
if (this.corruptionWatches[index]!.remaining <= 0) this.settleCorruption(index);
|
|
232
|
+
else index += 1;
|
|
233
|
+
}
|
|
212
234
|
}
|
|
213
235
|
|
|
214
|
-
private settleCorruption(): void {
|
|
215
|
-
const watch = this.
|
|
236
|
+
private settleCorruption(index: number): void {
|
|
237
|
+
const watch = this.corruptionWatches[index];
|
|
216
238
|
if (!watch) return;
|
|
217
239
|
const window = watch.window;
|
|
218
240
|
watch.finding.callIdCorrupt =
|
|
219
241
|
/fc_[0-9a-f]+[ \t]+mar-/.test(window)
|
|
220
242
|
|| /call_id: \S+[ \t]+\S+_0\b/.test(window);
|
|
221
|
-
|
|
243
|
+
this.recorded.push(watch.finding);
|
|
222
244
|
// Window text is discarded here; only booleans/offsets survive.
|
|
223
|
-
this.
|
|
245
|
+
this.corruptionWatches.splice(index, 1);
|
|
224
246
|
}
|
|
225
247
|
}
|
|
226
248
|
|
|
@@ -251,7 +273,11 @@ export class CursorEnvelopeEchoSniffer {
|
|
|
251
273
|
const stillPrefix = ECHO_MARKERS.some(marker =>
|
|
252
274
|
probe.length < marker.length && marker.startsWith(probe),
|
|
253
275
|
);
|
|
254
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
stillPrefix
|
|
278
|
+
&& this.byteCount <= MAX_SNIFF_BYTES
|
|
279
|
+
&& this.buffered.length < CURSOR_OUTPUT_GUARD_MAX_HOLD_BYTES
|
|
280
|
+
) {
|
|
255
281
|
return { kind: "hold" };
|
|
256
282
|
}
|
|
257
283
|
this.done = true;
|
|
@@ -316,7 +342,7 @@ export class CursorRoutingCommentarySniffer {
|
|
|
316
342
|
&& lineBreakCount < 2;
|
|
317
343
|
if (
|
|
318
344
|
this.byteCount < MAX_ROUTING_COMMENTARY_BYTES
|
|
319
|
-
&& this.buffered.length <
|
|
345
|
+
&& this.buffered.length < CURSOR_OUTPUT_GUARD_MAX_HOLD_BYTES
|
|
320
346
|
&& (lineBreakCount === 0 || pendingFailureClaim)
|
|
321
347
|
&& (hasRoutingHint || this.byteCount < 64)
|
|
322
348
|
) {
|
|
@@ -311,6 +311,7 @@ function rootPromptMessages(
|
|
|
311
311
|
const replayRuns = new Map<RootBlobCandidate["role"], {
|
|
312
312
|
text: string;
|
|
313
313
|
entry: RootBlobCandidate;
|
|
314
|
+
entryIndex: number;
|
|
314
315
|
length: number;
|
|
315
316
|
}>();
|
|
316
317
|
const toolCallCounts = new Map<string, number>();
|
|
@@ -337,13 +338,13 @@ function rootPromptMessages(
|
|
|
337
338
|
// half, so losing it re-primes the self-reinforcing loop the breaker exists to end.
|
|
338
339
|
{ ...opts, text: marked, messageIndex: previous.entry.messageIndex ?? opts.messageIndex },
|
|
339
340
|
);
|
|
340
|
-
entries[
|
|
341
|
-
replayRuns.set(role, { text: normalized, entry: replacement, length: runLength });
|
|
341
|
+
entries[previous.entryIndex] = replacement;
|
|
342
|
+
replayRuns.set(role, { text: normalized, entry: replacement, entryIndex: previous.entryIndex, length: runLength });
|
|
342
343
|
return;
|
|
343
344
|
}
|
|
344
345
|
const entry = rootBlobCandidate(payload, role, opts);
|
|
345
346
|
entries.push(entry);
|
|
346
|
-
replayRuns.set(role, { text: normalized, entry, length: 1 });
|
|
347
|
+
replayRuns.set(role, { text: normalized, entry, entryIndex: entries.length - 1, length: 1 });
|
|
347
348
|
};
|
|
348
349
|
|
|
349
350
|
for (let i = 0; i < messages.length; i++) {
|
|
@@ -949,11 +950,26 @@ function serializeToolCallArguments(args: Record<string, unknown>): string | und
|
|
|
949
950
|
|
|
950
951
|
/** Truncate to a byte budget without splitting a UTF-8 sequence. */
|
|
951
952
|
function truncateUtf8(text: string, maxBytes: number): string {
|
|
952
|
-
const encoded =
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
953
|
+
const encoded = new Uint8Array(Math.max(0, maxBytes));
|
|
954
|
+
const { read, written } = encoder.encodeInto(text, encoded);
|
|
955
|
+
return read === text.length ? text : decoder.decode(encoded.subarray(0, written));
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/** Return the UTF-8 length only when it fits the bound, without allocating an input-sized buffer. */
|
|
959
|
+
function boundedUtf8ByteLength(text: string, maxBytes: number): number | undefined {
|
|
960
|
+
let bytes = 0;
|
|
961
|
+
for (let i = 0; i < text.length; i++) {
|
|
962
|
+
const code = text.charCodeAt(i);
|
|
963
|
+
if (code < 0x80) bytes += 1;
|
|
964
|
+
else if (code < 0x800) bytes += 2;
|
|
965
|
+
else if (code >= 0xd800 && code <= 0xdbff && i + 1 < text.length
|
|
966
|
+
&& text.charCodeAt(i + 1) >= 0xdc00 && text.charCodeAt(i + 1) <= 0xdfff) {
|
|
967
|
+
bytes += 4;
|
|
968
|
+
i += 1;
|
|
969
|
+
} else bytes += 3;
|
|
970
|
+
if (bytes > maxBytes) return undefined;
|
|
971
|
+
}
|
|
972
|
+
return bytes;
|
|
957
973
|
}
|
|
958
974
|
|
|
959
975
|
/**
|
|
@@ -966,10 +982,8 @@ function truncateUtf8(text: string, maxBytes: number): string {
|
|
|
966
982
|
* exists to prevent. A bounded prefix still identifies the call (tool name plus the head of its
|
|
967
983
|
* arguments) while leaving the output room to survive.
|
|
968
984
|
*/
|
|
969
|
-
function
|
|
970
|
-
|
|
971
|
-
if (serialized === undefined) return "[unserializable arguments]";
|
|
972
|
-
if (encoder.encode(serialized).byteLength <= CURSOR_INVOCATION_ARGUMENTS_BYTE_LIMIT) return serialized;
|
|
985
|
+
function serializedToolCallArgumentsText(serialized: string): string {
|
|
986
|
+
if (boundedUtf8ByteLength(serialized, CURSOR_INVOCATION_ARGUMENTS_BYTE_LIMIT) !== undefined) return serialized;
|
|
973
987
|
// The budget is the size of the RENDERED line, so the marker has to come out of it rather than be
|
|
974
988
|
// added on top: otherwise every truncated invocation exceeds the declared limit by the marker.
|
|
975
989
|
const marker = "…[arguments truncated]";
|
|
@@ -978,6 +992,11 @@ function toolCallArgumentsText(args: Record<string, unknown>): string {
|
|
|
978
992
|
return `${truncateUtf8(serialized, keep)}${marker}`;
|
|
979
993
|
}
|
|
980
994
|
|
|
995
|
+
function toolCallArgumentsText(args: Record<string, unknown>): string {
|
|
996
|
+
const serialized = serializeToolCallArguments(args);
|
|
997
|
+
return serialized === undefined ? "[unserializable arguments]" : serializedToolCallArgumentsText(serialized);
|
|
998
|
+
}
|
|
999
|
+
|
|
981
1000
|
/**
|
|
982
1001
|
* The invocation that produced a replayed tool result, rendered as ONE descriptive line inside the
|
|
983
1002
|
* result envelope.
|
|
@@ -1052,8 +1071,15 @@ function restoreClippedInvocationArguments(
|
|
|
1052
1071
|
if (!call) continue;
|
|
1053
1072
|
const full = serializeToolCallArguments(call.arguments);
|
|
1054
1073
|
if (full === undefined) continue;
|
|
1055
|
-
const clipped =
|
|
1074
|
+
const clipped = serializedToolCallArgumentsText(full);
|
|
1056
1075
|
if (clipped === full) continue;
|
|
1076
|
+
// The replacement must add at least the raw UTF-8 argument-byte delta. Reject an impossible
|
|
1077
|
+
// restoration with a bounded scan before building the widened string, JSON, and byte array.
|
|
1078
|
+
const clippedBytes = encoder.encode(clipped).byteLength;
|
|
1079
|
+
// boundedUtf8ByteLength already gives up past clippedBytes + spare, so a returned number
|
|
1080
|
+
// always fits; a second size comparison here can never fire.
|
|
1081
|
+
const fullBytes = boundedUtf8ByteLength(full, clippedBytes + spare);
|
|
1082
|
+
if (fullBytes === undefined) continue;
|
|
1057
1083
|
const name = namespacedToolName(call.namespace, call.name);
|
|
1058
1084
|
// Anchored on the preceding newline. `toolResultToText` always emits the invocation after the
|
|
1059
1085
|
// `[tool_result]`, `call_id:` and `name:` lines, so the real line is never first — and
|
package/src/adapters/cursor.ts
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
import { runCursorTurnWithRetry } from "./cursor/transport-retry";
|
|
49
49
|
import { cursorRequestHasShellAlias, cursorRequestUsesCodeMode } from "./cursor/tool-definitions";
|
|
50
50
|
import {
|
|
51
|
+
CURSOR_OUTPUT_GUARD_MAX_HOLD_BYTES,
|
|
51
52
|
CURSOR_ECHO_RETRY_CONTINUATION_TEXT,
|
|
52
53
|
CURSOR_ROUTING_COMMENTARY_RETRY_TEXT,
|
|
53
54
|
CursorEnvelopeEchoSniffer,
|
|
@@ -315,6 +316,8 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
315
316
|
? new CursorRoutingCommentarySniffer()
|
|
316
317
|
: undefined;
|
|
317
318
|
let guardHeld: AdapterEvent[] = [];
|
|
319
|
+
let guardHeldBytes = 0;
|
|
320
|
+
const guardEncoder = new TextEncoder();
|
|
318
321
|
// Exactly-once observation: every client-bound text delta passes through here
|
|
319
322
|
// exactly once — held deltas only on release, ordinary deltas at emit time.
|
|
320
323
|
const emitTextObserved = (event: AdapterEvent): void => {
|
|
@@ -327,7 +330,49 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
327
330
|
emitTextObserved(held);
|
|
328
331
|
}
|
|
329
332
|
guardHeld = [];
|
|
333
|
+
guardHeldBytes = 0;
|
|
330
334
|
};
|
|
335
|
+
// A single frame can carry a multi-megabyte payload (the transport accepts up to the
|
|
336
|
+
// 16 MiB Cursor message bound), so the serialized size is projected — object overhead
|
|
337
|
+
// plus raw payload length — BEFORE any encoded copy exists. Escapes only inflate the
|
|
338
|
+
// exact figure, making the raw length a safe lower bound for the overflow decision.
|
|
339
|
+
const GUARD_EVENT_OVERHEAD_BYTES = 64;
|
|
340
|
+
const projectedGuardEventBytes = (event: AdapterEvent): number =>
|
|
341
|
+
GUARD_EVENT_OVERHEAD_BYTES
|
|
342
|
+
+ (event.type === "text_delta"
|
|
343
|
+
? Buffer.byteLength(event.text, "utf8")
|
|
344
|
+
: event.type === "thinking_delta"
|
|
345
|
+
? Buffer.byteLength(event.thinking, "utf8")
|
|
346
|
+
: 0);
|
|
347
|
+
const holdGuardEvent = (event: AdapterEvent) => {
|
|
348
|
+
if (guardHeldBytes + projectedGuardEventBytes(event) > CURSOR_OUTPUT_GUARD_MAX_HOLD_BYTES) {
|
|
349
|
+
// Too large to retain even unescaped: settle the sniffers, release what was held,
|
|
350
|
+
// and pass this event through without ever encoding it.
|
|
351
|
+
echoSniffer?.finish();
|
|
352
|
+
routingCommentarySniffer?.finish();
|
|
353
|
+
releaseGuardHeld();
|
|
354
|
+
if (event.type !== "heartbeat") emittedOutput = true;
|
|
355
|
+
emitTextObserved(event);
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
guardHeld.push(event);
|
|
359
|
+
// Count the complete retained representation, including per-event overhead, so an
|
|
360
|
+
// upstream cannot evade the cap with empty or non-text reasoning frames.
|
|
361
|
+
guardHeldBytes += guardEncoder.encode(JSON.stringify(event)).byteLength;
|
|
362
|
+
if (guardHeldBytes <= CURSOR_OUTPUT_GUARD_MAX_HOLD_BYTES) return true;
|
|
363
|
+
echoSniffer?.finish();
|
|
364
|
+
routingCommentarySniffer?.finish();
|
|
365
|
+
releaseGuardHeld();
|
|
366
|
+
return false;
|
|
367
|
+
};
|
|
368
|
+
// Bound each feed before a sniffer copies or encodes it. Their normal 40 B / 512 B
|
|
369
|
+
// hold thresholds are checked after classification, so one large frame previously
|
|
370
|
+
// let a late match inspect an arbitrary tail. Only these leading UTF-16 prefixes
|
|
371
|
+
// now participate in corrective retry; later text remains ordinary output.
|
|
372
|
+
const ECHO_SNIFF_FEED_MAX_CHARS = 512;
|
|
373
|
+
const ROUTING_SNIFF_FEED_MAX_CHARS = 2048;
|
|
374
|
+
const boundedSniffText = (text: string, maxChars: number): string =>
|
|
375
|
+
text.length > maxChars ? text.slice(0, maxChars) : text;
|
|
331
376
|
const guardsSettled = () =>
|
|
332
377
|
(!echoSniffer || echoSniffer.settled)
|
|
333
378
|
&& (!routingCommentarySniffer || routingCommentarySniffer.settled);
|
|
@@ -368,27 +413,30 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
368
413
|
}
|
|
369
414
|
if (!guardsSettled()) {
|
|
370
415
|
if (event.type === "text_delta") {
|
|
371
|
-
|
|
416
|
+
// Classify the delta before the aggregate-cap check: an oversized first
|
|
417
|
+
// delta must still pass the armed sniffers (echo/hallucination detection is
|
|
418
|
+
// prefix-based), so the cap cannot disarm them before they see the text.
|
|
372
419
|
if (echoSniffer && !echoSniffer.settled) {
|
|
373
|
-
const decision = echoSniffer.feed(event.text);
|
|
420
|
+
const decision = echoSniffer.feed(boundedSniffText(event.text, ECHO_SNIFF_FEED_MAX_CHARS));
|
|
374
421
|
if (decision.kind === "echo") {
|
|
375
422
|
guardHeld = [];
|
|
376
423
|
throw new CursorToolResultEchoError(decision.marker);
|
|
377
424
|
}
|
|
378
425
|
}
|
|
379
426
|
if (routingCommentarySniffer && !routingCommentarySniffer.settled) {
|
|
380
|
-
const decision = routingCommentarySniffer.feed(event.text);
|
|
427
|
+
const decision = routingCommentarySniffer.feed(boundedSniffText(event.text, ROUTING_SNIFF_FEED_MAX_CHARS));
|
|
381
428
|
if (decision.kind === "hallucination") {
|
|
382
429
|
guardHeld = [];
|
|
383
430
|
throw new CursorRoutingCommentaryError();
|
|
384
431
|
}
|
|
385
432
|
}
|
|
433
|
+
if (!holdGuardEvent(event)) continue;
|
|
386
434
|
if (guardsSettled()) releaseGuardHeld();
|
|
387
435
|
continue;
|
|
388
436
|
} else if (event.type === "thinking_delta" || event.type === "heartbeat") {
|
|
389
437
|
// Reasoning before first text stays ordered; liveness still passes through.
|
|
390
438
|
if (event.type === "thinking_delta") {
|
|
391
|
-
|
|
439
|
+
holdGuardEvent(event);
|
|
392
440
|
continue;
|
|
393
441
|
}
|
|
394
442
|
} else {
|
|
@@ -37,6 +37,7 @@ import { buildMetadata } from './metadata.js';
|
|
|
37
37
|
import { getCachedUserJwt } from './auth.js';
|
|
38
38
|
import { getCachedCatalog, ModelNotAvailableError, type CacheEntry } from './catalog.js';
|
|
39
39
|
import { anySignal, cancelBodyOnAbort } from '../../../lib/abort.js';
|
|
40
|
+
import { parseRetryAfterFromMessage } from '../../../lib/retry-delay.js';
|
|
40
41
|
import { resolveDevinApiBaseUrl } from '../../../oauth/devin/api-base.js';
|
|
41
42
|
|
|
42
43
|
/**
|
|
@@ -1071,6 +1072,13 @@ export class CloudChatError extends Error {
|
|
|
1071
1072
|
* a live rate limit was classified 502 and core's failover never rotated.
|
|
1072
1073
|
*/
|
|
1073
1074
|
public readonly status?: number,
|
|
1075
|
+
/**
|
|
1076
|
+
* Provider-stated recovery delay in seconds. The trailer parser extracts
|
|
1077
|
+
* it while the raw upstream text is still available, because the thrown
|
|
1078
|
+
* message is content-free: the stated-reset retry reads this typed field
|
|
1079
|
+
* instead of scraping untrusted trailer text out of error.message.
|
|
1080
|
+
*/
|
|
1081
|
+
public readonly retryAfterSeconds?: number,
|
|
1074
1082
|
) {
|
|
1075
1083
|
super(message);
|
|
1076
1084
|
this.name = 'CloudChatError';
|
|
@@ -1078,6 +1086,15 @@ export class CloudChatError extends Error {
|
|
|
1078
1086
|
}
|
|
1079
1087
|
|
|
1080
1088
|
const TRACE_ID_RE = /\(trace ID: ([0-9a-f]+)\)/i;
|
|
1089
|
+
const SAFE_CONNECT_CODES = new Set([
|
|
1090
|
+
'canceled', 'unknown', 'invalid_argument', 'deadline_exceeded', 'not_found', 'already_exists',
|
|
1091
|
+
'permission_denied', 'resource_exhausted', 'failed_precondition', 'aborted', 'out_of_range',
|
|
1092
|
+
'unimplemented', 'internal', 'unavailable', 'data_loss', 'unauthenticated',
|
|
1093
|
+
]);
|
|
1094
|
+
|
|
1095
|
+
function safeConnectCode(value: unknown): string | undefined {
|
|
1096
|
+
return typeof value === 'string' && SAFE_CONNECT_CODES.has(value) ? value : undefined;
|
|
1097
|
+
}
|
|
1081
1098
|
|
|
1082
1099
|
/**
|
|
1083
1100
|
* A quota refusal Cognition delivers as `permission_denied`.
|
|
@@ -1296,7 +1313,7 @@ export async function* streamChatEvents(req: CloudChatRequest): AsyncGenerator<C
|
|
|
1296
1313
|
// Bun + Node ReadableStream readers diverge on the type-level shape
|
|
1297
1314
|
// (Bun's includes a `readMany` method); both work the same at runtime.
|
|
1298
1315
|
const reader = resp.body.getReader() as ReadableStreamDefaultReader<Uint8Array>;
|
|
1299
|
-
let trailerError: { code?: string; message: string; traceId?: string } | null = null;
|
|
1316
|
+
let trailerError: { code?: string; message: string; opaqueDenial: boolean; retryAfterSeconds?: number; traceId?: string } | null = null;
|
|
1300
1317
|
let sawEos = false;
|
|
1301
1318
|
|
|
1302
1319
|
/**
|
|
@@ -1441,14 +1458,23 @@ export async function* streamChatEvents(req: CloudChatRequest): AsyncGenerator<C
|
|
|
1441
1458
|
const text = payload.toString('utf8');
|
|
1442
1459
|
if (text && text.includes('"error"')) {
|
|
1443
1460
|
let code: string | undefined;
|
|
1444
|
-
let message =
|
|
1461
|
+
let message = '';
|
|
1445
1462
|
try {
|
|
1446
1463
|
const j = JSON.parse(text) as { error?: { code?: string; message?: string } };
|
|
1447
|
-
code = j.error?.code;
|
|
1448
|
-
if (j.error?.message) message = j.error.message;
|
|
1449
|
-
} catch { /*
|
|
1464
|
+
code = safeConnectCode(j.error?.code);
|
|
1465
|
+
if (typeof j.error?.message === 'string') message = j.error.message;
|
|
1466
|
+
} catch { /* malformed trailers still become a content-free error */ }
|
|
1450
1467
|
const traceMatch = message.match(TRACE_ID_RE);
|
|
1451
|
-
trailerError = {
|
|
1468
|
+
trailerError = {
|
|
1469
|
+
code,
|
|
1470
|
+
message,
|
|
1471
|
+
opaqueDenial: /an internal error occurred/i.test(message),
|
|
1472
|
+
// Parsed here, where the raw text still exists: the thrown error
|
|
1473
|
+
// is content-free, so the stated-reset retry consumes this typed
|
|
1474
|
+
// field rather than the upstream sentence.
|
|
1475
|
+
retryAfterSeconds: parseRetryAfterFromMessage(message),
|
|
1476
|
+
traceId: traceMatch?.[1],
|
|
1477
|
+
};
|
|
1452
1478
|
}
|
|
1453
1479
|
continue;
|
|
1454
1480
|
}
|
|
@@ -1477,16 +1503,16 @@ export async function* streamChatEvents(req: CloudChatRequest): AsyncGenerator<C
|
|
|
1477
1503
|
// the catalog disagrees with the call, but the catalog can lag (a model
|
|
1478
1504
|
// that was enabled at fetch time may have been gated between then and
|
|
1479
1505
|
// now) or be missing (network failure caused a fall-through). When the
|
|
1480
|
-
//
|
|
1506
|
+
// trailer is this exact shape, swap in a message that names the
|
|
1481
1507
|
// model and explains the likely cause rather than re-passing
|
|
1482
|
-
// Cognition's opaque text.
|
|
1483
|
-
//
|
|
1508
|
+
// Cognition's opaque text. Raw trailer messages never leave this
|
|
1509
|
+
// parser: they can reflect the credential carried by the request.
|
|
1484
1510
|
// Both codes carry this shape. Cognition uses `invalid_argument` for a
|
|
1485
1511
|
// request it could not accept and `permission_denied` for one it would not,
|
|
1486
1512
|
// and the message body is the same opaque sentence either way.
|
|
1487
1513
|
const isOpaqueDenial =
|
|
1488
1514
|
(trailerError.code === 'permission_denied' || trailerError.code === 'invalid_argument') &&
|
|
1489
|
-
|
|
1515
|
+
trailerError.opaqueDenial;
|
|
1490
1516
|
if (isOpaqueDenial) {
|
|
1491
1517
|
const enriched =
|
|
1492
1518
|
`Cognition denied this request for model "${req.modelUid}" with the opaque ` +
|
|
@@ -1504,6 +1530,7 @@ export async function* streamChatEvents(req: CloudChatRequest): AsyncGenerator<C
|
|
|
1504
1530
|
trailerError.code,
|
|
1505
1531
|
trailerError.traceId,
|
|
1506
1532
|
connectTrailerHttpStatus(trailerError.code, trailerError.message),
|
|
1533
|
+
trailerError.retryAfterSeconds,
|
|
1507
1534
|
);
|
|
1508
1535
|
}
|
|
1509
1536
|
// Cognition also returns `permission_denied` when a tool description
|
|
@@ -1518,24 +1545,31 @@ export async function* streamChatEvents(req: CloudChatRequest): AsyncGenerator<C
|
|
|
1518
1545
|
`Cognition denied this request (permission_denied). If tool descriptions ` +
|
|
1519
1546
|
`are present, a blocklisted phrase may have triggered this — see the ` +
|
|
1520
1547
|
`COGNITION_BLOCKLIST_REWRITES table in cloud-direct/chat.ts. ` +
|
|
1521
|
-
// Keep the cloud's own sentence. Replacing it outright is what made the
|
|
1522
|
-
// two Codex entries in that table expensive to find: the message named
|
|
1523
|
-
// the table but dropped the only text that could have said whether this
|
|
1524
|
-
// was a phrase match at all.
|
|
1525
|
-
`(cloud message: ${trailerError.message}) ` +
|
|
1526
1548
|
`(cloud trace ID: ${trailerError.traceId ?? 'n/a'})`;
|
|
1527
1549
|
throw new CloudChatError(
|
|
1528
1550
|
enriched,
|
|
1529
1551
|
trailerError.code,
|
|
1530
1552
|
trailerError.traceId,
|
|
1531
1553
|
connectTrailerHttpStatus(trailerError.code, trailerError.message),
|
|
1554
|
+
trailerError.retryAfterSeconds,
|
|
1532
1555
|
);
|
|
1533
1556
|
}
|
|
1557
|
+
// Raw trailer messages never leave this parser: they can reflect the
|
|
1558
|
+
// credential carried by the request. The allowlisted code and the hex
|
|
1559
|
+
// trace id are the only upstream-controlled fields that reach the error.
|
|
1560
|
+
// The stated delay rides along in our own words so a client can still
|
|
1561
|
+
// tell how long to wait when local retry gives up, exceeds its cap, or
|
|
1562
|
+
// is disabled; the `~` keeps it from re-parsing as a downstream hint.
|
|
1534
1563
|
throw new CloudChatError(
|
|
1535
|
-
trailerError.
|
|
1564
|
+
`Cognition chat failed${trailerError.code ? ` (${trailerError.code})` : ''} ` +
|
|
1565
|
+
`(cloud trace ID: ${trailerError.traceId ?? 'n/a'})` +
|
|
1566
|
+
(trailerError.retryAfterSeconds === undefined
|
|
1567
|
+
? ''
|
|
1568
|
+
: `; retry after ~${trailerError.retryAfterSeconds}s`),
|
|
1536
1569
|
trailerError.code,
|
|
1537
1570
|
trailerError.traceId,
|
|
1538
1571
|
connectTrailerHttpStatus(trailerError.code, trailerError.message),
|
|
1572
|
+
trailerError.retryAfterSeconds,
|
|
1539
1573
|
);
|
|
1540
1574
|
}
|
|
1541
1575
|
// Truncation detection: the cloud always terminates a successful stream
|
|
@@ -112,8 +112,11 @@ export async function* streamChatEventsWithResetRetry(
|
|
|
112
112
|
&& error.status === 429
|
|
113
113
|
? error
|
|
114
114
|
: undefined;
|
|
115
|
+
// Prefer the typed field: trailer-derived errors are content-free now,
|
|
116
|
+
// so the parser preserved the stated delay there. The message scrape
|
|
117
|
+
// remains for errors whose text still carries a hint.
|
|
115
118
|
const waitSec = retryableError
|
|
116
|
-
? parseRetryAfterFromMessage(retryableError.message)
|
|
119
|
+
? retryableError.retryAfterSeconds ?? parseRetryAfterFromMessage(retryableError.message)
|
|
117
120
|
: undefined;
|
|
118
121
|
const waitMs = waitSec === undefined ? undefined : waitSec * 1000;
|
|
119
122
|
if (
|