@xopcai/xopc 0.0.55 → 0.0.56

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.
Files changed (38) hide show
  1. package/dist/extensions/telegram/xopc.extension.json +1 -1
  2. package/dist/gateway/static/root/assets/{agents-74EbWFJ1.js → agents-D6Suddtr.js} +2 -2
  3. package/dist/gateway/static/root/assets/{agents-74EbWFJ1.js.map → agents-D6Suddtr.js.map} +1 -1
  4. package/dist/gateway/static/root/assets/{apps-page-Drte0W-4.js → apps-page-Th-Hiuo-.js} +2 -2
  5. package/dist/gateway/static/root/assets/{apps-page-Drte0W-4.js.map → apps-page-Th-Hiuo-.js.map} +1 -1
  6. package/dist/gateway/static/root/assets/{channels-settings-JfEF7kEH.js → channels-settings-D1xRSJDZ.js} +2 -2
  7. package/dist/gateway/static/root/assets/{channels-settings-JfEF7kEH.js.map → channels-settings-D1xRSJDZ.js.map} +1 -1
  8. package/dist/gateway/static/root/assets/{cron-dreaming-jobs-BhOeOExN.js → cron-dreaming-jobs-3lVHkALf.js} +2 -2
  9. package/dist/gateway/static/root/assets/{cron-dreaming-jobs-BhOeOExN.js.map → cron-dreaming-jobs-3lVHkALf.js.map} +1 -1
  10. package/dist/gateway/static/root/assets/{cron-page-Cj2srMp3.js → cron-page-D7ngje2B.js} +2 -2
  11. package/dist/gateway/static/root/assets/{cron-page-Cj2srMp3.js.map → cron-page-D7ngje2B.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/{dist-DSj9MpxJ.js → dist-zjjnuQDo.js} +2 -2
  13. package/dist/gateway/static/root/assets/{dist-DSj9MpxJ.js.map → dist-zjjnuQDo.js.map} +1 -1
  14. package/dist/gateway/static/root/assets/{extension-debug-page-B9dAkTLR.js → extension-debug-page-D1j5i5LL.js} +2 -2
  15. package/dist/gateway/static/root/assets/{extension-debug-page-B9dAkTLR.js.map → extension-debug-page-D1j5i5LL.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/{extension-page-CosLqD45.js → extension-page-DFlhHkSt.js} +2 -2
  17. package/dist/gateway/static/root/assets/{extension-page-CosLqD45.js.map → extension-page-DFlhHkSt.js.map} +1 -1
  18. package/dist/gateway/static/root/assets/{extension-settings-page-BnJUuHga.js → extension-settings-page-BCa14gKB.js} +2 -2
  19. package/dist/gateway/static/root/assets/{extension-settings-page-BnJUuHga.js.map → extension-settings-page-BCa14gKB.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/{heartbeat-config-api-DKiU0FSn.js → heartbeat-config-api-D_OCqL2O.js} +2 -2
  21. package/dist/gateway/static/root/assets/{heartbeat-config-api-DKiU0FSn.js.map → heartbeat-config-api-D_OCqL2O.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/{index-B-L6DMBk.js → index-DzugsTKg.js} +4 -4
  23. package/dist/gateway/static/root/assets/{index-B-L6DMBk.js.map → index-DzugsTKg.js.map} +1 -1
  24. package/dist/gateway/static/root/assets/{logs-page-BVDgS2FD.js → logs-page-DlOnVTqZ.js} +2 -2
  25. package/dist/gateway/static/root/assets/{logs-page-BVDgS2FD.js.map → logs-page-DlOnVTqZ.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{sessions-page-C-OAYLwZ.js → sessions-page-CdvV35AY.js} +2 -2
  27. package/dist/gateway/static/root/assets/{sessions-page-C-OAYLwZ.js.map → sessions-page-CdvV35AY.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{settings-page-PgUZ4C2F.js → settings-page-DKWTjEWN.js} +2 -2
  29. package/dist/gateway/static/root/assets/{settings-page-PgUZ4C2F.js.map → settings-page-DKWTjEWN.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{skills-page-BJ4k9_us.js → skills-page-xq8DLd0E.js} +2 -2
  31. package/dist/gateway/static/root/assets/{skills-page-BJ4k9_us.js.map → skills-page-xq8DLd0E.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/{use-image-provider-credentials-OhVruFpB.js → use-image-provider-credentials-DEpLWx8J.js} +2 -2
  33. package/dist/gateway/static/root/assets/{use-image-provider-credentials-OhVruFpB.js.map → use-image-provider-credentials-DEpLWx8J.js.map} +1 -1
  34. package/dist/gateway/static/root/index.html +1 -1
  35. package/dist/package.js +1 -1
  36. package/dist/src/agent/agent-manager.js.map +1 -1
  37. package/dist/src/agent/service.js.map +1 -1
  38. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{o as r}from"./vendor-swr-B5fPo7KK.js";import{In as i,St as a,Ti as o,Xr as s,Y as c,Yr as l,Zr as u,_n as d,b as f,dr as p,hn as m,jn as h,kn as g,kr as _,li as v,ni as y,p as b,un as x,xt as S,y as C}from"./index-B-L6DMBk.js";var w=`/api/image/providers`;async function T(){return(await m(x(`/api/image/providers`)))?.payload?.providers??[]}var E=e(t(),1);function D(){return{apiKey:``,region:``,baseUrl:``,imageBaseUrl:``}}function O(e){return e?.apiKey?`••••••••••••`:``}function k(e,t){let n=(()=>{if(!e||typeof e!=`object`||!(`providersConfig`in e))return;let t=e.providersConfig;if(!(!t||typeof t!=`object`||Array.isArray(t)))return t})(),r={};for(let e of t){let t=n?.[e];r[e]={apiKey:O(t),region:t?.region??``,baseUrl:t?.baseUrl??``,imageBaseUrl:t?.imageBaseUrl??``}}return r}function A(e,t,n){let r=e[n].trim();if(r!==t[n].trim())return r||null}function j(e,t){let n=e.trim(),r=t.trim();if(n!==r&&!(b(n)&&b(r)))return n||(r?null:void 0)}function M(e,t,n){let r={};for(let i of e){let e=t[i]??D(),a=n[i]??D();if(JSON.stringify(e)===JSON.stringify(a))continue;let o={},s=j(e.apiKey,a.apiKey);s!==void 0&&(o.apiKey=s);let c=A(e,a,`region`);c!==void 0&&(o.region=c);let l=A(e,a,`baseUrl`);l!==void 0&&(o.baseUrl=l);let u=A(e,a,`imageBaseUrl`);u!==void 0&&(o.imageBaseUrl=u),Object.keys(o).length>0&&(r[i]=o)}return r}async function N(e){let t=await m(x(`/api/image/providers/${encodeURIComponent(e)}/reveal-api-key`),{method:`POST`,headers:{"Content-Type":`application/json`},body:`{}`});if(!t.ok||!t.payload)throw Error(t.error?.message??`Reveal failed`);return t.payload}async function P(e){Object.keys(e).length!==0&&(await m(x(`/api/config`),{method:`PATCH`,body:JSON.stringify({providersConfig:e})}),await S())}var F=n();function I({providerId:e,value:t,onChange:n,labels:r,apiKeyLinks:a,apiKeyLinkLabels:o}){let[d,p]=(0,E.useState)(!1),[m,h]=(0,E.useState)(void 0),[x,S]=(0,E.useState)(!1),[C,w]=(0,E.useState)(null),[T,D]=(0,E.useState)(!1),O=b(t);(0,E.useEffect)(()=>{O||(h(void 0),w(null))},[O,t]);let k=O&&d&&typeof m==`string`?m:t,A=!O||O&&d&&typeof m==`string`?`text`:`password`,j=!O&&t.trim().length>0&&!b(t)||!!d&&typeof m==`string`&&m.length>0,M=(0,E.useCallback)(async()=>{let e=!O&&t.trim()&&!b(t)?t.trim():typeof m==`string`&&m.length>0?m:``;if(e)try{await navigator.clipboard.writeText(e),D(!0),window.setTimeout(()=>D(!1),2e3)}catch{}},[O,m,t]),P=(0,E.useCallback)(async()=>{if(w(null),!O){p(e=>!e);return}if(m!==void 0){p(e=>!e);return}S(!0);try{h((await N(e)).apiKey??null),p(!0)}catch(e){w(e instanceof Error?e.message:r.loadFailed),h(null)}finally{S(!1)}},[O,e,m,r.loadFailed]);return(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-key-${e}`,children:r.apiKeyLabel}),a.length>0?(0,F.jsx)(`div`,{className:`flex flex-col gap-1`,children:a.map(e=>(0,F.jsxs)(`a`,{href:e.href,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,children:[f(e.kind,o),(0,F.jsx)(u,{className:`size-3`,"aria-hidden":!0})]},`${e.kind}-${e.href}`))}):null,O?(0,F.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:r.maskedHelp}):null,(0,F.jsxs)(`div`,{className:`relative min-w-0`,children:[(0,F.jsx)(`input`,{id:`img-cred-key-${e}`,type:A,autoComplete:`off`,spellCheck:!1,className:i(`w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,g),value:k,placeholder:O?`••••••••`:r.optionalPlaceholder,onChange:e=>{let t=e.target.value;O&&typeof m==`string`&&d&&t!==m&&(h(void 0),p(!1)),n(t)}}),(0,F.jsxs)(`div`,{className:`absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5`,children:[j?(0,F.jsx)(`button`,{type:`button`,className:i(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg`,c.transition,c.press,c.focusRingPanel),title:T?r.copied:r.copy,"aria-label":T?r.copied:r.copy,onClick:()=>void M(),children:T?(0,F.jsx)(v,{className:`size-4`}):(0,F.jsx)(y,{className:`size-4`})}):null,(0,F.jsx)(`button`,{type:`button`,className:i(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40`,c.transition,c.press,c.focusRingPanel),title:d?r.hide:r.show,"aria-label":d?r.hide:r.show,disabled:x,onClick:()=>void P(),children:x?(0,F.jsx)(_,{className:`size-4 animate-spin`,"aria-hidden":!0}):d?(0,F.jsx)(s,{className:`size-4`,"aria-hidden":!0}):(0,F.jsx)(l,{className:`size-4`,"aria-hidden":!0})})]})]}),O&&d&&m===null&&!C?(0,F.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:r.notInConfigFile}):null,C?(0,F.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:C}):null]})}function L(){return i(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,g)}function R(){return i(L(),`appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9`)}var z=`__custom__`;function B(e,t){if(!e.region.trim()&&!e.imageBaseUrl.trim())return``;let n=e.region.trim().toLowerCase();return t.some(e=>e.value===n)?n:z}function V(e,t){let n=e.baseUrl.trim().replace(/\/+$/,``);if(!n)return``;let r=t.map(e=>e.value.replace(/\/+$/,``)).indexOf(n);return r>=0?t[r].value:z}function H(e,t,n){return t===`beijing`?e.dashscopeRegion_beijing:t===`singapore`?e.dashscopeRegion_singapore:t===`us`?e.dashscopeRegion_us:n}function U(e,t){return t===`minimax`?e.minimaxClusterLabel:t===`fal`?e.falQueueBaseLabel:e.baseUrlLabel}function W(e,t){return t===`minimax`?e.minimaxClusterHint:t===`fal`?e.falQueueBaseHint:null}function G({summaries:e,credDraft:t,credDirty:n,credSaving:r,credError:a,credSavedFlash:s,credNoopFlash:c,updateCredRow:l,onDiscardCredentials:d,onSaveCredentials:f,extensionIds:m,showExtensionLinks:g,showImageModelsLink:v,language:y,apiKeyLinkLabels:b,messages:x}){if(e.length===0)return null;let S=e.some(e=>(e.ui?.regions?.length??0)>0),w=e.some(e=>(e.ui?.baseUrlPresets?.length??0)>0);return(0,F.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,F.jsxs)(`div`,{className:`flex flex-col gap-1 text-xs leading-relaxed text-fg-muted`,children:[(0,F.jsx)(`p`,{children:x.credentialsIntro}),S?(0,F.jsx)(`p`,{className:`text-fg-subtle`,children:x.regionHint}):null,w?(0,F.jsx)(`p`,{className:`text-fg-subtle`,children:x.endpointPresetsHint}):null,v?(0,F.jsx)(`p`,{children:(0,F.jsx)(o,{to:`/settings/image-models`,className:`font-medium text-accent hover:underline`,title:x.imageModelsLinkTitle,children:x.openImageModelsPage})}):null]}),a?(0,F.jsx)(`div`,{className:`rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300`,children:a}):null,(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[s?(0,F.jsx)(`span`,{className:`text-sm text-fg-muted`,children:x.credentialsSaved}):null,c?(0,F.jsx)(`span`,{className:`text-sm text-fg-muted`,children:x.credentialsNothingToSave}):null,(0,F.jsx)(h,{type:`button`,variant:`secondary`,onClick:d,disabled:!n||r,children:x.discardCredentials}),(0,F.jsx)(h,{type:`button`,variant:`primary`,onClick:f,disabled:!n||r,children:r?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(_,{className:`size-3.5 animate-spin`}),(0,F.jsx)(`span`,{className:`ml-1.5`,children:x.savingCredentials})]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(p,{className:`size-3.5`}),(0,F.jsx)(`span`,{className:`ml-1.5`,children:x.saveCredentials})]})})]}),(0,F.jsx)(`div`,{className:`flex flex-col gap-4`,children:e.map(e=>{let n=t[e.id]??D(),r=e.ui,a=g&&m.has(e.id)?`/settings/ext/${encodeURIComponent(e.id)}`:null;return(0,F.jsxs)(`div`,{className:`rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,F.jsx)(`span`,{className:`text-sm font-semibold text-fg`,children:e.label??e.id}),(0,F.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[`(`,e.id,`)`]}),a?(0,F.jsxs)(o,{to:a,className:`inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline`,title:x.extensionSettingsLinkTitle,children:[(0,F.jsx)(u,{className:`size-3`}),x.openExtensionSettings]}):null]}),e.configured?(0,F.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg`,children:x.configured}):(0,F.jsx)(`span`,{className:`rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300`,children:x.missingKey})]}),e.defaultModel?(0,F.jsxs)(`p`,{className:`mt-1 text-xs text-fg-subtle`,children:[(0,F.jsxs)(`span`,{className:`text-fg-muted`,children:[x.defaultModel,`:`]}),` `,e.id,`/`,e.defaultModel]}):null,e.models.length>0?(0,F.jsxs)(`p`,{className:`mt-0.5 text-xs text-fg-subtle`,children:[(0,F.jsxs)(`span`,{className:`text-fg-muted`,children:[x.modelsLabel,`:`]}),` `,e.models.map(t=>`${e.id}/${t}`).join(`, `)]}):null,(0,F.jsxs)(`div`,{className:`mt-4 grid gap-3 sm:grid-cols-2`,children:[(0,F.jsx)(I,{providerId:e.id,value:n.apiKey,onChange:t=>l(e.id,{apiKey:t}),apiKeyLinks:C(e.id,y),apiKeyLinkLabels:b,labels:{apiKeyLabel:x.apiKeyLabel,optionalPlaceholder:x.optionalPlaceholder,maskedHelp:x.apiKeyMaskedHelp,copy:x.apiKeyCopy,copied:x.apiKeyCopied,show:x.apiKeyShow,hide:x.apiKeyHide,notInConfigFile:x.apiKeyNotInConfigFile,loadFailed:x.apiKeyRevealFailed}}),r?.regions?.length?(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-region-preset-${e.id}`,children:x.regionLabel}),(0,F.jsxs)(`select`,{id:`img-cred-region-preset-${e.id}`,className:R(),value:B(n,r.regions),onChange:t=>{let n=t.target.value;if(n===``){l(e.id,{region:``,imageBaseUrl:``});return}if(n===z){l(e.id,{region:``,imageBaseUrl:``});return}let i=r.regions.find(e=>e.value===n);i&&l(e.id,{region:i.value,imageBaseUrl:i.imageBaseUrl})},children:[(0,F.jsx)(`option`,{value:``,children:x.regionPresetDefault}),r.regions.map(e=>(0,F.jsx)(`option`,{value:e.value,children:H(x,e.value,e.label)},e.value)),(0,F.jsx)(`option`,{value:z,children:x.regionPresetCustom})]}),B(n,r.regions)===z?(0,F.jsxs)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[(0,F.jsx)(`input`,{type:`text`,className:L(),value:n.region,placeholder:`region`,onChange:t=>l(e.id,{region:t.target.value})}),(0,F.jsx)(`input`,{type:`url`,className:L(),value:n.imageBaseUrl,placeholder:x.imageBaseUrlLabel,onChange:t=>l(e.id,{imageBaseUrl:t.target.value})})]}):null]}):null,r?.baseUrlPresets?.length?(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-base-preset-${e.id}`,children:U(x,r.baseUrlPresetKind)}),W(x,r.baseUrlPresetKind)?(0,F.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:W(x,r.baseUrlPresetKind)}):null,(0,F.jsxs)(`select`,{id:`img-cred-base-preset-${e.id}`,className:R(),value:V(n,r.baseUrlPresets),onChange:t=>{let n=t.target.value;if(n===``){l(e.id,{baseUrl:``});return}if(n===z){l(e.id,{baseUrl:``});return}l(e.id,{baseUrl:n.replace(/\/+$/,``)})},children:[(0,F.jsx)(`option`,{value:``,children:x.baseUrlPresetDefault}),r.baseUrlPresets.map(e=>(0,F.jsx)(`option`,{value:e.value,children:e.label},e.value)),(0,F.jsx)(`option`,{value:z,children:x.baseUrlPresetCustom})]}),V(n,r.baseUrlPresets)===z?(0,F.jsx)(`input`,{type:`url`,className:i(L(),`mt-2`),value:n.baseUrl,placeholder:`https://…`,onChange:t=>l(e.id,{baseUrl:t.target.value})}):null]}):null,r?.regions?.length&&B(n,r.regions)!==z?(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-imgbase-ro-${e.id}`,children:x.imageBaseUrlLabel}),(0,F.jsx)(`input`,{id:`img-cred-imgbase-ro-${e.id}`,type:`url`,readOnly:!0,className:i(L(),`cursor-not-allowed opacity-90`),value:n.imageBaseUrl,title:x.imageBaseUrlPresetHint}),(0,F.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:x.imageBaseUrlPresetHint})]}):null]})]},e.id)})})]})}function K(e){let t=a(d(e=>!!e.token)),n=t.data,i=(0,E.useMemo)(()=>e.map(e=>e.id),[e]),[o,s]=(0,E.useState)({}),[c,l]=(0,E.useState)({}),[u,f]=(0,E.useState)(!1),[p,m]=(0,E.useState)(null),[h,g]=(0,E.useState)(!1),[_,v]=(0,E.useState)(!1),y=(0,E.useMemo)(()=>k(n?.payload?.config,i),[n?.payload?.config,i]),b=(0,E.useMemo)(()=>JSON.stringify(o)!==JSON.stringify(c),[o,c]);return(0,E.useEffect)(()=>{b||(s(structuredClone(y)),l(structuredClone(y)))},[y,b]),{gwSwr:t,credDraft:o,credBaseline:c,credDirty:b,credSaving:u,credError:p,credSavedFlash:h,credNoopFlash:_,updateCredRow:(0,E.useCallback)((e,t)=>{s(n=>{let r=n[e]??D();return{...n,[e]:{...r,...t}}})},[]),onDiscardCredentials:(0,E.useCallback)(()=>{s(structuredClone(c)),m(null),g(!1),v(!1)},[c]),saveCredentials:(0,E.useCallback)(async e=>{let n=M(i,o,c);if(Object.keys(n).length===0){v(!0),window.setTimeout(()=>v(!1),2200);return}f(!0),m(null),g(!1);try{await P(n);let e=await t.mutate?.();r(x(w));let a=k(e?.payload?.config,i);s(structuredClone(a)),l(structuredClone(a)),g(!0),window.setTimeout(()=>g(!1),2e3)}catch(t){m(t instanceof Error?t.message:e)}finally{f(!1)}},[i,o,c,t])}}export{w as i,G as n,T as r,K as t};
2
- //# sourceMappingURL=use-image-provider-credentials-OhVruFpB.js.map
1
+ import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{o as r}from"./vendor-swr-B5fPo7KK.js";import{In as i,St as a,Ti as o,Xr as s,Y as c,Yr as l,Zr as u,_n as d,b as f,dr as p,hn as m,jn as h,kn as g,kr as _,li as v,ni as y,p as b,un as x,xt as S,y as C}from"./index-DzugsTKg.js";var w=`/api/image/providers`;async function T(){return(await m(x(`/api/image/providers`)))?.payload?.providers??[]}var E=e(t(),1);function D(){return{apiKey:``,region:``,baseUrl:``,imageBaseUrl:``}}function O(e){return e?.apiKey?`••••••••••••`:``}function k(e,t){let n=(()=>{if(!e||typeof e!=`object`||!(`providersConfig`in e))return;let t=e.providersConfig;if(!(!t||typeof t!=`object`||Array.isArray(t)))return t})(),r={};for(let e of t){let t=n?.[e];r[e]={apiKey:O(t),region:t?.region??``,baseUrl:t?.baseUrl??``,imageBaseUrl:t?.imageBaseUrl??``}}return r}function A(e,t,n){let r=e[n].trim();if(r!==t[n].trim())return r||null}function j(e,t){let n=e.trim(),r=t.trim();if(n!==r&&!(b(n)&&b(r)))return n||(r?null:void 0)}function M(e,t,n){let r={};for(let i of e){let e=t[i]??D(),a=n[i]??D();if(JSON.stringify(e)===JSON.stringify(a))continue;let o={},s=j(e.apiKey,a.apiKey);s!==void 0&&(o.apiKey=s);let c=A(e,a,`region`);c!==void 0&&(o.region=c);let l=A(e,a,`baseUrl`);l!==void 0&&(o.baseUrl=l);let u=A(e,a,`imageBaseUrl`);u!==void 0&&(o.imageBaseUrl=u),Object.keys(o).length>0&&(r[i]=o)}return r}async function N(e){let t=await m(x(`/api/image/providers/${encodeURIComponent(e)}/reveal-api-key`),{method:`POST`,headers:{"Content-Type":`application/json`},body:`{}`});if(!t.ok||!t.payload)throw Error(t.error?.message??`Reveal failed`);return t.payload}async function P(e){Object.keys(e).length!==0&&(await m(x(`/api/config`),{method:`PATCH`,body:JSON.stringify({providersConfig:e})}),await S())}var F=n();function I({providerId:e,value:t,onChange:n,labels:r,apiKeyLinks:a,apiKeyLinkLabels:o}){let[d,p]=(0,E.useState)(!1),[m,h]=(0,E.useState)(void 0),[x,S]=(0,E.useState)(!1),[C,w]=(0,E.useState)(null),[T,D]=(0,E.useState)(!1),O=b(t);(0,E.useEffect)(()=>{O||(h(void 0),w(null))},[O,t]);let k=O&&d&&typeof m==`string`?m:t,A=!O||O&&d&&typeof m==`string`?`text`:`password`,j=!O&&t.trim().length>0&&!b(t)||!!d&&typeof m==`string`&&m.length>0,M=(0,E.useCallback)(async()=>{let e=!O&&t.trim()&&!b(t)?t.trim():typeof m==`string`&&m.length>0?m:``;if(e)try{await navigator.clipboard.writeText(e),D(!0),window.setTimeout(()=>D(!1),2e3)}catch{}},[O,m,t]),P=(0,E.useCallback)(async()=>{if(w(null),!O){p(e=>!e);return}if(m!==void 0){p(e=>!e);return}S(!0);try{h((await N(e)).apiKey??null),p(!0)}catch(e){w(e instanceof Error?e.message:r.loadFailed),h(null)}finally{S(!1)}},[O,e,m,r.loadFailed]);return(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-key-${e}`,children:r.apiKeyLabel}),a.length>0?(0,F.jsx)(`div`,{className:`flex flex-col gap-1`,children:a.map(e=>(0,F.jsxs)(`a`,{href:e.href,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,children:[f(e.kind,o),(0,F.jsx)(u,{className:`size-3`,"aria-hidden":!0})]},`${e.kind}-${e.href}`))}):null,O?(0,F.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:r.maskedHelp}):null,(0,F.jsxs)(`div`,{className:`relative min-w-0`,children:[(0,F.jsx)(`input`,{id:`img-cred-key-${e}`,type:A,autoComplete:`off`,spellCheck:!1,className:i(`w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,g),value:k,placeholder:O?`••••••••`:r.optionalPlaceholder,onChange:e=>{let t=e.target.value;O&&typeof m==`string`&&d&&t!==m&&(h(void 0),p(!1)),n(t)}}),(0,F.jsxs)(`div`,{className:`absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5`,children:[j?(0,F.jsx)(`button`,{type:`button`,className:i(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg`,c.transition,c.press,c.focusRingPanel),title:T?r.copied:r.copy,"aria-label":T?r.copied:r.copy,onClick:()=>void M(),children:T?(0,F.jsx)(v,{className:`size-4`}):(0,F.jsx)(y,{className:`size-4`})}):null,(0,F.jsx)(`button`,{type:`button`,className:i(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40`,c.transition,c.press,c.focusRingPanel),title:d?r.hide:r.show,"aria-label":d?r.hide:r.show,disabled:x,onClick:()=>void P(),children:x?(0,F.jsx)(_,{className:`size-4 animate-spin`,"aria-hidden":!0}):d?(0,F.jsx)(s,{className:`size-4`,"aria-hidden":!0}):(0,F.jsx)(l,{className:`size-4`,"aria-hidden":!0})})]})]}),O&&d&&m===null&&!C?(0,F.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:r.notInConfigFile}):null,C?(0,F.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:C}):null]})}function L(){return i(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,g)}function R(){return i(L(),`appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9`)}var z=`__custom__`;function B(e,t){if(!e.region.trim()&&!e.imageBaseUrl.trim())return``;let n=e.region.trim().toLowerCase();return t.some(e=>e.value===n)?n:z}function V(e,t){let n=e.baseUrl.trim().replace(/\/+$/,``);if(!n)return``;let r=t.map(e=>e.value.replace(/\/+$/,``)).indexOf(n);return r>=0?t[r].value:z}function H(e,t,n){return t===`beijing`?e.dashscopeRegion_beijing:t===`singapore`?e.dashscopeRegion_singapore:t===`us`?e.dashscopeRegion_us:n}function U(e,t){return t===`minimax`?e.minimaxClusterLabel:t===`fal`?e.falQueueBaseLabel:e.baseUrlLabel}function W(e,t){return t===`minimax`?e.minimaxClusterHint:t===`fal`?e.falQueueBaseHint:null}function G({summaries:e,credDraft:t,credDirty:n,credSaving:r,credError:a,credSavedFlash:s,credNoopFlash:c,updateCredRow:l,onDiscardCredentials:d,onSaveCredentials:f,extensionIds:m,showExtensionLinks:g,showImageModelsLink:v,language:y,apiKeyLinkLabels:b,messages:x}){if(e.length===0)return null;let S=e.some(e=>(e.ui?.regions?.length??0)>0),w=e.some(e=>(e.ui?.baseUrlPresets?.length??0)>0);return(0,F.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,F.jsxs)(`div`,{className:`flex flex-col gap-1 text-xs leading-relaxed text-fg-muted`,children:[(0,F.jsx)(`p`,{children:x.credentialsIntro}),S?(0,F.jsx)(`p`,{className:`text-fg-subtle`,children:x.regionHint}):null,w?(0,F.jsx)(`p`,{className:`text-fg-subtle`,children:x.endpointPresetsHint}):null,v?(0,F.jsx)(`p`,{children:(0,F.jsx)(o,{to:`/settings/image-models`,className:`font-medium text-accent hover:underline`,title:x.imageModelsLinkTitle,children:x.openImageModelsPage})}):null]}),a?(0,F.jsx)(`div`,{className:`rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300`,children:a}):null,(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[s?(0,F.jsx)(`span`,{className:`text-sm text-fg-muted`,children:x.credentialsSaved}):null,c?(0,F.jsx)(`span`,{className:`text-sm text-fg-muted`,children:x.credentialsNothingToSave}):null,(0,F.jsx)(h,{type:`button`,variant:`secondary`,onClick:d,disabled:!n||r,children:x.discardCredentials}),(0,F.jsx)(h,{type:`button`,variant:`primary`,onClick:f,disabled:!n||r,children:r?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(_,{className:`size-3.5 animate-spin`}),(0,F.jsx)(`span`,{className:`ml-1.5`,children:x.savingCredentials})]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(p,{className:`size-3.5`}),(0,F.jsx)(`span`,{className:`ml-1.5`,children:x.saveCredentials})]})})]}),(0,F.jsx)(`div`,{className:`flex flex-col gap-4`,children:e.map(e=>{let n=t[e.id]??D(),r=e.ui,a=g&&m.has(e.id)?`/settings/ext/${encodeURIComponent(e.id)}`:null;return(0,F.jsxs)(`div`,{className:`rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,F.jsx)(`span`,{className:`text-sm font-semibold text-fg`,children:e.label??e.id}),(0,F.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[`(`,e.id,`)`]}),a?(0,F.jsxs)(o,{to:a,className:`inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline`,title:x.extensionSettingsLinkTitle,children:[(0,F.jsx)(u,{className:`size-3`}),x.openExtensionSettings]}):null]}),e.configured?(0,F.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg`,children:x.configured}):(0,F.jsx)(`span`,{className:`rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300`,children:x.missingKey})]}),e.defaultModel?(0,F.jsxs)(`p`,{className:`mt-1 text-xs text-fg-subtle`,children:[(0,F.jsxs)(`span`,{className:`text-fg-muted`,children:[x.defaultModel,`:`]}),` `,e.id,`/`,e.defaultModel]}):null,e.models.length>0?(0,F.jsxs)(`p`,{className:`mt-0.5 text-xs text-fg-subtle`,children:[(0,F.jsxs)(`span`,{className:`text-fg-muted`,children:[x.modelsLabel,`:`]}),` `,e.models.map(t=>`${e.id}/${t}`).join(`, `)]}):null,(0,F.jsxs)(`div`,{className:`mt-4 grid gap-3 sm:grid-cols-2`,children:[(0,F.jsx)(I,{providerId:e.id,value:n.apiKey,onChange:t=>l(e.id,{apiKey:t}),apiKeyLinks:C(e.id,y),apiKeyLinkLabels:b,labels:{apiKeyLabel:x.apiKeyLabel,optionalPlaceholder:x.optionalPlaceholder,maskedHelp:x.apiKeyMaskedHelp,copy:x.apiKeyCopy,copied:x.apiKeyCopied,show:x.apiKeyShow,hide:x.apiKeyHide,notInConfigFile:x.apiKeyNotInConfigFile,loadFailed:x.apiKeyRevealFailed}}),r?.regions?.length?(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-region-preset-${e.id}`,children:x.regionLabel}),(0,F.jsxs)(`select`,{id:`img-cred-region-preset-${e.id}`,className:R(),value:B(n,r.regions),onChange:t=>{let n=t.target.value;if(n===``){l(e.id,{region:``,imageBaseUrl:``});return}if(n===z){l(e.id,{region:``,imageBaseUrl:``});return}let i=r.regions.find(e=>e.value===n);i&&l(e.id,{region:i.value,imageBaseUrl:i.imageBaseUrl})},children:[(0,F.jsx)(`option`,{value:``,children:x.regionPresetDefault}),r.regions.map(e=>(0,F.jsx)(`option`,{value:e.value,children:H(x,e.value,e.label)},e.value)),(0,F.jsx)(`option`,{value:z,children:x.regionPresetCustom})]}),B(n,r.regions)===z?(0,F.jsxs)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[(0,F.jsx)(`input`,{type:`text`,className:L(),value:n.region,placeholder:`region`,onChange:t=>l(e.id,{region:t.target.value})}),(0,F.jsx)(`input`,{type:`url`,className:L(),value:n.imageBaseUrl,placeholder:x.imageBaseUrlLabel,onChange:t=>l(e.id,{imageBaseUrl:t.target.value})})]}):null]}):null,r?.baseUrlPresets?.length?(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-base-preset-${e.id}`,children:U(x,r.baseUrlPresetKind)}),W(x,r.baseUrlPresetKind)?(0,F.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:W(x,r.baseUrlPresetKind)}):null,(0,F.jsxs)(`select`,{id:`img-cred-base-preset-${e.id}`,className:R(),value:V(n,r.baseUrlPresets),onChange:t=>{let n=t.target.value;if(n===``){l(e.id,{baseUrl:``});return}if(n===z){l(e.id,{baseUrl:``});return}l(e.id,{baseUrl:n.replace(/\/+$/,``)})},children:[(0,F.jsx)(`option`,{value:``,children:x.baseUrlPresetDefault}),r.baseUrlPresets.map(e=>(0,F.jsx)(`option`,{value:e.value,children:e.label},e.value)),(0,F.jsx)(`option`,{value:z,children:x.baseUrlPresetCustom})]}),V(n,r.baseUrlPresets)===z?(0,F.jsx)(`input`,{type:`url`,className:i(L(),`mt-2`),value:n.baseUrl,placeholder:`https://…`,onChange:t=>l(e.id,{baseUrl:t.target.value})}):null]}):null,r?.regions?.length&&B(n,r.regions)!==z?(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,F.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-imgbase-ro-${e.id}`,children:x.imageBaseUrlLabel}),(0,F.jsx)(`input`,{id:`img-cred-imgbase-ro-${e.id}`,type:`url`,readOnly:!0,className:i(L(),`cursor-not-allowed opacity-90`),value:n.imageBaseUrl,title:x.imageBaseUrlPresetHint}),(0,F.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:x.imageBaseUrlPresetHint})]}):null]})]},e.id)})})]})}function K(e){let t=a(d(e=>!!e.token)),n=t.data,i=(0,E.useMemo)(()=>e.map(e=>e.id),[e]),[o,s]=(0,E.useState)({}),[c,l]=(0,E.useState)({}),[u,f]=(0,E.useState)(!1),[p,m]=(0,E.useState)(null),[h,g]=(0,E.useState)(!1),[_,v]=(0,E.useState)(!1),y=(0,E.useMemo)(()=>k(n?.payload?.config,i),[n?.payload?.config,i]),b=(0,E.useMemo)(()=>JSON.stringify(o)!==JSON.stringify(c),[o,c]);return(0,E.useEffect)(()=>{b||(s(structuredClone(y)),l(structuredClone(y)))},[y,b]),{gwSwr:t,credDraft:o,credBaseline:c,credDirty:b,credSaving:u,credError:p,credSavedFlash:h,credNoopFlash:_,updateCredRow:(0,E.useCallback)((e,t)=>{s(n=>{let r=n[e]??D();return{...n,[e]:{...r,...t}}})},[]),onDiscardCredentials:(0,E.useCallback)(()=>{s(structuredClone(c)),m(null),g(!1),v(!1)},[c]),saveCredentials:(0,E.useCallback)(async e=>{let n=M(i,o,c);if(Object.keys(n).length===0){v(!0),window.setTimeout(()=>v(!1),2200);return}f(!0),m(null),g(!1);try{await P(n);let e=await t.mutate?.();r(x(w));let a=k(e?.payload?.config,i);s(structuredClone(a)),l(structuredClone(a)),g(!0),window.setTimeout(()=>g(!1),2e3)}catch(t){m(t instanceof Error?t.message:e)}finally{f(!1)}},[i,o,c,t])}}export{w as i,G as n,T as r,K as t};
2
+ //# sourceMappingURL=use-image-provider-credentials-DEpLWx8J.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-image-provider-credentials-OhVruFpB.js","names":[],"sources":["../../../../../web/src/features/settings/image-providers-swr-key.ts","../../../../../web/src/features/settings/fetch-image-providers.ts","../../../../../web/src/features/settings/image-providers-config-api.ts","../../../../../web/src/features/settings/image-provider-api-key-field.tsx","../../../../../web/src/features/settings/image-provider-credentials-panel.tsx","../../../../../web/src/features/settings/use-image-provider-credentials.ts"],"sourcesContent":["/** Shared SWR key for GET `/api/image/providers` (image settings + extension image pages). */\nexport const IMAGE_PROVIDERS_SWR_KEY = '/api/image/providers';\n","import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport type { ImageGenProviderCredentialSummary } from '@/features/settings/use-image-provider-credentials';\n\nexport async function fetchImageProvidersList(): Promise<ImageGenProviderCredentialSummary[]> {\n const res = await fetchJson<{\n ok?: boolean;\n payload?: { providers?: ImageGenProviderCredentialSummary[] };\n }>(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n return res?.payload?.providers ?? [];\n}\n","import { isMaskedKey } from '@/features/settings/providers-api';\nimport { revalidateGatewayConfig } from '@/features/gateway/gateway-config-swr';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\n/** One row of image-provider credential fields (matches PATCH `providersConfig` subset). */\nexport type ImageProviderCredRow = {\n apiKey: string;\n region: string;\n baseUrl: string;\n imageBaseUrl: string;\n};\n\nexport function emptyImageProviderCredRow(): ImageProviderCredRow {\n return { apiKey: '', region: '', baseUrl: '', imageBaseUrl: '' };\n}\n\nexport type SafeProviderAuthEntry = {\n apiKey: string;\n region?: string;\n baseUrl?: string;\n imageBaseUrl?: string;\n};\n\nfunction maskedApiKeyDisplay(safe?: SafeProviderAuthEntry): string {\n if (!safe?.apiKey) return '';\n return '••••••••••••';\n}\n\n/** Read `payload.config.providersConfig` from GET /api/config (masked). */\nexport function imageProviderCredRowsFromConfigRoot(\n config: unknown,\n imageProviderIds: string[],\n): Record<string, ImageProviderCredRow> {\n const pc = (() => {\n if (!config || typeof config !== 'object' || !('providersConfig' in config)) return undefined;\n const v = (config as { providersConfig?: unknown }).providersConfig;\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n return v as Record<string, SafeProviderAuthEntry>;\n })();\n\n const out: Record<string, ImageProviderCredRow> = {};\n for (const id of imageProviderIds) {\n const safe = pc?.[id];\n out[id] = {\n apiKey: maskedApiKeyDisplay(safe),\n region: safe?.region ?? '',\n baseUrl: safe?.baseUrl ?? '',\n imageBaseUrl: safe?.imageBaseUrl ?? '',\n };\n }\n return out;\n}\n\nfunction optionalStringField(\n draft: ImageProviderCredRow,\n baseline: ImageProviderCredRow,\n key: keyof Pick<ImageProviderCredRow, 'region' | 'baseUrl' | 'imageBaseUrl'>,\n): string | null | undefined {\n const d = draft[key].trim();\n const b = baseline[key].trim();\n if (d === b) return undefined;\n if (!d) return null;\n return d;\n}\n\nfunction apiKeyPatchValue(draftKey: string, baselineKey: string): string | null | undefined {\n const d = draftKey.trim();\n const b = baselineKey.trim();\n if (d === b) return undefined;\n if (isMaskedKey(d) && isMaskedKey(b)) return undefined;\n if (!d) {\n if (!b) return undefined;\n return null;\n }\n return d;\n}\n\n/**\n * Build `providersConfig` PATCH entries only for image providers whose row changed.\n * Omits `apiKey` when unchanged (still masked); sends `null` to clear stored key.\n */\nexport function buildImageProvidersConfigPatch(\n imageProviderIds: string[],\n draft: Record<string, ImageProviderCredRow>,\n baseline: Record<string, ImageProviderCredRow>,\n): Record<string, Record<string, unknown>> {\n const patch: Record<string, Record<string, unknown>> = {};\n for (const id of imageProviderIds) {\n const d = draft[id] ?? emptyImageProviderCredRow();\n const b = baseline[id] ?? emptyImageProviderCredRow();\n if (JSON.stringify(d) === JSON.stringify(b)) continue;\n\n const entry: Record<string, unknown> = {};\n const keyDelta = apiKeyPatchValue(d.apiKey, b.apiKey);\n if (keyDelta !== undefined) {\n entry.apiKey = keyDelta;\n }\n const region = optionalStringField(d, b, 'region');\n if (region !== undefined) entry.region = region;\n const baseUrl = optionalStringField(d, b, 'baseUrl');\n if (baseUrl !== undefined) entry.baseUrl = baseUrl;\n const imageBaseUrl = optionalStringField(d, b, 'imageBaseUrl');\n if (imageBaseUrl !== undefined) entry.imageBaseUrl = imageBaseUrl;\n\n if (Object.keys(entry).length > 0) {\n patch[id] = entry;\n }\n }\n return patch;\n}\n\nexport type RevealImageProviderApiKeyPayload = {\n id: string;\n apiKey: string | null;\n source: 'config' | 'none';\n};\n\n/** POST /api/image/providers/:id/reveal-api-key — plaintext only when stored in config file. */\nexport async function revealImageProviderConfigApiKey(providerId: string): Promise<RevealImageProviderApiKeyPayload> {\n const data = await fetchJson<{\n ok?: boolean;\n payload?: RevealImageProviderApiKeyPayload;\n error?: { message?: string };\n }>(apiUrl(`/api/image/providers/${encodeURIComponent(providerId)}/reveal-api-key`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (!data.ok || !data.payload) {\n throw new Error(data.error?.message ?? 'Reveal failed');\n }\n return data.payload;\n}\n\nexport async function patchImageProvidersConfig(\n patch: Record<string, Record<string, unknown>>,\n): Promise<void> {\n if (Object.keys(patch).length === 0) return;\n await fetchJson(apiUrl('/api/config'), {\n method: 'PATCH',\n body: JSON.stringify({ providersConfig: patch }),\n });\n await revalidateGatewayConfig();\n}\n","import { CheckCircle2, Copy, ExternalLink, Eye, EyeOff, Loader2 } from 'lucide-react';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { revealImageProviderConfigApiKey } from '@/features/settings/image-providers-config-api';\nimport type { ApiKeyLinkKind } from '@/features/settings/provider-enrichment';\nimport { providerApiKeyLinkLabel } from '@/features/settings/provider-enrichment';\nimport { isMaskedKey } from '@/features/settings/providers-api';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport { interaction } from '@/lib/interaction';\nimport { cn } from '@/lib/cn';\n\nexport type ImageProviderApiKeyFieldLabels = {\n apiKeyLabel: string;\n optionalPlaceholder: string;\n maskedHelp: string;\n copy: string;\n copied: string;\n show: string;\n hide: string;\n notInConfigFile: string;\n loadFailed: string;\n};\n\nexport function ImageProviderApiKeyField({\n providerId,\n value,\n onChange,\n labels,\n apiKeyLinks,\n apiKeyLinkLabels,\n}: {\n providerId: string;\n value: string;\n onChange: (next: string) => void;\n labels: ImageProviderApiKeyFieldLabels;\n apiKeyLinks: { href: string; kind: ApiKeyLinkKind }[];\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n}) {\n const [showKey, setShowKey] = useState(false);\n /** `undefined` = not fetched; `null` = fetched, not in config file; string = plaintext from config */\n const [revealed, setRevealed] = useState<string | null | undefined>(undefined);\n const [revealLoading, setRevealLoading] = useState(false);\n const [revealErr, setRevealErr] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const masked = isMaskedKey(value);\n\n useEffect(() => {\n if (!masked) {\n setRevealed(undefined);\n setRevealErr(null);\n }\n }, [masked, value]);\n\n const inputValue = (() => {\n if (!masked) return value;\n if (showKey && typeof revealed === 'string') return revealed;\n return value;\n })();\n\n const inputType =\n !masked || (masked && showKey && typeof revealed === 'string') ? ('text' as const) : ('password' as const);\n\n const copyEnabled =\n (!masked && value.trim().length > 0 && !isMaskedKey(value)) ||\n (Boolean(showKey) && typeof revealed === 'string' && revealed.length > 0);\n\n const copyKey = useCallback(async () => {\n const text =\n !masked && value.trim() && !isMaskedKey(value)\n ? value.trim()\n : typeof revealed === 'string' && revealed.length > 0\n ? revealed\n : '';\n if (!text) return;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 2000);\n } catch {\n /* ignore */\n }\n }, [masked, revealed, value]);\n\n const toggleEye = useCallback(async () => {\n setRevealErr(null);\n if (!masked) {\n setShowKey((s) => !s);\n return;\n }\n if (revealed !== undefined) {\n setShowKey((s) => !s);\n return;\n }\n setRevealLoading(true);\n try {\n const payload = await revealImageProviderConfigApiKey(providerId);\n setRevealed(payload.apiKey ?? null);\n setShowKey(true);\n } catch (e) {\n setRevealErr(e instanceof Error ? e.message : labels.loadFailed);\n setRevealed(null);\n } finally {\n setRevealLoading(false);\n }\n }, [masked, providerId, revealed, labels.loadFailed]);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-key-${providerId}`}>\n {labels.apiKeyLabel}\n </label>\n {apiKeyLinks.length > 0 ? (\n <div className=\"flex flex-col gap-1\">\n {apiKeyLinks.map((link) => (\n <a\n key={`${link.kind}-${link.href}`}\n href={link.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n >\n {providerApiKeyLinkLabel(link.kind, apiKeyLinkLabels)}\n <ExternalLink className=\"size-3\" aria-hidden />\n </a>\n ))}\n </div>\n ) : null}\n {masked ? <p className=\"text-[11px] text-fg-subtle\">{labels.maskedHelp}</p> : null}\n <div className=\"relative min-w-0\">\n <input\n id={`img-cred-key-${providerId}`}\n type={inputType}\n autoComplete=\"off\"\n spellCheck={false}\n className={cn(\n 'w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n )}\n value={inputValue}\n placeholder={masked ? '••••••••' : labels.optionalPlaceholder}\n onChange={(e) => {\n const next = e.target.value;\n if (masked && typeof revealed === 'string' && showKey && next !== revealed) {\n setRevealed(undefined);\n setShowKey(false);\n }\n onChange(next);\n }}\n />\n <div className=\"absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5\">\n {copyEnabled ? (\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={copied ? labels.copied : labels.copy}\n aria-label={copied ? labels.copied : labels.copy}\n onClick={() => void copyKey()}\n >\n {copied ? <CheckCircle2 className=\"size-4\" /> : <Copy className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={showKey ? labels.hide : labels.show}\n aria-label={showKey ? labels.hide : labels.show}\n disabled={revealLoading}\n onClick={() => void toggleEye()}\n >\n {revealLoading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : showKey ? (\n <EyeOff className=\"size-4\" aria-hidden />\n ) : (\n <Eye className=\"size-4\" aria-hidden />\n )}\n </button>\n </div>\n </div>\n {masked && showKey && revealed === null && !revealErr ? (\n <p className=\"text-xs text-amber-700 dark:text-amber-400/90\">{labels.notInConfigFile}</p>\n ) : null}\n {revealErr ? <p className=\"text-xs text-red-600 dark:text-red-400\">{revealErr}</p> : null}\n </div>\n );\n}\n","import { ExternalLink, Loader2, Save } from 'lucide-react';\nimport { Link } from 'react-router-dom';\n\nimport { Button } from '@/components/ui/button';\nimport { ImageProviderApiKeyField } from '@/features/settings/image-provider-api-key-field';\nimport { emptyImageProviderCredRow, type ImageProviderCredRow } from '@/features/settings/image-providers-config-api';\nimport { getOrderedApiKeyLinks } from '@/features/settings/provider-enrichment';\nimport type {\n ImageGenProviderCredentialSummary,\n ImageProviderUiMetadata,\n} from '@/features/settings/use-image-provider-credentials';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport type { StoredLanguage } from '@/lib/storage';\nimport { cn } from '@/lib/cn';\n\nfunction inputClass(): string {\n return cn(\n 'w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n );\n}\n\nfunction selectClass(): string {\n return cn(inputClass(), 'appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9');\n}\n\nconst CUSTOM_SENTINEL = '__custom__';\n\nfunction dashscopeSelectValue(\n row: ImageProviderCredRow,\n regions: NonNullable<ImageProviderUiMetadata['regions']>,\n): string {\n if (!row.region.trim() && !row.imageBaseUrl.trim()) return '';\n const r = row.region.trim().toLowerCase();\n if (regions.some((x) => x.value === r)) return r;\n return CUSTOM_SENTINEL;\n}\n\nfunction baseUrlSelectValue(\n row: ImageProviderCredRow,\n presets: NonNullable<ImageProviderUiMetadata['baseUrlPresets']>,\n): string {\n const b = row.baseUrl.trim().replace(/\\/+$/, '');\n if (!b) return '';\n const norm = presets.map((p) => p.value.replace(/\\/+$/, ''));\n const idx = norm.indexOf(b);\n if (idx >= 0) return presets[idx].value;\n return CUSTOM_SENTINEL;\n}\n\nexport type ImageProviderCredentialsPanelMessages = {\n credentialsIntro: string;\n regionHint: string;\n endpointPresetsHint: string;\n apiKeyLabel: string;\n optionalPlaceholder: string;\n regionLabel: string;\n baseUrlLabel: string;\n imageBaseUrlLabel: string;\n saveCredentials: string;\n savingCredentials: string;\n credentialsSaved: string;\n discardCredentials: string;\n credentialsNothingToSave: string;\n credentialsSaveError: string;\n regionPresetDefault: string;\n regionPresetCustom: string;\n baseUrlPresetDefault: string;\n baseUrlPresetCustom: string;\n openExtensionSettings: string;\n openImageModelsPage: string;\n extensionSettingsLinkTitle: string;\n imageModelsLinkTitle: string;\n configured: string;\n missingKey: string;\n defaultModel: string;\n modelsLabel: string;\n imageBaseUrlPresetHint: string;\n dashscopeRegion_beijing: string;\n dashscopeRegion_singapore: string;\n dashscopeRegion_us: string;\n apiKeyMaskedHelp: string;\n apiKeyCopy: string;\n apiKeyCopied: string;\n apiKeyShow: string;\n apiKeyHide: string;\n apiKeyNotInConfigFile: string;\n apiKeyRevealFailed: string;\n minimaxClusterLabel: string;\n minimaxClusterHint: string;\n falQueueBaseLabel: string;\n falQueueBaseHint: string;\n};\n\nfunction translateDashscopeRegion(m: ImageProviderCredentialsPanelMessages, value: string, serverLabel: string) {\n if (value === 'beijing') return m.dashscopeRegion_beijing;\n if (value === 'singapore') return m.dashscopeRegion_singapore;\n if (value === 'us') return m.dashscopeRegion_us;\n return serverLabel;\n}\n\nfunction baseUrlPresetBlockTitle(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string {\n if (kind === 'minimax') return t.minimaxClusterLabel;\n if (kind === 'fal') return t.falQueueBaseLabel;\n return t.baseUrlLabel;\n}\n\nfunction baseUrlPresetBlockHint(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string | null {\n if (kind === 'minimax') return t.minimaxClusterHint;\n if (kind === 'fal') return t.falQueueBaseHint;\n return null;\n}\n\nexport function ImageProviderCredentialsPanel({\n summaries,\n credDraft,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n onSaveCredentials,\n extensionIds,\n showExtensionLinks,\n showImageModelsLink,\n language,\n apiKeyLinkLabels,\n messages: t,\n}: {\n summaries: ImageGenProviderCredentialSummary[];\n credDraft: Record<string, ImageProviderCredRow>;\n credDirty: boolean;\n credSaving: boolean;\n credError: string | null;\n credSavedFlash: boolean;\n credNoopFlash: boolean;\n updateCredRow: (id: string, patch: Partial<ImageProviderCredRow>) => void;\n onDiscardCredentials: () => void;\n onSaveCredentials: () => void;\n /** Extension ids present in gateway discovery (for deep links). */\n extensionIds: Set<string>;\n showExtensionLinks: boolean;\n showImageModelsLink: boolean;\n language: StoredLanguage;\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n messages: ImageProviderCredentialsPanelMessages;\n}) {\n const empty = summaries.length === 0;\n\n if (empty) {\n return null;\n }\n\n const anyRegionUi = summaries.some((s) => (s.ui?.regions?.length ?? 0) > 0);\n const anyBaseUrlPresets = summaries.some((s) => (s.ui?.baseUrlPresets?.length ?? 0) > 0);\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1 text-xs leading-relaxed text-fg-muted\">\n <p>{t.credentialsIntro}</p>\n {anyRegionUi ? <p className=\"text-fg-subtle\">{t.regionHint}</p> : null}\n {anyBaseUrlPresets ? <p className=\"text-fg-subtle\">{t.endpointPresetsHint}</p> : null}\n {showImageModelsLink ? (\n <p>\n <Link\n to=\"/settings/image-models\"\n className=\"font-medium text-accent hover:underline\"\n title={t.imageModelsLinkTitle}\n >\n {t.openImageModelsPage}\n </Link>\n </p>\n ) : null}\n </div>\n {credError ? (\n <div className=\"rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300\">\n {credError}\n </div>\n ) : null}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {credSavedFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsSaved}</span>\n ) : null}\n {credNoopFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsNothingToSave}</span>\n ) : null}\n <Button type=\"button\" variant=\"secondary\" onClick={onDiscardCredentials} disabled={!credDirty || credSaving}>\n {t.discardCredentials}\n </Button>\n <Button type=\"button\" variant=\"primary\" onClick={onSaveCredentials} disabled={!credDirty || credSaving}>\n {credSaving ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n <span className=\"ml-1.5\">{t.savingCredentials}</span>\n </>\n ) : (\n <>\n <Save className=\"size-3.5\" />\n <span className=\"ml-1.5\">{t.saveCredentials}</span>\n </>\n )}\n </Button>\n </div>\n <div className=\"flex flex-col gap-4\">\n {summaries.map((p) => {\n const row = credDraft[p.id] ?? emptyImageProviderCredRow();\n const ui = p.ui;\n const extPath =\n showExtensionLinks && extensionIds.has(p.id)\n ? `/settings/ext/${encodeURIComponent(p.id)}`\n : null;\n return (\n <div\n key={p.id}\n className=\"rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-fg\">{p.label ?? p.id}</span>\n <span className=\"text-xs text-fg-subtle\">({p.id})</span>\n {extPath ? (\n <Link\n to={extPath}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n title={t.extensionSettingsLinkTitle}\n >\n <ExternalLink className=\"size-3\" />\n {t.openExtensionSettings}\n </Link>\n ) : null}\n </div>\n {p.configured ? (\n <span className=\"rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg\">\n {t.configured}\n </span>\n ) : (\n <span className=\"rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300\">\n {t.missingKey}\n </span>\n )}\n </div>\n {p.defaultModel ? (\n <p className=\"mt-1 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.defaultModel}:</span> {p.id}/{p.defaultModel}\n </p>\n ) : null}\n {p.models.length > 0 ? (\n <p className=\"mt-0.5 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.modelsLabel}:</span>{' '}\n {p.models.map((mm) => `${p.id}/${mm}`).join(', ')}\n </p>\n ) : null}\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <ImageProviderApiKeyField\n providerId={p.id}\n value={row.apiKey}\n onChange={(next) => updateCredRow(p.id, { apiKey: next })}\n apiKeyLinks={getOrderedApiKeyLinks(p.id, language)}\n apiKeyLinkLabels={apiKeyLinkLabels}\n labels={{\n apiKeyLabel: t.apiKeyLabel,\n optionalPlaceholder: t.optionalPlaceholder,\n maskedHelp: t.apiKeyMaskedHelp,\n copy: t.apiKeyCopy,\n copied: t.apiKeyCopied,\n show: t.apiKeyShow,\n hide: t.apiKeyHide,\n notInConfigFile: t.apiKeyNotInConfigFile,\n loadFailed: t.apiKeyRevealFailed,\n }}\n />\n\n {ui?.regions?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-region-preset-${p.id}`}>\n {t.regionLabel}\n </label>\n <select\n id={`img-cred-region-preset-${p.id}`}\n className={selectClass()}\n value={dashscopeSelectValue(row, ui.regions)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n const opt = ui.regions!.find((x) => x.value === v);\n if (opt) {\n updateCredRow(p.id, { region: opt.value, imageBaseUrl: opt.imageBaseUrl });\n }\n }}\n >\n <option value=\"\">{t.regionPresetDefault}</option>\n {ui.regions.map((r) => (\n <option key={r.value} value={r.value}>\n {translateDashscopeRegion(t, r.value, r.label)}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.regionPresetCustom}</option>\n </select>\n {dashscopeSelectValue(row, ui.regions) === CUSTOM_SENTINEL ? (\n <div className=\"mt-2 grid gap-2 sm:grid-cols-2\">\n <input\n type=\"text\"\n className={inputClass()}\n value={row.region}\n placeholder=\"region\"\n onChange={(e) => updateCredRow(p.id, { region: e.target.value })}\n />\n <input\n type=\"url\"\n className={inputClass()}\n value={row.imageBaseUrl}\n placeholder={t.imageBaseUrlLabel}\n onChange={(e) => updateCredRow(p.id, { imageBaseUrl: e.target.value })}\n />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {ui?.baseUrlPresets?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-base-preset-${p.id}`}>\n {baseUrlPresetBlockTitle(t, ui.baseUrlPresetKind)}\n </label>\n {baseUrlPresetBlockHint(t, ui.baseUrlPresetKind) ? (\n <p className=\"text-[11px] text-fg-subtle\">{baseUrlPresetBlockHint(t, ui.baseUrlPresetKind)}</p>\n ) : null}\n <select\n id={`img-cred-base-preset-${p.id}`}\n className={selectClass()}\n value={baseUrlSelectValue(row, ui.baseUrlPresets)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n updateCredRow(p.id, { baseUrl: v.replace(/\\/+$/, '') });\n }}\n >\n <option value=\"\">{t.baseUrlPresetDefault}</option>\n {ui.baseUrlPresets.map((b) => (\n <option key={b.value} value={b.value}>\n {b.label}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.baseUrlPresetCustom}</option>\n </select>\n {baseUrlSelectValue(row, ui.baseUrlPresets) === CUSTOM_SENTINEL ? (\n <input\n type=\"url\"\n className={cn(inputClass(), 'mt-2')}\n value={row.baseUrl}\n placeholder=\"https://…\"\n onChange={(e) => updateCredRow(p.id, { baseUrl: e.target.value })}\n />\n ) : null}\n </div>\n ) : null}\n\n {ui?.regions?.length && dashscopeSelectValue(row, ui.regions) !== CUSTOM_SENTINEL ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-imgbase-ro-${p.id}`}>\n {t.imageBaseUrlLabel}\n </label>\n <input\n id={`img-cred-imgbase-ro-${p.id}`}\n type=\"url\"\n readOnly\n className={cn(inputClass(), 'cursor-not-allowed opacity-90')}\n value={row.imageBaseUrl}\n title={t.imageBaseUrlPresetHint}\n />\n <p className=\"text-[11px] text-fg-subtle\">{t.imageBaseUrlPresetHint}</p>\n </div>\n ) : null}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport { mutate } from 'swr';\n\nimport { useGatewayConfigSwr } from '@/features/gateway/gateway-config-swr';\nimport {\n buildImageProvidersConfigPatch,\n emptyImageProviderCredRow,\n imageProviderCredRowsFromConfigRoot,\n patchImageProvidersConfig,\n type ImageProviderCredRow,\n} from '@/features/settings/image-providers-config-api';\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport { apiUrl } from '@/lib/url';\nimport { useGatewayStore } from '@/stores/gateway-store';\n\nexport type ImageProviderUiRegionOption = {\n value: string;\n label: string;\n imageBaseUrl: string;\n};\n\nexport type ImageProviderUiBaseUrlPreset = {\n value: string;\n label: string;\n};\n\nexport type ImageProviderUiMetadata = {\n regions?: ImageProviderUiRegionOption[];\n baseUrlPresets?: ImageProviderUiBaseUrlPreset[];\n baseUrlPresetKind?: 'fal' | 'minimax' | 'google' | 'openai';\n};\n\nexport type ImageGenProviderCredentialSummary = {\n id: string;\n label?: string;\n defaultModel?: string;\n models: string[];\n configured?: boolean;\n ui?: ImageProviderUiMetadata;\n};\n\nexport function useImageProviderCredentials(summaries: ImageGenProviderCredentialSummary[]) {\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const gwSwr = useGatewayConfigSwr(hasToken);\n const gwCfg = gwSwr.data;\n\n const ids = useMemo(() => summaries.map((s) => s.id), [summaries]);\n\n const [credDraft, setCredDraft] = useState<Record<string, ImageProviderCredRow>>({});\n const [credBaseline, setCredBaseline] = useState<Record<string, ImageProviderCredRow>>({});\n const [credSaving, setCredSaving] = useState(false);\n const [credError, setCredError] = useState<string | null>(null);\n const [credSavedFlash, setCredSavedFlash] = useState(false);\n const [credNoopFlash, setCredNoopFlash] = useState(false);\n\n const credRowsFromServer = useMemo(\n () => imageProviderCredRowsFromConfigRoot(gwCfg?.payload?.config, ids),\n [gwCfg?.payload?.config, ids],\n );\n\n const credDirty = useMemo(\n () => JSON.stringify(credDraft) !== JSON.stringify(credBaseline),\n [credDraft, credBaseline],\n );\n\n useEffect(() => {\n if (!credDirty) {\n setCredDraft(structuredClone(credRowsFromServer));\n setCredBaseline(structuredClone(credRowsFromServer));\n }\n }, [credRowsFromServer, credDirty]);\n\n const updateCredRow = useCallback((id: string, patch: Partial<ImageProviderCredRow>) => {\n setCredDraft((prev) => {\n const base = prev[id] ?? emptyImageProviderCredRow();\n return { ...prev, [id]: { ...base, ...patch } };\n });\n }, []);\n\n const onDiscardCredentials = useCallback(() => {\n setCredDraft(structuredClone(credBaseline));\n setCredError(null);\n setCredSavedFlash(false);\n setCredNoopFlash(false);\n }, [credBaseline]);\n\n const saveCredentials = useCallback(\n async (errorFallback: string) => {\n const patch = buildImageProvidersConfigPatch(ids, credDraft, credBaseline);\n if (Object.keys(patch).length === 0) {\n setCredNoopFlash(true);\n window.setTimeout(() => setCredNoopFlash(false), 2200);\n return;\n }\n setCredSaving(true);\n setCredError(null);\n setCredSavedFlash(false);\n try {\n await patchImageProvidersConfig(patch);\n const updated = await gwSwr.mutate?.();\n void mutate(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n const nextRows = imageProviderCredRowsFromConfigRoot(updated?.payload?.config, ids);\n setCredDraft(structuredClone(nextRows));\n setCredBaseline(structuredClone(nextRows));\n setCredSavedFlash(true);\n window.setTimeout(() => setCredSavedFlash(false), 2000);\n } catch (e) {\n setCredError(e instanceof Error ? e.message : errorFallback);\n } finally {\n setCredSaving(false);\n }\n },\n [ids, credDraft, credBaseline, gwSwr],\n );\n\n return {\n gwSwr,\n credDraft,\n credBaseline,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n saveCredentials,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-image-provider-credentials-DEpLWx8J.js","names":[],"sources":["../../../../../web/src/features/settings/image-providers-swr-key.ts","../../../../../web/src/features/settings/fetch-image-providers.ts","../../../../../web/src/features/settings/image-providers-config-api.ts","../../../../../web/src/features/settings/image-provider-api-key-field.tsx","../../../../../web/src/features/settings/image-provider-credentials-panel.tsx","../../../../../web/src/features/settings/use-image-provider-credentials.ts"],"sourcesContent":["/** Shared SWR key for GET `/api/image/providers` (image settings + extension image pages). */\nexport const IMAGE_PROVIDERS_SWR_KEY = '/api/image/providers';\n","import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport type { ImageGenProviderCredentialSummary } from '@/features/settings/use-image-provider-credentials';\n\nexport async function fetchImageProvidersList(): Promise<ImageGenProviderCredentialSummary[]> {\n const res = await fetchJson<{\n ok?: boolean;\n payload?: { providers?: ImageGenProviderCredentialSummary[] };\n }>(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n return res?.payload?.providers ?? [];\n}\n","import { isMaskedKey } from '@/features/settings/providers-api';\nimport { revalidateGatewayConfig } from '@/features/gateway/gateway-config-swr';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\n/** One row of image-provider credential fields (matches PATCH `providersConfig` subset). */\nexport type ImageProviderCredRow = {\n apiKey: string;\n region: string;\n baseUrl: string;\n imageBaseUrl: string;\n};\n\nexport function emptyImageProviderCredRow(): ImageProviderCredRow {\n return { apiKey: '', region: '', baseUrl: '', imageBaseUrl: '' };\n}\n\nexport type SafeProviderAuthEntry = {\n apiKey: string;\n region?: string;\n baseUrl?: string;\n imageBaseUrl?: string;\n};\n\nfunction maskedApiKeyDisplay(safe?: SafeProviderAuthEntry): string {\n if (!safe?.apiKey) return '';\n return '••••••••••••';\n}\n\n/** Read `payload.config.providersConfig` from GET /api/config (masked). */\nexport function imageProviderCredRowsFromConfigRoot(\n config: unknown,\n imageProviderIds: string[],\n): Record<string, ImageProviderCredRow> {\n const pc = (() => {\n if (!config || typeof config !== 'object' || !('providersConfig' in config)) return undefined;\n const v = (config as { providersConfig?: unknown }).providersConfig;\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n return v as Record<string, SafeProviderAuthEntry>;\n })();\n\n const out: Record<string, ImageProviderCredRow> = {};\n for (const id of imageProviderIds) {\n const safe = pc?.[id];\n out[id] = {\n apiKey: maskedApiKeyDisplay(safe),\n region: safe?.region ?? '',\n baseUrl: safe?.baseUrl ?? '',\n imageBaseUrl: safe?.imageBaseUrl ?? '',\n };\n }\n return out;\n}\n\nfunction optionalStringField(\n draft: ImageProviderCredRow,\n baseline: ImageProviderCredRow,\n key: keyof Pick<ImageProviderCredRow, 'region' | 'baseUrl' | 'imageBaseUrl'>,\n): string | null | undefined {\n const d = draft[key].trim();\n const b = baseline[key].trim();\n if (d === b) return undefined;\n if (!d) return null;\n return d;\n}\n\nfunction apiKeyPatchValue(draftKey: string, baselineKey: string): string | null | undefined {\n const d = draftKey.trim();\n const b = baselineKey.trim();\n if (d === b) return undefined;\n if (isMaskedKey(d) && isMaskedKey(b)) return undefined;\n if (!d) {\n if (!b) return undefined;\n return null;\n }\n return d;\n}\n\n/**\n * Build `providersConfig` PATCH entries only for image providers whose row changed.\n * Omits `apiKey` when unchanged (still masked); sends `null` to clear stored key.\n */\nexport function buildImageProvidersConfigPatch(\n imageProviderIds: string[],\n draft: Record<string, ImageProviderCredRow>,\n baseline: Record<string, ImageProviderCredRow>,\n): Record<string, Record<string, unknown>> {\n const patch: Record<string, Record<string, unknown>> = {};\n for (const id of imageProviderIds) {\n const d = draft[id] ?? emptyImageProviderCredRow();\n const b = baseline[id] ?? emptyImageProviderCredRow();\n if (JSON.stringify(d) === JSON.stringify(b)) continue;\n\n const entry: Record<string, unknown> = {};\n const keyDelta = apiKeyPatchValue(d.apiKey, b.apiKey);\n if (keyDelta !== undefined) {\n entry.apiKey = keyDelta;\n }\n const region = optionalStringField(d, b, 'region');\n if (region !== undefined) entry.region = region;\n const baseUrl = optionalStringField(d, b, 'baseUrl');\n if (baseUrl !== undefined) entry.baseUrl = baseUrl;\n const imageBaseUrl = optionalStringField(d, b, 'imageBaseUrl');\n if (imageBaseUrl !== undefined) entry.imageBaseUrl = imageBaseUrl;\n\n if (Object.keys(entry).length > 0) {\n patch[id] = entry;\n }\n }\n return patch;\n}\n\nexport type RevealImageProviderApiKeyPayload = {\n id: string;\n apiKey: string | null;\n source: 'config' | 'none';\n};\n\n/** POST /api/image/providers/:id/reveal-api-key — plaintext only when stored in config file. */\nexport async function revealImageProviderConfigApiKey(providerId: string): Promise<RevealImageProviderApiKeyPayload> {\n const data = await fetchJson<{\n ok?: boolean;\n payload?: RevealImageProviderApiKeyPayload;\n error?: { message?: string };\n }>(apiUrl(`/api/image/providers/${encodeURIComponent(providerId)}/reveal-api-key`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (!data.ok || !data.payload) {\n throw new Error(data.error?.message ?? 'Reveal failed');\n }\n return data.payload;\n}\n\nexport async function patchImageProvidersConfig(\n patch: Record<string, Record<string, unknown>>,\n): Promise<void> {\n if (Object.keys(patch).length === 0) return;\n await fetchJson(apiUrl('/api/config'), {\n method: 'PATCH',\n body: JSON.stringify({ providersConfig: patch }),\n });\n await revalidateGatewayConfig();\n}\n","import { CheckCircle2, Copy, ExternalLink, Eye, EyeOff, Loader2 } from 'lucide-react';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { revealImageProviderConfigApiKey } from '@/features/settings/image-providers-config-api';\nimport type { ApiKeyLinkKind } from '@/features/settings/provider-enrichment';\nimport { providerApiKeyLinkLabel } from '@/features/settings/provider-enrichment';\nimport { isMaskedKey } from '@/features/settings/providers-api';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport { interaction } from '@/lib/interaction';\nimport { cn } from '@/lib/cn';\n\nexport type ImageProviderApiKeyFieldLabels = {\n apiKeyLabel: string;\n optionalPlaceholder: string;\n maskedHelp: string;\n copy: string;\n copied: string;\n show: string;\n hide: string;\n notInConfigFile: string;\n loadFailed: string;\n};\n\nexport function ImageProviderApiKeyField({\n providerId,\n value,\n onChange,\n labels,\n apiKeyLinks,\n apiKeyLinkLabels,\n}: {\n providerId: string;\n value: string;\n onChange: (next: string) => void;\n labels: ImageProviderApiKeyFieldLabels;\n apiKeyLinks: { href: string; kind: ApiKeyLinkKind }[];\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n}) {\n const [showKey, setShowKey] = useState(false);\n /** `undefined` = not fetched; `null` = fetched, not in config file; string = plaintext from config */\n const [revealed, setRevealed] = useState<string | null | undefined>(undefined);\n const [revealLoading, setRevealLoading] = useState(false);\n const [revealErr, setRevealErr] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const masked = isMaskedKey(value);\n\n useEffect(() => {\n if (!masked) {\n setRevealed(undefined);\n setRevealErr(null);\n }\n }, [masked, value]);\n\n const inputValue = (() => {\n if (!masked) return value;\n if (showKey && typeof revealed === 'string') return revealed;\n return value;\n })();\n\n const inputType =\n !masked || (masked && showKey && typeof revealed === 'string') ? ('text' as const) : ('password' as const);\n\n const copyEnabled =\n (!masked && value.trim().length > 0 && !isMaskedKey(value)) ||\n (Boolean(showKey) && typeof revealed === 'string' && revealed.length > 0);\n\n const copyKey = useCallback(async () => {\n const text =\n !masked && value.trim() && !isMaskedKey(value)\n ? value.trim()\n : typeof revealed === 'string' && revealed.length > 0\n ? revealed\n : '';\n if (!text) return;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 2000);\n } catch {\n /* ignore */\n }\n }, [masked, revealed, value]);\n\n const toggleEye = useCallback(async () => {\n setRevealErr(null);\n if (!masked) {\n setShowKey((s) => !s);\n return;\n }\n if (revealed !== undefined) {\n setShowKey((s) => !s);\n return;\n }\n setRevealLoading(true);\n try {\n const payload = await revealImageProviderConfigApiKey(providerId);\n setRevealed(payload.apiKey ?? null);\n setShowKey(true);\n } catch (e) {\n setRevealErr(e instanceof Error ? e.message : labels.loadFailed);\n setRevealed(null);\n } finally {\n setRevealLoading(false);\n }\n }, [masked, providerId, revealed, labels.loadFailed]);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-key-${providerId}`}>\n {labels.apiKeyLabel}\n </label>\n {apiKeyLinks.length > 0 ? (\n <div className=\"flex flex-col gap-1\">\n {apiKeyLinks.map((link) => (\n <a\n key={`${link.kind}-${link.href}`}\n href={link.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n >\n {providerApiKeyLinkLabel(link.kind, apiKeyLinkLabels)}\n <ExternalLink className=\"size-3\" aria-hidden />\n </a>\n ))}\n </div>\n ) : null}\n {masked ? <p className=\"text-[11px] text-fg-subtle\">{labels.maskedHelp}</p> : null}\n <div className=\"relative min-w-0\">\n <input\n id={`img-cred-key-${providerId}`}\n type={inputType}\n autoComplete=\"off\"\n spellCheck={false}\n className={cn(\n 'w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n )}\n value={inputValue}\n placeholder={masked ? '••••••••' : labels.optionalPlaceholder}\n onChange={(e) => {\n const next = e.target.value;\n if (masked && typeof revealed === 'string' && showKey && next !== revealed) {\n setRevealed(undefined);\n setShowKey(false);\n }\n onChange(next);\n }}\n />\n <div className=\"absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5\">\n {copyEnabled ? (\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={copied ? labels.copied : labels.copy}\n aria-label={copied ? labels.copied : labels.copy}\n onClick={() => void copyKey()}\n >\n {copied ? <CheckCircle2 className=\"size-4\" /> : <Copy className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={showKey ? labels.hide : labels.show}\n aria-label={showKey ? labels.hide : labels.show}\n disabled={revealLoading}\n onClick={() => void toggleEye()}\n >\n {revealLoading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : showKey ? (\n <EyeOff className=\"size-4\" aria-hidden />\n ) : (\n <Eye className=\"size-4\" aria-hidden />\n )}\n </button>\n </div>\n </div>\n {masked && showKey && revealed === null && !revealErr ? (\n <p className=\"text-xs text-amber-700 dark:text-amber-400/90\">{labels.notInConfigFile}</p>\n ) : null}\n {revealErr ? <p className=\"text-xs text-red-600 dark:text-red-400\">{revealErr}</p> : null}\n </div>\n );\n}\n","import { ExternalLink, Loader2, Save } from 'lucide-react';\nimport { Link } from 'react-router-dom';\n\nimport { Button } from '@/components/ui/button';\nimport { ImageProviderApiKeyField } from '@/features/settings/image-provider-api-key-field';\nimport { emptyImageProviderCredRow, type ImageProviderCredRow } from '@/features/settings/image-providers-config-api';\nimport { getOrderedApiKeyLinks } from '@/features/settings/provider-enrichment';\nimport type {\n ImageGenProviderCredentialSummary,\n ImageProviderUiMetadata,\n} from '@/features/settings/use-image-provider-credentials';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport type { StoredLanguage } from '@/lib/storage';\nimport { cn } from '@/lib/cn';\n\nfunction inputClass(): string {\n return cn(\n 'w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n );\n}\n\nfunction selectClass(): string {\n return cn(inputClass(), 'appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9');\n}\n\nconst CUSTOM_SENTINEL = '__custom__';\n\nfunction dashscopeSelectValue(\n row: ImageProviderCredRow,\n regions: NonNullable<ImageProviderUiMetadata['regions']>,\n): string {\n if (!row.region.trim() && !row.imageBaseUrl.trim()) return '';\n const r = row.region.trim().toLowerCase();\n if (regions.some((x) => x.value === r)) return r;\n return CUSTOM_SENTINEL;\n}\n\nfunction baseUrlSelectValue(\n row: ImageProviderCredRow,\n presets: NonNullable<ImageProviderUiMetadata['baseUrlPresets']>,\n): string {\n const b = row.baseUrl.trim().replace(/\\/+$/, '');\n if (!b) return '';\n const norm = presets.map((p) => p.value.replace(/\\/+$/, ''));\n const idx = norm.indexOf(b);\n if (idx >= 0) return presets[idx].value;\n return CUSTOM_SENTINEL;\n}\n\nexport type ImageProviderCredentialsPanelMessages = {\n credentialsIntro: string;\n regionHint: string;\n endpointPresetsHint: string;\n apiKeyLabel: string;\n optionalPlaceholder: string;\n regionLabel: string;\n baseUrlLabel: string;\n imageBaseUrlLabel: string;\n saveCredentials: string;\n savingCredentials: string;\n credentialsSaved: string;\n discardCredentials: string;\n credentialsNothingToSave: string;\n credentialsSaveError: string;\n regionPresetDefault: string;\n regionPresetCustom: string;\n baseUrlPresetDefault: string;\n baseUrlPresetCustom: string;\n openExtensionSettings: string;\n openImageModelsPage: string;\n extensionSettingsLinkTitle: string;\n imageModelsLinkTitle: string;\n configured: string;\n missingKey: string;\n defaultModel: string;\n modelsLabel: string;\n imageBaseUrlPresetHint: string;\n dashscopeRegion_beijing: string;\n dashscopeRegion_singapore: string;\n dashscopeRegion_us: string;\n apiKeyMaskedHelp: string;\n apiKeyCopy: string;\n apiKeyCopied: string;\n apiKeyShow: string;\n apiKeyHide: string;\n apiKeyNotInConfigFile: string;\n apiKeyRevealFailed: string;\n minimaxClusterLabel: string;\n minimaxClusterHint: string;\n falQueueBaseLabel: string;\n falQueueBaseHint: string;\n};\n\nfunction translateDashscopeRegion(m: ImageProviderCredentialsPanelMessages, value: string, serverLabel: string) {\n if (value === 'beijing') return m.dashscopeRegion_beijing;\n if (value === 'singapore') return m.dashscopeRegion_singapore;\n if (value === 'us') return m.dashscopeRegion_us;\n return serverLabel;\n}\n\nfunction baseUrlPresetBlockTitle(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string {\n if (kind === 'minimax') return t.minimaxClusterLabel;\n if (kind === 'fal') return t.falQueueBaseLabel;\n return t.baseUrlLabel;\n}\n\nfunction baseUrlPresetBlockHint(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string | null {\n if (kind === 'minimax') return t.minimaxClusterHint;\n if (kind === 'fal') return t.falQueueBaseHint;\n return null;\n}\n\nexport function ImageProviderCredentialsPanel({\n summaries,\n credDraft,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n onSaveCredentials,\n extensionIds,\n showExtensionLinks,\n showImageModelsLink,\n language,\n apiKeyLinkLabels,\n messages: t,\n}: {\n summaries: ImageGenProviderCredentialSummary[];\n credDraft: Record<string, ImageProviderCredRow>;\n credDirty: boolean;\n credSaving: boolean;\n credError: string | null;\n credSavedFlash: boolean;\n credNoopFlash: boolean;\n updateCredRow: (id: string, patch: Partial<ImageProviderCredRow>) => void;\n onDiscardCredentials: () => void;\n onSaveCredentials: () => void;\n /** Extension ids present in gateway discovery (for deep links). */\n extensionIds: Set<string>;\n showExtensionLinks: boolean;\n showImageModelsLink: boolean;\n language: StoredLanguage;\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n messages: ImageProviderCredentialsPanelMessages;\n}) {\n const empty = summaries.length === 0;\n\n if (empty) {\n return null;\n }\n\n const anyRegionUi = summaries.some((s) => (s.ui?.regions?.length ?? 0) > 0);\n const anyBaseUrlPresets = summaries.some((s) => (s.ui?.baseUrlPresets?.length ?? 0) > 0);\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1 text-xs leading-relaxed text-fg-muted\">\n <p>{t.credentialsIntro}</p>\n {anyRegionUi ? <p className=\"text-fg-subtle\">{t.regionHint}</p> : null}\n {anyBaseUrlPresets ? <p className=\"text-fg-subtle\">{t.endpointPresetsHint}</p> : null}\n {showImageModelsLink ? (\n <p>\n <Link\n to=\"/settings/image-models\"\n className=\"font-medium text-accent hover:underline\"\n title={t.imageModelsLinkTitle}\n >\n {t.openImageModelsPage}\n </Link>\n </p>\n ) : null}\n </div>\n {credError ? (\n <div className=\"rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300\">\n {credError}\n </div>\n ) : null}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {credSavedFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsSaved}</span>\n ) : null}\n {credNoopFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsNothingToSave}</span>\n ) : null}\n <Button type=\"button\" variant=\"secondary\" onClick={onDiscardCredentials} disabled={!credDirty || credSaving}>\n {t.discardCredentials}\n </Button>\n <Button type=\"button\" variant=\"primary\" onClick={onSaveCredentials} disabled={!credDirty || credSaving}>\n {credSaving ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n <span className=\"ml-1.5\">{t.savingCredentials}</span>\n </>\n ) : (\n <>\n <Save className=\"size-3.5\" />\n <span className=\"ml-1.5\">{t.saveCredentials}</span>\n </>\n )}\n </Button>\n </div>\n <div className=\"flex flex-col gap-4\">\n {summaries.map((p) => {\n const row = credDraft[p.id] ?? emptyImageProviderCredRow();\n const ui = p.ui;\n const extPath =\n showExtensionLinks && extensionIds.has(p.id)\n ? `/settings/ext/${encodeURIComponent(p.id)}`\n : null;\n return (\n <div\n key={p.id}\n className=\"rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-fg\">{p.label ?? p.id}</span>\n <span className=\"text-xs text-fg-subtle\">({p.id})</span>\n {extPath ? (\n <Link\n to={extPath}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n title={t.extensionSettingsLinkTitle}\n >\n <ExternalLink className=\"size-3\" />\n {t.openExtensionSettings}\n </Link>\n ) : null}\n </div>\n {p.configured ? (\n <span className=\"rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg\">\n {t.configured}\n </span>\n ) : (\n <span className=\"rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300\">\n {t.missingKey}\n </span>\n )}\n </div>\n {p.defaultModel ? (\n <p className=\"mt-1 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.defaultModel}:</span> {p.id}/{p.defaultModel}\n </p>\n ) : null}\n {p.models.length > 0 ? (\n <p className=\"mt-0.5 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.modelsLabel}:</span>{' '}\n {p.models.map((mm) => `${p.id}/${mm}`).join(', ')}\n </p>\n ) : null}\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <ImageProviderApiKeyField\n providerId={p.id}\n value={row.apiKey}\n onChange={(next) => updateCredRow(p.id, { apiKey: next })}\n apiKeyLinks={getOrderedApiKeyLinks(p.id, language)}\n apiKeyLinkLabels={apiKeyLinkLabels}\n labels={{\n apiKeyLabel: t.apiKeyLabel,\n optionalPlaceholder: t.optionalPlaceholder,\n maskedHelp: t.apiKeyMaskedHelp,\n copy: t.apiKeyCopy,\n copied: t.apiKeyCopied,\n show: t.apiKeyShow,\n hide: t.apiKeyHide,\n notInConfigFile: t.apiKeyNotInConfigFile,\n loadFailed: t.apiKeyRevealFailed,\n }}\n />\n\n {ui?.regions?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-region-preset-${p.id}`}>\n {t.regionLabel}\n </label>\n <select\n id={`img-cred-region-preset-${p.id}`}\n className={selectClass()}\n value={dashscopeSelectValue(row, ui.regions)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n const opt = ui.regions!.find((x) => x.value === v);\n if (opt) {\n updateCredRow(p.id, { region: opt.value, imageBaseUrl: opt.imageBaseUrl });\n }\n }}\n >\n <option value=\"\">{t.regionPresetDefault}</option>\n {ui.regions.map((r) => (\n <option key={r.value} value={r.value}>\n {translateDashscopeRegion(t, r.value, r.label)}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.regionPresetCustom}</option>\n </select>\n {dashscopeSelectValue(row, ui.regions) === CUSTOM_SENTINEL ? (\n <div className=\"mt-2 grid gap-2 sm:grid-cols-2\">\n <input\n type=\"text\"\n className={inputClass()}\n value={row.region}\n placeholder=\"region\"\n onChange={(e) => updateCredRow(p.id, { region: e.target.value })}\n />\n <input\n type=\"url\"\n className={inputClass()}\n value={row.imageBaseUrl}\n placeholder={t.imageBaseUrlLabel}\n onChange={(e) => updateCredRow(p.id, { imageBaseUrl: e.target.value })}\n />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {ui?.baseUrlPresets?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-base-preset-${p.id}`}>\n {baseUrlPresetBlockTitle(t, ui.baseUrlPresetKind)}\n </label>\n {baseUrlPresetBlockHint(t, ui.baseUrlPresetKind) ? (\n <p className=\"text-[11px] text-fg-subtle\">{baseUrlPresetBlockHint(t, ui.baseUrlPresetKind)}</p>\n ) : null}\n <select\n id={`img-cred-base-preset-${p.id}`}\n className={selectClass()}\n value={baseUrlSelectValue(row, ui.baseUrlPresets)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n updateCredRow(p.id, { baseUrl: v.replace(/\\/+$/, '') });\n }}\n >\n <option value=\"\">{t.baseUrlPresetDefault}</option>\n {ui.baseUrlPresets.map((b) => (\n <option key={b.value} value={b.value}>\n {b.label}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.baseUrlPresetCustom}</option>\n </select>\n {baseUrlSelectValue(row, ui.baseUrlPresets) === CUSTOM_SENTINEL ? (\n <input\n type=\"url\"\n className={cn(inputClass(), 'mt-2')}\n value={row.baseUrl}\n placeholder=\"https://…\"\n onChange={(e) => updateCredRow(p.id, { baseUrl: e.target.value })}\n />\n ) : null}\n </div>\n ) : null}\n\n {ui?.regions?.length && dashscopeSelectValue(row, ui.regions) !== CUSTOM_SENTINEL ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-imgbase-ro-${p.id}`}>\n {t.imageBaseUrlLabel}\n </label>\n <input\n id={`img-cred-imgbase-ro-${p.id}`}\n type=\"url\"\n readOnly\n className={cn(inputClass(), 'cursor-not-allowed opacity-90')}\n value={row.imageBaseUrl}\n title={t.imageBaseUrlPresetHint}\n />\n <p className=\"text-[11px] text-fg-subtle\">{t.imageBaseUrlPresetHint}</p>\n </div>\n ) : null}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport { mutate } from 'swr';\n\nimport { useGatewayConfigSwr } from '@/features/gateway/gateway-config-swr';\nimport {\n buildImageProvidersConfigPatch,\n emptyImageProviderCredRow,\n imageProviderCredRowsFromConfigRoot,\n patchImageProvidersConfig,\n type ImageProviderCredRow,\n} from '@/features/settings/image-providers-config-api';\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport { apiUrl } from '@/lib/url';\nimport { useGatewayStore } from '@/stores/gateway-store';\n\nexport type ImageProviderUiRegionOption = {\n value: string;\n label: string;\n imageBaseUrl: string;\n};\n\nexport type ImageProviderUiBaseUrlPreset = {\n value: string;\n label: string;\n};\n\nexport type ImageProviderUiMetadata = {\n regions?: ImageProviderUiRegionOption[];\n baseUrlPresets?: ImageProviderUiBaseUrlPreset[];\n baseUrlPresetKind?: 'fal' | 'minimax' | 'google' | 'openai';\n};\n\nexport type ImageGenProviderCredentialSummary = {\n id: string;\n label?: string;\n defaultModel?: string;\n models: string[];\n configured?: boolean;\n ui?: ImageProviderUiMetadata;\n};\n\nexport function useImageProviderCredentials(summaries: ImageGenProviderCredentialSummary[]) {\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const gwSwr = useGatewayConfigSwr(hasToken);\n const gwCfg = gwSwr.data;\n\n const ids = useMemo(() => summaries.map((s) => s.id), [summaries]);\n\n const [credDraft, setCredDraft] = useState<Record<string, ImageProviderCredRow>>({});\n const [credBaseline, setCredBaseline] = useState<Record<string, ImageProviderCredRow>>({});\n const [credSaving, setCredSaving] = useState(false);\n const [credError, setCredError] = useState<string | null>(null);\n const [credSavedFlash, setCredSavedFlash] = useState(false);\n const [credNoopFlash, setCredNoopFlash] = useState(false);\n\n const credRowsFromServer = useMemo(\n () => imageProviderCredRowsFromConfigRoot(gwCfg?.payload?.config, ids),\n [gwCfg?.payload?.config, ids],\n );\n\n const credDirty = useMemo(\n () => JSON.stringify(credDraft) !== JSON.stringify(credBaseline),\n [credDraft, credBaseline],\n );\n\n useEffect(() => {\n if (!credDirty) {\n setCredDraft(structuredClone(credRowsFromServer));\n setCredBaseline(structuredClone(credRowsFromServer));\n }\n }, [credRowsFromServer, credDirty]);\n\n const updateCredRow = useCallback((id: string, patch: Partial<ImageProviderCredRow>) => {\n setCredDraft((prev) => {\n const base = prev[id] ?? emptyImageProviderCredRow();\n return { ...prev, [id]: { ...base, ...patch } };\n });\n }, []);\n\n const onDiscardCredentials = useCallback(() => {\n setCredDraft(structuredClone(credBaseline));\n setCredError(null);\n setCredSavedFlash(false);\n setCredNoopFlash(false);\n }, [credBaseline]);\n\n const saveCredentials = useCallback(\n async (errorFallback: string) => {\n const patch = buildImageProvidersConfigPatch(ids, credDraft, credBaseline);\n if (Object.keys(patch).length === 0) {\n setCredNoopFlash(true);\n window.setTimeout(() => setCredNoopFlash(false), 2200);\n return;\n }\n setCredSaving(true);\n setCredError(null);\n setCredSavedFlash(false);\n try {\n await patchImageProvidersConfig(patch);\n const updated = await gwSwr.mutate?.();\n void mutate(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n const nextRows = imageProviderCredRowsFromConfigRoot(updated?.payload?.config, ids);\n setCredDraft(structuredClone(nextRows));\n setCredBaseline(structuredClone(nextRows));\n setCredSavedFlash(true);\n window.setTimeout(() => setCredSavedFlash(false), 2000);\n } catch (e) {\n setCredError(e instanceof Error ? e.message : errorFallback);\n } finally {\n setCredSaving(false);\n }\n },\n [ids, credDraft, credBaseline, gwSwr],\n );\n\n return {\n gwSwr,\n credDraft,\n credBaseline,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n saveCredentials,\n };\n}\n"],"mappings":"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"}
@@ -9,7 +9,7 @@
9
9
  <link rel="icon" type="image/svg+xml" href="/logo.svg" />
10
10
  <link rel="apple-touch-icon" href="/logo.svg" />
11
11
  <title>xopc</title>
12
- <script type="module" crossorigin src="/assets/index-B-L6DMBk.js"></script>
12
+ <script type="module" crossorigin src="/assets/index-DzugsTKg.js"></script>
13
13
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-DWdDZTNf.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-CXAvob9m.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DbimaAId.js">
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.0.55";
2
+ var version = "0.0.56";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"agent-manager.js","names":[],"sources":["../../../src/agent/agent-manager.ts"],"sourcesContent":["/**\n * Agent Manager - Manages Agent instances per session\n *\n * Each session gets its own Agent instance for true isolation\n * and concurrent processing across sessions.\n */\n\nimport {\n Agent,\n type AgentMessage,\n type AgentEvent,\n type ThinkingLevel,\n} from '@earendil-works/pi-agent-core';\nimport type { Model, Api } from '@earendil-works/pi-ai';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\nimport { resolveAgentProfileDir } from './agent-scope.js';\nimport {\n type EffectiveAgentProfile,\n resolveAgentWorkspaceDir as resolveAgentWorkspaceDirFromProfile,\n resolveEffectiveAgentProfileForSession,\n} from '../config/agent-profile.js';\nimport { expandWorkspacePathString } from '../config/workspace-path.js';\nimport type { ModelManager } from './models/manager.js';\nimport { createLogger } from '../utils/logger.js';\nimport { resolveProviderApiKeySync } from '../auth/sync-provider-auth.js';\nimport { resolveModel, getDefaultModelSync, getApiKeySync } from '../providers/index.js';\nimport { createExtensionAwareStreamFn } from '../providers/extension-stream-bridge.js';\nimport { CredentialResolver } from '../auth/credentials.js';\nimport { resolveBundledSkillsDir, resolveStateDir } from '../config/paths.js';\nimport { loadProfileMarkdownFiles, extractTextContent, type ProfileMarkdownFile } from './context/workspace.js';\nimport { SkillManager } from './skills/index.js';\nimport { SystemPromptBuilder } from './prompt/service-prompt-builder.js';\nimport { AgentToolsFactory } from './tools/factory.js';\nimport type { GatewayClarifyRequestFn } from './tools/clarify-tool.js';\nimport type { ExtensionRegistryImpl as ExtensionRegistry } from '../extensions/index.js';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport type { CronService } from '../cron/index.js';\nimport type { SessionStore } from '../session/store.js';\nimport { isValidSkillEnvVarName } from './skills/required-env-vars.js';\nimport type { SessionContext } from './session/session-context.js';\nimport type { Skill, SkillMarkdownPreviewPayload } from './skills/types.js';\nimport { resolveLocalizedSkillMarkdown, resolveLocalizedSkillMeta } from './skills/skill-view-path.js';\nimport { createSkillConfigManager } from './skills/config.js';\nimport { isUnderManagedSkillsDir } from './skills/managed-store.js';\nimport { loadSkillsLock, type SkillHubLockEntry } from './skills/hub-lock.js';\nimport { basename, resolve, sep } from 'node:path';\n\nimport { BuiltinMemoryStore } from './memory/builtin-memory-store.js';\nimport { createMemoryManagerFromConfig } from './memory/create-memory-manager.js';\nimport { injectPrefetchIntoUserMessage } from './memory/inject-prefetch.js';\nimport {\n isCuratedMemoryInPrompt,\n isMemorySubsystemEnabled,\n resolveBuiltinMemoryStoreConfig,\n shouldInjectMemoryPrefetchThisTurn,\n} from './memory/memory-config.js';\nimport type { MemoryManager } from './memory/manager.js';\nimport type { MemorySnapshot } from './memory/types.js';\nimport { extractAgentUserPlainText } from './memory/user-message-text.js';\nimport { resolveBackgroundReviewSettings } from './background-review/settings.js';\nimport { runBackgroundReviewTurn } from './background-review/run-background-review.js';\nimport {\n isAssistantTurnAborted,\n isAssistantTurnFailed,\n} from './orchestration/llm-turn-retry.js';\n\nconst log = createLogger('AgentManager');\n\n/** Counters for optional post-turn memory/skill review (see `agents.defaults.backgroundReview`). */\nexport interface BackgroundNudgeState {\n turnsSinceMemory: number;\n itersSinceSkill: number;\n pendingMemoryReview: boolean;\n unsubscribe?: () => void;\n}\n\nexport interface SkillCatalogEntry {\n directoryId: string;\n name: string;\n description: string;\n source: Skill['source'];\n path: string;\n managed: boolean;\n /** User toggle in ~/.xopc/skills.json (`entries[name].enabled`). Default true. */\n enabled: boolean;\n /** When true, skill is never injected into `<available_skills>` (SKILL.md frontmatter). */\n disableModelInvocation: boolean;\n /** Hub install provenance when under ~/.xopc/skills and listed in skills-lock.json. */\n hub?: SkillHubLockEntry;\n}\n\nexport interface AgentManagerConfig {\n workspace: string;\n model?: string;\n config?: Config;\n extensionRegistry?: ExtensionRegistry;\n hookRunner?: import('../extensions/index.js').ExtensionHookRunner;\n bus: MessageBus;\n getCurrentContext: () => SessionContext | null;\n /** Session persistence (enables `session_search` when set). */\n getSessionStore?: () => SessionStore;\n /** Clears per-session profile default on teardown. */\n getModelManager?: () => ModelManager;\n // Thinking configuration\n thinkingLevel?: ThinkingLevel;\n reasoningLevel?: 'off' | 'on' | 'stream';\n verboseLevel?: 'off' | 'on' | 'full';\n gatewayClarify?: { requestClarification: GatewayClarifyRequestFn };\n /** Gateway: exposes CronService for the `cronjob` tool. */\n getCronService?: () => CronService | undefined;\n}\n\nexport interface AgentInstance {\n agent: Agent;\n sessionKey: string;\n createdAt: number;\n lastUsedAt: number;\n /** Curated agent-home `memories/` snapshot frozen at agent creation (prefix cache). */\n curatedMemorySnapshot: MemorySnapshot;\n effectiveProfile: EffectiveAgentProfile;\n resolvedWorkspacePath: string;\n /** Tool names registered on this agent (for skill indexing / tool gating). */\n registeredToolNames: string[];\n /** Declared env var names from skill_view; shell reads values from process.env at spawn time. */\n skillEnvPassthroughKeys: Set<string>;\n backgroundNudge: BackgroundNudgeState;\n}\n\ninterface WorkspaceRuntime {\n skillManager: SkillManager;\n systemPromptBuilder: SystemPromptBuilder;\n builtinMemoryStore: BuiltinMemoryStore;\n memoryManager: MemoryManager;\n}\n\nexport class AgentManager {\n private agents = new Map<string, AgentInstance>();\n private config: AgentManagerConfig;\n private toolsFactory: AgentToolsFactory;\n\n private mergedConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n /** Default agent workspace (effective profile for `getDefaultAgentId`). */\n private baseWorkspacePath: string;\n /** Per-session absolute markdown workspace when `SessionAgentConfig.workingDirectoryOverride` is set. */\n private sessionWorkspaceOverrides = new Map<string, string>();\n private defaultModel: string;\n private credentialCache = new Map<string, string>();\n private credentialResolver: CredentialResolver;\n private workspaceRuntimes = new Map<string, WorkspaceRuntime>();\n /** Per-session user-message index for prefetch injection cadence. */\n private memoryPrefetchUserTurn = new Map<string, number>();\n\n constructor(config: AgentManagerConfig) {\n this.config = config;\n this.baseWorkspacePath = this.computeBaseWorkspacePath();\n const baseRt = this.getWorkspaceRuntime(this.baseWorkspacePath);\n\n this.toolsFactory = new AgentToolsFactory({\n workspace: this.baseWorkspacePath,\n extensionRegistry: config.extensionRegistry,\n getCurrentContext: config.getCurrentContext,\n hookRunner: config.hookRunner,\n bus: config.bus,\n getConfig: () => this.mergedConfig(),\n getPrimaryModel: () => this.resolveModelStringToModel(this.pickDefaultModelRef()),\n getBuiltinMemoryStore: () => baseRt.builtinMemoryStore,\n getMemoryManager: () => baseRt.memoryManager,\n getSessionStore: config.getSessionStore,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n getSkillIndexingContext: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return undefined;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return undefined;\n return {\n registeredToolNames: inst.registeredToolNames,\n skillAllowlist: inst.effectiveProfile.skillsAllowlist,\n };\n },\n onSkillsFilesystemMutate: () => {\n this.refreshSkillsAfterDiskChange();\n },\n getSkillPassthroughEnvVarNames: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return [];\n return [...(this.agents.get(ctx.sessionKey)?.skillEnvPassthroughKeys ?? [])];\n },\n registerSkillEnvPassthrough: (names: string[]) => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return;\n for (const n of names) {\n if (isValidSkillEnvVarName(n)) {\n inst.skillEnvPassthroughKeys.add(n.trim());\n }\n }\n },\n });\n\n this.defaultModel = config.model || getDefaultModelSync(config.config);\n\n this.credentialResolver = new CredentialResolver();\n this.warmCredentialCache().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Credential cache pre-warm failed: ${em}`);\n });\n }\n\n private computeBaseWorkspacePath(): string {\n const cfg = this.config.config;\n if (!cfg) {\n return expandWorkspacePathString(this.config.workspace);\n }\n return resolveEffectiveAgentProfileForSession(cfg, null).resolvedWorkspacePath;\n }\n\n /**\n * Workspace root for inbound attachments / side effects for this session's agent id.\n * Uses in-memory session workspace overrides when the session has a persisted `workingDirectoryOverride`.\n */\n getResolvedWorkspaceForSession(sessionKey: string): string {\n const cfg = this.config.config!;\n const fromMap = this.sessionWorkspaceOverrides.get(sessionKey);\n if (fromMap !== undefined) {\n return fromMap;\n }\n return resolveEffectiveAgentProfileForSession(cfg, sessionKey).resolvedWorkspacePath;\n }\n\n /**\n * Sync in-memory workspace override from session config (after load or PATCH).\n * Pass `null` to clear when the session has no `workingDirectoryOverride` on disk.\n */\n setSessionWorkspaceOverride(sessionKey: string, absolutePath: string | null): void {\n if (absolutePath === null || absolutePath === '') {\n this.sessionWorkspaceOverrides.delete(sessionKey);\n } else {\n this.sessionWorkspaceOverrides.set(sessionKey, absolutePath);\n }\n }\n\n /** Merged `thinkingDefault` for this session's agent id (defaults + `agents.list`). */\n getThinkingDefaultForSession(\n sessionKey: string,\n ): import('./transcript/thinking-types.js').ThinkLevel | undefined {\n const cfg = this.mergedConfig();\n if (!cfg) {\n return undefined;\n }\n return resolveEffectiveAgentProfileForSession(cfg, sessionKey).thinkingDefault;\n }\n\n private getWorkspaceRuntime(resolvedPath: string): WorkspaceRuntime {\n const existing = this.workspaceRuntimes.get(resolvedPath);\n if (existing) {\n return existing;\n }\n\n const builtinMemoryStore = new BuiltinMemoryStore(\n resolveBuiltinMemoryStoreConfig(resolvedPath, this.config.config),\n );\n const memoryManager = createMemoryManagerFromConfig(\n resolvedPath,\n builtinMemoryStore,\n this.config.config,\n );\n const skillManager = new SkillManager(resolvedPath, resolveBundledSkillsDir());\n const systemPromptBuilder = new SystemPromptBuilder({\n workspace: resolvedPath,\n config: this.config.config!,\n skillManager,\n });\n\n const rt: WorkspaceRuntime = {\n skillManager,\n systemPromptBuilder,\n builtinMemoryStore,\n memoryManager,\n };\n this.workspaceRuntimes.set(resolvedPath, rt);\n return rt;\n }\n\n private pickDefaultModelRef(): string {\n const cfg = this.mergedConfig();\n const ref = getAgentDefaultModelRef(cfg);\n return ref?.trim() || getDefaultModelSync(cfg);\n }\n\n private resolveModelStringToModel(modelRef: string): Model<Api> {\n try {\n return resolveModel(modelRef);\n } catch {\n const fallback = getDefaultModelSync(this.mergedConfig());\n log.warn({ modelRef, fallback }, 'Model not found, using default');\n return resolveModel(fallback);\n }\n }\n\n /**\n * Keep defaults in sync when config is hot-reloaded or saved from the UI.\n */\n updateAgentDefaults(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.defaultModel = ref || getDefaultModelSync(config);\n this.baseWorkspacePath = this.computeBaseWorkspacePath();\n void this.toolsFactory.shutdownBrowser();\n for (const rt of this.workspaceRuntimes.values()) {\n void rt.memoryManager.shutdownAll().catch(() => {});\n }\n this.workspaceRuntimes.clear();\n this.toolsFactory = new AgentToolsFactory({\n workspace: this.baseWorkspacePath,\n extensionRegistry: this.config.extensionRegistry,\n getCurrentContext: this.config.getCurrentContext,\n bus: this.config.bus,\n getConfig: () => this.mergedConfig(),\n getPrimaryModel: () => this.resolveModelStringToModel(this.pickDefaultModelRef()),\n getBuiltinMemoryStore: () => this.getWorkspaceRuntime(this.baseWorkspacePath).builtinMemoryStore,\n getMemoryManager: () => this.getWorkspaceRuntime(this.baseWorkspacePath).memoryManager,\n getSessionStore: this.config.getSessionStore,\n gatewayClarify: this.config.gatewayClarify,\n getCronService: this.config.getCronService,\n getSkillIndexingContext: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return undefined;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return undefined;\n return {\n registeredToolNames: inst.registeredToolNames,\n skillAllowlist: inst.effectiveProfile.skillsAllowlist,\n };\n },\n onSkillsFilesystemMutate: () => {\n this.refreshSkillsAfterDiskChange();\n },\n getSkillPassthroughEnvVarNames: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return [];\n return [...(this.agents.get(ctx.sessionKey)?.skillEnvPassthroughKeys ?? [])];\n },\n registerSkillEnvPassthrough: (names: string[]) => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return;\n for (const n of names) {\n if (isValidSkillEnvVarName(n)) {\n inst.skillEnvPassthroughKeys.add(n.trim());\n }\n }\n },\n });\n }\n\n getMemoryManager(): MemoryManager {\n return this.getWorkspaceRuntime(this.baseWorkspacePath).memoryManager;\n }\n\n private getMemoryManagerForSession(sessionKey: string): MemoryManager {\n const path = this.getResolvedWorkspaceForSession(sessionKey);\n return this.getWorkspaceRuntime(path).memoryManager;\n }\n\n /**\n * Prefix the user turn with fenced prefetched memory (external providers).\n */\n async applyMemoryPrefetchToUserMessage(\n userMessage: AgentMessage,\n sessionKey: string,\n ): Promise<AgentMessage> {\n if (!isMemorySubsystemEnabled(this.config.config)) {\n return userMessage;\n }\n const plain = extractAgentUserPlainText(userMessage);\n const turn = (this.memoryPrefetchUserTurn.get(sessionKey) ?? 0) + 1;\n this.memoryPrefetchUserTurn.set(sessionKey, turn);\n if (!shouldInjectMemoryPrefetchThisTurn(this.config.config, turn)) {\n return userMessage;\n }\n return injectPrefetchIntoUserMessage(\n this.getMemoryManagerForSession(sessionKey),\n sessionKey,\n userMessage,\n plain,\n );\n }\n\n /**\n * After a completed turn: sync external providers and queue next-turn prefetch.\n */\n afterAgentTurn(sessionKey: string, userPlainText: string): void {\n if (!isMemorySubsystemEnabled(this.config.config)) {\n return;\n }\n const assistant = this.getLastAssistantContent(sessionKey) ?? '';\n const mm = this.getMemoryManagerForSession(sessionKey);\n mm.syncAll(userPlainText, assistant, { sessionId: sessionKey });\n mm.queuePrefetchAll(userPlainText, { sessionId: sessionKey });\n }\n\n /**\n * Call once per user turn before the main `agent.prompt` (via {@link runAgentTurnWithModelFallbacks} `beforeUserPrompt`).\n */\n beginBackgroundReviewUserTurn(sessionKey: string): void {\n const inst = this.agents.get(sessionKey);\n if (!inst?.backgroundNudge) return;\n const cfg = resolveBackgroundReviewSettings(this.config.config);\n if (!cfg.enabled || cfg.memoryNudgeInterval <= 0) return;\n if (!inst.registeredToolNames.includes('curated_memory')) return;\n inst.backgroundNudge.turnsSinceMemory += 1;\n if (inst.backgroundNudge.turnsSinceMemory >= cfg.memoryNudgeInterval) {\n inst.backgroundNudge.pendingMemoryReview = true;\n inst.backgroundNudge.turnsSinceMemory = 0;\n }\n }\n\n /**\n * After a successful main turn (after memory sync via `afterAgentTurn`), may run a quiet follow-up for memory/skills.\n */\n scheduleBackgroundReviewAfterUserTurn(sessionKey: string): void {\n void this.runBackgroundReviewIfNeeded(sessionKey).catch((err) => {\n log.warn({ err, sessionKey }, 'Background review failed');\n });\n }\n\n private async runBackgroundReviewIfNeeded(sessionKey: string): Promise<void> {\n const inst = this.agents.get(sessionKey);\n if (!inst?.backgroundNudge) return;\n const settings = resolveBackgroundReviewSettings(this.config.config);\n if (!settings.enabled) return;\n if (isAssistantTurnAborted(inst.agent) || isAssistantTurnFailed(inst.agent)) return;\n const last = this.getLastAssistantContent(sessionKey);\n if (!last?.trim()) return;\n\n const reviewMemory = inst.backgroundNudge.pendingMemoryReview;\n inst.backgroundNudge.pendingMemoryReview = false;\n\n let reviewSkills = false;\n if (settings.skillNudgeInterval > 0 && inst.registeredToolNames.includes('skill_manage')) {\n if (inst.backgroundNudge.itersSinceSkill >= settings.skillNudgeInterval) {\n reviewSkills = true;\n inst.backgroundNudge.itersSinceSkill = 0;\n }\n }\n\n if (!reviewMemory && !reviewSkills) return;\n\n const rt = this.getWorkspaceRuntime(inst.resolvedWorkspacePath);\n await runBackgroundReviewTurn({\n sessionKey,\n mainAgent: inst.agent,\n settings,\n reviewMemory,\n reviewSkills,\n registeredToolNames: inst.registeredToolNames,\n skillAllowlist: inst.effectiveProfile.skillsAllowlist,\n workspacePath: inst.resolvedWorkspacePath,\n skillManager: rt.skillManager,\n builtinMemoryStore: rt.builtinMemoryStore,\n memoryManager: rt.memoryManager,\n getConfig: () => this.mergedConfig(),\n onSkillsFilesystemMutate: () => this.refreshSkillsAfterDiskChange(),\n });\n }\n\n private attachBackgroundNudgeListeners(sessionKey: string): void {\n const inst = this.agents.get(sessionKey);\n if (!inst?.backgroundNudge) return;\n inst.backgroundNudge.unsubscribe?.();\n const unsub = inst.agent.subscribe((ev: AgentEvent) => {\n const cfg = resolveBackgroundReviewSettings(this.config.config);\n if (!cfg.enabled || cfg.skillNudgeInterval <= 0) return;\n if (!inst.registeredToolNames.includes('skill_manage')) return;\n if (ev.type === 'turn_start') {\n inst.backgroundNudge.itersSinceSkill += 1;\n }\n if (ev.type === 'tool_execution_end') {\n const te = ev as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n if (\n !te.isError &&\n typeof te.toolName === 'string' &&\n te.toolName.trim() === 'skill_manage'\n ) {\n inst.backgroundNudge.itersSinceSkill = 0;\n }\n }\n });\n inst.backgroundNudge.unsubscribe = unsub;\n }\n\n /**\n * Expand `/skill:name` user text into the full skill block for the current turn (WebChat, channels).\n */\n expandSkillUserText(text: string): string {\n const ctx = this.config.getCurrentContext?.();\n const path = ctx?.sessionKey\n ? this.getResolvedWorkspaceForSession(ctx.sessionKey)\n : this.baseWorkspacePath;\n return this.getWorkspaceRuntime(path).skillManager.expandCommand(text);\n }\n\n /**\n * Structured SKILL.md preview for the gateway console.\n * When `lang` is provided (e.g. \"zh\"), tries SKILL-{lang}.md first; falls back to SKILL.md.\n */\n getSkillMarkdownSource(skillName: string, lang?: string): SkillMarkdownPreviewPayload | null {\n const skill = this.getWorkspaceRuntime(this.baseWorkspacePath).skillManager.findSkill(skillName);\n if (!skill) return null;\n\n // Try localized file for display\n if (lang) {\n const localized = resolveLocalizedSkillMarkdown(skill, lang);\n if (localized) return localized;\n }\n\n return {\n name: skill.name,\n description: skill.description,\n bodyMarkdown: skill.content,\n disableModelInvocation: skill.disableModelInvocation,\n metadata: skill.metadata,\n toolConditions: skill.toolConditions,\n requiredEnvVarNames: skill.requiredEnvVarNames,\n };\n }\n\n private loadProfileMarkdownForProfile(profile: EffectiveAgentProfile): ProfileMarkdownFile[] {\n const cfg = this.config.config!;\n const profileDir = resolveAgentProfileDir(cfg, profile.agentId);\n return loadProfileMarkdownFiles(profileDir);\n }\n\n getSkillCatalog(lang?: string): SkillCatalogEntry[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n const lock = loadSkillsLock();\n return this.getWorkspaceRuntime(this.baseWorkspacePath).skillManager.getSkills().map((s) => {\n const base = resolve(s.baseDir);\n const managed = isUnderManagedSkillsDir(s.baseDir);\n const directoryId = base.split(sep).filter(Boolean).pop() || s.name;\n const enabled = !(skillsConfig.entries?.[s.name]?.enabled === false);\n const hubKey = managed ? basename(base) : '';\n const hub = managed && hubKey ? lock.entries[hubKey] : undefined;\n\n // Attempt localized name/description for display\n const localized = lang ? resolveLocalizedSkillMeta(s, lang) : null;\n\n return {\n directoryId,\n name: localized?.name ?? s.name,\n description: localized?.description ?? s.description,\n category: s.category,\n source: s.source,\n path: s.baseDir,\n managed,\n enabled,\n disableModelInvocation: s.disableModelInvocation,\n ...(hub ? { hub } : {}),\n };\n });\n }\n\n /**\n * After ~/.xopc/skills.json changes (enable/disable), refresh `<available_skills>` on active agents.\n */\n refreshSkillsAfterSkillConfigChange(): void {\n const cfg = this.config.config!;\n const touched = new Set<string>();\n for (const instance of this.agents.values()) {\n const rt = this.getWorkspaceRuntime(instance.resolvedWorkspacePath);\n if (!touched.has(instance.resolvedWorkspacePath)) {\n rt.skillManager.refreshPromptFromConfig();\n touched.add(instance.resolvedWorkspacePath);\n }\n const profileMarkdownFiles = this.loadProfileMarkdownForProfile(instance.effectiveProfile);\n const newPrompt = rt.systemPromptBuilder.build(profileMarkdownFiles, {\n curatedMemorySnapshot: instance.curatedMemorySnapshot,\n externalMemoryInstructions: rt.memoryManager.buildExternalSystemPrompt(),\n workspaceOverride: instance.resolvedWorkspacePath,\n profileMarkdownPathRoot: resolveAgentProfileDir(cfg, instance.effectiveProfile.agentId),\n systemPromptOverride: instance.effectiveProfile.systemPromptOverride,\n skillAllowlist: instance.effectiveProfile.skillsAllowlist,\n registeredToolNames: instance.registeredToolNames,\n });\n instance.agent.state.systemPrompt = newPrompt;\n }\n log.info({ agents: this.agents.size }, 'Skill toggles applied; system prompt updated');\n }\n\n /**\n * Reload skills from disk and refresh system prompt on all active Agent instances.\n */\n refreshSkillsAfterDiskChange(): void {\n const cfg = this.config.config!;\n // Reload every workspace SkillManager first. When there are no active agent sessions\n // (e.g. gateway UI only), the loop below runs zero times — without this, `getSkillCatalog()`\n // and delete flows still see stale in-memory skills after ~/.xopc/skills changes.\n for (const rt of this.workspaceRuntimes.values()) {\n rt.skillManager.reload();\n }\n\n const touched = new Set<string>();\n for (const instance of this.agents.values()) {\n const rt = this.getWorkspaceRuntime(instance.resolvedWorkspacePath);\n if (!touched.has(instance.resolvedWorkspacePath)) {\n touched.add(instance.resolvedWorkspacePath);\n }\n const profileMarkdownFiles = this.loadProfileMarkdownForProfile(instance.effectiveProfile);\n const newPrompt = rt.systemPromptBuilder.rebuild(profileMarkdownFiles, {\n curatedMemorySnapshot: instance.curatedMemorySnapshot,\n externalMemoryInstructions: rt.memoryManager.buildExternalSystemPrompt(),\n workspaceOverride: instance.resolvedWorkspacePath,\n profileMarkdownPathRoot: resolveAgentProfileDir(cfg, instance.effectiveProfile.agentId),\n systemPromptOverride: instance.effectiveProfile.systemPromptOverride,\n skillAllowlist: instance.effectiveProfile.skillsAllowlist,\n registeredToolNames: instance.registeredToolNames,\n });\n instance.agent.state.systemPrompt = newPrompt;\n }\n log.info({ agents: this.agents.size }, 'Skills refreshed; system prompt updated');\n }\n\n /**\n * Get or create an Agent instance for a session\n */\n getOrCreateAgent(sessionKey: string): Agent {\n const cfg = this.config.config!;\n const targetPath = this.getResolvedWorkspaceForSession(sessionKey);\n const existing = this.agents.get(sessionKey);\n if (existing) {\n if (existing.resolvedWorkspacePath !== targetPath) {\n this.removeAgent(sessionKey);\n } else {\n existing.lastUsedAt = Date.now();\n if (!existing.backgroundNudge) {\n existing.backgroundNudge = {\n turnsSinceMemory: 0,\n itersSinceSkill: 0,\n pendingMemoryReview: false,\n };\n this.attachBackgroundNudgeListeners(sessionKey);\n }\n log.debug({ sessionKey }, 'Reusing existing agent instance');\n return existing.agent;\n }\n }\n\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const resolvedPath = targetPath;\n const rt = this.getWorkspaceRuntime(resolvedPath);\n\n if (isMemorySubsystemEnabled(cfg)) {\n void rt.memoryManager\n .initializeAll(sessionKey, { workspace: resolvedPath })\n .catch((err) => log.warn({ err, sessionKey }, 'memory initializeAll failed'));\n }\n\n const curatedOn = isCuratedMemoryInPrompt(cfg);\n if (curatedOn) {\n rt.builtinMemoryStore.loadFromDiskSync();\n }\n const snap = curatedOn ? rt.builtinMemoryStore.getSnapshot() : { memory: '', user: '' };\n const curatedMemorySnapshot: MemorySnapshot = { memory: snap.memory, user: snap.user };\n\n const { agent, registeredToolNames } = this.createAgentForProfile(\n sessionKey,\n profile,\n resolvedPath,\n rt,\n curatedMemorySnapshot,\n );\n\n this.agents.set(sessionKey, {\n agent,\n sessionKey,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n curatedMemorySnapshot,\n effectiveProfile: profile,\n resolvedWorkspacePath: resolvedPath,\n registeredToolNames,\n skillEnvPassthroughKeys: new Set<string>(),\n backgroundNudge: {\n turnsSinceMemory: 0,\n itersSinceSkill: 0,\n pendingMemoryReview: false,\n },\n });\n\n this.attachBackgroundNudgeListeners(sessionKey);\n\n const modelRef = profile.primaryModelRef?.trim() || this.defaultModel;\n this.config.getModelManager?.().setSessionProfileDefault(sessionKey, modelRef);\n\n log.debug({ sessionKey, totalAgents: this.agents.size, agentId: profile.agentId }, 'Created new agent instance');\n return agent;\n }\n\n /**\n * Get existing agent for a session (if any)\n */\n getAgent(sessionKey: string): Agent | undefined {\n return this.agents.get(sessionKey)?.agent;\n }\n\n /**\n * Check if an agent exists for a session\n */\n hasAgent(sessionKey: string): boolean {\n return this.agents.has(sessionKey);\n }\n\n /**\n * Remove an agent instance\n */\n removeAgent(sessionKey: string): boolean {\n const instance = this.agents.get(sessionKey);\n if (instance) {\n instance.backgroundNudge?.unsubscribe?.();\n void this.toolsFactory.closeBrowserPageForSession(sessionKey);\n instance.agent.abort();\n this.agents.delete(sessionKey);\n this.memoryPrefetchUserTurn.delete(sessionKey);\n this.config.getModelManager?.().clearSessionProfileDefault(sessionKey);\n log.info({ sessionKey, totalAgents: this.agents.size }, 'Removed agent instance');\n return true;\n }\n return false;\n }\n\n /**\n * Get all active session keys\n */\n getActiveSessions(): string[] {\n return Array.from(this.agents.keys());\n }\n\n /**\n * Get agent count\n */\n getAgentCount(): number {\n return this.agents.size;\n }\n\n /**\n * Set thinking level for a session's agent\n */\n setThinkingLevel(sessionKey: string, level: ThinkingLevel): void {\n const instance = this.agents.get(sessionKey);\n if (instance) {\n instance.agent.state.thinkingLevel = level;\n log.debug({ sessionKey, thinkingLevel: level }, 'Set thinking level for agent');\n }\n }\n\n /**\n * Dispose all agents\n */\n dispose(): void {\n void this.toolsFactory.shutdownBrowser();\n for (const instance of this.agents.values()) {\n instance.backgroundNudge?.unsubscribe?.();\n instance.agent.abort();\n }\n this.agents.clear();\n this.memoryPrefetchUserTurn.clear();\n this.sessionWorkspaceOverrides.clear();\n for (const rt of this.workspaceRuntimes.values()) {\n void rt.memoryManager.shutdownAll().catch(() => {});\n }\n this.workspaceRuntimes.clear();\n log.debug('All agent instances disposed');\n }\n\n async warmCredentialCache(): Promise<void> {\n const profiles = await this.credentialResolver.listProfiles();\n for (const profile of profiles) {\n const secret = profile.key?.trim()\n ? profile.key.trim()\n : profile.envVar\n ? process.env[profile.envVar]?.trim()\n : undefined;\n if (secret) {\n this.credentialCache.set(profile.provider.toLowerCase(), secret);\n }\n }\n log.debug({ count: this.credentialCache.size }, 'Credential cache warmed');\n }\n\n async refreshCredentials(): Promise<void> {\n this.credentialCache.clear();\n await this.warmCredentialCache();\n }\n\n private resolveApiKeyWithCache(provider: string): string | undefined {\n const key = provider.toLowerCase();\n const cached = this.credentialCache.get(key);\n if (cached) return cached;\n\n const fromDisk = resolveProviderApiKeySync(provider);\n if (fromDisk) {\n this.credentialCache.set(key, fromDisk);\n return fromDisk;\n }\n\n const fromRegistryOrEnv = getApiKeySync(provider);\n if (fromRegistryOrEnv) {\n this.credentialCache.set(key, fromRegistryOrEnv);\n return fromRegistryOrEnv;\n }\n return undefined;\n }\n\n private createAgentForProfile(\n _sessionKey: string,\n profile: EffectiveAgentProfile,\n resolvedWorkspacePath: string,\n rt: WorkspaceRuntime,\n curatedMemorySnapshot: MemorySnapshot,\n ): { agent: Agent; registeredToolNames: string[] } {\n const modelRef = profile.primaryModelRef?.trim() || this.defaultModel;\n const model = this.resolveModelStringToModel(modelRef);\n\n const profileMarkdownFiles = this.loadProfileMarkdownForProfile(profile);\n const tools = this.toolsFactory.createAllTools({\n workspace: resolvedWorkspacePath,\n profileMarkdownRoot: resolveAgentProfileDir(this.config.config!, profile.agentId),\n disabledTools: profile.tools.disable,\n getPrimaryModel: () => this.resolveModelStringToModel(modelRef),\n getBuiltinMemoryStore: () => rt.builtinMemoryStore,\n getMemoryManager: () => rt.memoryManager,\n getSkillManager: () => rt.skillManager,\n });\n const registeredToolNames = tools.map((t) => t.name);\n\n const thinkingLevel =\n (profile.thinkingDefault as ThinkingLevel | undefined) ?? this.config.thinkingLevel ?? 'medium';\n\n const agent = new Agent({\n initialState: {\n systemPrompt: rt.systemPromptBuilder.build(profileMarkdownFiles, {\n curatedMemorySnapshot,\n externalMemoryInstructions: rt.memoryManager.buildExternalSystemPrompt(),\n workspaceOverride: resolvedWorkspacePath,\n profileMarkdownPathRoot: resolveAgentProfileDir(this.config.config!, profile.agentId),\n systemPromptOverride: profile.systemPromptOverride,\n skillAllowlist: profile.skillsAllowlist,\n registeredToolNames,\n }),\n model,\n thinkingLevel,\n tools,\n messages: [],\n },\n streamFn: createExtensionAwareStreamFn(),\n getApiKey: (provider: string) => this.resolveApiKeyWithCache(provider),\n });\n return { agent, registeredToolNames };\n }\n\n /**\n * Set model for a specific session\n */\n setModelForSession(sessionKey: string, modelId: string): boolean {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n log.warn(\n { sessionKey, modelId, activeSessionCount: this.agents.size },\n `setModelForSession: no agent instance for session (create session / run turn first); modelId=${modelId}`,\n );\n return false;\n }\n\n try {\n const model = resolveModel(modelId);\n instance.agent.state.model = model;\n log.info({ sessionKey, modelId }, 'Model set for session');\n return true;\n } catch (err) {\n log.error({ err, sessionKey, modelId }, 'Failed to set model for session');\n return false;\n }\n }\n\n /**\n * Get last assistant content from a session's agent\n */\n getLastAssistantContent(sessionKey: string): string | null {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return null;\n }\n\n const messages = instance.agent.state.messages;\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'assistant') {\n const content = msg.content;\n if (Array.isArray(content)) {\n return extractTextContent(content as Array<{ type: string; text?: string }>);\n }\n return String(content);\n }\n }\n return null;\n }\n\n /**\n * Replace messages for a session's agent\n */\n replaceMessages(sessionKey: string, messages: AgentMessage[]): boolean {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return false;\n }\n\n instance.agent.state.messages = messages;\n return true;\n }\n\n /**\n * Get messages for a session's agent\n */\n getMessages(sessionKey: string): AgentMessage[] | null {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return null;\n }\n\n return instance.agent.state.messages;\n }\n\n /**\n * Subscribe to agent events for a session\n */\n subscribeToSession(sessionKey: string, callback: (event: AgentEvent) => void): (() => void) | null {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return null;\n }\n\n return instance.agent.subscribe(callback);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-manager.js","names":[],"sources":["../../../src/agent/agent-manager.ts"],"sourcesContent":["/**\n * Agent Manager - Manages Agent instances per session\n *\n * Each session gets its own Agent instance for true isolation\n * and concurrent processing across sessions.\n */\n\nimport {\n Agent,\n type AgentMessage,\n type AgentEvent,\n type ThinkingLevel,\n} from '@earendil-works/pi-agent-core';\nimport type { Model, Api } from '@earendil-works/pi-ai';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\nimport { resolveAgentProfileDir } from './agent-scope.js';\nimport {\n type EffectiveAgentProfile,\n resolveEffectiveAgentProfileForSession,\n} from '../config/agent-profile.js';\nimport { expandWorkspacePathString } from '../config/workspace-path.js';\nimport type { ModelManager } from './models/manager.js';\nimport { createLogger } from '../utils/logger.js';\nimport { resolveProviderApiKeySync } from '../auth/sync-provider-auth.js';\nimport { resolveModel, getDefaultModelSync, getApiKeySync } from '../providers/index.js';\nimport { createExtensionAwareStreamFn } from '../providers/extension-stream-bridge.js';\nimport { CredentialResolver } from '../auth/credentials.js';\nimport { resolveBundledSkillsDir, resolveStateDir } from '../config/paths.js';\nimport { loadProfileMarkdownFiles, extractTextContent, type ProfileMarkdownFile } from './context/workspace.js';\nimport { SkillManager } from './skills/index.js';\nimport { SystemPromptBuilder } from './prompt/service-prompt-builder.js';\nimport { AgentToolsFactory } from './tools/factory.js';\nimport type { GatewayClarifyRequestFn } from './tools/clarify-tool.js';\nimport type { ExtensionRegistryImpl as ExtensionRegistry } from '../extensions/index.js';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport type { CronService } from '../cron/index.js';\nimport type { SessionStore } from '../session/store.js';\nimport { isValidSkillEnvVarName } from './skills/required-env-vars.js';\nimport type { SessionContext } from './session/session-context.js';\nimport type { Skill, SkillMarkdownPreviewPayload } from './skills/types.js';\nimport { resolveLocalizedSkillMarkdown, resolveLocalizedSkillMeta } from './skills/skill-view-path.js';\nimport { createSkillConfigManager } from './skills/config.js';\nimport { isUnderManagedSkillsDir } from './skills/managed-store.js';\nimport { loadSkillsLock, type SkillHubLockEntry } from './skills/hub-lock.js';\nimport { basename, resolve, sep } from 'node:path';\n\nimport { BuiltinMemoryStore } from './memory/builtin-memory-store.js';\nimport { createMemoryManagerFromConfig } from './memory/create-memory-manager.js';\nimport { injectPrefetchIntoUserMessage } from './memory/inject-prefetch.js';\nimport {\n isCuratedMemoryInPrompt,\n isMemorySubsystemEnabled,\n resolveBuiltinMemoryStoreConfig,\n shouldInjectMemoryPrefetchThisTurn,\n} from './memory/memory-config.js';\nimport type { MemoryManager } from './memory/manager.js';\nimport type { MemorySnapshot } from './memory/types.js';\nimport { extractAgentUserPlainText } from './memory/user-message-text.js';\nimport { resolveBackgroundReviewSettings } from './background-review/settings.js';\nimport { runBackgroundReviewTurn } from './background-review/run-background-review.js';\nimport {\n isAssistantTurnAborted,\n isAssistantTurnFailed,\n} from './orchestration/llm-turn-retry.js';\n\nconst log = createLogger('AgentManager');\n\n/** Counters for optional post-turn memory/skill review (see `agents.defaults.backgroundReview`). */\nexport interface BackgroundNudgeState {\n turnsSinceMemory: number;\n itersSinceSkill: number;\n pendingMemoryReview: boolean;\n unsubscribe?: () => void;\n}\n\nexport interface SkillCatalogEntry {\n directoryId: string;\n name: string;\n description: string;\n source: Skill['source'];\n path: string;\n managed: boolean;\n /** User toggle in ~/.xopc/skills.json (`entries[name].enabled`). Default true. */\n enabled: boolean;\n /** When true, skill is never injected into `<available_skills>` (SKILL.md frontmatter). */\n disableModelInvocation: boolean;\n /** Hub install provenance when under ~/.xopc/skills and listed in skills-lock.json. */\n hub?: SkillHubLockEntry;\n}\n\nexport interface AgentManagerConfig {\n workspace: string;\n model?: string;\n config?: Config;\n extensionRegistry?: ExtensionRegistry;\n hookRunner?: import('../extensions/index.js').ExtensionHookRunner;\n bus: MessageBus;\n getCurrentContext: () => SessionContext | null;\n /** Session persistence (enables `session_search` when set). */\n getSessionStore?: () => SessionStore;\n /** Clears per-session profile default on teardown. */\n getModelManager?: () => ModelManager;\n // Thinking configuration\n thinkingLevel?: ThinkingLevel;\n reasoningLevel?: 'off' | 'on' | 'stream';\n verboseLevel?: 'off' | 'on' | 'full';\n gatewayClarify?: { requestClarification: GatewayClarifyRequestFn };\n /** Gateway: exposes CronService for the `cronjob` tool. */\n getCronService?: () => CronService | undefined;\n}\n\nexport interface AgentInstance {\n agent: Agent;\n sessionKey: string;\n createdAt: number;\n lastUsedAt: number;\n /** Curated agent-home `memories/` snapshot frozen at agent creation (prefix cache). */\n curatedMemorySnapshot: MemorySnapshot;\n effectiveProfile: EffectiveAgentProfile;\n resolvedWorkspacePath: string;\n /** Tool names registered on this agent (for skill indexing / tool gating). */\n registeredToolNames: string[];\n /** Declared env var names from skill_view; shell reads values from process.env at spawn time. */\n skillEnvPassthroughKeys: Set<string>;\n backgroundNudge: BackgroundNudgeState;\n}\n\ninterface WorkspaceRuntime {\n skillManager: SkillManager;\n systemPromptBuilder: SystemPromptBuilder;\n builtinMemoryStore: BuiltinMemoryStore;\n memoryManager: MemoryManager;\n}\n\nexport class AgentManager {\n private agents = new Map<string, AgentInstance>();\n private config: AgentManagerConfig;\n private toolsFactory: AgentToolsFactory;\n\n private mergedConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n /** Default agent workspace (effective profile for `getDefaultAgentId`). */\n private baseWorkspacePath: string;\n /** Per-session absolute markdown workspace when `SessionAgentConfig.workingDirectoryOverride` is set. */\n private sessionWorkspaceOverrides = new Map<string, string>();\n private defaultModel: string;\n private credentialCache = new Map<string, string>();\n private credentialResolver: CredentialResolver;\n private workspaceRuntimes = new Map<string, WorkspaceRuntime>();\n /** Per-session user-message index for prefetch injection cadence. */\n private memoryPrefetchUserTurn = new Map<string, number>();\n\n constructor(config: AgentManagerConfig) {\n this.config = config;\n this.baseWorkspacePath = this.computeBaseWorkspacePath();\n const baseRt = this.getWorkspaceRuntime(this.baseWorkspacePath);\n\n this.toolsFactory = new AgentToolsFactory({\n workspace: this.baseWorkspacePath,\n extensionRegistry: config.extensionRegistry,\n getCurrentContext: config.getCurrentContext,\n hookRunner: config.hookRunner,\n bus: config.bus,\n getConfig: () => this.mergedConfig(),\n getPrimaryModel: () => this.resolveModelStringToModel(this.pickDefaultModelRef()),\n getBuiltinMemoryStore: () => baseRt.builtinMemoryStore,\n getMemoryManager: () => baseRt.memoryManager,\n getSessionStore: config.getSessionStore,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n getSkillIndexingContext: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return undefined;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return undefined;\n return {\n registeredToolNames: inst.registeredToolNames,\n skillAllowlist: inst.effectiveProfile.skillsAllowlist,\n };\n },\n onSkillsFilesystemMutate: () => {\n this.refreshSkillsAfterDiskChange();\n },\n getSkillPassthroughEnvVarNames: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return [];\n return [...(this.agents.get(ctx.sessionKey)?.skillEnvPassthroughKeys ?? [])];\n },\n registerSkillEnvPassthrough: (names: string[]) => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return;\n for (const n of names) {\n if (isValidSkillEnvVarName(n)) {\n inst.skillEnvPassthroughKeys.add(n.trim());\n }\n }\n },\n });\n\n this.defaultModel = config.model || getDefaultModelSync(config.config);\n\n this.credentialResolver = new CredentialResolver();\n this.warmCredentialCache().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Credential cache pre-warm failed: ${em}`);\n });\n }\n\n private computeBaseWorkspacePath(): string {\n const cfg = this.config.config;\n if (!cfg) {\n return expandWorkspacePathString(this.config.workspace);\n }\n return resolveEffectiveAgentProfileForSession(cfg, null).resolvedWorkspacePath;\n }\n\n /**\n * Workspace root for inbound attachments / side effects for this session's agent id.\n * Uses in-memory session workspace overrides when the session has a persisted `workingDirectoryOverride`.\n */\n getResolvedWorkspaceForSession(sessionKey: string): string {\n const cfg = this.config.config!;\n const fromMap = this.sessionWorkspaceOverrides.get(sessionKey);\n if (fromMap !== undefined) {\n return fromMap;\n }\n return resolveEffectiveAgentProfileForSession(cfg, sessionKey).resolvedWorkspacePath;\n }\n\n /**\n * Sync in-memory workspace override from session config (after load or PATCH).\n * Pass `null` to clear when the session has no `workingDirectoryOverride` on disk.\n */\n setSessionWorkspaceOverride(sessionKey: string, absolutePath: string | null): void {\n if (absolutePath === null || absolutePath === '') {\n this.sessionWorkspaceOverrides.delete(sessionKey);\n } else {\n this.sessionWorkspaceOverrides.set(sessionKey, absolutePath);\n }\n }\n\n /** Merged `thinkingDefault` for this session's agent id (defaults + `agents.list`). */\n getThinkingDefaultForSession(\n sessionKey: string,\n ): import('./transcript/thinking-types.js').ThinkLevel | undefined {\n const cfg = this.mergedConfig();\n if (!cfg) {\n return undefined;\n }\n return resolveEffectiveAgentProfileForSession(cfg, sessionKey).thinkingDefault;\n }\n\n private getWorkspaceRuntime(resolvedPath: string): WorkspaceRuntime {\n const existing = this.workspaceRuntimes.get(resolvedPath);\n if (existing) {\n return existing;\n }\n\n const builtinMemoryStore = new BuiltinMemoryStore(\n resolveBuiltinMemoryStoreConfig(resolvedPath, this.config.config),\n );\n const memoryManager = createMemoryManagerFromConfig(\n resolvedPath,\n builtinMemoryStore,\n this.config.config,\n );\n const skillManager = new SkillManager(resolvedPath, resolveBundledSkillsDir());\n const systemPromptBuilder = new SystemPromptBuilder({\n workspace: resolvedPath,\n config: this.config.config!,\n skillManager,\n });\n\n const rt: WorkspaceRuntime = {\n skillManager,\n systemPromptBuilder,\n builtinMemoryStore,\n memoryManager,\n };\n this.workspaceRuntimes.set(resolvedPath, rt);\n return rt;\n }\n\n private pickDefaultModelRef(): string {\n const cfg = this.mergedConfig();\n const ref = getAgentDefaultModelRef(cfg);\n return ref?.trim() || getDefaultModelSync(cfg);\n }\n\n private resolveModelStringToModel(modelRef: string): Model<Api> {\n try {\n return resolveModel(modelRef);\n } catch {\n const fallback = getDefaultModelSync(this.mergedConfig());\n log.warn({ modelRef, fallback }, 'Model not found, using default');\n return resolveModel(fallback);\n }\n }\n\n /**\n * Keep defaults in sync when config is hot-reloaded or saved from the UI.\n */\n updateAgentDefaults(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.defaultModel = ref || getDefaultModelSync(config);\n this.baseWorkspacePath = this.computeBaseWorkspacePath();\n void this.toolsFactory.shutdownBrowser();\n for (const rt of this.workspaceRuntimes.values()) {\n void rt.memoryManager.shutdownAll().catch(() => {});\n }\n this.workspaceRuntimes.clear();\n this.toolsFactory = new AgentToolsFactory({\n workspace: this.baseWorkspacePath,\n extensionRegistry: this.config.extensionRegistry,\n getCurrentContext: this.config.getCurrentContext,\n bus: this.config.bus,\n getConfig: () => this.mergedConfig(),\n getPrimaryModel: () => this.resolveModelStringToModel(this.pickDefaultModelRef()),\n getBuiltinMemoryStore: () => this.getWorkspaceRuntime(this.baseWorkspacePath).builtinMemoryStore,\n getMemoryManager: () => this.getWorkspaceRuntime(this.baseWorkspacePath).memoryManager,\n getSessionStore: this.config.getSessionStore,\n gatewayClarify: this.config.gatewayClarify,\n getCronService: this.config.getCronService,\n getSkillIndexingContext: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return undefined;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return undefined;\n return {\n registeredToolNames: inst.registeredToolNames,\n skillAllowlist: inst.effectiveProfile.skillsAllowlist,\n };\n },\n onSkillsFilesystemMutate: () => {\n this.refreshSkillsAfterDiskChange();\n },\n getSkillPassthroughEnvVarNames: () => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return [];\n return [...(this.agents.get(ctx.sessionKey)?.skillEnvPassthroughKeys ?? [])];\n },\n registerSkillEnvPassthrough: (names: string[]) => {\n const ctx = this.config.getCurrentContext?.();\n if (!ctx?.sessionKey) return;\n const inst = this.agents.get(ctx.sessionKey);\n if (!inst) return;\n for (const n of names) {\n if (isValidSkillEnvVarName(n)) {\n inst.skillEnvPassthroughKeys.add(n.trim());\n }\n }\n },\n });\n }\n\n getMemoryManager(): MemoryManager {\n return this.getWorkspaceRuntime(this.baseWorkspacePath).memoryManager;\n }\n\n private getMemoryManagerForSession(sessionKey: string): MemoryManager {\n const path = this.getResolvedWorkspaceForSession(sessionKey);\n return this.getWorkspaceRuntime(path).memoryManager;\n }\n\n /**\n * Prefix the user turn with fenced prefetched memory (external providers).\n */\n async applyMemoryPrefetchToUserMessage(\n userMessage: AgentMessage,\n sessionKey: string,\n ): Promise<AgentMessage> {\n if (!isMemorySubsystemEnabled(this.config.config)) {\n return userMessage;\n }\n const plain = extractAgentUserPlainText(userMessage);\n const turn = (this.memoryPrefetchUserTurn.get(sessionKey) ?? 0) + 1;\n this.memoryPrefetchUserTurn.set(sessionKey, turn);\n if (!shouldInjectMemoryPrefetchThisTurn(this.config.config, turn)) {\n return userMessage;\n }\n return injectPrefetchIntoUserMessage(\n this.getMemoryManagerForSession(sessionKey),\n sessionKey,\n userMessage,\n plain,\n );\n }\n\n /**\n * After a completed turn: sync external providers and queue next-turn prefetch.\n */\n afterAgentTurn(sessionKey: string, userPlainText: string): void {\n if (!isMemorySubsystemEnabled(this.config.config)) {\n return;\n }\n const assistant = this.getLastAssistantContent(sessionKey) ?? '';\n const mm = this.getMemoryManagerForSession(sessionKey);\n mm.syncAll(userPlainText, assistant, { sessionId: sessionKey });\n mm.queuePrefetchAll(userPlainText, { sessionId: sessionKey });\n }\n\n /**\n * Call once per user turn before the main `agent.prompt` (via {@link runAgentTurnWithModelFallbacks} `beforeUserPrompt`).\n */\n beginBackgroundReviewUserTurn(sessionKey: string): void {\n const inst = this.agents.get(sessionKey);\n if (!inst?.backgroundNudge) return;\n const cfg = resolveBackgroundReviewSettings(this.config.config);\n if (!cfg.enabled || cfg.memoryNudgeInterval <= 0) return;\n if (!inst.registeredToolNames.includes('curated_memory')) return;\n inst.backgroundNudge.turnsSinceMemory += 1;\n if (inst.backgroundNudge.turnsSinceMemory >= cfg.memoryNudgeInterval) {\n inst.backgroundNudge.pendingMemoryReview = true;\n inst.backgroundNudge.turnsSinceMemory = 0;\n }\n }\n\n /**\n * After a successful main turn (after memory sync via `afterAgentTurn`), may run a quiet follow-up for memory/skills.\n */\n scheduleBackgroundReviewAfterUserTurn(sessionKey: string): void {\n void this.runBackgroundReviewIfNeeded(sessionKey).catch((err) => {\n log.warn({ err, sessionKey }, 'Background review failed');\n });\n }\n\n private async runBackgroundReviewIfNeeded(sessionKey: string): Promise<void> {\n const inst = this.agents.get(sessionKey);\n if (!inst?.backgroundNudge) return;\n const settings = resolveBackgroundReviewSettings(this.config.config);\n if (!settings.enabled) return;\n if (isAssistantTurnAborted(inst.agent) || isAssistantTurnFailed(inst.agent)) return;\n const last = this.getLastAssistantContent(sessionKey);\n if (!last?.trim()) return;\n\n const reviewMemory = inst.backgroundNudge.pendingMemoryReview;\n inst.backgroundNudge.pendingMemoryReview = false;\n\n let reviewSkills = false;\n if (settings.skillNudgeInterval > 0 && inst.registeredToolNames.includes('skill_manage')) {\n if (inst.backgroundNudge.itersSinceSkill >= settings.skillNudgeInterval) {\n reviewSkills = true;\n inst.backgroundNudge.itersSinceSkill = 0;\n }\n }\n\n if (!reviewMemory && !reviewSkills) return;\n\n const rt = this.getWorkspaceRuntime(inst.resolvedWorkspacePath);\n await runBackgroundReviewTurn({\n sessionKey,\n mainAgent: inst.agent,\n settings,\n reviewMemory,\n reviewSkills,\n registeredToolNames: inst.registeredToolNames,\n skillAllowlist: inst.effectiveProfile.skillsAllowlist,\n workspacePath: inst.resolvedWorkspacePath,\n skillManager: rt.skillManager,\n builtinMemoryStore: rt.builtinMemoryStore,\n memoryManager: rt.memoryManager,\n getConfig: () => this.mergedConfig(),\n onSkillsFilesystemMutate: () => this.refreshSkillsAfterDiskChange(),\n });\n }\n\n private attachBackgroundNudgeListeners(sessionKey: string): void {\n const inst = this.agents.get(sessionKey);\n if (!inst?.backgroundNudge) return;\n inst.backgroundNudge.unsubscribe?.();\n const unsub = inst.agent.subscribe((ev: AgentEvent) => {\n const cfg = resolveBackgroundReviewSettings(this.config.config);\n if (!cfg.enabled || cfg.skillNudgeInterval <= 0) return;\n if (!inst.registeredToolNames.includes('skill_manage')) return;\n if (ev.type === 'turn_start') {\n inst.backgroundNudge.itersSinceSkill += 1;\n }\n if (ev.type === 'tool_execution_end') {\n const te = ev as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n if (\n !te.isError &&\n typeof te.toolName === 'string' &&\n te.toolName.trim() === 'skill_manage'\n ) {\n inst.backgroundNudge.itersSinceSkill = 0;\n }\n }\n });\n inst.backgroundNudge.unsubscribe = unsub;\n }\n\n /**\n * Expand `/skill:name` user text into the full skill block for the current turn (WebChat, channels).\n */\n expandSkillUserText(text: string): string {\n const ctx = this.config.getCurrentContext?.();\n const path = ctx?.sessionKey\n ? this.getResolvedWorkspaceForSession(ctx.sessionKey)\n : this.baseWorkspacePath;\n return this.getWorkspaceRuntime(path).skillManager.expandCommand(text);\n }\n\n /**\n * Structured SKILL.md preview for the gateway console.\n * When `lang` is provided (e.g. \"zh\"), tries SKILL-{lang}.md first; falls back to SKILL.md.\n */\n getSkillMarkdownSource(skillName: string, lang?: string): SkillMarkdownPreviewPayload | null {\n const skill = this.getWorkspaceRuntime(this.baseWorkspacePath).skillManager.findSkill(skillName);\n if (!skill) return null;\n\n // Try localized file for display\n if (lang) {\n const localized = resolveLocalizedSkillMarkdown(skill, lang);\n if (localized) return localized;\n }\n\n return {\n name: skill.name,\n description: skill.description,\n bodyMarkdown: skill.content,\n disableModelInvocation: skill.disableModelInvocation,\n metadata: skill.metadata,\n toolConditions: skill.toolConditions,\n requiredEnvVarNames: skill.requiredEnvVarNames,\n };\n }\n\n private loadProfileMarkdownForProfile(profile: EffectiveAgentProfile): ProfileMarkdownFile[] {\n const cfg = this.config.config!;\n const profileDir = resolveAgentProfileDir(cfg, profile.agentId);\n return loadProfileMarkdownFiles(profileDir);\n }\n\n getSkillCatalog(lang?: string): SkillCatalogEntry[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n const lock = loadSkillsLock();\n return this.getWorkspaceRuntime(this.baseWorkspacePath).skillManager.getSkills().map((s) => {\n const base = resolve(s.baseDir);\n const managed = isUnderManagedSkillsDir(s.baseDir);\n const directoryId = base.split(sep).filter(Boolean).pop() || s.name;\n const enabled = !(skillsConfig.entries?.[s.name]?.enabled === false);\n const hubKey = managed ? basename(base) : '';\n const hub = managed && hubKey ? lock.entries[hubKey] : undefined;\n\n // Attempt localized name/description for display\n const localized = lang ? resolveLocalizedSkillMeta(s, lang) : null;\n\n return {\n directoryId,\n name: localized?.name ?? s.name,\n description: localized?.description ?? s.description,\n category: s.category,\n source: s.source,\n path: s.baseDir,\n managed,\n enabled,\n disableModelInvocation: s.disableModelInvocation,\n ...(hub ? { hub } : {}),\n };\n });\n }\n\n /**\n * After ~/.xopc/skills.json changes (enable/disable), refresh `<available_skills>` on active agents.\n */\n refreshSkillsAfterSkillConfigChange(): void {\n const cfg = this.config.config!;\n const touched = new Set<string>();\n for (const instance of this.agents.values()) {\n const rt = this.getWorkspaceRuntime(instance.resolvedWorkspacePath);\n if (!touched.has(instance.resolvedWorkspacePath)) {\n rt.skillManager.refreshPromptFromConfig();\n touched.add(instance.resolvedWorkspacePath);\n }\n const profileMarkdownFiles = this.loadProfileMarkdownForProfile(instance.effectiveProfile);\n const newPrompt = rt.systemPromptBuilder.build(profileMarkdownFiles, {\n curatedMemorySnapshot: instance.curatedMemorySnapshot,\n externalMemoryInstructions: rt.memoryManager.buildExternalSystemPrompt(),\n workspaceOverride: instance.resolvedWorkspacePath,\n profileMarkdownPathRoot: resolveAgentProfileDir(cfg, instance.effectiveProfile.agentId),\n systemPromptOverride: instance.effectiveProfile.systemPromptOverride,\n skillAllowlist: instance.effectiveProfile.skillsAllowlist,\n registeredToolNames: instance.registeredToolNames,\n });\n instance.agent.state.systemPrompt = newPrompt;\n }\n log.info({ agents: this.agents.size }, 'Skill toggles applied; system prompt updated');\n }\n\n /**\n * Reload skills from disk and refresh system prompt on all active Agent instances.\n */\n refreshSkillsAfterDiskChange(): void {\n const cfg = this.config.config!;\n // Reload every workspace SkillManager first. When there are no active agent sessions\n // (e.g. gateway UI only), the loop below runs zero times — without this, `getSkillCatalog()`\n // and delete flows still see stale in-memory skills after ~/.xopc/skills changes.\n for (const rt of this.workspaceRuntimes.values()) {\n rt.skillManager.reload();\n }\n\n const touched = new Set<string>();\n for (const instance of this.agents.values()) {\n const rt = this.getWorkspaceRuntime(instance.resolvedWorkspacePath);\n if (!touched.has(instance.resolvedWorkspacePath)) {\n touched.add(instance.resolvedWorkspacePath);\n }\n const profileMarkdownFiles = this.loadProfileMarkdownForProfile(instance.effectiveProfile);\n const newPrompt = rt.systemPromptBuilder.rebuild(profileMarkdownFiles, {\n curatedMemorySnapshot: instance.curatedMemorySnapshot,\n externalMemoryInstructions: rt.memoryManager.buildExternalSystemPrompt(),\n workspaceOverride: instance.resolvedWorkspacePath,\n profileMarkdownPathRoot: resolveAgentProfileDir(cfg, instance.effectiveProfile.agentId),\n systemPromptOverride: instance.effectiveProfile.systemPromptOverride,\n skillAllowlist: instance.effectiveProfile.skillsAllowlist,\n registeredToolNames: instance.registeredToolNames,\n });\n instance.agent.state.systemPrompt = newPrompt;\n }\n log.info({ agents: this.agents.size }, 'Skills refreshed; system prompt updated');\n }\n\n /**\n * Get or create an Agent instance for a session\n */\n getOrCreateAgent(sessionKey: string): Agent {\n const cfg = this.config.config!;\n const targetPath = this.getResolvedWorkspaceForSession(sessionKey);\n const existing = this.agents.get(sessionKey);\n if (existing) {\n if (existing.resolvedWorkspacePath !== targetPath) {\n this.removeAgent(sessionKey);\n } else {\n existing.lastUsedAt = Date.now();\n if (!existing.backgroundNudge) {\n existing.backgroundNudge = {\n turnsSinceMemory: 0,\n itersSinceSkill: 0,\n pendingMemoryReview: false,\n };\n this.attachBackgroundNudgeListeners(sessionKey);\n }\n log.debug({ sessionKey }, 'Reusing existing agent instance');\n return existing.agent;\n }\n }\n\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const resolvedPath = targetPath;\n const rt = this.getWorkspaceRuntime(resolvedPath);\n\n if (isMemorySubsystemEnabled(cfg)) {\n void rt.memoryManager\n .initializeAll(sessionKey, { workspace: resolvedPath })\n .catch((err) => log.warn({ err, sessionKey }, 'memory initializeAll failed'));\n }\n\n const curatedOn = isCuratedMemoryInPrompt(cfg);\n if (curatedOn) {\n rt.builtinMemoryStore.loadFromDiskSync();\n }\n const snap = curatedOn ? rt.builtinMemoryStore.getSnapshot() : { memory: '', user: '' };\n const curatedMemorySnapshot: MemorySnapshot = { memory: snap.memory, user: snap.user };\n\n const { agent, registeredToolNames } = this.createAgentForProfile(\n sessionKey,\n profile,\n resolvedPath,\n rt,\n curatedMemorySnapshot,\n );\n\n this.agents.set(sessionKey, {\n agent,\n sessionKey,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n curatedMemorySnapshot,\n effectiveProfile: profile,\n resolvedWorkspacePath: resolvedPath,\n registeredToolNames,\n skillEnvPassthroughKeys: new Set<string>(),\n backgroundNudge: {\n turnsSinceMemory: 0,\n itersSinceSkill: 0,\n pendingMemoryReview: false,\n },\n });\n\n this.attachBackgroundNudgeListeners(sessionKey);\n\n const modelRef = profile.primaryModelRef?.trim() || this.defaultModel;\n this.config.getModelManager?.().setSessionProfileDefault(sessionKey, modelRef);\n\n log.debug({ sessionKey, totalAgents: this.agents.size, agentId: profile.agentId }, 'Created new agent instance');\n return agent;\n }\n\n /**\n * Get existing agent for a session (if any)\n */\n getAgent(sessionKey: string): Agent | undefined {\n return this.agents.get(sessionKey)?.agent;\n }\n\n /**\n * Check if an agent exists for a session\n */\n hasAgent(sessionKey: string): boolean {\n return this.agents.has(sessionKey);\n }\n\n /**\n * Remove an agent instance\n */\n removeAgent(sessionKey: string): boolean {\n const instance = this.agents.get(sessionKey);\n if (instance) {\n instance.backgroundNudge?.unsubscribe?.();\n void this.toolsFactory.closeBrowserPageForSession(sessionKey);\n instance.agent.abort();\n this.agents.delete(sessionKey);\n this.memoryPrefetchUserTurn.delete(sessionKey);\n this.config.getModelManager?.().clearSessionProfileDefault(sessionKey);\n log.info({ sessionKey, totalAgents: this.agents.size }, 'Removed agent instance');\n return true;\n }\n return false;\n }\n\n /**\n * Get all active session keys\n */\n getActiveSessions(): string[] {\n return Array.from(this.agents.keys());\n }\n\n /**\n * Get agent count\n */\n getAgentCount(): number {\n return this.agents.size;\n }\n\n /**\n * Set thinking level for a session's agent\n */\n setThinkingLevel(sessionKey: string, level: ThinkingLevel): void {\n const instance = this.agents.get(sessionKey);\n if (instance) {\n instance.agent.state.thinkingLevel = level;\n log.debug({ sessionKey, thinkingLevel: level }, 'Set thinking level for agent');\n }\n }\n\n /**\n * Dispose all agents\n */\n dispose(): void {\n void this.toolsFactory.shutdownBrowser();\n for (const instance of this.agents.values()) {\n instance.backgroundNudge?.unsubscribe?.();\n instance.agent.abort();\n }\n this.agents.clear();\n this.memoryPrefetchUserTurn.clear();\n this.sessionWorkspaceOverrides.clear();\n for (const rt of this.workspaceRuntimes.values()) {\n void rt.memoryManager.shutdownAll().catch(() => {});\n }\n this.workspaceRuntimes.clear();\n log.debug('All agent instances disposed');\n }\n\n async warmCredentialCache(): Promise<void> {\n const profiles = await this.credentialResolver.listProfiles();\n for (const profile of profiles) {\n const secret = profile.key?.trim()\n ? profile.key.trim()\n : profile.envVar\n ? process.env[profile.envVar]?.trim()\n : undefined;\n if (secret) {\n this.credentialCache.set(profile.provider.toLowerCase(), secret);\n }\n }\n log.debug({ count: this.credentialCache.size }, 'Credential cache warmed');\n }\n\n async refreshCredentials(): Promise<void> {\n this.credentialCache.clear();\n await this.warmCredentialCache();\n }\n\n private resolveApiKeyWithCache(provider: string): string | undefined {\n const key = provider.toLowerCase();\n const cached = this.credentialCache.get(key);\n if (cached) return cached;\n\n const fromDisk = resolveProviderApiKeySync(provider);\n if (fromDisk) {\n this.credentialCache.set(key, fromDisk);\n return fromDisk;\n }\n\n const fromRegistryOrEnv = getApiKeySync(provider);\n if (fromRegistryOrEnv) {\n this.credentialCache.set(key, fromRegistryOrEnv);\n return fromRegistryOrEnv;\n }\n return undefined;\n }\n\n private createAgentForProfile(\n _sessionKey: string,\n profile: EffectiveAgentProfile,\n resolvedWorkspacePath: string,\n rt: WorkspaceRuntime,\n curatedMemorySnapshot: MemorySnapshot,\n ): { agent: Agent; registeredToolNames: string[] } {\n const modelRef = profile.primaryModelRef?.trim() || this.defaultModel;\n const model = this.resolveModelStringToModel(modelRef);\n\n const profileMarkdownFiles = this.loadProfileMarkdownForProfile(profile);\n const tools = this.toolsFactory.createAllTools({\n workspace: resolvedWorkspacePath,\n profileMarkdownRoot: resolveAgentProfileDir(this.config.config!, profile.agentId),\n disabledTools: profile.tools.disable,\n getPrimaryModel: () => this.resolveModelStringToModel(modelRef),\n getBuiltinMemoryStore: () => rt.builtinMemoryStore,\n getMemoryManager: () => rt.memoryManager,\n getSkillManager: () => rt.skillManager,\n });\n const registeredToolNames = tools.map((t) => t.name);\n\n const thinkingLevel =\n (profile.thinkingDefault as ThinkingLevel | undefined) ?? this.config.thinkingLevel ?? 'medium';\n\n const agent = new Agent({\n initialState: {\n systemPrompt: rt.systemPromptBuilder.build(profileMarkdownFiles, {\n curatedMemorySnapshot,\n externalMemoryInstructions: rt.memoryManager.buildExternalSystemPrompt(),\n workspaceOverride: resolvedWorkspacePath,\n profileMarkdownPathRoot: resolveAgentProfileDir(this.config.config!, profile.agentId),\n systemPromptOverride: profile.systemPromptOverride,\n skillAllowlist: profile.skillsAllowlist,\n registeredToolNames,\n }),\n model,\n thinkingLevel,\n tools,\n messages: [],\n },\n streamFn: createExtensionAwareStreamFn(),\n getApiKey: (provider: string) => this.resolveApiKeyWithCache(provider),\n });\n return { agent, registeredToolNames };\n }\n\n /**\n * Set model for a specific session\n */\n setModelForSession(sessionKey: string, modelId: string): boolean {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n log.warn(\n { sessionKey, modelId, activeSessionCount: this.agents.size },\n `setModelForSession: no agent instance for session (create session / run turn first); modelId=${modelId}`,\n );\n return false;\n }\n\n try {\n const model = resolveModel(modelId);\n instance.agent.state.model = model;\n log.info({ sessionKey, modelId }, 'Model set for session');\n return true;\n } catch (err) {\n log.error({ err, sessionKey, modelId }, 'Failed to set model for session');\n return false;\n }\n }\n\n /**\n * Get last assistant content from a session's agent\n */\n getLastAssistantContent(sessionKey: string): string | null {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return null;\n }\n\n const messages = instance.agent.state.messages;\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'assistant') {\n const content = msg.content;\n if (Array.isArray(content)) {\n return extractTextContent(content as Array<{ type: string; text?: string }>);\n }\n return String(content);\n }\n }\n return null;\n }\n\n /**\n * Replace messages for a session's agent\n */\n replaceMessages(sessionKey: string, messages: AgentMessage[]): boolean {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return false;\n }\n\n instance.agent.state.messages = messages;\n return true;\n }\n\n /**\n * Get messages for a session's agent\n */\n getMessages(sessionKey: string): AgentMessage[] | null {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return null;\n }\n\n return instance.agent.state.messages;\n }\n\n /**\n * Subscribe to agent events for a session\n */\n subscribeToSession(sessionKey: string, callback: (event: AgentEvent) => void): (() => void) | null {\n const instance = this.agents.get(sessionKey);\n if (!instance) {\n return null;\n }\n\n return instance.agent.subscribe(callback);\n }\n}\n"],"mappings":"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"}