@chrysb/alphaclaw 0.9.23 → 0.9.25

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.
@@ -3629,7 +3629,7 @@ Please report this to https://github.com/markedjs/marked.`,t){let s="<p>An error
3629
3629
  </div>
3630
3630
  `:null}
3631
3631
  </div>
3632
- `};var Qx={off:"Off",on:"On",minimal:"Minimal",low:"Low",medium:"Medium",high:"High",adaptive:"Adaptive",xhigh:"Extra high",max:"Maximum"},Ti=(t="")=>{let e=String(t||"").trim().toLowerCase();return e?Qx[e]?Qx[e]:e.split(/[-_]/g).filter(Boolean).map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join(" "):""},ev=(t="")=>{let e=Ti(t);return e?`Inherited: ${e}`:"Inherited"},tv=(t=[])=>{let e=(Array.isArray(t)?t:[]).map(n=>String(n?.id||n||"").trim().toLowerCase()).filter(Boolean);return e.length===0?!1:!(e.length===1&&e[0]==="off")};var un=null,ar=t=>String(t?.key||t?.token||t?.access||"").trim(),nv="No models found",sv="Failed to load model settings",Wl=t=>{let e=!!t,n=String(t||"").trim(),s=!e,[o,r]=x(()=>s&&un?.catalog||[]),[i,a]=x(()=>s&&un?.catalogStatus||{source:"",fetchedAt:null,stale:!1,refreshing:!1}),[l,c]=x(()=>s&&un?.primary||""),[d,u]=x(()=>s&&un?.configuredModels||{}),[p,f]=x(()=>s&&un?.authProfiles||[]),[g,m]=x(()=>s&&un?.authOrder||{}),[h,b]=x(()=>s&&un?.codexStatus||{connected:!1}),[y,v]=x(()=>!(s&&un)),[$,w]=x(!1),[S,A]=x(()=>!!(s&&un)),[C,k]=x(""),[_,L]=x({}),[I,F]=x({}),N=le(un?.primary||""),E=le(un?.configuredModels||{}),T=q(re=>{e||(un={...un||{},...re})},[e]),O=n?`/api/models/config?agentId=${encodeURIComponent(n)}`:"/api/models/config",z=at(ds,rs,{maxAgeMs:3e4}),H=at(O,()=>Cg(e?{agentId:t}:void 0),{maxAgeMs:3e4}),K=at("/api/codex/status",Zo,{maxAgeMs:15e3}),Q=Ie(rs,ul,{enabled:S&&pl(i),pauseWhenHidden:!0,cacheKey:ds}),te=q(re=>{k(ae=>re.length>0?ae===nv?"":ae:ae||nv)},[]),U=q(re=>{let ae=hi(re),ve={source:String(re?.source||""),fetchedAt:Number(re?.fetchedAt||0)||null,stale:!!re?.stale,refreshing:!!re?.refreshing};return r(ae),a(ve),T({catalog:ae,catalogStatus:ve}),te(ae),ae},[te,T]),W=q(async()=>{S||v(!0),k("");try{let[re,ae,ve]=await Promise.all([z.refresh({force:!0}),H.refresh({force:!0}),K.refresh({force:!0})]),Se=U(re),V=ae.primary||"",se=ae.configuredModels||{},X=ae.authProfiles||[],ke=ae.authOrder||{};c(V),u(se),f(X),m(ke),b(ve||{connected:!1}),L({}),F({}),N.current=V,E.current=se,T({catalog:Se,primary:V,configuredModels:se,authProfiles:X,authOrder:ke,codexStatus:ve||{connected:!1}})}catch(re){k(sv),D(`${sv}: ${re.message}`,"error")}finally{A(!0),v(!1)}},[U,z,K,H,S,T]);P(()=>{W()},[t]),P(()=>{Q.data&&U(Q.data)},[U,Q.data]);let ue=re=>JSON.stringify(Object.keys(re).sort().reduce((ae,ve)=>(ae[ve]=re[ve],ae),{})),he=l!==N.current||ue(d)!==ue(E.current),ee=(()=>{let re=Object.entries(_).some(([ve,Se])=>{let V=p.find(se=>se.id===ve);return ar(Se)!==ar(V)}),ae=Object.entries(I).some(([ve,Se])=>{let V=g[ve];return JSON.stringify(Se)!==JSON.stringify(V)});return re||ae})(),oe=he||ee,ie=q(re=>{re&&u(ae=>{let ve={...ae,[re]:{}};return T({configuredModels:ve}),ve})},[T]),fe=q(re=>{if(u(ae=>{let ve={...ae};return delete ve[re],T({configuredModels:ve}),ve}),l===re){let ve=Object.keys(d).filter(Se=>Se!==re)[0]||"";c(ve),T({primary:ve})}},[l,d,T]),J=q(re=>{c(re),T({primary:re})},[T]),ce=q((re,ae)=>{let ve=p.find(Se=>Se.id===re);if(ar(ae)===ar(ve)){L(Se=>{let V={...Se};return delete V[re],V});return}L(Se=>({...Se,[re]:ae}))},[p]),ye=q((re,ae)=>{let ve=g[re]||null;if(JSON.stringify(ae)===JSON.stringify(ve)){F(Se=>{let V={...Se};return delete V[re],V});return}F(Se=>({...Se,[re]:ae}))},[g]),Z=q(re=>_[re]!==void 0?_[re]:p.find(ve=>ve.id===re)||null,[_,p]),j=q(re=>I[re]!==void 0?I[re]:g[re]||null,[I,g]),Y=q(()=>{let re=N.current||"",ae=E.current||{};c(re),u(ae),L({}),F({}),T({primary:re,configuredModels:ae})},[T]),be=q(async()=>{if(!$){w(!0);try{let re=Object.entries(_).filter(([ve,Se])=>{let V=p.find(se=>se.id===ve);return ar(Se)!==ar(V)}).map(([ve,Se])=>({id:ve,...Se})),ae=await _g({primary:l,configuredModels:d,profiles:re.length>0?re:void 0,authOrder:Object.keys(I).length>0?I:void 0,...e?{agentId:t}:{}});if(!ae.ok)throw new Error(ae.error||"Failed to save config");D("Changes saved","success"),ae.syncWarning&&D(`Saved, but git-sync failed: ${ae.syncWarning}`,"warning"),eu(ds),await W()}catch(re){D(re.message||"Failed to save changes","error")}finally{w(!1)}}},[$,l,d,_,I,p,e,t,W]),pe=q(async()=>{let re;try{re=await Zo(),b(re||{connected:!1}),T({codexStatus:re||{connected:!1}})}catch{b({connected:!1}),T({codexStatus:{connected:!1}});return}try{eu(ds);let ae=await z.refresh({force:!0});U(ae)}catch{}},[U,z,T]);return{catalog:o,primary:l,configuredModels:d,authProfiles:p,authOrder:g,codexStatus:h,loading:y,saving:$,ready:S,error:C,isDirty:oe,refresh:W,addModel:ie,removeModel:fe,setPrimaryModel:J,editProfile:ce,editAuthOrder:ye,getProfileValue:Z,getEffectiveOrder:j,cancelChanges:Y,saveAll:be,refreshCodexStatus:pe}};var Q_=t=>t?typeof t=="string"?t:t.primary||null:null,ov=(t=[],e="")=>t.find(n=>String(n?.key||"").trim()===String(e||"").trim())||null,rv=({agent:t={},onUpdateAgent:e=async()=>{}})=>{let[n,s]=x(!1),[o,r]=x(!1),[i,a]=x(!1),[l,c]=x({levels:[],inheritedDefault:"off",modelDefault:"off"}),[d,u]=x(!1),{catalog:p,primary:f,configuredModels:g,authProfiles:m,codexStatus:h,loading:b,ready:y}=Wl(),v=Q_(t.model),$=v||f||"",w=!!v&&String(v).trim()!==String(f||"").trim(),S=String(t.thinkingDefault||"").trim(),A=String(l.inheritedDefault||l.modelDefault||"off").trim(),C=!!S&&S!==A,k=tv(l.levels);P(()=>{let te=String($||"").trim();if(!te.includes("/")){c({levels:[],inheritedDefault:"off",modelDefault:"off"});return}let U=!1;return u(!0),kg(te).then(W=>{U||!W?.ok||c({levels:Array.isArray(W.levels)?W.levels:[],inheritedDefault:String(W.inheritedDefault||"off").trim(),modelDefault:String(W.modelDefault||"off").trim()})}).finally(()=>{U||u(!1)}),()=>{U=!0}},[$]);let _=B(()=>Fl({authProfiles:m,codexStatus:h}),[m,h]),L=B(()=>Object.keys(g||{}).map(te=>ov(p,te)||Mi(te)).filter(te=>{let U=Bl(te.key);return!!_[U]}).sort((te,U)=>{let W=ir(Fs(te))-ir(Fs(U));return W!==0?W:String(te?.label||te?.key).localeCompare(String(U?.label||U?.key))}),[p,g,_]),I=B(()=>ov(p,$)||($?Mi($):null),[p,$]),F=B(()=>L.filter(te=>{let U=Fs(te);return U==="anthropic"||U==="openai"}),[L]),N=B(()=>{if(!I)return[];let te=String(I?.key||"").trim(),U=L.filter(W=>String(W?.key||"").trim()!==te);return[I,...U]},[L,I]),E=B(()=>new Set(N.map(te=>String(te?.key||"").trim()).filter(Boolean)),[N]),T=B(()=>L.filter(te=>!E.has(String(te?.key||"").trim())),[L,E]),O=async te=>{let U=String(te||"").trim();if(!(!U||U===$)){s(!0);try{await e(String(t.id||"").trim(),{model:{primary:U}},"Agent model updated")}finally{s(!1)}}},z=async()=>{if(w){s(!0);try{await e(String(t.id||"").trim(),{model:null},"Agent model reset to default")}finally{s(!1)}}},H=async te=>{let U=String(te||"").trim();if(!U){if(!C)return;r(!0);try{await e(String(t.id||"").trim(),{thinkingDefault:null},"Agent thinking level reset to default")}finally{r(!1)}return}if(U!==S){r(!0);try{await e(String(t.id||"").trim(),{thinkingDefault:U},"Agent thinking level updated")}finally{r(!1)}}},K=C?S:"",Q=B(()=>{let te=new Set,U=[],W=(ue,he)=>{let ee=String(ue||"").trim();!ee||te.has(ee)||(te.add(ee),U.push({value:ee,label:String(he||Ti(ee)).trim()}))};for(let ue of l.levels)W(ue?.id,Ti(ue?.label||ue?.id));return S&&!te.has(S)&&W(S,`${Ti(S)} (custom)`),U},[S,l.levels]);return{authorizedModelOptions:L,canEditModel:y&&!b,effectiveModel:$,effectiveModelEntry:I,handleClearModelOverride:z,handleSelectModel:O,handleSelectThinkingDefault:H,hasDistinctModelOverride:w,hasDistinctThinkingOverride:C,inheritedThinkingDefault:A,loading:!y||b,menuOpen:i,modelEntries:N,popularModels:F,remainingModelOptions:T,setMenuOpen:a,showThinkingSelect:k,thinkingOptionsLoading:d,thinkingSelectOptions:Q,thinkingSelectValue:K,formatInheritedThinkingLabel:ev,updatingModel:n,updatingThinking:o}};var zt=R.bind(M),iv=({agent:t={},saving:e=!1,onUpdateAgent:n=async()=>{},onSwitchToModels:s=()=>{}})=>{let{authorizedModelOptions:o,canEditModel:r,effectiveModel:i,effectiveModelEntry:a,formatInheritedThinkingLabel:l,handleClearModelOverride:c,handleSelectModel:d,handleSelectThinkingDefault:u,hasDistinctModelOverride:p,hasDistinctThinkingOverride:f,inheritedThinkingDefault:g,loading:m,menuOpen:h,modelEntries:b,popularModels:y,remainingModelOptions:v,setMenuOpen:$,showThinkingSelect:w,thinkingOptionsLoading:S,thinkingSelectOptions:A,thinkingSelectValue:C,updatingModel:k,updatingThinking:_}=rv({agent:t,onUpdateAgent:n});return zt`
3632
+ `};var Qx={off:"Off",on:"On",minimal:"Minimal",low:"Low",medium:"Medium",high:"High",adaptive:"Adaptive",xhigh:"Extra high",max:"Maximum"},Ti=(t="")=>{let e=String(t||"").trim().toLowerCase();return e?Qx[e]?Qx[e]:e.split(/[-_]/g).filter(Boolean).map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join(" "):""},ev=(t="")=>{let e=Ti(t);return e?`Inherited: ${e}`:"Inherited"},tv=(t=[])=>{let e=(Array.isArray(t)?t:[]).map(n=>String(n?.id||n||"").trim().toLowerCase()).filter(Boolean);return e.length===0?!1:!(e.length===1&&e[0]==="off")};var un=null,ar=t=>String(t?.key||t?.token||t?.access||"").trim(),nv="No models found",sv="Failed to load model settings",Wl=t=>{let e=!!t,n=String(t||"").trim(),s=!e,[o,r]=x(()=>s&&un?.catalog||[]),[i,a]=x(()=>s&&un?.catalogStatus||{source:"",fetchedAt:null,stale:!1,refreshing:!1}),[l,c]=x(()=>s&&un?.primary||""),[d,u]=x(()=>s&&un?.configuredModels||{}),[p,f]=x(()=>s&&un?.authProfiles||[]),[g,m]=x(()=>s&&un?.authOrder||{}),[h,b]=x(()=>s&&un?.codexStatus||{connected:!1}),[y,v]=x(()=>!(s&&un)),[$,w]=x(!1),[S,A]=x(()=>!!(s&&un)),[C,k]=x(""),[_,L]=x({}),[I,F]=x({}),N=le(un?.primary||""),E=le(un?.configuredModels||{}),T=q(re=>{e||(un={...un||{},...re})},[e]),O=n?`/api/models/config?agentId=${encodeURIComponent(n)}`:"/api/models/config",z=at(ds,rs,{maxAgeMs:3e4}),H=at(O,()=>Cg(e?{agentId:t}:void 0),{maxAgeMs:3e4}),K=at("/api/codex/status",Zo,{maxAgeMs:15e3}),Q=Ie(rs,ul,{enabled:S&&pl(i),pauseWhenHidden:!0,cacheKey:ds}),te=q(re=>{k(ae=>re.length>0?ae===nv?"":ae:ae||nv)},[]),U=q(re=>{let ae=hi(re),ve={source:String(re?.source||""),fetchedAt:Number(re?.fetchedAt||0)||null,stale:!!re?.stale,refreshing:!!re?.refreshing};return r(ae),a(ve),T({catalog:ae,catalogStatus:ve}),te(ae),ae},[te,T]),W=q(async()=>{S||v(!0),k("");try{let[re,ae,ve]=await Promise.all([z.refresh({force:!0}),H.refresh({force:!0}),K.refresh({force:!0})]),Se=U(re),V=ae.primary||"",se=ae.configuredModels||{},X=ae.authProfiles||[],ke=ae.authOrder||{};c(V),u(se),f(X),m(ke),b(ve||{connected:!1}),L({}),F({}),N.current=V,E.current=se,T({catalog:Se,primary:V,configuredModels:se,authProfiles:X,authOrder:ke,codexStatus:ve||{connected:!1}})}catch(re){k(sv),D(`${sv}: ${re.message}`,"error")}finally{A(!0),v(!1)}},[U,z,K,H,S,T]);P(()=>{W()},[t]),P(()=>{Q.data&&U(Q.data)},[U,Q.data]);let ue=re=>JSON.stringify(Object.keys(re).sort().reduce((ae,ve)=>(ae[ve]=re[ve],ae),{})),he=l!==N.current||ue(d)!==ue(E.current),ee=(()=>{let re=Object.entries(_).some(([ve,Se])=>{let V=p.find(se=>se.id===ve);return ar(Se)!==ar(V)}),ae=Object.entries(I).some(([ve,Se])=>{let V=g[ve];return JSON.stringify(Se)!==JSON.stringify(V)});return re||ae})(),oe=he||ee,ie=q(re=>{if(!re)return;let ae=o.find(Se=>Se?.key===re),ve=ae?.agentRuntime?{agentRuntime:ae.agentRuntime}:{};u(Se=>{let V={...Se,[re]:ve};return T({configuredModels:V}),V})},[o,T]),fe=q(re=>{if(u(ae=>{let ve={...ae};return delete ve[re],T({configuredModels:ve}),ve}),l===re){let ve=Object.keys(d).filter(Se=>Se!==re)[0]||"";c(ve),T({primary:ve})}},[l,d,T]),J=q(re=>{c(re),T({primary:re})},[T]),ce=q((re,ae)=>{let ve=p.find(Se=>Se.id===re);if(ar(ae)===ar(ve)){L(Se=>{let V={...Se};return delete V[re],V});return}L(Se=>({...Se,[re]:ae}))},[p]),ye=q((re,ae)=>{let ve=g[re]||null;if(JSON.stringify(ae)===JSON.stringify(ve)){F(Se=>{let V={...Se};return delete V[re],V});return}F(Se=>({...Se,[re]:ae}))},[g]),Z=q(re=>_[re]!==void 0?_[re]:p.find(ve=>ve.id===re)||null,[_,p]),j=q(re=>I[re]!==void 0?I[re]:g[re]||null,[I,g]),Y=q(()=>{let re=N.current||"",ae=E.current||{};c(re),u(ae),L({}),F({}),T({primary:re,configuredModels:ae})},[T]),be=q(async()=>{if(!$){w(!0);try{let re=Object.entries(_).filter(([ve,Se])=>{let V=p.find(se=>se.id===ve);return ar(Se)!==ar(V)}).map(([ve,Se])=>({id:ve,...Se})),ae=await _g({primary:l,configuredModels:d,profiles:re.length>0?re:void 0,authOrder:Object.keys(I).length>0?I:void 0,...e?{agentId:t}:{}});if(!ae.ok)throw new Error(ae.error||"Failed to save config");D("Changes saved","success"),ae.syncWarning&&D(`Saved, but git-sync failed: ${ae.syncWarning}`,"warning"),eu(ds),await W()}catch(re){D(re.message||"Failed to save changes","error")}finally{w(!1)}}},[$,l,d,_,I,p,e,t,W]),pe=q(async()=>{let re;try{re=await Zo(),b(re||{connected:!1}),T({codexStatus:re||{connected:!1}})}catch{b({connected:!1}),T({codexStatus:{connected:!1}});return}try{eu(ds);let ae=await z.refresh({force:!0});U(ae)}catch{}},[U,z,T]);return{catalog:o,primary:l,configuredModels:d,authProfiles:p,authOrder:g,codexStatus:h,loading:y,saving:$,ready:S,error:C,isDirty:oe,refresh:W,addModel:ie,removeModel:fe,setPrimaryModel:J,editProfile:ce,editAuthOrder:ye,getProfileValue:Z,getEffectiveOrder:j,cancelChanges:Y,saveAll:be,refreshCodexStatus:pe}};var Q_=t=>t?typeof t=="string"?t:t.primary||null:null,ov=(t=[],e="")=>t.find(n=>String(n?.key||"").trim()===String(e||"").trim())||null,rv=({agent:t={},onUpdateAgent:e=async()=>{}})=>{let[n,s]=x(!1),[o,r]=x(!1),[i,a]=x(!1),[l,c]=x({levels:[],inheritedDefault:"off",modelDefault:"off"}),[d,u]=x(!1),{catalog:p,primary:f,configuredModels:g,authProfiles:m,codexStatus:h,loading:b,ready:y}=Wl(),v=Q_(t.model),$=v||f||"",w=!!v&&String(v).trim()!==String(f||"").trim(),S=String(t.thinkingDefault||"").trim(),A=String(l.inheritedDefault||l.modelDefault||"off").trim(),C=!!S&&S!==A,k=tv(l.levels);P(()=>{let te=String($||"").trim();if(!te.includes("/")){c({levels:[],inheritedDefault:"off",modelDefault:"off"});return}let U=!1;return u(!0),kg(te).then(W=>{U||!W?.ok||c({levels:Array.isArray(W.levels)?W.levels:[],inheritedDefault:String(W.inheritedDefault||"off").trim(),modelDefault:String(W.modelDefault||"off").trim()})}).finally(()=>{U||u(!1)}),()=>{U=!0}},[$]);let _=B(()=>Fl({authProfiles:m,codexStatus:h}),[m,h]),L=B(()=>Object.keys(g||{}).map(te=>ov(p,te)||Mi(te)).filter(te=>{let U=Bl(te.key);return!!_[U]}).sort((te,U)=>{let W=ir(Fs(te))-ir(Fs(U));return W!==0?W:String(te?.label||te?.key).localeCompare(String(U?.label||U?.key))}),[p,g,_]),I=B(()=>ov(p,$)||($?Mi($):null),[p,$]),F=B(()=>L.filter(te=>{let U=Fs(te);return U==="anthropic"||U==="openai"}),[L]),N=B(()=>{if(!I)return[];let te=String(I?.key||"").trim(),U=L.filter(W=>String(W?.key||"").trim()!==te);return[I,...U]},[L,I]),E=B(()=>new Set(N.map(te=>String(te?.key||"").trim()).filter(Boolean)),[N]),T=B(()=>L.filter(te=>!E.has(String(te?.key||"").trim())),[L,E]),O=async te=>{let U=String(te||"").trim();if(!(!U||U===$)){s(!0);try{await e(String(t.id||"").trim(),{model:{primary:U}},"Agent model updated")}finally{s(!1)}}},z=async()=>{if(w){s(!0);try{await e(String(t.id||"").trim(),{model:null},"Agent model reset to default")}finally{s(!1)}}},H=async te=>{let U=String(te||"").trim();if(!U){if(!C)return;r(!0);try{await e(String(t.id||"").trim(),{thinkingDefault:null},"Agent thinking level reset to default")}finally{r(!1)}return}if(U!==S){r(!0);try{await e(String(t.id||"").trim(),{thinkingDefault:U},"Agent thinking level updated")}finally{r(!1)}}},K=C?S:"",Q=B(()=>{let te=new Set,U=[],W=(ue,he)=>{let ee=String(ue||"").trim();!ee||te.has(ee)||(te.add(ee),U.push({value:ee,label:String(he||Ti(ee)).trim()}))};for(let ue of l.levels)W(ue?.id,Ti(ue?.label||ue?.id));return S&&!te.has(S)&&W(S,`${Ti(S)} (custom)`),U},[S,l.levels]);return{authorizedModelOptions:L,canEditModel:y&&!b,effectiveModel:$,effectiveModelEntry:I,handleClearModelOverride:z,handleSelectModel:O,handleSelectThinkingDefault:H,hasDistinctModelOverride:w,hasDistinctThinkingOverride:C,inheritedThinkingDefault:A,loading:!y||b,menuOpen:i,modelEntries:N,popularModels:F,remainingModelOptions:T,setMenuOpen:a,showThinkingSelect:k,thinkingOptionsLoading:d,thinkingSelectOptions:Q,thinkingSelectValue:K,formatInheritedThinkingLabel:ev,updatingModel:n,updatingThinking:o}};var zt=R.bind(M),iv=({agent:t={},saving:e=!1,onUpdateAgent:n=async()=>{},onSwitchToModels:s=()=>{}})=>{let{authorizedModelOptions:o,canEditModel:r,effectiveModel:i,effectiveModelEntry:a,formatInheritedThinkingLabel:l,handleClearModelOverride:c,handleSelectModel:d,handleSelectThinkingDefault:u,hasDistinctModelOverride:p,hasDistinctThinkingOverride:f,inheritedThinkingDefault:g,loading:m,menuOpen:h,modelEntries:b,popularModels:y,remainingModelOptions:v,setMenuOpen:$,showThinkingSelect:w,thinkingOptionsLoading:S,thinkingSelectOptions:A,thinkingSelectValue:C,updatingModel:k,updatingThinking:_}=rv({agent:t,onUpdateAgent:n});return zt`
3633
3633
  <div class="bg-surface border border-border rounded-xl p-4 space-y-3">
3634
3634
  <div class="flex items-start justify-between gap-3">
3635
3635
  <h3 class="card-label">Model</h3>
@@ -199,13 +199,17 @@ export const useModels = (agentId) => {
199
199
  const addModel = useCallback(
200
200
  (modelKey) => {
201
201
  if (!modelKey) return;
202
+ const catalogEntry = catalog.find((model) => model?.key === modelKey);
203
+ const modelConfig = catalogEntry?.agentRuntime
204
+ ? { agentRuntime: catalogEntry.agentRuntime }
205
+ : {};
202
206
  setConfiguredModels((prev) => {
203
- const next = { ...prev, [modelKey]: {} };
207
+ const next = { ...prev, [modelKey]: modelConfig };
204
208
  updateCache({ configuredModels: next });
205
209
  return next;
206
210
  });
207
211
  },
208
- [updateCache],
212
+ [catalog, updateCache],
209
213
  );
210
214
 
211
215
  const removeModel = useCallback(
@@ -1,5 +1,6 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
+ const { DatabaseSync } = require("node:sqlite");
3
4
  const { AUTH_PROFILES_PATH, CODEX_PROFILE_ID, OPENCLAW_DIR } = require("./constants");
4
5
 
5
6
  const kDefaultAgentId = "main";
@@ -54,6 +55,86 @@ const resolveAgentDir = (agentId = kDefaultAgentId) =>
54
55
  const resolveAuthProfilesPath = (agentId = kDefaultAgentId) =>
55
56
  path.join(resolveAgentDir(agentId), "auth-profiles.json");
56
57
 
58
+ const resolveAuthDatabasePath = (agentId = kDefaultAgentId) =>
59
+ path.join(resolveAgentDir(agentId), "openclaw-agent.sqlite");
60
+
61
+ const loadSqliteAuthStore = (agentId = kDefaultAgentId) => {
62
+ const databasePath = resolveAuthDatabasePath(agentId);
63
+ if (!fs.existsSync(databasePath)) return null;
64
+ let database;
65
+ try {
66
+ database = new DatabaseSync(databasePath, { readOnly: true });
67
+ const secretsRow = database
68
+ .prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?")
69
+ .get("primary");
70
+ if (!secretsRow?.store_json) return null;
71
+ const secrets = JSON.parse(secretsRow.store_json);
72
+ const stateRow = database
73
+ .prepare("SELECT state_json FROM auth_profile_state WHERE state_key = ?")
74
+ .get("primary");
75
+ const state = stateRow?.state_json ? JSON.parse(stateRow.state_json) : {};
76
+ return {
77
+ version: Number(secrets.version || state.version || 1),
78
+ profiles: secrets.profiles || {},
79
+ order: state.order,
80
+ lastGood: state.lastGood,
81
+ usageStats: state.usageStats,
82
+ };
83
+ } catch {
84
+ return null;
85
+ } finally {
86
+ database?.close();
87
+ }
88
+ };
89
+
90
+ const saveSqliteAuthStore = (agentId, store) => {
91
+ const databasePath = resolveAuthDatabasePath(agentId);
92
+ if (!fs.existsSync(databasePath)) return false;
93
+ let database;
94
+ try {
95
+ database = new DatabaseSync(databasePath);
96
+ const now = Date.now();
97
+ const secrets = {
98
+ version: Number(store.version || 1),
99
+ profiles: store.profiles || {},
100
+ };
101
+ const state = {
102
+ version: Number(store.version || 1),
103
+ ...(store.order !== undefined ? { order: store.order } : {}),
104
+ ...(store.lastGood !== undefined ? { lastGood: store.lastGood } : {}),
105
+ ...(store.usageStats !== undefined ? { usageStats: store.usageStats } : {}),
106
+ };
107
+ database.exec("BEGIN IMMEDIATE");
108
+ database
109
+ .prepare(
110
+ `INSERT INTO auth_profile_store (store_key, store_json, updated_at)
111
+ VALUES (?, ?, ?)
112
+ ON CONFLICT(store_key) DO UPDATE SET
113
+ store_json = excluded.store_json,
114
+ updated_at = excluded.updated_at`,
115
+ )
116
+ .run("primary", JSON.stringify(secrets), now);
117
+ database
118
+ .prepare(
119
+ `INSERT INTO auth_profile_state (state_key, state_json, updated_at)
120
+ VALUES (?, ?, ?)
121
+ ON CONFLICT(state_key) DO UPDATE SET
122
+ state_json = excluded.state_json,
123
+ updated_at = excluded.updated_at`,
124
+ )
125
+ .run("primary", JSON.stringify(state), now);
126
+ database.exec("COMMIT");
127
+ return true;
128
+ } catch {
129
+ try {
130
+ database?.exec("ROLLBACK");
131
+ } catch {}
132
+ return false;
133
+ } finally {
134
+ database?.close();
135
+ }
136
+ };
137
+
57
138
  const resolveOpenclawConfigPath = () =>
58
139
  path.join(OPENCLAW_DIR, "openclaw.json");
59
140
 
@@ -61,6 +142,8 @@ const hasCompletedOnboardingConfig = (cfg) =>
61
142
  String(cfg?.agents?.defaults?.model?.primary || "").trim().includes("/");
62
143
 
63
144
  const loadAuthStore = (agentId = kDefaultAgentId) => {
145
+ const sqliteStore = loadSqliteAuthStore(agentId);
146
+ if (sqliteStore) return sqliteStore;
64
147
  const storePath = resolveAuthProfilesPath(agentId);
65
148
  let store = { version: 1, profiles: {} };
66
149
  try {
@@ -86,6 +169,7 @@ const loadAuthStore = (agentId = kDefaultAgentId) => {
86
169
  };
87
170
 
88
171
  const saveAuthStore = (agentId, store) => {
172
+ if (saveSqliteAuthStore(agentId, store)) return;
89
173
  const storePath = resolveAuthProfilesPath(agentId);
90
174
  fs.mkdirSync(path.dirname(storePath), { recursive: true });
91
175
  fs.writeFileSync(
@@ -115,13 +115,21 @@ const normalizeOnboardingModels = (models) => {
115
115
  const deduped = new Map();
116
116
  for (const model of models || []) {
117
117
  if (!model?.key || typeof model.key !== "string") continue;
118
- const provider = resolveModelProvider(model.key);
118
+ const sourceProvider = resolveModelProvider(model.key);
119
+ const isCodexRuntimeModel = sourceProvider === "codex";
120
+ const key = isCodexRuntimeModel
121
+ ? `openai/${model.key.split("/").slice(1).join("/")}`
122
+ : model.key;
123
+ const provider = resolveModelProvider(key);
119
124
  if (!kOnboardingModelProviders.has(provider)) continue;
120
- if (!deduped.has(model.key)) {
121
- deduped.set(model.key, {
122
- key: model.key,
125
+ if (!deduped.has(key)) {
126
+ deduped.set(key, {
127
+ key,
123
128
  provider,
124
129
  label: model.name || model.key,
130
+ ...(isCodexRuntimeModel
131
+ ? { agentRuntime: { id: "codex" } }
132
+ : {}),
125
133
  });
126
134
  }
127
135
  }
@@ -38,6 +38,7 @@ const normalizeCachedModels = ({
38
38
  (Array.isArray(models) ? models : []).map((model) => ({
39
39
  key: model?.key,
40
40
  name: model?.label || model?.name || model?.key,
41
+ ...(model?.agentRuntime ? { agentRuntime: model.agentRuntime } : {}),
41
42
  })),
42
43
  );
43
44
 
@@ -1,6 +1,7 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { pathToFileURL } = require("url");
4
+ const { DatabaseSync } = require("node:sqlite");
4
5
 
5
6
  const findOpenclawDistModule = (prefix) => {
6
7
  const entryPath = require.resolve("openclaw");
@@ -16,6 +17,53 @@ const writeConfig = (configPath, cfg) => {
16
17
  fs.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\n`, "utf8");
17
18
  };
18
19
 
20
+ const hasCanonicalCodexOauthProfile = (configPath) => {
21
+ const databasePath = path.join(
22
+ path.dirname(configPath),
23
+ "agents",
24
+ "main",
25
+ "agent",
26
+ "openclaw-agent.sqlite",
27
+ );
28
+ if (!fs.existsSync(databasePath)) return false;
29
+ let database;
30
+ try {
31
+ database = new DatabaseSync(databasePath, { readOnly: true });
32
+ const row = database
33
+ .prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?")
34
+ .get("primary");
35
+ const store = row?.store_json ? JSON.parse(row.store_json) : {};
36
+ return Object.values(store.profiles || {}).some(
37
+ (profile) =>
38
+ profile?.type === "oauth" &&
39
+ profile?.provider === "openai" &&
40
+ profile?.access &&
41
+ profile?.refresh,
42
+ );
43
+ } catch {
44
+ return false;
45
+ } finally {
46
+ database?.close();
47
+ }
48
+ };
49
+
50
+ const restoreCanonicalCodexRuntimeModels = ({ configPath, cfg }) => {
51
+ if (!hasCanonicalCodexOauthProfile(configPath)) return false;
52
+ const configuredModels = cfg?.agents?.defaults?.models;
53
+ if (!configuredModels || typeof configuredModels !== "object") return false;
54
+ let changed = false;
55
+ for (const [modelKey, modelConfig] of Object.entries(configuredModels)) {
56
+ if (!modelKey.startsWith("openai/gpt-")) continue;
57
+ if (modelConfig?.agentRuntime?.id === "codex") continue;
58
+ configuredModels[modelKey] = {
59
+ ...(modelConfig && typeof modelConfig === "object" ? modelConfig : {}),
60
+ agentRuntime: { id: "codex" },
61
+ };
62
+ changed = true;
63
+ }
64
+ return changed;
65
+ };
66
+
19
67
  const migrateLegacyCodexState = async ({
20
68
  configPath = process.env.OPENCLAW_CONFIG_PATH,
21
69
  env = process.env,
@@ -60,6 +108,11 @@ const migrateLegacyCodexState = async ({
60
108
  warnings.push(...sqliteMigration.warnings);
61
109
  if (sqliteMigration.configChanged) writeConfig(configPath, nextCfg);
62
110
 
111
+ if (restoreCanonicalCodexRuntimeModels({ configPath, cfg: nextCfg })) {
112
+ changes.push("Restored Codex runtime metadata for canonical OpenAI models.");
113
+ writeConfig(configPath, nextCfg);
114
+ }
115
+
63
116
  const sessionRepair = await routeModule.i({
64
117
  cfg: nextCfg,
65
118
  env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.9.23",
3
+ "version": "0.9.25",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },