@bitkyc08/opencodex 2.7.12 → 2.7.17
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/{index-B19F0Ren.js → index-m4o3xsSn.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/base.ts +2 -0
- package/src/adapters/cursor/discovery.ts +15 -7
- package/src/adapters/cursor/effort-map.ts +6 -0
- package/src/adapters/cursor/exec-policy.ts +1 -1
- package/src/adapters/cursor/live-models.ts +11 -3
- package/src/adapters/cursor/native-exec-fs.ts +1 -1
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +1 -1
- package/src/adapters/cursor/tool-definitions.ts +3 -0
- package/src/adapters/google-http.ts +1 -1
- package/src/adapters/kiro-retry.ts +1 -1
- package/src/adapters/openai-chat.ts +53 -5
- package/src/adapters/openai-responses.ts +50 -1
- package/src/bridge.ts +25 -10
- package/src/lib/upstream-retry.ts +6 -0
- package/src/providers/registry.ts +1 -1
- package/src/server/responses.ts +4 -3
- package/src/web-search/loop.ts +11 -6
|
@@ -37,4 +37,4 @@ v2: 多线程代理(spawn_agent)。所有模型使用 v2 协作界面。
|
|
|
37
37
|
"model": "gpt-5.4",
|
|
38
38
|
"input": "Hello, world!"
|
|
39
39
|
}'`})]})]})}function mn({apiBase:e}){let t=Te(),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(``),[c,l]=(0,_.useState)(!1),[u,d]=(0,_.useState)(!0),f=(0,_.useCallback)(async()=>{try{let t=await fetch(`${e}/api/claude-code`).then(e=>e.json());r({...t,systemEnv:t.systemEnv!==!1,fastMode:t.fastMode??null,maxContextTokens:t.maxContextTokens??null,autoContext:t.autoContext!==!1,autoCompactWindow:t.autoCompactWindow??null,injectAgents:t.injectAgents!==!1,effectiveModelEnv:t.effectiveModelEnv??{}}),a(Object.entries(t.modelMap??{}).map(([e,t])=>({from:e,to:String(t)})))}catch{l(!1),s(t(`claude.loadFail`))}finally{d(!1)}},[e,t]);(0,_.useEffect)(()=>{let e=window.setTimeout(()=>{f()},0);return()=>window.clearTimeout(e)},[f]);let p=(0,_.useMemo)(()=>{let e=(n?.available??[]).map(e=>({value:e,label:L(e)}));return[{value:``,label:t(`claude.slotUnset`)},...e]},[n?.available,t]),m=(0,_.useMemo)(()=>{let e=[1e5,2e5,25e4,3e5,35e4,4e5,5e5,6e5,75e4,9e5,1e6],r=e=>e>=1e6?`1M`:`${Math.round(e/1e3)}k`,i=n?.autoCompactWindow??null,a=i!==null&&!e.includes(i)?[...e,i].sort((e,t)=>e-t):e;return[{value:``,label:t(`claude.autoCompactDefault`)},...a.map(e=>({value:String(e),label:r(e)}))]},[n?.autoCompactWindow,t]),h=async()=>{if(!n)return;s(``);let r={};for(let e of i)e.from.trim()&&e.to.trim()&&(r[e.from.trim()]=e.to.trim());try{let i=await fetch(`${e}/api/claude-code`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({enabled:n.enabled,systemEnv:n.systemEnv,fastMode:n.fastMode,autoContext:n.autoContext,autoCompactWindow:n.autoCompactWindow,injectAgents:n.injectAgents,smallFastModel:n.smallFastModel,modelMap:r,webSearchSidecar:n.webSearchSidecar?{backend:n.webSearchSidecar.backend??null,model:n.webSearchSidecar.model??``}:null,visionSidecar:n.visionSidecar?{backend:n.visionSidecar.backend??null,model:n.visionSidecar.model??``}:null})}),a=await i.json();l(i.ok),s(i.ok?t(`claude.saved`):a.error||t(`claude.saveFailed`)),i.ok&&await f()}catch{l(!1),s(t(`claude.networkError`))}};if(u)return(0,S.jsx)(`div`,{className:`muted`,style:{padding:8},children:t(`claude.loading`)});if(!n)return(0,S.jsx)(Me,{tone:`err`,children:o||t(`claude.loadFail`)});let g=`http://127.0.0.1:${n.port}`,v=n.effectiveModelEnv,y=n.autoContext&&n.maxContextTokens===null,b=[`export ANTHROPIC_BASE_URL=${g}`,`# no ANTHROPIC_AUTH_TOKEN: your claude.ai login (and connectors) stay active`,`export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1`,...y?[`export CLAUDE_CODE_AUTO_COMPACT_WINDOW=${n.autoCompactWindow??35e4}`]:[],...[`ANTHROPIC_MODEL`,`ANTHROPIC_DEFAULT_OPUS_MODEL`,`ANTHROPIC_DEFAULT_SONNET_MODEL`,`ANTHROPIC_DEFAULT_HAIKU_MODEL`,`ANTHROPIC_DEFAULT_FABLE_MODEL`].filter(e=>v[e]).map(e=>`export ${e}=${v[e]}`),`claude`].join(`
|
|
40
|
-
`);return(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`div`,{className:`page-head`,children:(0,S.jsxs)(`h2`,{children:[t(`nav.claude`),` Code`]})}),(0,S.jsx)(`p`,{className:`page-sub`,children:t(`claude.subtitle`)}),o&&(0,S.jsx)(Me,{tone:c?`ok`:`err`,children:o}),(0,S.jsxs)(`div`,{className:`card`,style:{overflow:`hidden`},children:[(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.enabledLabel`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.enabledHint`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.enabled,onChange:e=>r({...n,enabled:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.systemEnv`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.systemEnvDesc`)}),n.systemEnv&&(0,S.jsx)(`span`,{className:`desc`,style:{color:`var(--red)`},children:t(`claude.systemEnvWarn`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.systemEnv,onChange:e=>r({...n,systemEnv:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.fastMode`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.fastModeDesc`)})]}),(0,S.jsxs)(`select`,{value:n.fastMode===null?`auto`:n.fastMode?`on`:`off`,onChange:e=>{let t=e.target.value;r({...n,fastMode:t===`auto`?null:t===`on`})},style:{padding:`5px 10px`,borderRadius:`var(--radius-xs)`,border:`1px solid var(--border)`,background:`var(--surface)`,color:`var(--text)`,fontSize:12.5,fontWeight:500},children:[(0,S.jsx)(`option`,{value:`auto`,children:t(`claude.fastAuto`)}),(0,S.jsx)(`option`,{value:`on`,children:t(`claude.fastOn`)}),(0,S.jsx)(`option`,{value:`off`,children:t(`claude.fastOff`)})]})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.autoContext`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.autoContextDesc`)}),n.maxContextTokens!==null&&(0,S.jsx)(`span`,{className:`desc`,style:{color:`var(--muted)`},children:t(`claude.autoContextInert`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.autoContext,onChange:e=>r({...n,autoContext:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),n.autoContext&&(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.autoCompactWindow`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.autoCompactWindowDesc`)}),n.autoCompactWindow!==null&&(0,S.jsx)(`span`,{className:`desc`,style:{color:`var(--red)`},children:t(`claude.autoCompactWindowWarn`)})]}),(0,S.jsx)(Ne,{value:n.autoCompactWindow===null?``:String(n.autoCompactWindow),options:m,onChange:e=>r({...n,autoCompactWindow:e===``?null:Number(e)}),label:t(`claude.autoCompactWindow`),style:{minWidth:130}})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.injectAgents`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.injectAgentsDesc`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.injectAgents,onChange:e=>r({...n,injectAgents:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),[`webSearchSidecar`,`visionSidecar`].map(e=>{let i=n[e],a=e===`webSearchSidecar`?`claude.webSearchSidecar`:`claude.visionSidecar`,o=e===`webSearchSidecar`?`claude.webSearchSidecarHint`:`claude.visionSidecarHint`;return(0,S.jsxs)(`div`,{className:`setting-row`,style:{alignItems:`flex-start`},children:[(0,S.jsxs)(`div`,{className:`setting-label setting-copy`,style:{flex:1},children:[(0,S.jsx)(`span`,{className:`title`,children:t(a)}),(0,S.jsx)(`span`,{className:`desc`,children:t(o)})]}),(0,S.jsxs)(`div`,{className:`setting-controls`,style:{display:`flex`,gap:8},children:[(0,S.jsx)(Ne,{value:i?i.backend??`auto`:`inherit`,options:[{value:`inherit`,label:t(`claude.useMainSetting`)},{value:`auto`,label:t(`dash.backendAuto`)},{value:`openai`,label:t(`dash.backendOpenAI`)},{value:`anthropic`,label:t(`dash.backendAnthropic`)}],onChange:t=>r({...n,[e]:t===`inherit`?void 0:{...i,backend:t===`auto`?void 0:t}}),label:t(`dash.sidecarBackend`)}),(0,S.jsx)(`input`,{className:`input mono`,value:i?.model??``,onChange:t=>r({...n,[e]:{...i,model:t.target.value}}),placeholder:t(`claude.sidecarModelPlaceholder`),disabled:!i,"aria-label":t(`dash.sidecarModel`),style:{minWidth:210}})]})]},e)})]}),(0,S.jsx)(`div`,{className:`h-section`,children:t(`claude.quickstart`)}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:(0,S.jsx)(De,{k:`claude.quickstartHint`,cmd:`ocx claude`})}),(0,S.jsx)(`pre`,{className:`mono card`,style:{padding:`10px 14px`,overflowX:`auto`,margin:0},children:`ocx claude`}),(0,S.jsxs)(`details`,{style:{margin:`10px 0 0`},children:[(0,S.jsx)(`summary`,{className:`muted`,style:{fontSize:12.5,cursor:`pointer`,padding:`2px 2px`},children:t(`claude.manualEnv`)}),(0,S.jsx)(`pre`,{className:`mono card`,style:{padding:`10px 14px`,overflowX:`auto`,margin:`6px 0 0`,fontSize:12},children:b})]}),(0,S.jsx)(`div`,{className:`h-section`,children:t(`claude.smallFastModel`)}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:t(`claude.smallFastModelHint`)}),(0,S.jsx)(Ne,{value:n.smallFastModel,options:p,onChange:e=>r({...n,smallFastModel:e}),label:t(`claude.smallFastModel`),style:{maxWidth:420}}),(0,S.jsxs)(`div`,{className:`h-section`,children:[t(`claude.modelMap`),` `,(0,S.jsx)(`span`,{className:`count`,children:i.length})]}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:t(`claude.modelMapHint`)}),(0,S.jsx)(`div`,{className:`stack`,style:{gap:8},children:i.map((e,n)=>(0,S.jsxs)(`div`,{className:`row`,style:{gap:8},children:[(0,S.jsx)(`input`,{className:`input mono`,value:e.from,placeholder:t(`claude.mapFrom`),onChange:e=>a(t=>t.map((t,r)=>r===n?{...t,from:e.target.value}:t)),style:{flex:1}}),(0,S.jsx)(`span`,{className:`muted`,"aria-hidden":!0,children:`→`}),(0,S.jsx)(`input`,{className:`input mono`,value:e.to,placeholder:t(`claude.mapTo`),onChange:e=>a(t=>t.map((t,r)=>r===n?{...t,to:e.target.value}:t)),style:{flex:1}}),(0,S.jsx)(`button`,{className:`btn btn-ghost btn-icon btn-sm`,onClick:()=>a(e=>e.filter((e,t)=>t!==n)),"aria-label":t(`claude.removeMapping`),style:{color:`var(--red)`},children:(0,S.jsx)(k,{})})]},n))}),(0,S.jsx)(`div`,{style:{marginTop:8},children:(0,S.jsxs)(`button`,{className:`btn btn-ghost btn-sm`,onClick:()=>a(e=>[...e,{from:``,to:``}]),children:[(0,S.jsx)(re,{}),` `,t(`claude.addMapping`)]})}),(0,S.jsx)(`div`,{style:{marginTop:14},children:(0,S.jsx)(`button`,{className:`btn btn-primary`,onClick:h,children:t(`common.save`)})}),(0,S.jsxs)(`div`,{className:`h-section`,children:[t(`claude.aliases`),` `,(0,S.jsx)(`span`,{className:`count`,children:n.aliases.length})]}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:t(`claude.aliasesHint`)}),n.aliases.length===0?(0,S.jsx)(`div`,{className:`muted`,style:{fontSize:12.5},children:t(`claude.none`)}):(0,S.jsx)(`div`,{className:`stack`,style:{gap:6,maxHeight:320,overflowY:`auto`},children:Array.from(n.aliases.reduce((e,t)=>{let n=/\(([^)]+)\)\s*$/.exec(t.display_name),r=n?n[1]:`etc`;return(e.get(r)??e.set(r,[]).get(r)).push(t),e},new Map)).map(([e,t])=>(0,S.jsxs)(`div`,{children:[(0,S.jsxs)(`div`,{className:`muted`,style:{fontSize:11.5,fontWeight:600,textTransform:`uppercase`,letterSpacing:0,margin:`6px 2px 4px`},children:[e,` · `,t.length]}),(0,S.jsx)(`div`,{className:`stack`,style:{gap:4},children:t.map(e=>(0,S.jsxs)(`div`,{className:`card row`,style:{padding:`6px 12px`,gap:10},children:[(0,S.jsx)(`code`,{className:`mono`,style:{flex:1,fontSize:12},children:e.id}),(0,S.jsx)(`span`,{className:`muted`,style:{fontSize:12},children:e.display_name})]},e.id))})]},e))})]})}var hn=`opencodex-api-token`,gn=!1,_n=null;function vn(e){try{let t=e instanceof Request?e.url:String(e);return new URL(t,window.location.href).pathname}catch{return null}}function yn(e){let t=vn(e);return!!t&&(t.startsWith(`/api/`)||t.startsWith(`/v1/`))}function bn(){try{return sessionStorage.getItem(hn)?.trim()||null}catch{return null}}function xn(e){try{sessionStorage.setItem(hn,e)}catch{}}function Sn(){try{sessionStorage.removeItem(hn)}catch{}}function Cn(e,t,n){let r=new Headers(t?.headers??(e instanceof Request?e.headers:void 0));return r.set(`X-OpenCodex-API-Key`,n),e instanceof Request?[new Request(e,{headers:r}),t?{...t,headers:r}:void 0]:[e,{...t,headers:r}]}async function wn(){return _n||(_n=Promise.resolve().then(()=>window.prompt(`OpenCodex API token`)?.trim()||null).finally(()=>{_n=null}),_n)}function Tn(){if(gn)return;gn=!0;let e=window.fetch.bind(window);window.fetch=async(t,n)=>{if(!yn(t))return e(t,n);let r=bn(),[i,a]=r?Cn(t,n,r):[t,n],o=await e(i,a);if(o.status!==401)return o;r&&Sn();let s=await wn();if(!s)return o;xn(s);let[c,l]=Cn(t,n,s),u=await e(c,l);return u.status===401&&Sn(),u}}Tn();var En=new Set([`dashboard`,`providers`,`models`,`subagents`,`logs`,`debug`,`usage`,`codex-auth`,`api`,`claude`]);function Dn(){let e=location.hash.replace(/^#\/?/,``);return En.has(e)?e:`dashboard`}var On=``,kn=`ocx-theme`,An=[{id:`dashboard`,tkey:`nav.dashboard`,Icon:w},{id:`providers`,tkey:`nav.providers`,Icon:T},{id:`models`,tkey:`nav.models`,Icon:E},{id:`subagents`,tkey:`nav.subagents`,Icon:ee},{id:`logs`,tkey:`nav.logs`,Icon:D},{id:`debug`,tkey:`nav.debug`,Icon:te},{id:`usage`,tkey:`nav.usage`,Icon:ne},{id:`codex-auth`,tkey:`nav.codexAuth`,Icon:ce},{id:`api`,tkey:`nav.api`,Icon:he},{id:`claude`,tkey:`nav.claude`,Icon:ge}],jn={light:fe,dark:pe,system:me},Mn={light:`theme.light`,dark:`theme.dark`,system:`theme.system`};function Nn(e){if(!e||typeof e!=`object`||!(`version`in e))return null;let t=e.version;return typeof t==`string`&&t.length>0?t:null}function Pn(){let e=localStorage.getItem(kn);return e===`light`||e===`dark`?e:`system`}function Fn(){let[e,t]=(0,_.useState)(Dn),[n,r]=(0,_.useState)(Pn),[i,a]=(0,_.useState)(null),{locale:o,setLocale:s}=we(),c=Te();(0,_.useEffect)(()=>{let e=()=>t(Dn());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useEffect)(()=>{let t=`#${e}`;window.location.hash!==t&&(window.location.hash=e)},[e]),(0,_.useEffect)(()=>{let e=document.documentElement;n===`system`?(e.removeAttribute(`data-theme`),localStorage.removeItem(kn)):(e.setAttribute(`data-theme`,n),localStorage.setItem(kn,n))},[n]),(0,_.useEffect)(()=>{let e=!1,t=async()=>{try{let t=await fetch(`${On}/healthz`);if(!t.ok)return;let n=Nn(await t.json());!e&&n&&a(n)}catch{}};t();let n=setInterval(t,3e4);return()=>{e=!0,clearInterval(n)}},[]);let l=()=>r(e=>e===`light`?`dark`:e===`dark`?`system`:`light`),u=jn[n],d=i??`2.7.12`,[f,p]=(0,_.useState)(!1),[m,h]=(0,_.useState)(null);return(0,_.useEffect)(()=>{let e=!1;return fetch(`${On}/api/claude-code`).then(e=>e.json()).then(t=>{!e&&typeof t.enabled==`boolean`&&h(t.enabled)}).catch(()=>{}),()=>{e=!0}},[]),(0,S.jsxs)(`div`,{className:`app`,children:[(0,S.jsxs)(`aside`,{className:`sidebar`,children:[(0,S.jsxs)(`div`,{className:`brand`,children:[(0,S.jsx)(`span`,{className:`brand-logo`,role:`img`,"aria-label":`opencodex logo`}),(0,S.jsx)(`span`,{className:`name`,children:`opencodex`}),(0,S.jsxs)(`span`,{className:`ver`,children:[`v`,d]})]}),(0,S.jsx)(`nav`,{children:An.map(({id:n,tkey:r,Icon:i})=>(0,S.jsxs)(`button`,{className:`nav-item${e===n?` active`:``}`,"data-page":n,onClick:()=>t(n),"aria-current":e===n?`page`:void 0,children:[(0,S.jsx)(i,{}),` `,c(r)]},n))}),(0,S.jsxs)(`div`,{className:`sidebar-foot`,children:[m!==null&&(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle`,onClick:async()=>{if(m===null)return;let e=!m;h(e);try{(await fetch(`${On}/api/claude-code`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({enabled:e})})).ok||h(!e)}catch{h(!e)}},"aria-pressed":m,"aria-label":c(`claude.toggleAria`),title:c(`claude.toggleAria`),style:m?{color:`var(--accent)`}:void 0,children:[(0,S.jsx)(ge,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:m?`Claude ON`:`Claude OFF`})]}),(0,S.jsxs)(`div`,{className:`lang-toggle`,children:[(0,S.jsx)(he,{"aria-hidden":!0}),(0,S.jsx)(Ne,{value:o,options:ye.map(e=>({value:e.code,label:e.name})),onChange:e=>s(e),label:c(`lang.label`),placement:`right`,style:{flex:1,minWidth:0,width:`100%`}})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle`,onClick:l,"aria-label":`${c(`theme.label`)}: ${c(Mn[n])}`,title:`${c(`theme.label`)}: ${c(Mn[n])}`,children:[(0,S.jsx)(u,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:c(Mn[n])})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle stop-toggle`,onClick:async()=>{if(confirm(c(`dash.stopConfirm`))){p(!0);try{await fetch(`${On}/api/stop`,{method:`POST`})}catch{}}},disabled:f,"aria-label":c(`dash.stop`),title:c(`dash.stop`),children:[(0,S.jsx)(I,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:c(f?`dash.stopping`:`dash.stop`)})]}),(0,S.jsxs)(`a`,{className:`sidebar-link`,href:`https://github.com/lidge-jun/opencodex`,target:`_blank`,rel:`noreferrer`,children:[(0,S.jsx)(F,{}),` `,c(`common.github`)]})]})]}),(0,S.jsx)(`main`,{className:`main`,children:(0,S.jsxs)(`div`,{className:`main-inner`,children:[e===`dashboard`&&(0,S.jsx)(Ke,{apiBase:On}),e===`providers`&&(0,S.jsx)(st,{apiBase:On}),e===`models`&&(0,S.jsx)(pt,{apiBase:On}),e===`subagents`&&(0,S.jsx)(mt,{apiBase:On}),e===`logs`&&(0,S.jsx)(Yt,{apiBase:On}),e===`debug`&&(0,S.jsx)(Qt,{apiBase:On}),e===`usage`&&(0,S.jsx)(on,{apiBase:On}),e===`codex-auth`&&(0,S.jsx)(cn,{apiBase:On}),e===`api`&&(0,S.jsx)(pn,{apiBase:On}),e===`claude`&&(0,S.jsx)(mn,{apiBase:On})]})})]})}v.createRoot(document.getElementById(`root`)).render((0,S.jsx)(_.StrictMode,{children:(0,S.jsx)(Ee,{children:(0,S.jsx)(Fn,{})})}));
|
|
40
|
+
`);return(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`div`,{className:`page-head`,children:(0,S.jsxs)(`h2`,{children:[t(`nav.claude`),` Code`]})}),(0,S.jsx)(`p`,{className:`page-sub`,children:t(`claude.subtitle`)}),o&&(0,S.jsx)(Me,{tone:c?`ok`:`err`,children:o}),(0,S.jsxs)(`div`,{className:`card`,style:{overflow:`hidden`},children:[(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.enabledLabel`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.enabledHint`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.enabled,onChange:e=>r({...n,enabled:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.systemEnv`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.systemEnvDesc`)}),n.systemEnv&&(0,S.jsx)(`span`,{className:`desc`,style:{color:`var(--red)`},children:t(`claude.systemEnvWarn`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.systemEnv,onChange:e=>r({...n,systemEnv:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.fastMode`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.fastModeDesc`)})]}),(0,S.jsxs)(`select`,{value:n.fastMode===null?`auto`:n.fastMode?`on`:`off`,onChange:e=>{let t=e.target.value;r({...n,fastMode:t===`auto`?null:t===`on`})},style:{padding:`5px 10px`,borderRadius:`var(--radius-xs)`,border:`1px solid var(--border)`,background:`var(--surface)`,color:`var(--text)`,fontSize:12.5,fontWeight:500},children:[(0,S.jsx)(`option`,{value:`auto`,children:t(`claude.fastAuto`)}),(0,S.jsx)(`option`,{value:`on`,children:t(`claude.fastOn`)}),(0,S.jsx)(`option`,{value:`off`,children:t(`claude.fastOff`)})]})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.autoContext`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.autoContextDesc`)}),n.maxContextTokens!==null&&(0,S.jsx)(`span`,{className:`desc`,style:{color:`var(--muted)`},children:t(`claude.autoContextInert`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.autoContext,onChange:e=>r({...n,autoContext:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),n.autoContext&&(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.autoCompactWindow`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.autoCompactWindowDesc`)}),n.autoCompactWindow!==null&&(0,S.jsx)(`span`,{className:`desc`,style:{color:`var(--red)`},children:t(`claude.autoCompactWindowWarn`)})]}),(0,S.jsx)(Ne,{value:n.autoCompactWindow===null?``:String(n.autoCompactWindow),options:m,onChange:e=>r({...n,autoCompactWindow:e===``?null:Number(e)}),label:t(`claude.autoCompactWindow`),style:{minWidth:130}})]}),(0,S.jsxs)(`div`,{className:`setting-row`,children:[(0,S.jsxs)(`div`,{className:`setting-label`,children:[(0,S.jsx)(`span`,{className:`title`,children:t(`claude.injectAgents`)}),(0,S.jsx)(`span`,{className:`desc`,children:t(`claude.injectAgentsDesc`)})]}),(0,S.jsxs)(`label`,{className:`toggle`,children:[(0,S.jsx)(`input`,{type:`checkbox`,checked:n.injectAgents,onChange:e=>r({...n,injectAgents:e.target.checked})}),(0,S.jsx)(`span`,{className:`slider`})]})]}),[`webSearchSidecar`,`visionSidecar`].map(e=>{let i=n[e],a=e===`webSearchSidecar`?`claude.webSearchSidecar`:`claude.visionSidecar`,o=e===`webSearchSidecar`?`claude.webSearchSidecarHint`:`claude.visionSidecarHint`;return(0,S.jsxs)(`div`,{className:`setting-row`,style:{alignItems:`flex-start`},children:[(0,S.jsxs)(`div`,{className:`setting-label setting-copy`,style:{flex:1},children:[(0,S.jsx)(`span`,{className:`title`,children:t(a)}),(0,S.jsx)(`span`,{className:`desc`,children:t(o)})]}),(0,S.jsxs)(`div`,{className:`setting-controls`,style:{display:`flex`,gap:8},children:[(0,S.jsx)(Ne,{value:i?i.backend??`auto`:`inherit`,options:[{value:`inherit`,label:t(`claude.useMainSetting`)},{value:`auto`,label:t(`dash.backendAuto`)},{value:`openai`,label:t(`dash.backendOpenAI`)},{value:`anthropic`,label:t(`dash.backendAnthropic`)}],onChange:t=>r({...n,[e]:t===`inherit`?void 0:{...i,backend:t===`auto`?void 0:t}}),label:t(`dash.sidecarBackend`)}),(0,S.jsx)(`input`,{className:`input mono`,value:i?.model??``,onChange:t=>r({...n,[e]:{...i,model:t.target.value}}),placeholder:t(`claude.sidecarModelPlaceholder`),disabled:!i,"aria-label":t(`dash.sidecarModel`),style:{minWidth:210}})]})]},e)})]}),(0,S.jsx)(`div`,{className:`h-section`,children:t(`claude.quickstart`)}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:(0,S.jsx)(De,{k:`claude.quickstartHint`,cmd:`ocx claude`})}),(0,S.jsx)(`pre`,{className:`mono card`,style:{padding:`10px 14px`,overflowX:`auto`,margin:0},children:`ocx claude`}),(0,S.jsxs)(`details`,{style:{margin:`10px 0 0`},children:[(0,S.jsx)(`summary`,{className:`muted`,style:{fontSize:12.5,cursor:`pointer`,padding:`2px 2px`},children:t(`claude.manualEnv`)}),(0,S.jsx)(`pre`,{className:`mono card`,style:{padding:`10px 14px`,overflowX:`auto`,margin:`6px 0 0`,fontSize:12},children:b})]}),(0,S.jsx)(`div`,{className:`h-section`,children:t(`claude.smallFastModel`)}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:t(`claude.smallFastModelHint`)}),(0,S.jsx)(Ne,{value:n.smallFastModel,options:p,onChange:e=>r({...n,smallFastModel:e}),label:t(`claude.smallFastModel`),style:{maxWidth:420}}),(0,S.jsxs)(`div`,{className:`h-section`,children:[t(`claude.modelMap`),` `,(0,S.jsx)(`span`,{className:`count`,children:i.length})]}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:t(`claude.modelMapHint`)}),(0,S.jsx)(`div`,{className:`stack`,style:{gap:8},children:i.map((e,n)=>(0,S.jsxs)(`div`,{className:`row`,style:{gap:8},children:[(0,S.jsx)(`input`,{className:`input mono`,value:e.from,placeholder:t(`claude.mapFrom`),onChange:e=>a(t=>t.map((t,r)=>r===n?{...t,from:e.target.value}:t)),style:{flex:1}}),(0,S.jsx)(`span`,{className:`muted`,"aria-hidden":!0,children:`→`}),(0,S.jsx)(`input`,{className:`input mono`,value:e.to,placeholder:t(`claude.mapTo`),onChange:e=>a(t=>t.map((t,r)=>r===n?{...t,to:e.target.value}:t)),style:{flex:1}}),(0,S.jsx)(`button`,{className:`btn btn-ghost btn-icon btn-sm`,onClick:()=>a(e=>e.filter((e,t)=>t!==n)),"aria-label":t(`claude.removeMapping`),style:{color:`var(--red)`},children:(0,S.jsx)(k,{})})]},n))}),(0,S.jsx)(`div`,{style:{marginTop:8},children:(0,S.jsxs)(`button`,{className:`btn btn-ghost btn-sm`,onClick:()=>a(e=>[...e,{from:``,to:``}]),children:[(0,S.jsx)(re,{}),` `,t(`claude.addMapping`)]})}),(0,S.jsx)(`div`,{style:{marginTop:14},children:(0,S.jsx)(`button`,{className:`btn btn-primary`,onClick:h,children:t(`common.save`)})}),(0,S.jsxs)(`div`,{className:`h-section`,children:[t(`claude.aliases`),` `,(0,S.jsx)(`span`,{className:`count`,children:n.aliases.length})]}),(0,S.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,margin:`0 0 8px`},children:t(`claude.aliasesHint`)}),n.aliases.length===0?(0,S.jsx)(`div`,{className:`muted`,style:{fontSize:12.5},children:t(`claude.none`)}):(0,S.jsx)(`div`,{className:`stack`,style:{gap:6,maxHeight:320,overflowY:`auto`},children:Array.from(n.aliases.reduce((e,t)=>{let n=/\(([^)]+)\)\s*$/.exec(t.display_name),r=n?n[1]:`etc`;return(e.get(r)??e.set(r,[]).get(r)).push(t),e},new Map)).map(([e,t])=>(0,S.jsxs)(`div`,{children:[(0,S.jsxs)(`div`,{className:`muted`,style:{fontSize:11.5,fontWeight:600,textTransform:`uppercase`,letterSpacing:0,margin:`6px 2px 4px`},children:[e,` · `,t.length]}),(0,S.jsx)(`div`,{className:`stack`,style:{gap:4},children:t.map(e=>(0,S.jsxs)(`div`,{className:`card row`,style:{padding:`6px 12px`,gap:10},children:[(0,S.jsx)(`code`,{className:`mono`,style:{flex:1,fontSize:12},children:e.id}),(0,S.jsx)(`span`,{className:`muted`,style:{fontSize:12},children:e.display_name})]},e.id))})]},e))})]})}var hn=`opencodex-api-token`,gn=!1,_n=null;function vn(e){try{let t=e instanceof Request?e.url:String(e);return new URL(t,window.location.href).pathname}catch{return null}}function yn(e){let t=vn(e);return!!t&&(t.startsWith(`/api/`)||t.startsWith(`/v1/`))}function bn(){try{return sessionStorage.getItem(hn)?.trim()||null}catch{return null}}function xn(e){try{sessionStorage.setItem(hn,e)}catch{}}function Sn(){try{sessionStorage.removeItem(hn)}catch{}}function Cn(e,t,n){let r=new Headers(t?.headers??(e instanceof Request?e.headers:void 0));return r.set(`X-OpenCodex-API-Key`,n),e instanceof Request?[new Request(e,{headers:r}),t?{...t,headers:r}:void 0]:[e,{...t,headers:r}]}async function wn(){return _n||(_n=Promise.resolve().then(()=>window.prompt(`OpenCodex API token`)?.trim()||null).finally(()=>{_n=null}),_n)}function Tn(){if(gn)return;gn=!0;let e=window.fetch.bind(window);window.fetch=async(t,n)=>{if(!yn(t))return e(t,n);let r=bn(),[i,a]=r?Cn(t,n,r):[t,n],o=await e(i,a);if(o.status!==401)return o;r&&Sn();let s=await wn();if(!s)return o;xn(s);let[c,l]=Cn(t,n,s),u=await e(c,l);return u.status===401&&Sn(),u}}Tn();var En=new Set([`dashboard`,`providers`,`models`,`subagents`,`logs`,`debug`,`usage`,`codex-auth`,`api`,`claude`]);function Dn(){let e=location.hash.replace(/^#\/?/,``);return En.has(e)?e:`dashboard`}var On=``,kn=`ocx-theme`,An=[{id:`dashboard`,tkey:`nav.dashboard`,Icon:w},{id:`providers`,tkey:`nav.providers`,Icon:T},{id:`models`,tkey:`nav.models`,Icon:E},{id:`subagents`,tkey:`nav.subagents`,Icon:ee},{id:`logs`,tkey:`nav.logs`,Icon:D},{id:`debug`,tkey:`nav.debug`,Icon:te},{id:`usage`,tkey:`nav.usage`,Icon:ne},{id:`codex-auth`,tkey:`nav.codexAuth`,Icon:ce},{id:`api`,tkey:`nav.api`,Icon:he},{id:`claude`,tkey:`nav.claude`,Icon:ge}],jn={light:fe,dark:pe,system:me},Mn={light:`theme.light`,dark:`theme.dark`,system:`theme.system`};function Nn(e){if(!e||typeof e!=`object`||!(`version`in e))return null;let t=e.version;return typeof t==`string`&&t.length>0?t:null}function Pn(){let e=localStorage.getItem(kn);return e===`light`||e===`dark`?e:`system`}function Fn(){let[e,t]=(0,_.useState)(Dn),[n,r]=(0,_.useState)(Pn),[i,a]=(0,_.useState)(null),{locale:o,setLocale:s}=we(),c=Te();(0,_.useEffect)(()=>{let e=()=>t(Dn());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useEffect)(()=>{let t=`#${e}`;window.location.hash!==t&&(window.location.hash=e)},[e]),(0,_.useEffect)(()=>{let e=document.documentElement;n===`system`?(e.removeAttribute(`data-theme`),localStorage.removeItem(kn)):(e.setAttribute(`data-theme`,n),localStorage.setItem(kn,n))},[n]),(0,_.useEffect)(()=>{let e=!1,t=async()=>{try{let t=await fetch(`${On}/healthz`);if(!t.ok)return;let n=Nn(await t.json());!e&&n&&a(n)}catch{}};t();let n=setInterval(t,3e4);return()=>{e=!0,clearInterval(n)}},[]);let l=()=>r(e=>e===`light`?`dark`:e===`dark`?`system`:`light`),u=jn[n],d=i??`2.7.17`,[f,p]=(0,_.useState)(!1),[m,h]=(0,_.useState)(null);return(0,_.useEffect)(()=>{let e=!1;return fetch(`${On}/api/claude-code`).then(e=>e.json()).then(t=>{!e&&typeof t.enabled==`boolean`&&h(t.enabled)}).catch(()=>{}),()=>{e=!0}},[]),(0,S.jsxs)(`div`,{className:`app`,children:[(0,S.jsxs)(`aside`,{className:`sidebar`,children:[(0,S.jsxs)(`div`,{className:`brand`,children:[(0,S.jsx)(`span`,{className:`brand-logo`,role:`img`,"aria-label":`opencodex logo`}),(0,S.jsx)(`span`,{className:`name`,children:`opencodex`}),(0,S.jsxs)(`span`,{className:`ver`,children:[`v`,d]})]}),(0,S.jsx)(`nav`,{children:An.map(({id:n,tkey:r,Icon:i})=>(0,S.jsxs)(`button`,{className:`nav-item${e===n?` active`:``}`,"data-page":n,onClick:()=>t(n),"aria-current":e===n?`page`:void 0,children:[(0,S.jsx)(i,{}),` `,c(r)]},n))}),(0,S.jsxs)(`div`,{className:`sidebar-foot`,children:[m!==null&&(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle`,onClick:async()=>{if(m===null)return;let e=!m;h(e);try{(await fetch(`${On}/api/claude-code`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({enabled:e})})).ok||h(!e)}catch{h(!e)}},"aria-pressed":m,"aria-label":c(`claude.toggleAria`),title:c(`claude.toggleAria`),style:m?{color:`var(--accent)`}:void 0,children:[(0,S.jsx)(ge,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:m?`Claude ON`:`Claude OFF`})]}),(0,S.jsxs)(`div`,{className:`lang-toggle`,children:[(0,S.jsx)(he,{"aria-hidden":!0}),(0,S.jsx)(Ne,{value:o,options:ye.map(e=>({value:e.code,label:e.name})),onChange:e=>s(e),label:c(`lang.label`),placement:`right`,style:{flex:1,minWidth:0,width:`100%`}})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle`,onClick:l,"aria-label":`${c(`theme.label`)}: ${c(Mn[n])}`,title:`${c(`theme.label`)}: ${c(Mn[n])}`,children:[(0,S.jsx)(u,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:c(Mn[n])})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle stop-toggle`,onClick:async()=>{if(confirm(c(`dash.stopConfirm`))){p(!0);try{await fetch(`${On}/api/stop`,{method:`POST`})}catch{}}},disabled:f,"aria-label":c(`dash.stop`),title:c(`dash.stop`),children:[(0,S.jsx)(I,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:c(f?`dash.stopping`:`dash.stop`)})]}),(0,S.jsxs)(`a`,{className:`sidebar-link`,href:`https://github.com/lidge-jun/opencodex`,target:`_blank`,rel:`noreferrer`,children:[(0,S.jsx)(F,{}),` `,c(`common.github`)]})]})]}),(0,S.jsx)(`main`,{className:`main`,children:(0,S.jsxs)(`div`,{className:`main-inner`,children:[e===`dashboard`&&(0,S.jsx)(Ke,{apiBase:On}),e===`providers`&&(0,S.jsx)(st,{apiBase:On}),e===`models`&&(0,S.jsx)(pt,{apiBase:On}),e===`subagents`&&(0,S.jsx)(mt,{apiBase:On}),e===`logs`&&(0,S.jsx)(Yt,{apiBase:On}),e===`debug`&&(0,S.jsx)(Qt,{apiBase:On}),e===`usage`&&(0,S.jsx)(on,{apiBase:On}),e===`codex-auth`&&(0,S.jsx)(cn,{apiBase:On}),e===`api`&&(0,S.jsx)(pn,{apiBase:On}),e===`claude`&&(0,S.jsx)(mn,{apiBase:On})]})})]})}v.createRoot(document.getElementById(`root`)).render((0,S.jsx)(_.StrictMode,{children:(0,S.jsx)(Ee,{children:(0,S.jsx)(Fn,{})})}));
|
package/gui/dist/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-m4o3xsSn.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-Cq8maiJf.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/package.json
CHANGED
package/src/adapters/base.ts
CHANGED
|
@@ -51,4 +51,6 @@ export interface AdapterFetchContext {
|
|
|
51
51
|
timeoutMs?: number;
|
|
52
52
|
/** Return final non-2xx responses untouched so the caller can own the error-body read. */
|
|
53
53
|
returnRawErrors?: boolean;
|
|
54
|
+
/** Whether the upstream response will be consumed as a stream; adapters may select low-latency transport settings. */
|
|
55
|
+
stream?: boolean;
|
|
54
56
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cursorModelEffortLadder } from "./effort-map";
|
|
1
|
+
import { CANONICAL_EFFORT_SUFFIXES, cursorModelEffortLadder } from "./effort-map";
|
|
2
2
|
|
|
3
3
|
export interface CursorModelInfo {
|
|
4
4
|
id: string;
|
|
@@ -55,18 +55,26 @@ export function normalizeCursorModels(models: readonly CursorModelInfo[]): Curso
|
|
|
55
55
|
return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Strip the `cursor-` wire prefix that some Cursor GetUsableModels responses prepend to model ids
|
|
60
|
+
* (e.g. `cursor-grok-4.5-high` instead of `grok-4.5-high`). Applied at the comparison boundary
|
|
61
|
+
* so upstream wire ids stay verbatim everywhere else (issue #117).
|
|
62
|
+
*/
|
|
63
|
+
function stripCursorWirePrefix(id: string): string {
|
|
64
|
+
return id.startsWith("cursor-") ? id.slice("cursor-".length) : id;
|
|
65
|
+
}
|
|
62
66
|
|
|
63
67
|
/**
|
|
64
68
|
* True when a configured Cursor base model should remain exposed after live GetUsableModels filtering.
|
|
65
69
|
* Live ids are full effort-suffixed variants (`claude-4.6-opus-high`); base ids match exactly or by prefix.
|
|
66
70
|
*/
|
|
67
71
|
export function isCursorModelAvailableForAccount(modelId: string, liveIds: readonly string[]): boolean {
|
|
68
|
-
return liveIds.some(
|
|
69
|
-
id
|
|
72
|
+
return liveIds.some(raw => {
|
|
73
|
+
const id = stripCursorWirePrefix(raw);
|
|
74
|
+
if (id === modelId) return true;
|
|
75
|
+
const effortPrefix = `${modelId}-`;
|
|
76
|
+
return id.startsWith(effortPrefix) && CANONICAL_EFFORT_SUFFIXES.has(id.slice(effortPrefix.length));
|
|
77
|
+
});
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
/** Codex-facing id for Cursor's auto-router. Always kept in the catalog even when live discovery omits it. */
|
|
@@ -46,6 +46,12 @@ const CURSOR_MODEL_EFFORT_TIERS: Record<string, readonly string[]> = {
|
|
|
46
46
|
"gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
/** All effort suffixes accepted when matching live Cursor model ids to configured base ids. */
|
|
50
|
+
export const CANONICAL_EFFORT_SUFFIXES: ReadonlySet<string> = new Set([
|
|
51
|
+
"none",
|
|
52
|
+
...Object.values(CURSOR_MODEL_EFFORT_TIERS).flat(),
|
|
53
|
+
]);
|
|
54
|
+
|
|
49
55
|
const CANONICAL_CODEX_EFFORT_ORDER = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
50
56
|
|
|
51
57
|
function normalizeRequestedEffort(reasoning: string | undefined): string | undefined {
|
|
@@ -10,7 +10,7 @@ export const CURSOR_SANDBOX_FULL_ACCESS_RE = /sandbox_mode[^\n]{0,80}danger-full
|
|
|
10
10
|
export function resolveCursorNativeExecMode(provider: OcxProviderConfig): CursorNativeExecMode {
|
|
11
11
|
const mode = provider.nativeLocalExec;
|
|
12
12
|
if (mode === "off" || mode === "codex-sandbox" || mode === "on") return mode;
|
|
13
|
-
return provider.unsafeAllowNativeLocalExec === true ? "on" : "
|
|
13
|
+
return provider.unsafeAllowNativeLocalExec === true ? "on" : "codex-sandbox";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -94,9 +94,17 @@ export async function fetchCursorUsableModels(opts: CursorUsableModelsOptions):
|
|
|
94
94
|
const response = fromBinary(GetUsableModelsResponseSchema, new Uint8Array(Buffer.concat(chunks)));
|
|
95
95
|
// Account filtering uses wire `model_id` values only. Aliases like `composer-2-5` must not
|
|
96
96
|
// make stale configured ids such as `composer-2` look activated.
|
|
97
|
-
const ids =
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const ids: string[] = [];
|
|
98
|
+
const seenIds = new Set<string>();
|
|
99
|
+
for (const model of response.models ?? []) {
|
|
100
|
+
const rawId = (model as { modelId?: string }).modelId;
|
|
101
|
+
if (typeof rawId !== "string") continue;
|
|
102
|
+
const id = rawId.trim();
|
|
103
|
+
if (id.length === 0 || /[\x00-\x1f]/.test(id) || seenIds.has(id)) continue;
|
|
104
|
+
seenIds.add(id);
|
|
105
|
+
ids.push(id);
|
|
106
|
+
if (ids.length === 500) break;
|
|
107
|
+
}
|
|
100
108
|
close(ids.length > 0 ? { ok: true, models: ids } : { ok: false, error: "empty" });
|
|
101
109
|
} catch {
|
|
102
110
|
close({ ok: false, error: "decode", detail: "Invalid GetUsableModels protobuf response" });
|
|
@@ -44,7 +44,7 @@ function codexNativeMutationRefusal(operation: "write" | "delete"): string {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const NATIVE_LOCAL_EXEC_DISABLED =
|
|
47
|
-
"Cursor native local filesystem execution is
|
|
47
|
+
"Cursor native local filesystem execution is not available for this request. Use the exec_command tool with equivalent shell commands (cat, head, ls, rg, grep) for file reads and searches, or apply_patch for file edits.";
|
|
48
48
|
|
|
49
49
|
export function rejectReadExecForPolicy(execMsg: ExecServerMessage): Uint8Array {
|
|
50
50
|
if (execMsg.message.case !== "readArgs") throw new Error("invalid read exec");
|
|
@@ -7,7 +7,7 @@ export interface CursorNativeNetworkDeps {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const NATIVE_FETCH_DISABLED =
|
|
10
|
-
"Cursor native fetch execution is
|
|
10
|
+
"Cursor native fetch execution is not available for this request. Use the exec_command tool with curl or wget to make network requests instead.";
|
|
11
11
|
|
|
12
12
|
export function rejectFetchExecForPolicy(execMsg: ExecServerMessage): Uint8Array {
|
|
13
13
|
if (execMsg.message.case !== "fetchArgs") throw new Error("invalid fetch exec");
|
|
@@ -24,7 +24,7 @@ const backgroundShells = new Map<number, { child: ChildProcessWithoutNullStreams
|
|
|
24
24
|
let nextShellId = 1;
|
|
25
25
|
|
|
26
26
|
const NATIVE_SHELL_DISABLED =
|
|
27
|
-
"Cursor native shell execution is
|
|
27
|
+
"Cursor native shell execution is not available for this request. Use the exec_command tool to run shell commands instead.";
|
|
28
28
|
|
|
29
29
|
function rejectedShellResult(command: string, cwd: string, started: number) {
|
|
30
30
|
return create(ShellResultSchema, {
|
|
@@ -272,6 +272,9 @@ export function buildCursorToolGuidanceSystemNote(
|
|
|
272
272
|
? `Use ${discoveryTools} only for explicit discovery/resource tasks, not generic tool-count demos.`
|
|
273
273
|
: undefined,
|
|
274
274
|
"Do not count or report a tool call unless a tool result was actually returned.",
|
|
275
|
+
hasBareExec
|
|
276
|
+
? "If a built-in file read, directory listing, grep, or shell operation is rejected by the runtime, use \`exec_command\` with the equivalent shell command instead (e.g. \`cat\`, \`ls\`, \`rg\`, \`grep\`). For file edits, use \`apply_patch\` when available."
|
|
277
|
+
: undefined,
|
|
275
278
|
].filter((note): note is string => typeof note === "string");
|
|
276
279
|
return notes.join(" ");
|
|
277
280
|
}
|
|
@@ -36,7 +36,7 @@ export async function fetchGoogleWithRetry(label: string, request: AdapterReques
|
|
|
36
36
|
method: request.method,
|
|
37
37
|
headers: request.headers,
|
|
38
38
|
body: request.body,
|
|
39
|
-
}, timeoutMs, ctx.abortSignal);
|
|
39
|
+
}, timeoutMs, ctx.abortSignal, ctx.stream);
|
|
40
40
|
if (!retryableGoogleStatus(res.status) || attempt === GOOGLE_RETRY_ATTEMPTS - 1) {
|
|
41
41
|
return ctx.returnRawErrors ? res : normalizeFinalGoogleError(label, res, ctx.abortSignal);
|
|
42
42
|
}
|
|
@@ -39,7 +39,7 @@ export async function fetchKiroWithRetry(request: AdapterRequest, ctx: AdapterFe
|
|
|
39
39
|
method: request.method,
|
|
40
40
|
headers: request.headers,
|
|
41
41
|
body: request.body,
|
|
42
|
-
}, timeoutMs, ctx.abortSignal);
|
|
42
|
+
}, timeoutMs, ctx.abortSignal, ctx.stream);
|
|
43
43
|
if (!retryableKiroStatus(res.status) || attempt === KIRO_RETRY_ATTEMPTS - 1) {
|
|
44
44
|
return ctx.returnRawErrors ? res : normalizeFinalKiroHttpError(res, ctx.abortSignal);
|
|
45
45
|
}
|
|
@@ -125,7 +125,49 @@ function safeToolName(name: string | undefined): string {
|
|
|
125
125
|
return sanitized;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
const XAI_SCHEMA_BASE_URLS = new Set(["api.x.ai", "cli-chat-proxy.grok.com"]);
|
|
129
|
+
|
|
130
|
+
function isXaiSchemaTarget(provider: OcxProviderConfig): boolean {
|
|
131
|
+
try {
|
|
132
|
+
return XAI_SCHEMA_BASE_URLS.has(new URL(provider.baseUrl).hostname);
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function expandXaiRootObjectSchemas(schema: unknown): Record<string, unknown>[] | undefined {
|
|
139
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return undefined;
|
|
140
|
+
const obj = schema as Record<string, unknown>;
|
|
141
|
+
const compositionKey = ["oneOf", "anyOf"].find(key => Array.isArray(obj[key]));
|
|
142
|
+
if (!compositionKey) {
|
|
143
|
+
if (obj.type !== undefined && obj.type !== "object") return undefined;
|
|
144
|
+
return [{ ...obj, type: "object" }];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const siblings = Object.fromEntries(Object.entries(obj).filter(([key]) => key !== compositionKey));
|
|
148
|
+
const branches = obj[compositionKey];
|
|
149
|
+
if (!Array.isArray(branches)) return undefined;
|
|
150
|
+
const expanded: Record<string, unknown>[] = [];
|
|
151
|
+
for (const branch of branches) {
|
|
152
|
+
const variants = expandXaiRootObjectSchemas(branch);
|
|
153
|
+
if (!variants) return undefined;
|
|
154
|
+
for (const variant of variants) expanded.push({ ...siblings, ...variant });
|
|
155
|
+
}
|
|
156
|
+
return expanded.length > 0 ? expanded : undefined;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function normalizeXaiToolParameters(parameters: unknown): Record<string, unknown> | undefined {
|
|
160
|
+
const variants = expandXaiRootObjectSchemas(parameters);
|
|
161
|
+
if (!variants) return undefined;
|
|
162
|
+
if (variants.length === 1) return variants[0];
|
|
163
|
+
const root = parameters && typeof parameters === "object" && !Array.isArray(parameters)
|
|
164
|
+
? parameters as Record<string, unknown>
|
|
165
|
+
: {};
|
|
166
|
+
const metadata = Object.fromEntries(Object.entries(root).filter(([key]) => key !== "oneOf" && key !== "anyOf" && key !== "type"));
|
|
167
|
+
return { ...metadata, oneOf: variants };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function toolsToChatFormat(parsed: OcxParsedRequest, provider: OcxProviderConfig): unknown[] | undefined {
|
|
129
171
|
if (!parsed.context.tools || parsed.context.tools.length === 0) return undefined;
|
|
130
172
|
const allowed = isAllowedToolChoice(parsed.options.toolChoice)
|
|
131
173
|
? new Set(parsed.options.toolChoice.allowedTools)
|
|
@@ -134,15 +176,21 @@ function toolsToChatFormat(parsed: OcxParsedRequest): unknown[] | undefined {
|
|
|
134
176
|
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed))
|
|
135
177
|
: parsed.context.tools;
|
|
136
178
|
if (tools.length === 0) return undefined;
|
|
137
|
-
|
|
179
|
+
const xaiTarget = isXaiSchemaTarget(provider);
|
|
180
|
+
const formatted = tools.flatMap(t => {
|
|
181
|
+
const parameters = xaiTarget ? normalizeXaiToolParameters(t.parameters) : t.parameters;
|
|
182
|
+
if (parameters === undefined) return [];
|
|
183
|
+
return [{
|
|
138
184
|
type: "function",
|
|
139
185
|
function: {
|
|
140
186
|
name: namespacedToolName(t.namespace, t.name),
|
|
141
187
|
description: t.description,
|
|
142
|
-
parameters
|
|
188
|
+
parameters,
|
|
143
189
|
...(t.strict !== undefined ? { strict: t.strict } : {}),
|
|
144
190
|
},
|
|
145
|
-
|
|
191
|
+
}];
|
|
192
|
+
});
|
|
193
|
+
return formatted.length > 0 ? formatted : undefined;
|
|
146
194
|
}
|
|
147
195
|
|
|
148
196
|
function toolChoiceToChatFormat(tc: OcxParsedRequest["options"]["toolChoice"], tools: OcxParsedRequest["context"]["tools"]): unknown {
|
|
@@ -190,7 +238,7 @@ export function createOpenAIChatAdapter(provider: OcxProviderConfig): ProviderAd
|
|
|
190
238
|
}
|
|
191
239
|
|
|
192
240
|
const messages = messagesToChatFormat(parsed, provider);
|
|
193
|
-
const tools = toolsToChatFormat(parsed);
|
|
241
|
+
const tools = toolsToChatFormat(parsed, provider);
|
|
194
242
|
const toolChoice = toolChoiceToChatFormat(parsed.options.toolChoice, parsed.context.tools);
|
|
195
243
|
|
|
196
244
|
const body: Record<string, unknown> = {
|
|
@@ -52,6 +52,55 @@ function sanitizeReasoningInputContent(body: unknown): unknown {
|
|
|
52
52
|
return changed ? { ...raw, input } : body;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function stripInvalidItemIds(body: unknown): unknown {
|
|
56
|
+
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
57
|
+
|
|
58
|
+
const validPrefixes: Record<string, string> = {
|
|
59
|
+
message: "msg_",
|
|
60
|
+
reasoning: "rs_",
|
|
61
|
+
function_call: "fc_",
|
|
62
|
+
custom_tool_call: "ctc_",
|
|
63
|
+
tool_search_call: "tsc_",
|
|
64
|
+
web_search_call: "ws_",
|
|
65
|
+
};
|
|
66
|
+
let changed = false;
|
|
67
|
+
const input = body.input.map(item => {
|
|
68
|
+
if (!isPlainObject(item) || typeof item.type !== "string") return item;
|
|
69
|
+
const validPrefix = validPrefixes[item.type];
|
|
70
|
+
if (!validPrefix) return item;
|
|
71
|
+
if (typeof item.id === "string" && item.id.startsWith(validPrefix)) return item;
|
|
72
|
+
if (!("id" in item)) return item;
|
|
73
|
+
changed = true;
|
|
74
|
+
const next = { ...item };
|
|
75
|
+
delete next.id;
|
|
76
|
+
return next;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return changed ? { ...body, input } : body;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* When `store` is false, the upstream API does not persist response items. Any item ID
|
|
84
|
+
* forwarded in `input` is then interpreted as a reference to a stored item that does not
|
|
85
|
+
* exist, producing a 404. Strip all item IDs in this case — `call_id` pairing is unaffected.
|
|
86
|
+
* Matches codex-rs behavior (core/src/client.rs:918-925).
|
|
87
|
+
*/
|
|
88
|
+
function stripItemIdsWhenUnstored(body: unknown): unknown {
|
|
89
|
+
if (!isPlainObject(body) || body.store !== false) return body;
|
|
90
|
+
if (!Array.isArray(body.input)) return body;
|
|
91
|
+
|
|
92
|
+
let changed = false;
|
|
93
|
+
const input = body.input.map(item => {
|
|
94
|
+
if (!isPlainObject(item) || !("id" in item)) return item;
|
|
95
|
+
changed = true;
|
|
96
|
+
const next = { ...item };
|
|
97
|
+
delete next.id;
|
|
98
|
+
return next;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return changed ? { ...body, input } : body;
|
|
102
|
+
}
|
|
103
|
+
|
|
55
104
|
/**
|
|
56
105
|
* Replace proxy-minted compaction items (`encrypted_content` starting with `ocx1:`) with plain
|
|
57
106
|
* user messages before forwarding to the ChatGPT backend. Our envelope is transparent base64, not
|
|
@@ -282,7 +331,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
282
331
|
url,
|
|
283
332
|
method: "POST",
|
|
284
333
|
headers,
|
|
285
|
-
body: JSON.stringify(stripUnsupportedHostedTools(sanitizeReasoningInputContent(scrubOcxCompactionItems(outBody)))),
|
|
334
|
+
body: JSON.stringify(stripItemIdsWhenUnstored(stripInvalidItemIds(stripUnsupportedHostedTools(sanitizeReasoningInputContent(scrubOcxCompactionItems(outBody)))))),
|
|
286
335
|
};
|
|
287
336
|
},
|
|
288
337
|
|
package/src/bridge.ts
CHANGED
|
@@ -146,11 +146,13 @@ export function bridgeToResponsesSSE(
|
|
|
146
146
|
|
|
147
147
|
return new ReadableStream<Uint8Array>({
|
|
148
148
|
async start(controller) {
|
|
149
|
+
let emittedSinceYield = false;
|
|
149
150
|
const emit = (name: string, data: Record<string, unknown>) => {
|
|
150
151
|
if (closed) return;
|
|
151
152
|
activity = true;
|
|
152
153
|
try {
|
|
153
154
|
controller.enqueue(encoder.encode(sseEvent(name, { type: name, sequence_number: seq++, ...data })));
|
|
155
|
+
emittedSinceYield = true;
|
|
154
156
|
} catch {
|
|
155
157
|
closed = true;
|
|
156
158
|
}
|
|
@@ -264,7 +266,7 @@ export function bridgeToResponsesSSE(
|
|
|
264
266
|
let currentToolCall: { itemId: string; outputIndex: number; callId: string; name: string; args: string; namespace?: string; freeform?: boolean; toolSearch?: boolean; inputEmitted?: string } | null = null;
|
|
265
267
|
// Open native web-search cell (between begin and end). Holds the output index allocated on
|
|
266
268
|
// begin so the matching done reuses it; closed as `failed` if the stream terminates early.
|
|
267
|
-
let currentWebSearch: { itemId: string; outputIndex: number } | null = null;
|
|
269
|
+
let currentWebSearch: { itemId: string; eventId: string; outputIndex: number } | null = null;
|
|
268
270
|
// Sources from completed web searches, awaiting the next assistant message. Attached as
|
|
269
271
|
// url_citation annotations on that message (the desktop app's Sources chip), then cleared so
|
|
270
272
|
// they bind to exactly one message. Deduped by URL across multiple searches in the turn.
|
|
@@ -399,9 +401,19 @@ export function bridgeToResponsesSSE(
|
|
|
399
401
|
// we synthesize response.completed below, so Codex never hits the parser's
|
|
400
402
|
// "stream closed before response.completed" (responses.rs) -> ApiError::Stream.
|
|
401
403
|
let terminated = false;
|
|
404
|
+
let macrotaskFired = true;
|
|
405
|
+
let macrotaskTimer: ReturnType<typeof setTimeout> | undefined;
|
|
402
406
|
|
|
403
407
|
try {
|
|
404
408
|
for await (const event of events) {
|
|
409
|
+
if (!macrotaskFired && emittedSinceYield) {
|
|
410
|
+
await new Promise<void>(r => setTimeout(r, 0));
|
|
411
|
+
macrotaskFired = true;
|
|
412
|
+
}
|
|
413
|
+
emittedSinceYield = false;
|
|
414
|
+
macrotaskFired = false;
|
|
415
|
+
if (macrotaskTimer !== undefined) clearTimeout(macrotaskTimer);
|
|
416
|
+
macrotaskTimer = setTimeout(() => { macrotaskFired = true; macrotaskTimer = undefined; }, 0);
|
|
405
417
|
activity = true;
|
|
406
418
|
stallTicks = 0;
|
|
407
419
|
// Compaction turns emit ONLY the synthetic compaction item + response.completed. The
|
|
@@ -498,12 +510,12 @@ export function bridgeToResponsesSSE(
|
|
|
498
510
|
if (currentRawReasoning) closeCurrentRawReasoning();
|
|
499
511
|
flushHiddenRawReasoning();
|
|
500
512
|
if (currentToolCall) closeCurrentToolCall();
|
|
501
|
-
const itemId = `fc_${uuid()}`;
|
|
502
513
|
const mapped = toolNsMap?.get(event.name);
|
|
503
514
|
const realName = mapped?.name ?? event.name;
|
|
504
515
|
const ns = mapped?.namespace;
|
|
505
516
|
const toolSearch = toolSearchToolNames?.has(realName) ?? false;
|
|
506
517
|
const freeform = !toolSearch && (freeformToolNames?.has(realName) ?? false);
|
|
518
|
+
const itemId = `${toolSearch ? "tsc" : freeform ? "ctc" : "fc"}_${uuid()}`;
|
|
507
519
|
const item = toolSearch
|
|
508
520
|
? { type: "tool_search_call", id: itemId, call_id: event.id, execution: "client", arguments: {}, status: "in_progress" }
|
|
509
521
|
: freeform
|
|
@@ -554,23 +566,25 @@ export function bridgeToResponsesSSE(
|
|
|
554
566
|
flushHiddenRawReasoning();
|
|
555
567
|
if (currentToolCall) closeCurrentToolCall();
|
|
556
568
|
if (currentWebSearch) closeCurrentWebSearch("completed", []);
|
|
569
|
+
const wsItemId = `ws_${uuid()}`;
|
|
557
570
|
emit("response.output_item.added", {
|
|
558
571
|
output_index: outputIndex,
|
|
559
|
-
item: { type: "web_search_call", id:
|
|
572
|
+
item: { type: "web_search_call", id: wsItemId, status: "in_progress" },
|
|
560
573
|
});
|
|
561
|
-
currentWebSearch = { itemId: event.id, outputIndex };
|
|
574
|
+
currentWebSearch = { itemId: wsItemId, eventId: event.id, outputIndex };
|
|
562
575
|
break;
|
|
563
576
|
}
|
|
564
577
|
case "web_search_call_end": {
|
|
565
578
|
// The sidecar resolved — finalize the cell as "Searched <query>". If no begin opened
|
|
566
579
|
// (defensive), synthesize the added frame first so the done has a matching item.
|
|
567
|
-
if (!currentWebSearch || currentWebSearch.
|
|
580
|
+
if (!currentWebSearch || currentWebSearch.eventId !== event.id) {
|
|
568
581
|
if (currentWebSearch) closeCurrentWebSearch("completed", []);
|
|
582
|
+
const wsItemId2 = `ws_${uuid()}`;
|
|
569
583
|
emit("response.output_item.added", {
|
|
570
584
|
output_index: outputIndex,
|
|
571
|
-
item: { type: "web_search_call", id:
|
|
585
|
+
item: { type: "web_search_call", id: wsItemId2, status: "in_progress" },
|
|
572
586
|
});
|
|
573
|
-
currentWebSearch = { itemId: event.id, outputIndex };
|
|
587
|
+
currentWebSearch = { itemId: wsItemId2, eventId: event.id, outputIndex };
|
|
574
588
|
}
|
|
575
589
|
closeCurrentWebSearch(event.status ?? "completed", event.queries, event.sources);
|
|
576
590
|
// Queue this search's sources for the next assistant message (dedup by URL).
|
|
@@ -650,6 +664,7 @@ export function bridgeToResponsesSSE(
|
|
|
650
664
|
}
|
|
651
665
|
|
|
652
666
|
if (beat) clearInterval(beat);
|
|
667
|
+
if (macrotaskTimer !== undefined) clearTimeout(macrotaskTimer);
|
|
653
668
|
|
|
654
669
|
if (!terminated) {
|
|
655
670
|
// The adapter generator ended without an explicit done/error event. Mark as incomplete
|
|
@@ -782,13 +797,13 @@ export function buildResponseJSON(
|
|
|
782
797
|
const freeform = !toolSearch && (options?.freeformToolNames?.has(realName) ?? false);
|
|
783
798
|
if (toolSearch) {
|
|
784
799
|
output.push({
|
|
785
|
-
type: "tool_search_call", id: `
|
|
800
|
+
type: "tool_search_call", id: `tsc_${uuid()}`,
|
|
786
801
|
call_id: currentToolCallId, execution: "client",
|
|
787
802
|
arguments: parseArgsObj(currentToolCallArgs), status: "completed",
|
|
788
803
|
});
|
|
789
804
|
} else if (freeform) {
|
|
790
805
|
output.push({
|
|
791
|
-
type: "custom_tool_call", id: `
|
|
806
|
+
type: "custom_tool_call", id: `ctc_${uuid()}`,
|
|
792
807
|
call_id: currentToolCallId, name: realName,
|
|
793
808
|
input: freeformInput(currentToolCallArgs), status: "completed",
|
|
794
809
|
});
|
|
@@ -862,7 +877,7 @@ export function buildResponseJSON(
|
|
|
862
877
|
if (currentRawReasoning) flushRawReasoning();
|
|
863
878
|
flushToolCall();
|
|
864
879
|
output.push({
|
|
865
|
-
type: "web_search_call", id:
|
|
880
|
+
type: "web_search_call", id: `ws_${uuid()}`, status: e.status ?? "completed",
|
|
866
881
|
action: webSearchAction(e.queries),
|
|
867
882
|
...(e.sources && e.sources.length > 0 ? { sources: e.sources } : {}),
|
|
868
883
|
});
|
|
@@ -94,11 +94,17 @@ export async function fetchWithAttemptDeadline(
|
|
|
94
94
|
init: RequestInit,
|
|
95
95
|
timeoutMs: number,
|
|
96
96
|
abortSignal?: AbortSignal,
|
|
97
|
+
preferIdentityEncoding = false,
|
|
97
98
|
): Promise<Response> {
|
|
98
99
|
const attemptTimeout = clearableDeadline(timeoutMs, abortSignal);
|
|
100
|
+
const headers = new Headers(init.headers);
|
|
101
|
+
if (preferIdentityEncoding && !headers.has("accept-encoding")) {
|
|
102
|
+
headers.set("accept-encoding", "identity");
|
|
103
|
+
}
|
|
99
104
|
try {
|
|
100
105
|
return await fetch(url, {
|
|
101
106
|
...init,
|
|
107
|
+
headers,
|
|
102
108
|
signal: attemptTimeout.signal,
|
|
103
109
|
});
|
|
104
110
|
} finally {
|
|
@@ -202,7 +202,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
202
202
|
authKind: "oauth",
|
|
203
203
|
featured: false,
|
|
204
204
|
dashboardPreset: true,
|
|
205
|
-
note: "Experimental Cursor bridge. Live transport and live model discovery are enabled after a standalone PKCE browser login via 'ocx login cursor'; native read/write/delete/shell/fetch execution
|
|
205
|
+
note: "Experimental Cursor bridge. Live transport and live model discovery are enabled after a standalone PKCE browser login via 'ocx login cursor'; native read/write/delete/shell/fetch execution defaults to codex-sandbox mode (auto-enabled when the request declares Codex danger-full-access sandbox); override with \"nativeLocalExec\": \"on\" (always) or \"codex-sandbox\" (only for requests declaring the Codex danger-full-access sandbox; the declaration is caller-controlled prose the proxy cannot verify, and the auth-free loopback bind admits any process on this host, including other local users — enable only where every data-plane client is trusted) — legacy \"unsafeAllowNativeLocalExec\": true still means \"on\" — on providers.cursor in ~/.opencodex/config.json (dashboard: Providers → Cursor → Edit JSON) for a trusted local experiment.",
|
|
206
206
|
models: cursorModelIds(CURSOR_STATIC_MODELS),
|
|
207
207
|
liveModels: true,
|
|
208
208
|
defaultModel: "auto",
|
package/src/server/responses.ts
CHANGED
|
@@ -346,9 +346,10 @@ export function sanitizeEncryptedContentInPlace(input: unknown): number {
|
|
|
346
346
|
&& (child as { type?: unknown }).type === "agent_message"
|
|
347
347
|
&& !hasEncryptedContentPart((child as { content?: unknown }).content)
|
|
348
348
|
) {
|
|
349
|
-
const message = child as { type: string; role?: string; author?: unknown; recipient?: unknown };
|
|
349
|
+
const message = child as { type: string; role?: string; id?: unknown; author?: unknown; recipient?: unknown };
|
|
350
350
|
message.type = "message";
|
|
351
351
|
message.role = "user";
|
|
352
|
+
delete message.id;
|
|
352
353
|
delete message.author;
|
|
353
354
|
delete message.recipient;
|
|
354
355
|
}
|
|
@@ -923,7 +924,7 @@ export async function handleResponses(
|
|
|
923
924
|
let upstreamResponse: Response;
|
|
924
925
|
try {
|
|
925
926
|
upstreamResponse = adapter.fetchResponse
|
|
926
|
-
? await adapter.fetchResponse(request, { abortSignal: upstream.signal, timeoutMs: connectMs })
|
|
927
|
+
? await adapter.fetchResponse(request, { abortSignal: upstream.signal, timeoutMs: connectMs, stream: parsed.stream })
|
|
927
928
|
: await fetchWithResetRetry(
|
|
928
929
|
() => fetchWithHeaderTimeout(request.url, {
|
|
929
930
|
method: request.method, headers: request.headers, body: request.body,
|
|
@@ -962,7 +963,7 @@ export async function handleResponses(
|
|
|
962
963
|
const retryRequest = await retryAdapter.buildRequest(parsed, { headers: selectedForwardHeaders });
|
|
963
964
|
try {
|
|
964
965
|
upstreamResponse = retryAdapter.fetchResponse
|
|
965
|
-
? await retryAdapter.fetchResponse(retryRequest, { abortSignal: upstream.signal, timeoutMs: connectMs })
|
|
966
|
+
? await retryAdapter.fetchResponse(retryRequest, { abortSignal: upstream.signal, timeoutMs: connectMs, stream: parsed.stream })
|
|
966
967
|
: await fetchWithHeaderTimeout(retryRequest.url, {
|
|
967
968
|
method: retryRequest.method, headers: retryRequest.headers, body: retryRequest.body,
|
|
968
969
|
}, upstream.signal, connectMs, parsed.stream);
|
package/src/web-search/loop.ts
CHANGED
|
@@ -271,14 +271,19 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
271
271
|
abortSignal: headerDeadline.signal,
|
|
272
272
|
timeoutMs: connectTimeoutMs,
|
|
273
273
|
returnRawErrors: true,
|
|
274
|
+
stream: true,
|
|
274
275
|
})
|
|
275
276
|
: await fetchWithResetRetry(
|
|
276
|
-
() =>
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
277
|
+
() => {
|
|
278
|
+
const h = new Headers(request.headers);
|
|
279
|
+
if (!h.has("accept-encoding")) h.set("accept-encoding", "identity");
|
|
280
|
+
return fetch(request.url, {
|
|
281
|
+
method: request.method,
|
|
282
|
+
headers: h,
|
|
283
|
+
body: request.body,
|
|
284
|
+
signal: headerDeadline.signal,
|
|
285
|
+
});
|
|
286
|
+
},
|
|
282
287
|
{ abortSignal: headerDeadline.signal, label: "web-search-loop" },
|
|
283
288
|
);
|
|
284
289
|
return { response, responseAdapter: requestAdapter };
|