@openchambery/web 2.0.0-beta.23 → 2.0.0-beta.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/assets/{AssistantView-xcvH8rjG.js → AssistantView-CUqIFFyH.js} +3 -3
  2. package/dist/assets/{DiagramView-DNG8vo4Y.js → DiagramView-DhEXKsIH.js} +1 -1
  3. package/dist/assets/{MarkdownRenderer-CEJAAr1D.js → MarkdownRenderer-CyacU8IE.js} +2 -2
  4. package/dist/assets/{MarkdownRendererImpl-Cr2TAqhw.js → MarkdownRendererImpl-DgO-73sa.js} +1 -1
  5. package/dist/assets/{MarkstreamRendererImpl-nZuUzTxY.js → MarkstreamRendererImpl-CZPmsOrD.js} +1 -1
  6. package/dist/assets/{MobileDetailNavigation-DtKMM3yi.js → MobileDetailNavigation-BGKiZT4X.js} +1 -1
  7. package/dist/assets/{MobileShareBridge-B7WXWIu6.js → MobileShareBridge-BFkZ-if6.js} +1 -1
  8. package/dist/assets/{MobileSurface-DpWdHT3N.js → MobileSurface-CiQ9yAML.js} +1 -1
  9. package/dist/assets/{MultiRunWindow-BUKkRUqu.js → MultiRunWindow-BZLr1op3.js} +1 -1
  10. package/dist/assets/{SettingsView-8E-wbaVn.js → SettingsView-Bo1Xj4mZ.js} +1 -1
  11. package/dist/assets/{SettingsWindow-Bqmgbx0M.js → SettingsWindow-BMISHpTN.js} +1 -1
  12. package/dist/assets/{TerminalView-68VbSeR-.js → TerminalView-DKCcoHzi.js} +1 -1
  13. package/dist/assets/{ToolOutputDialog-CVK4WsvG.js → ToolOutputDialog-ByptQgFf.js} +1 -1
  14. package/dist/assets/{appThemeRegistry-9e16JGaU.js → appThemeRegistry-b59_2rLn.js} +1 -1
  15. package/dist/assets/{fileMentionTouchSelection-Dn-wohRQ.js → fileMentionTouchSelection-CMG5yrcJ.js} +1 -1
  16. package/dist/assets/{gitApi-BA9aM1SG.js → gitApi-CYFnq-nn.js} +1 -1
  17. package/dist/assets/{insertionBoundaries-CI9ok09h.js → insertionBoundaries-C5b2f_mO.js} +1 -1
  18. package/dist/assets/{main-CcAGHLHU.js → main-Bz3MB9dk.js} +4 -4
  19. package/dist/assets/{main-DgAK4oel.js → main-DOpUCN5f.js} +2 -2
  20. package/dist/assets/{mcpQueries-2M1PZ3lm.js → mcpQueries-Bxq_6P-n.js} +1 -1
  21. package/dist/assets/{miniChat-Cc9LRMjb.js → miniChat-CBpEIoJn.js} +2 -2
  22. package/dist/assets/{mobile-D85ParE1.js → mobile-CiFeGY2g.js} +2 -2
  23. package/dist/assets/{multirun-DvtrTdrM.js → multirun-DnD1iWqj.js} +1 -1
  24. package/dist/assets/{projectMeta-D_YBAmPw.js → projectMeta-B1ShmM7W.js} +1 -1
  25. package/dist/assets/{renderElectronMiniChatApp-29E_I_Wu.js → renderElectronMiniChatApp-DePrSFtn.js} +2 -2
  26. package/dist/assets/{renderMobileApp-VE8moPSo.js → renderMobileApp-CiGHeia2.js} +4 -4
  27. package/dist/assets/runtimeConfig-CgFmzQWA.js +2 -0
  28. package/dist/assets/{runtimeEndpointReset-B71dh9nk.js → runtimeEndpointReset-Dw46SzSQ.js} +1 -1
  29. package/dist/assets/{sessionChangeSummary-Dk4PbQaU.js → sessionChangeSummary-Bg9UCEjA.js} +1 -1
  30. package/dist/assets/{sessionLookup-BcQ28pj8.js → sessionLookup-4MZgvAtH.js} +1 -1
  31. package/dist/assets/src-OzbnJsph.css +2 -0
  32. package/dist/assets/{useAppFontEffects-BIH0h1v7.js → useAppFontEffects-Ykf_fh2W.js} +6 -6
  33. package/dist/assets/{useDesktopWindowControlsLayout-DvKPPF8d.js → useDesktopWindowControlsLayout-kH_CrZfG.js} +1 -1
  34. package/dist/assets/{useEffectiveDirectory-BXEpJMOS.js → useEffectiveDirectory-CpsCUPDZ.js} +1 -1
  35. package/dist/assets/{useMobileNavigationStore-Cu3jliDp.js → useMobileNavigationStore-CBo6pohX.js} +5 -5
  36. package/dist/assets/{useSessionAutoCleanup-CH8TehHH.js → useSessionAutoCleanup-BuFi27vR.js} +1 -1
  37. package/dist/assets/{useWorkerHighlightedLines-PqKAaqkW.js → useWorkerHighlightedLines-N119bHfO.js} +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/mini-chat.html +3 -3
  40. package/dist/mobile.html +3 -3
  41. package/package.json +1 -1
  42. package/server/lib/relay/DOCUMENTATION.md +1 -0
  43. package/server/lib/relay/service.js +1 -0
  44. package/server/lib/relay/service.test.js +13 -0
  45. package/server/lib/session-title/DOCUMENTATION.md +16 -6
  46. package/server/lib/session-title/native-runtime.test.js +17 -5
  47. package/server/lib/session-title/runtime.js +45 -9
  48. package/dist/assets/runtimeConfig-CZrkgYcD.js +0 -2
  49. package/dist/assets/src-qYyYBKwA.css +0 -2
@@ -1,4 +1,4 @@
1
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{n as t}from"./runtime-fetch-CiqQd__Y.js";import{En as n,in as r}from"./persistence-CMWO3jFo.js";import{nt as i,rt as a}from"./vendor-base-ui-CnHfUmDN.js";import{n as o}from"./platform-B0p43squ.js";import{v as s}from"./useUIStore-Brsa2mKs.js";import{n as c}from"./vendor-react-DLPElpoQ.js";import{a as l,n as u}from"./vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js";import{f as d,r as f,t as p}from"./button-e8fYWIMM.js";import{n as m}from"./Icon-D7NdMmbi.js";import{i as h,n as g,t as _}from"./tooltip-C64SdRvR.js";import{n as v}from"./useThemeSystem-DMzWVJaW.js";import{n as y}from"./markdownTheme-DM5h1i18.js";import{n as b}from"./MarkdownRenderer-CEJAAr1D.js";import{n as x,o as S,s as C,t as w}from"./markdown-worker-bS5OgGO3.js";import{a as T,i as E,o as D,t as ee}from"./imageSource-BpUsiwFd.js";import{t as te}from"./vendor-tanstack-react-virtual-tlDgH6PG.js";var O=e(a(),1),k=c(),A=i(),ne=`w-[min(30rem,calc(100cqw-1rem))] min-w-[280px] max-w-full overflow-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]`,j={maxWidth:`min(28rem, calc(100cqw - 4ch))`,backgroundColor:`var(--surface-elevated)`,color:`var(--surface-elevated-foreground)`},M=`flex items-center justify-between gap-3 border-b border-[var(--surface-subtle)] px-3 py-2.5`,N=`text-xs font-medium leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,re=`text-xs tabular-nums leading-4 text-muted-foreground md:text-[0.8125rem] md:leading-5`,P=`m-0 list-none divide-y divide-[var(--surface-subtle)] p-0`,F=`${P} max-h-[min(22rem,50vh)] overflow-y-auto`,ie=`${P} w-full`,ae={className:`p-0 rounded-none`,maxHeightClass:`max-h-[46vh]`,disableHorizontal:!0},I=`px-2.5 py-1.5 text-xs leading-4 md:py-2 md:text-[0.8125rem] md:leading-5`,oe=`flex min-w-0 items-center gap-1 text-xs leading-4 text-muted-foreground md:gap-1.5 md:text-[0.8125rem] md:leading-5`,se=`min-w-0 truncate text-xs leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,ce=`inline-flex shrink-0 items-center gap-1 text-xs leading-4 tabular-nums text-muted-foreground md:text-[0.8125rem] md:leading-5`,le=`h-3 w-3 shrink-0 md:h-3.5 md:w-3.5`,ue={in_progress:`text-foreground`,pending:`text-foreground`,completed:`text-muted-foreground line-through`,cancelled:`text-muted-foreground line-through`},L={in_progress:`chat.statusRow.todo.status.inProgress`,pending:`chat.statusRow.todo.status.pending`,completed:`chat.statusRow.todo.status.completed`,cancelled:`chat.statusRow.todo.status.cancelled`},R=e=>{let t=(0,k.c)(26),{todo:n,isActive:r,activeRef:i}=e,{t:a}=d(),o=z.has(n.status)?n.status:`pending`,s=L[o],c;t[0]!==a||t[1]!==s?(c=a(s),t[0]=a,t[1]=s,t[2]=c):c=t[2];let l=c,u;t[3]===o?u=t[4]:(u=o===`in_progress`?(0,A.jsx)(m,{name:`record-circle`,className:`h-3.5 w-3.5 text-[var(--status-info)]`,"aria-hidden":`true`}):o===`completed`?(0,A.jsx)(m,{name:`checkbox-circle`,className:`h-3.5 w-3.5 text-[var(--status-success)]`,"aria-hidden":`true`}):(0,A.jsx)(m,{name:`time`,className:`h-3.5 w-3.5 text-muted-foreground`,"aria-hidden":`true`}),t[3]=o,t[4]=u);let p=u,v=r?`step`:void 0,y=r&&`bg-[var(--surface-muted)] before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-[var(--status-info)]`,b;t[5]===y?b=t[6]:(b=f(`group relative grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] items-start gap-2.5`,I,y),t[5]=y,t[6]=b);let x;t[7]!==p||t[8]!==l?(x=(0,A.jsx)(h,{asChild:!0,children:(0,A.jsx)(`span`,{className:`flex size-4 items-center justify-center`,"aria-label":l,children:p})}),t[7]=p,t[8]=l,t[9]=x):x=t[9];let S;t[10]===l?S=t[11]:(S=(0,A.jsx)(g,{side:`left`,sideOffset:6,children:l}),t[10]=l,t[11]=S);let C;t[12]!==x||t[13]!==S?(C=(0,A.jsxs)(_,{children:[x,S]}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w=ue[o],T;t[15]===w?T=t[16]:(T=f(`block whitespace-normal break-words`,w),t[15]=w,t[16]=T);let E;t[17]!==T||t[18]!==n.content?(E=(0,A.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,A.jsx)(`span`,{className:T,children:n.content})}),t[17]=T,t[18]=n.content,t[19]=E):E=t[19];let D;return t[20]!==i||t[21]!==E||t[22]!==v||t[23]!==b||t[24]!==C?(D=(0,A.jsxs)(`li`,{ref:i,"aria-current":v,className:b,children:[C,E]}),t[20]=i,t[21]=E,t[22]=v,t[23]=b,t[24]=C,t[25]=D):D=t[25],D},z=new Set([`in_progress`,`pending`,`completed`,`cancelled`]),B=`image/png`,V=l(`OpenChamberMedia`),H=e=>{let t=e.toLowerCase().split(`;`)[0]?.trim()||B;if(t===`image/jpeg`||t===`image/jpg`)return`jpg`;if(t===`image/webp`)return`webp`;if(t===`image/gif`)return`gif`;if(t===`image/svg+xml`)return`svg`;if(t===`image/png`)return`png`;let n=t.split(`/`)[1];return n&&/^[a-z0-9.+-]+$/i.test(n)?n:`png`},U=(e,t)=>{let n=`image.${H(t)}`,r=(((e||``).trim()||n).split(/[\\/]/).pop()||n).replace(/[^\w.\- ()[\]]+/g,`_`).replace(/^\.+/,``);return r?/\.[a-z0-9]{1,8}$/i.test(r)?r:`${r}.${H(t)}`:n},W=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{if(typeof r.result==`string`){t(r.result);return}n(Error(`Failed to encode image`))},r.onerror=()=>n(r.error??Error(`Failed to encode image`)),r.readAsDataURL(e)}),de=async e=>{let t=await W(e),n=t.indexOf(`,`);return n>=0?t.slice(n+1):t},fe=(e,t)=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)},G=async(e,t)=>{let n=await fetch(e,{signal:t,cache:`no-store`});if(!n.ok)throw Error(`Image fetch failed (${n.status})`);return n.blob()},pe=async(e,t=B)=>{if(!e.naturalWidth||!e.naturalHeight)return null;let n=e.currentSrc||e.src;if(n&&(/^(?:blob:|data:|openchamber-asset:)/i.test(n)||/^https?:/i.test(n)||/^\/api(?=\/|[?#]|$)/.test(n)))try{let e=await G(n);if(e.size>0)return e}catch{}try{let n=document.createElement(`canvas`);n.width=e.naturalWidth,n.height=e.naturalHeight;let r=n.getContext(`2d`);if(!r)return null;r.drawImage(e,0,0);let i=t.startsWith(`image/`)?t:B,a=await new Promise(e=>{n.toBlob(t=>e(t),i===`image/jpg`?`image/jpeg`:i)});return a&&a.size>0?a:null}catch{return null}},me=async(e,t)=>{let n=``;try{return n=await ee(e,t??new AbortController().signal),await G(n,t)}finally{n&&T(n)}},he=async(e,n)=>{let r=e.effectiveDirectory??``,i=null;e.prefetchedBlob&&e.prefetchedBlob.size>0&&(i=e.prefetchedBlob);let a=e.displayUrl?.trim();if(!i&&a)try{i=await G(a,n)}catch{i=null}if(!i){let a=e.sourceUrl.trim();if(!a)throw Error(`Image source is empty`);if(/^(?:data|blob):/i.test(a)||/^https?:/i.test(a)||/^\/api(?=\/|[?#]|$)/.test(a))i=await G(a,n);else{let e=D(a,r);if(E(e))try{i=await me(e.path,n)}catch{let r=await t(`/api/fs/raw`,{method:`GET`,cache:`no-store`,query:{path:e.path},signal:n});if(!r.ok)throw Error(`Image source request failed with status ${r.status}`);i=await r.blob()}else i=await G(e.source,n)}}if(!i||i.size<=0)throw Error(`Image is empty`);let o=(e.mimeType||(i.type&&i.type.startsWith(`image/`)?i.type:``)||B).split(`;`)[0].trim()||B;return{blob:i,filename:U(e.filename,o),mimeType:o}},ge=()=>!o()||u===void 0?!1:u.isPluginAvailable(`OpenChamberMedia`),_e=async(e,t)=>{let{blob:i,filename:a,mimeType:o}=await he(e,t);if(ge()){let e=await de(i);return await V.saveImage({dataBase64:e,mimeType:o,filename:a}),`saved`}if(r()){let e=n()?.vscode;if(e?.saveImage){let t=await W(i);return await e.saveImage({fileName:a,dataUrl:t}),`saved`}}return fe(i,a),`downloaded`},ve=e=>e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`),ye=e=>e?Object.entries(e).map(([e,t])=>t==null?``:`${e.startsWith(`--`)?e:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}:${typeof t==`number`?`${t}px`:t};`).join(``):``,K=(e,t,n)=>{let r=e.querySelector(`pre`);r&&(r.style.margin=`0`,r.style.background=`transparent`,r.style.whiteSpace=t?`pre-wrap`:`pre`,t&&(r.style.wordBreak=`break-word`,r.style.overflowWrap=`break-word`));let i=e.querySelector(`code`);if(i){let e=ye(n);e&&i.setAttribute(`style`,`${i.getAttribute(`style`)??``}${e}`)}},be=e=>`<pre><code>${ve(e)}</code></pre>`,xe=e=>{let t=(0,k.c)(21),{code:n,language:r,className:i,style:a,codeStyle:o,wrap:s}=e,c=s!==void 0&&s,{currentTheme:l}=v(),u=O.useRef(null),d;t[0]===l?d=t[1]:(d=y(l),t[0]=l,t[1]=d);let p=d,m,h;t[2]!==n||t[3]!==o||t[4]!==c?(m=()=>{let e=u.current;e&&(e.innerHTML=be(n),K(e,c,o))},h=[n,c,o],t[2]=n,t[3]=o,t[4]=c,t[5]=m,t[6]=h):(m=t[5],h=t[6]),O.useLayoutEffect(m,h);let g,_;t[7]!==n||t[8]!==o||t[9]!==r||t[10]!==c?(g=()=>{let e=u.current;if(!e)return;let t=!0,i=new AbortController;return w(n,(r||`text`).toLowerCase(),{signal:i.signal,priority:`visible`}).then(n=>{!t||!e||!n||(e.innerHTML=n,K(e,c,o))}),()=>{t=!1,i.abort()}},_=[n,r,c,o],t[7]=n,t[8]=o,t[9]=r,t[10]=c,t[11]=g,t[12]=_):(g=t[11],_=t[12]),O.useEffect(g,_);let b;t[13]===i?b=t[14]:(b=f(`typography-code`,i),t[13]=i,t[14]=b);let x=p,S;t[15]!==a||t[16]!==x?(S={...x,...a},t[15]=a,t[16]=x,t[17]=S):S=t[17];let C;return t[18]!==b||t[19]!==S?(C=(0,A.jsx)(`div`,{ref:u,className:b,style:S}),t[18]=b,t[19]=S,t[20]=C):C=t[20],C},Se=e=>{let t=e.replace(/^<file>\s*\n?/,``).replace(/\n?<\/file>\s*$/,``);return t=t.replace(/^\s*\d{5}\|\s?/gm,``),t.trim()},Ce=(e,t=``)=>{if(typeof e==`string`)return e;if(e==null)return t;if(typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`)return String(e);try{return JSON.stringify(e)}catch{return t}},we=e=>e?e.includes(`<diagnostics`)||e.includes(`<file_diagnostics>`)||e.includes(`LSP errors detected`)||e.includes(`This file has errors`):!1,Te=e=>e?e.replace(/\n{0,2}LSP errors detected[\s\S]*?<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/\n{0,2}This file has errors[\s\S]*?<\/file_diagnostics>/g,``).replace(/<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/<file_diagnostics>[\s\S]*?<\/file_diagnostics>/g,``).trim():``,Ee=(e,t)=>!e||typeof e!=`object`?String(e):C(e,t||``),q=e=>{if(typeof e==`string`){let t=e.trim();return t.length>0?t:void 0}if(e&&typeof e==`object`){let t=e.patch;if(typeof t==`string`){let e=t.trim();return e.length>0?e:void 0}}},De=e=>{if(!e||typeof e!=`object`)return;let t=q(e.patch)??q(e.diff);if(t)return t;let n=Array.isArray(e.files)?e.files:[];for(let e of n){if(!e||typeof e!=`object`)continue;let t=q(e.patch)??q(e.diff);if(t)return t}},Oe=e=>{if(!e||typeof e!=`string`)return{data:null,isJson:!1};let t=e.trim();if(!t.startsWith(`{`)&&!t.startsWith(`[`)||!t.endsWith(`}`)&&!t.endsWith(`]`)||t.length<2)return{data:null,isJson:!1};try{let e=JSON.parse(t);return typeof e==`object`&&e?{data:e,isJson:!0}:{data:null,isJson:!1}}catch{return{data:null,isJson:!1}}},ke=(e,t,n)=>{let r=Se(e);if((t===`edit`||t===`multiedit`||t===`write`||t===`apply_patch`)&&we(r)&&(r=Te(r)),(t===`edit`||t===`multiedit`||t===`apply_patch`)&&r.trim().length===0){let e=De(n);if(e)return e}return r},Ae=e=>{let t=e.match(/<type>(file|directory)<\/type>/i)?.[1]?.toLowerCase()??`unknown`,n=(e.match(/<content>([\s\S]*?)<\/content>/i)?.[1]??e).replace(/\r\n/g,`
1
+ import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{n as t}from"./runtime-fetch-CiqQd__Y.js";import{En as n,in as r}from"./persistence-CMWO3jFo.js";import{nt as i,rt as a}from"./vendor-base-ui-CnHfUmDN.js";import{n as o}from"./platform-B0p43squ.js";import{v as s}from"./useUIStore-Brsa2mKs.js";import{n as c}from"./vendor-react-DLPElpoQ.js";import{a as l,n as u}from"./vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js";import{f as d,r as f,t as p}from"./button-e8fYWIMM.js";import{n as m}from"./Icon-D7NdMmbi.js";import{i as h,n as g,t as _}from"./tooltip-C64SdRvR.js";import{n as v}from"./useThemeSystem-DMzWVJaW.js";import{n as y}from"./markdownTheme-DM5h1i18.js";import{n as b}from"./MarkdownRenderer-CyacU8IE.js";import{n as x,o as S,s as C,t as w}from"./markdown-worker-bS5OgGO3.js";import{a as T,i as E,o as D,t as ee}from"./imageSource-BpUsiwFd.js";import{t as te}from"./vendor-tanstack-react-virtual-tlDgH6PG.js";var O=e(a(),1),k=c(),A=i(),ne=`w-[min(30rem,calc(100cqw-1rem))] min-w-[280px] max-w-full overflow-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]`,j={maxWidth:`min(28rem, calc(100cqw - 4ch))`,backgroundColor:`var(--surface-elevated)`,color:`var(--surface-elevated-foreground)`},M=`flex items-center justify-between gap-3 border-b border-[var(--surface-subtle)] px-3 py-2.5`,N=`text-xs font-medium leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,re=`text-xs tabular-nums leading-4 text-muted-foreground md:text-[0.8125rem] md:leading-5`,P=`m-0 list-none divide-y divide-[var(--surface-subtle)] p-0`,F=`${P} max-h-[min(22rem,50vh)] overflow-y-auto`,ie=`${P} w-full`,ae={className:`p-0 rounded-none`,maxHeightClass:`max-h-[46vh]`,disableHorizontal:!0},I=`px-2.5 py-1.5 text-xs leading-4 md:py-2 md:text-[0.8125rem] md:leading-5`,oe=`flex min-w-0 items-center gap-1 text-xs leading-4 text-muted-foreground md:gap-1.5 md:text-[0.8125rem] md:leading-5`,se=`min-w-0 truncate text-xs leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,ce=`inline-flex shrink-0 items-center gap-1 text-xs leading-4 tabular-nums text-muted-foreground md:text-[0.8125rem] md:leading-5`,le=`h-3 w-3 shrink-0 md:h-3.5 md:w-3.5`,ue={in_progress:`text-foreground`,pending:`text-foreground`,completed:`text-muted-foreground line-through`,cancelled:`text-muted-foreground line-through`},L={in_progress:`chat.statusRow.todo.status.inProgress`,pending:`chat.statusRow.todo.status.pending`,completed:`chat.statusRow.todo.status.completed`,cancelled:`chat.statusRow.todo.status.cancelled`},R=e=>{let t=(0,k.c)(26),{todo:n,isActive:r,activeRef:i}=e,{t:a}=d(),o=z.has(n.status)?n.status:`pending`,s=L[o],c;t[0]!==a||t[1]!==s?(c=a(s),t[0]=a,t[1]=s,t[2]=c):c=t[2];let l=c,u;t[3]===o?u=t[4]:(u=o===`in_progress`?(0,A.jsx)(m,{name:`record-circle`,className:`h-3.5 w-3.5 text-[var(--status-info)]`,"aria-hidden":`true`}):o===`completed`?(0,A.jsx)(m,{name:`checkbox-circle`,className:`h-3.5 w-3.5 text-[var(--status-success)]`,"aria-hidden":`true`}):(0,A.jsx)(m,{name:`time`,className:`h-3.5 w-3.5 text-muted-foreground`,"aria-hidden":`true`}),t[3]=o,t[4]=u);let p=u,v=r?`step`:void 0,y=r&&`bg-[var(--surface-muted)] before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-[var(--status-info)]`,b;t[5]===y?b=t[6]:(b=f(`group relative grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] items-start gap-2.5`,I,y),t[5]=y,t[6]=b);let x;t[7]!==p||t[8]!==l?(x=(0,A.jsx)(h,{asChild:!0,children:(0,A.jsx)(`span`,{className:`flex size-4 items-center justify-center`,"aria-label":l,children:p})}),t[7]=p,t[8]=l,t[9]=x):x=t[9];let S;t[10]===l?S=t[11]:(S=(0,A.jsx)(g,{side:`left`,sideOffset:6,children:l}),t[10]=l,t[11]=S);let C;t[12]!==x||t[13]!==S?(C=(0,A.jsxs)(_,{children:[x,S]}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w=ue[o],T;t[15]===w?T=t[16]:(T=f(`block whitespace-normal break-words`,w),t[15]=w,t[16]=T);let E;t[17]!==T||t[18]!==n.content?(E=(0,A.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,A.jsx)(`span`,{className:T,children:n.content})}),t[17]=T,t[18]=n.content,t[19]=E):E=t[19];let D;return t[20]!==i||t[21]!==E||t[22]!==v||t[23]!==b||t[24]!==C?(D=(0,A.jsxs)(`li`,{ref:i,"aria-current":v,className:b,children:[C,E]}),t[20]=i,t[21]=E,t[22]=v,t[23]=b,t[24]=C,t[25]=D):D=t[25],D},z=new Set([`in_progress`,`pending`,`completed`,`cancelled`]),B=`image/png`,V=l(`OpenChamberMedia`),H=e=>{let t=e.toLowerCase().split(`;`)[0]?.trim()||B;if(t===`image/jpeg`||t===`image/jpg`)return`jpg`;if(t===`image/webp`)return`webp`;if(t===`image/gif`)return`gif`;if(t===`image/svg+xml`)return`svg`;if(t===`image/png`)return`png`;let n=t.split(`/`)[1];return n&&/^[a-z0-9.+-]+$/i.test(n)?n:`png`},U=(e,t)=>{let n=`image.${H(t)}`,r=(((e||``).trim()||n).split(/[\\/]/).pop()||n).replace(/[^\w.\- ()[\]]+/g,`_`).replace(/^\.+/,``);return r?/\.[a-z0-9]{1,8}$/i.test(r)?r:`${r}.${H(t)}`:n},W=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{if(typeof r.result==`string`){t(r.result);return}n(Error(`Failed to encode image`))},r.onerror=()=>n(r.error??Error(`Failed to encode image`)),r.readAsDataURL(e)}),de=async e=>{let t=await W(e),n=t.indexOf(`,`);return n>=0?t.slice(n+1):t},fe=(e,t)=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)},G=async(e,t)=>{let n=await fetch(e,{signal:t,cache:`no-store`});if(!n.ok)throw Error(`Image fetch failed (${n.status})`);return n.blob()},pe=async(e,t=B)=>{if(!e.naturalWidth||!e.naturalHeight)return null;let n=e.currentSrc||e.src;if(n&&(/^(?:blob:|data:|openchamber-asset:)/i.test(n)||/^https?:/i.test(n)||/^\/api(?=\/|[?#]|$)/.test(n)))try{let e=await G(n);if(e.size>0)return e}catch{}try{let n=document.createElement(`canvas`);n.width=e.naturalWidth,n.height=e.naturalHeight;let r=n.getContext(`2d`);if(!r)return null;r.drawImage(e,0,0);let i=t.startsWith(`image/`)?t:B,a=await new Promise(e=>{n.toBlob(t=>e(t),i===`image/jpg`?`image/jpeg`:i)});return a&&a.size>0?a:null}catch{return null}},me=async(e,t)=>{let n=``;try{return n=await ee(e,t??new AbortController().signal),await G(n,t)}finally{n&&T(n)}},he=async(e,n)=>{let r=e.effectiveDirectory??``,i=null;e.prefetchedBlob&&e.prefetchedBlob.size>0&&(i=e.prefetchedBlob);let a=e.displayUrl?.trim();if(!i&&a)try{i=await G(a,n)}catch{i=null}if(!i){let a=e.sourceUrl.trim();if(!a)throw Error(`Image source is empty`);if(/^(?:data|blob):/i.test(a)||/^https?:/i.test(a)||/^\/api(?=\/|[?#]|$)/.test(a))i=await G(a,n);else{let e=D(a,r);if(E(e))try{i=await me(e.path,n)}catch{let r=await t(`/api/fs/raw`,{method:`GET`,cache:`no-store`,query:{path:e.path},signal:n});if(!r.ok)throw Error(`Image source request failed with status ${r.status}`);i=await r.blob()}else i=await G(e.source,n)}}if(!i||i.size<=0)throw Error(`Image is empty`);let o=(e.mimeType||(i.type&&i.type.startsWith(`image/`)?i.type:``)||B).split(`;`)[0].trim()||B;return{blob:i,filename:U(e.filename,o),mimeType:o}},ge=()=>!o()||u===void 0?!1:u.isPluginAvailable(`OpenChamberMedia`),_e=async(e,t)=>{let{blob:i,filename:a,mimeType:o}=await he(e,t);if(ge()){let e=await de(i);return await V.saveImage({dataBase64:e,mimeType:o,filename:a}),`saved`}if(r()){let e=n()?.vscode;if(e?.saveImage){let t=await W(i);return await e.saveImage({fileName:a,dataUrl:t}),`saved`}}return fe(i,a),`downloaded`},ve=e=>e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`),ye=e=>e?Object.entries(e).map(([e,t])=>t==null?``:`${e.startsWith(`--`)?e:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}:${typeof t==`number`?`${t}px`:t};`).join(``):``,K=(e,t,n)=>{let r=e.querySelector(`pre`);r&&(r.style.margin=`0`,r.style.background=`transparent`,r.style.whiteSpace=t?`pre-wrap`:`pre`,t&&(r.style.wordBreak=`break-word`,r.style.overflowWrap=`break-word`));let i=e.querySelector(`code`);if(i){let e=ye(n);e&&i.setAttribute(`style`,`${i.getAttribute(`style`)??``}${e}`)}},be=e=>`<pre><code>${ve(e)}</code></pre>`,xe=e=>{let t=(0,k.c)(21),{code:n,language:r,className:i,style:a,codeStyle:o,wrap:s}=e,c=s!==void 0&&s,{currentTheme:l}=v(),u=O.useRef(null),d;t[0]===l?d=t[1]:(d=y(l),t[0]=l,t[1]=d);let p=d,m,h;t[2]!==n||t[3]!==o||t[4]!==c?(m=()=>{let e=u.current;e&&(e.innerHTML=be(n),K(e,c,o))},h=[n,c,o],t[2]=n,t[3]=o,t[4]=c,t[5]=m,t[6]=h):(m=t[5],h=t[6]),O.useLayoutEffect(m,h);let g,_;t[7]!==n||t[8]!==o||t[9]!==r||t[10]!==c?(g=()=>{let e=u.current;if(!e)return;let t=!0,i=new AbortController;return w(n,(r||`text`).toLowerCase(),{signal:i.signal,priority:`visible`}).then(n=>{!t||!e||!n||(e.innerHTML=n,K(e,c,o))}),()=>{t=!1,i.abort()}},_=[n,r,c,o],t[7]=n,t[8]=o,t[9]=r,t[10]=c,t[11]=g,t[12]=_):(g=t[11],_=t[12]),O.useEffect(g,_);let b;t[13]===i?b=t[14]:(b=f(`typography-code`,i),t[13]=i,t[14]=b);let x=p,S;t[15]!==a||t[16]!==x?(S={...x,...a},t[15]=a,t[16]=x,t[17]=S):S=t[17];let C;return t[18]!==b||t[19]!==S?(C=(0,A.jsx)(`div`,{ref:u,className:b,style:S}),t[18]=b,t[19]=S,t[20]=C):C=t[20],C},Se=e=>{let t=e.replace(/^<file>\s*\n?/,``).replace(/\n?<\/file>\s*$/,``);return t=t.replace(/^\s*\d{5}\|\s?/gm,``),t.trim()},Ce=(e,t=``)=>{if(typeof e==`string`)return e;if(e==null)return t;if(typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`)return String(e);try{return JSON.stringify(e)}catch{return t}},we=e=>e?e.includes(`<diagnostics`)||e.includes(`<file_diagnostics>`)||e.includes(`LSP errors detected`)||e.includes(`This file has errors`):!1,Te=e=>e?e.replace(/\n{0,2}LSP errors detected[\s\S]*?<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/\n{0,2}This file has errors[\s\S]*?<\/file_diagnostics>/g,``).replace(/<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/<file_diagnostics>[\s\S]*?<\/file_diagnostics>/g,``).trim():``,Ee=(e,t)=>!e||typeof e!=`object`?String(e):C(e,t||``),q=e=>{if(typeof e==`string`){let t=e.trim();return t.length>0?t:void 0}if(e&&typeof e==`object`){let t=e.patch;if(typeof t==`string`){let e=t.trim();return e.length>0?e:void 0}}},De=e=>{if(!e||typeof e!=`object`)return;let t=q(e.patch)??q(e.diff);if(t)return t;let n=Array.isArray(e.files)?e.files:[];for(let e of n){if(!e||typeof e!=`object`)continue;let t=q(e.patch)??q(e.diff);if(t)return t}},Oe=e=>{if(!e||typeof e!=`string`)return{data:null,isJson:!1};let t=e.trim();if(!t.startsWith(`{`)&&!t.startsWith(`[`)||!t.endsWith(`}`)&&!t.endsWith(`]`)||t.length<2)return{data:null,isJson:!1};try{let e=JSON.parse(t);return typeof e==`object`&&e?{data:e,isJson:!0}:{data:null,isJson:!1}}catch{return{data:null,isJson:!1}}},ke=(e,t,n)=>{let r=Se(e);if((t===`edit`||t===`multiedit`||t===`write`||t===`apply_patch`)&&we(r)&&(r=Te(r)),(t===`edit`||t===`multiedit`||t===`apply_patch`)&&r.trim().length===0){let e=De(n);if(e)return e}return r},Ae=e=>{let t=e.match(/<type>(file|directory)<\/type>/i)?.[1]?.toLowerCase()??`unknown`,n=(e.match(/<content>([\s\S]*?)<\/content>/i)?.[1]??e).replace(/\r\n/g,`
2
2
  `).split(`
3
3
  `),r=e=>/\(\s*File has more lines\..*offset.*\)/i.test(e.trim());return{type:t,lines:n.map(e=>{let n=e.trim(),i=n.startsWith(`(`)&&n.endsWith(`)`)||r(n);if(t!==`directory`){let t=e.match(/^(\d+):\s?(.*)$/);if(t){let e=t[2],n=e.trim(),i=n.startsWith(`(`)&&n.endsWith(`)`)||r(n);return{lineNumber:i?null:Number(t[1]),text:e,isInfo:i}}}return{lineNumber:null,text:e,isInfo:i}}).filter((e,t,n)=>{if(e.text.trim().length>0)return!0;let r=n[t-1],i=n[t+1],a=!!(r?.isInfo||i?.isInfo),o=e.lineNumber!==null;return!(a&&o)})}},je=(e,t)=>{try{let n=e.trim().split(`
4
4
  `).filter(Boolean);if(n.length===0)return null;let r=[];return n.forEach(e=>{let t=e.match(/^(\s*)(.+)$/);if(t){let[,e,n]=t,i=Math.floor(e.length/2),a=!n.endsWith(`/`);r.push({name:n.replace(/\/$/,``),depth:i,isFile:a})}}),(0,A.jsx)(`div`,{className:f(`w-full min-w-0 font-mono space-y-0.5`,t?.unstyled?null:`p-3 bg-muted/20 rounded-xl border border-border/30`),style:s.tool.popup,children:r.map((e,t)=>(0,A.jsx)(`div`,{className:`min-w-0`,style:{paddingLeft:`${e.depth*20}px`},children:e.isFile?(0,A.jsx)(`span`,{className:`text-foreground/90 block truncate`,children:e.name}):(0,A.jsxs)(`span`,{className:`font-semibold text-foreground block truncate`,children:[e.name,`/`]})},t))})}catch{return null}},Me={backgroundColor:`var(--status-info)`,opacity:.6},Ne=(e,t,n)=>{try{let r=e.trim().split(`
package/dist/index.html CHANGED
@@ -552,7 +552,7 @@
552
552
  opacity: 0.72;
553
553
  }
554
554
  </style>
555
- <script type="module" crossorigin src="/assets/main-DgAK4oel.js"></script>
555
+ <script type="module" crossorigin src="/assets/main-DOpUCN5f.js"></script>
556
556
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
557
557
  <link rel="modulepreload" crossorigin href="/assets/runtime-auth-D66yq6ie.js">
558
558
  <link rel="modulepreload" crossorigin href="/assets/runtime-switch-BJM2k_uH.js">
@@ -572,8 +572,8 @@
572
572
  <link rel="modulepreload" crossorigin href="/assets/terminalApi-CJyWCuc8.js">
573
573
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
574
574
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
575
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-CZrkgYcD.js">
576
- <link rel="stylesheet" crossorigin href="/assets/src-qYyYBKwA.css">
575
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-CgFmzQWA.js">
576
+ <link rel="stylesheet" crossorigin href="/assets/src-OzbnJsph.css">
577
577
  </head>
578
578
  <body class="h-full bg-background text-foreground">
579
579
  <!-- Keep the startup layer outside #root. React replaces every child of
@@ -74,7 +74,7 @@
74
74
  }
75
75
  </style>
76
76
 
77
- <script type="module" crossorigin src="/assets/miniChat-Cc9LRMjb.js"></script>
77
+ <script type="module" crossorigin src="/assets/miniChat-CBpEIoJn.js"></script>
78
78
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
79
79
  <link rel="modulepreload" crossorigin href="/assets/runtime-auth-D66yq6ie.js">
80
80
  <link rel="modulepreload" crossorigin href="/assets/runtime-switch-BJM2k_uH.js">
@@ -94,8 +94,8 @@
94
94
  <link rel="modulepreload" crossorigin href="/assets/terminalApi-CJyWCuc8.js">
95
95
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
96
96
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
97
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-CZrkgYcD.js">
98
- <link rel="stylesheet" crossorigin href="/assets/src-qYyYBKwA.css">
97
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-CgFmzQWA.js">
98
+ <link rel="stylesheet" crossorigin href="/assets/src-OzbnJsph.css">
99
99
  </head>
100
100
  <body class="h-full bg-background text-foreground">
101
101
  <div id="root" class="h-full">
package/dist/mobile.html CHANGED
@@ -63,7 +63,7 @@
63
63
  })();
64
64
  </script>
65
65
 
66
- <script type="module" crossorigin src="/assets/mobile-D85ParE1.js"></script>
66
+ <script type="module" crossorigin src="/assets/mobile-CiFeGY2g.js"></script>
67
67
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
68
68
  <link rel="modulepreload" crossorigin href="/assets/runtime-auth-D66yq6ie.js">
69
69
  <link rel="modulepreload" crossorigin href="/assets/runtime-switch-BJM2k_uH.js">
@@ -83,8 +83,8 @@
83
83
  <link rel="modulepreload" crossorigin href="/assets/terminalApi-CJyWCuc8.js">
84
84
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
85
85
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
86
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-CZrkgYcD.js">
87
- <link rel="stylesheet" crossorigin href="/assets/src-qYyYBKwA.css">
86
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-CgFmzQWA.js">
87
+ <link rel="stylesheet" crossorigin href="/assets/src-OzbnJsph.css">
88
88
  </head>
89
89
  <body class="h-full bg-background text-foreground">
90
90
  <div id="root" class="h-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openchambery/web",
3
- "version": "2.0.0-beta.23",
3
+ "version": "2.0.0-beta.25",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
@@ -118,6 +118,7 @@ Catalog loaders (`loadProviders` / `loadAgents`) and assistant Query keys gate w
118
118
  - The tunnel is transparent to the app: adding relay support to a feature should not require the feature to know the relay exists — it goes through the shared runtime transport helpers.
119
119
  - The two implementations stay byte-compatible and the wire format is versioned/negotiated so mixed client/host app versions degrade gracefully rather than break. The Layer 1 Relay does not inspect application versions; unsupported Host/Client features are omitted at the endpoints.
120
120
  - Host-control reconnect storms are bounded at both ends: the Host does not reset backoff on a brief `open`, and the Relay rate-limits host-control upgrades per IP and per `serverId` (`4029`) without replacing a live control socket.
121
+ - Concurrent service starts recheck the endpoint's active Host after awaiting identity, so each endpoint has one managed Host and stopping the service closes every started Host.
121
122
  - Tunneled HTTP/WS always dial `getLocalPort()` on loopback; there is no client-selected target-port override on the host dispatcher.
122
123
 
123
124
  For the operational rules that keep future changes (new WebSocket endpoints, transport refactors, terminal/voice porting) from breaking this, load the `relay-transport` skill.
@@ -313,6 +313,7 @@ export const createRelayService = ({
313
313
  }
314
314
  const identity = await identityRuntime.getRelayIdentity();
315
315
  for (const relayUrl of pending) {
316
+ if (hostClients.has(relayUrl)) continue;
316
317
  const hostClient = startRelayHost({
317
318
  relayUrl,
318
319
  identity,
@@ -123,6 +123,19 @@ describe('relay service pairing endpoint', () => {
123
123
  delete process.env.OPENCHAMBER_RELAY_URL;
124
124
  });
125
125
 
126
+ it('starts only one host per endpoint during concurrent startup and stops every started host', async () => {
127
+ const { service, settings } = createTestRelayService({ hasRelayDemand: async () => true });
128
+ await settings.write({ privateRelay: { enabled: true, relayUrl: DEFAULT_RELAY_URL } });
129
+
130
+ try {
131
+ await Promise.all([service.reconcile(), service.reconcile()]);
132
+ expect(relayMocks.starts).toEqual([DEFAULT_RELAY_URL]);
133
+ } finally {
134
+ service.stop();
135
+ }
136
+ expect(relayMocks.stops).toEqual(relayMocks.starts);
137
+ });
138
+
126
139
  it('appends a new pairing endpoint (multi-relay) while keeping the primary', async () => {
127
140
  const { service, settings } = createTestRelayService();
128
141
 
@@ -50,18 +50,21 @@ extension-only VS Code retains the limitation below.
50
50
  assistant reply run concurrently. Generation publishes `isGenerating` via
51
51
  the existing Host metadata path, then the final title through `session.updated`.
52
52
  Later user admissions/idle events do not regenerate the initial title.
53
- The legacy first-idle edge remains a fallback when no admission was observed.
54
- A fork title (`(fork #n)`) waits for its first
55
- newly-created user message; the matching assistant completion triggers an
56
- immediate title refresh that bypasses inherited title metadata and throttle.
53
+ If `session.created` was missed, the admission's session read recovers
54
+ an empty/default title with no previous auto-title metadata and starts
55
+ immediately. The legacy first-idle edge remains a fallback when no
56
+ admission was observed.
57
+ A fork title (`(fork #n)`) refreshes on its first newly-admitted user
58
+ message, bypassing inherited title metadata and throttle without waiting
59
+ for the assistant. Legacy message-only events retain the idle fallback.
57
60
  If the fork's `session.created` was lost (SSE reconnect gap, server or
58
61
  OpenCode restart), the first newly-created user message lazily re-registers
59
62
  the pending fork from the session read inside `recordUserActivity` (fork
60
63
  title + message created after the fork + activity timestamp not yet past
61
- the fork time), so the first-reply refresh still fires.
64
+ the fork time), so the first-send refresh still fires.
62
65
  Ordinary later idle transitions do **not** arm another refresh. Any
63
66
  `busy`/`retry` status or a fresh user `message.updated` still clears an
64
- ordinary already-armed timer; an admitted first-title timer is exempt.
67
+ ordinary already-armed timer; admitted initial/fork-title timers are exempt.
65
68
  A sidebar smart-title request sets `titleRefresh.requestedAt`; its
66
69
  `session.updated` event arms the same flow immediately (forced / manual
67
70
  refresh is unaffected by the background gate).
@@ -141,6 +144,13 @@ auto updates for that session.
141
144
  Title language follows the user's real message text. Assistant responses, tool
142
145
  output, and transcript labels provide context without selecting the title language.
143
146
 
147
+ Attachments do not gate title generation. Admission retains user text plus at
148
+ most eight bounded filename/MIME descriptors, never attachment bytes or URLs.
149
+ Attachment-only messages also start generation; the text-only summary model can
150
+ name the attachment subject from those descriptors, but is explicitly told that
151
+ file contents are not included. Transcript-based/manual generation uses the
152
+ same file descriptors. This does not perform image recognition or file parsing.
153
+
144
154
  - Lives in the web server, so VS Code (extension-only) does not generate
145
155
  refreshes; it still receives `session.updated` title changes produced by a
146
156
  web/desktop instance of the same OpenCode server.
@@ -110,10 +110,12 @@ it('uses the real v2 client and publishes the generated title without another se
110
110
  }
111
111
  });
112
112
 
113
- it('starts the first title from admitted user text while the assistant is still running', async () => {
113
+ it.each(['new', 'missed-create', 'fork', 'missed-fork-create'].flatMap((scenario) =>
114
+ ['text', 'text-and-file', 'file-only'].map((content) => ({ scenario, content })),
115
+ ))('starts the first title before assistant completion ($scenario, $content)', async ({ scenario, content }) => {
114
116
  vi.useFakeTimers();
115
117
  const session = {
116
- id: 'ses_first', title: '', time: { created: 1, updated: 1 },
118
+ id: 'ses_first', title: scenario.includes('fork') ? 'Original (fork #1)' : '', time: { created: 1, updated: 1 },
117
119
  metadata: {},
118
120
  };
119
121
  let finishTitle;
@@ -135,26 +137,36 @@ it('starts the first title from admitted user text while the assistant is still
135
137
  });
136
138
  const emit = (type, data) => runtime.processPayload({ type, data, created: 1000, location: { directory: '/repo' } });
137
139
  try {
138
- emit('session.created', { sessionID: session.id, location: { directory: '/repo' } });
140
+ if (!scenario.startsWith('missed')) {
141
+ emit('session.created', { sessionID: session.id, title: session.title, time: session.time, location: { directory: '/repo' } });
142
+ }
139
143
  emit('session.inbox.enqueued', { sessionID: session.id, inboxID: 'msg_synthetic', item: {
140
144
  type: 'synthetic', delivery: 'steer', payload: { text: 'Internal instructions' },
141
145
  } });
142
146
  await vi.runAllTimersAsync();
143
147
  expect(generate).not.toHaveBeenCalled();
144
148
  emit('session.inbox.enqueued', { sessionID: session.id, inboxID: 'msg_first', item: {
145
- type: 'user', delivery: 'steer', payload: { text: 'Implement a session title generator' },
149
+ type: 'user', delivery: 'steer', payload: {
150
+ text: content === 'file-only' ? '' : 'Implement a session title generator',
151
+ ...(content !== 'text' ? { files: [{ name: 'title-bug.png', mime: 'image/png', source: { type: 'inline' }, data: 'YQ=='.repeat(100_000) }] } : {}),
152
+ },
146
153
  } });
147
154
  emit('session.execution.started', { sessionID: session.id });
148
155
  emit('session.status', { sessionID: session.id, status: { type: 'busy' } });
149
156
  await vi.runAllTimersAsync();
150
157
  expect(generate).toHaveBeenCalledTimes(1);
151
- expect(generate.mock.calls[0][0].prompt).toContain('Implement a session title generator');
158
+ const prompt = generate.mock.calls[0][0].prompt;
159
+ if (content !== 'file-only') expect(prompt).toContain('Implement a session title generator');
160
+ if (content !== 'text') expect(prompt).toContain('title-bug.png');
161
+ expect(prompt).not.toContain('YQ==');
162
+ expect(prompt.length).toBeLessThan(4000);
152
163
  expect(session.metadata.openchamber?.titleRefresh?.isGenerating).toBe(true);
153
164
  expect(access.messages).not.toHaveBeenCalled();
154
165
  finishTitle({ text: 'Session titles', providerID: 'test', modelID: 'test' });
155
166
  await vi.runAllTimersAsync();
156
167
  expect(session.title).toBe('Session titles');
157
168
  expect(session.metadata.openchamber.titleRefresh.isGenerating).toBeUndefined();
169
+ expect(access.messages).not.toHaveBeenCalled();
158
170
  emit('session.execution.succeeded', { sessionID: session.id });
159
171
  emit('session.inbox.enqueued', { sessionID: session.id, inboxID: 'msg_second', item: {
160
172
  type: 'user', delivery: 'steer', payload: { text: 'Follow up on the implementation' },
@@ -1,7 +1,7 @@
1
1
  // Session title refresh: regenerate the sidebar title from the conversation's
2
2
  // MAIN SUBJECT (overall feature / goal), not just the last wrap-up utterance.
3
3
  // OpenCode only titles once from the first user message. Auto refresh is sparse
4
- // for title stability: first idle of a new session, first newly-sent reply on a
4
+ // for title stability: first user admission of a new session, first send on a
5
5
  // fork, plus explicit smart-title / forced refresh. Continuing the same work
6
6
  // should keep naming the thing being done — not "commit and push". This
7
7
  // watcher throttles to at most one refresh per session every TITLE_THROTTLE_MS
@@ -201,13 +201,22 @@ const extractUserMessage = (payload) => {
201
201
  const item = properties.item;
202
202
  if (typeof properties.sessionID !== 'string' || item?.type !== 'user') return null;
203
203
  const text = typeof item.payload?.text === 'string' ? item.payload.text.slice(0, TRANSCRIPT_USER_CHAR_LIMIT) : '';
204
+ const files = Array.isArray(item.payload?.files) ? item.payload.files.slice(0, 8) : [];
205
+ const parts = [
206
+ ...(text.trim() ? [{ type: 'text', text }] : []),
207
+ ...files.filter((file) => file && typeof file === 'object').map((file) => ({
208
+ type: 'file',
209
+ filename: typeof file.name === 'string' ? file.name.slice(0, 200) : '',
210
+ mime: typeof file.mime === 'string' ? file.mime.slice(0, 100) : '',
211
+ })),
212
+ ];
204
213
  return {
205
214
  sessionId: properties.sessionID,
206
215
  createdAt: payload.created ?? 0,
207
216
  directory: payload.location?.directory ?? '',
208
- initialRecord: text.trim() ? {
217
+ initialRecord: parts.length ? {
209
218
  info: { id: properties.inboxID, role: 'user' },
210
- parts: [{ type: 'text', text }],
219
+ parts,
211
220
  } : null,
212
221
  };
213
222
  }
@@ -230,7 +239,13 @@ const messagePartsToText = (message) => {
230
239
  ? TRANSCRIPT_ASSISTANT_CHAR_LIMIT
231
240
  : TRANSCRIPT_USER_CHAR_LIMIT;
232
241
  return parts
233
- .map((part) => (part?.type === 'text' && typeof part.text === 'string' ? part.text : ''))
242
+ .map((part) => {
243
+ if (part?.type === 'text' && typeof part.text === 'string') return part.text;
244
+ if (part?.type !== 'file') return '';
245
+ const filename = typeof part.filename === 'string' ? part.filename.slice(0, 200) : '';
246
+ const mime = typeof part.mime === 'string' ? part.mime.slice(0, 100) : '';
247
+ return `[Attachment: ${filename || mime || 'file'}${filename && mime ? ` (${mime})` : ''}; contents not included]`;
248
+ })
234
249
  .filter(Boolean)
235
250
  .join('\n')
236
251
  .slice(0, charLimit);
@@ -495,7 +510,7 @@ export const createSessionTitleRuntime = ({
495
510
  });
496
511
  };
497
512
 
498
- const recordUserActivity = async (sessionId, directory, createdAt) => {
513
+ const recordUserActivity = async (sessionId, directory, createdAt, initialRecord) => {
499
514
  if (!Number.isFinite(createdAt) || createdAt <= 0) return;
500
515
  const session = await openCodeFetch(`/session/${encodeURIComponent(sessionId)}`, { directory })
501
516
  .catch(() => null);
@@ -508,7 +523,7 @@ export const createSessionTitleRuntime = ({
508
523
  // never learned about the fork. The first newly-created user message on a
509
524
  // fork-titled session (message created after the fork, before any activity
510
525
  // timestamp advanced past the fork) rebuilds that pending entry so the
511
- // matching idle still triggers the first-refresh. Idempotent: the normal
526
+ // first admission can trigger the refresh. Idempotent: the normal
512
527
  // session.created path already holds a pending entry, and once activity
513
528
  // advances past the fork time later messages never re-register.
514
529
  const forkCreated = Number(session?.time?.created);
@@ -526,6 +541,26 @@ export const createSessionTitleRuntime = ({
526
541
  });
527
542
  }
528
543
 
544
+ // Recover first-send admission even when session.created was missed.
545
+ // Use the admitted text: the message projection may not exist yet.
546
+ if (initialRecord && !stopped && !inflight.has(sessionId) && !initialRefreshes.has(sessionId)
547
+ && !forkFirstRefreshes.has(sessionId) && !session.parentID && !isSystemOwnedSession(session)) {
548
+ const pendingFork = forkFirstSendPending.get(sessionId);
549
+ const firstForkSend = pendingFork && createdAt > pendingFork.createdAt;
550
+ const untitled = !session.title || isDefaultSessionTitle(session.title);
551
+ if (firstForkSend || (untitled && !meta.generatedAt && !meta.lastAutoTitle)) {
552
+ newSessions.delete(sessionId);
553
+ if (firstForkSend) {
554
+ forkFirstSendPending.delete(sessionId);
555
+ forkFirstRefreshes.add(sessionId);
556
+ } else {
557
+ initialRefreshes.add(sessionId);
558
+ }
559
+ initialUserMessages.set(sessionId, initialRecord);
560
+ armTimer(sessionId, directory, 0);
561
+ }
562
+ }
563
+
529
564
  const previous = Number(meta.titleRefresh.activityUpdatedAt);
530
565
  const activityUpdatedAt = Number.isFinite(previous) ? Math.max(previous, createdAt) : createdAt;
531
566
  if (activityUpdatedAt === previous) return;
@@ -683,7 +718,7 @@ export const createSessionTitleRuntime = ({
683
718
  return;
684
719
  }
685
720
 
686
- const latestMessages = await fetchRecentMessages(sessionId, directory);
721
+ const latestMessages = initialUserMessage ? null : await fetchRecentMessages(sessionId, directory);
687
722
  const latestAssistantId = (() => {
688
723
  if (!latestMessages) return null;
689
724
  for (let i = latestMessages.length - 1; i >= 0; i -= 1) {
@@ -777,7 +812,7 @@ export const createSessionTitleRuntime = ({
777
812
  initialRefreshes.add(status.sessionId);
778
813
  armTimer(status.sessionId, status.directory || directoryHint, 0);
779
814
  }
780
- } else if (!initialRefreshes.has(status.sessionId)) {
815
+ } else if (!initialRefreshes.has(status.sessionId) && !forkFirstRefreshes.has(status.sessionId)) {
781
816
  clearTimer(status.sessionId);
782
817
  }
783
818
  return;
@@ -797,6 +832,7 @@ export const createSessionTitleRuntime = ({
797
832
  userMessage.sessionId,
798
833
  userMessage.directory || directoryHint,
799
834
  userMessage.createdAt,
835
+ userMessage.initialRecord,
800
836
  ).catch((error) => {
801
837
  console.warn('[session-title] failed to record user activity:', error?.message || error);
802
838
  });
@@ -804,7 +840,7 @@ export const createSessionTitleRuntime = ({
804
840
  // session settles. Only a message created after the timer was armed
805
841
  // means the user actually moved on.
806
842
  const armed = timers.get(userMessage.sessionId);
807
- if (armed && !initialRefreshes.has(userMessage.sessionId) && userMessage.createdAt >= armed.armedAt) {
843
+ if (armed && !initialRefreshes.has(userMessage.sessionId) && !forkFirstRefreshes.has(userMessage.sessionId) && userMessage.createdAt >= armed.armedAt) {
808
844
  clearTimer(userMessage.sessionId);
809
845
  }
810
846
  }
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/vendor-capacitor-app-PzT84wSx.js","assets/rolldown-runtime-C0FnF6B9.js","assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js"])))=>i.map(i=>d[i]);
2
- import{f as e,g as t,h as n,l as r,p as i}from"./runtime-auth-D66yq6ie.js";import{a,d as o,f as s,p as c,u as l}from"./runtime-switch-BJM2k_uH.js";import{n as u,t as d}from"./runtime-fetch-CiqQd__Y.js";import{rt as f}from"./persistence-CMWO3jFo.js";import{t as ee}from"./desktopRelayRestore-DDJtljeu.js";import{$ as te,A as ne,B as re,Ct as ie,D as ae,E as oe,F as se,H as ce,I as le,J as ue,L as de,M as fe,N as pe,P as me,Q as he,R as ge,S as _e,T as ve,U as ye,V as be,W as xe,X as Se,Y as p,Z as Ce,_ as we,_t as Te,a as Ee,at as De,b as Oe,bt as ke,c as Ae,ct as je,d as Me,dt as m,et as h,f as g,ft as _,g as v,gt as y,h as b,ht as x,i as Ne,j as Pe,k as Fe,l as Ie,lt as Le,m as Re,mt as ze,n as S,nt as Be,o as Ve,ot as He,p as Ue,pt as We,q as Ge,s as Ke,st as qe,t as Je,tt as Ye,u as Xe,ut as Ze,v as C,vt as Qe,x as w,y as $e,yt as T}from"./platform-B0p43squ.js";import{n as E,o as D}from"./vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js";import{a as et,c as tt,l as nt,n as rt,r as it,s as at,t as ot}from"./terminalApi-CJyWCuc8.js";import{t as O}from"./types-BuiqMmh_.js";import{n as k,t as A}from"./capgoAdapter-DCowZ2ab.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var st=e=>{let t=e?.retry;return{maxRetries:t?.maxRetries??3,initialRetryDelay:t?.initialDelayMs??1e3,maxRetryDelay:t?.maxDelayMs??8e3,connectionTimeout:e?.connectionTimeoutMs??1e4}},ct=()=>({async createSession(e){return it(e)},connect(e,t,n){let r=rt(e,t.onEvent,t.onError,st(n));return{close:()=>r()}},async sendInput(e,t){await nt(e,t)},async resize(e){await at(e.sessionId,e.cols,e.rows)},async close(e){await ot(e)},async restartSession(e,t){return tt(e,{cwd:t.cwd??``,cols:t.cols,rows:t.rows})},async forceKill(e){await et(e)}}),lt=()=>({checkIsGitRepository:Ae,getGitStatus:de,getGitDiff:pe,getGitFileDiff:me,revertGitFile:He,stageGitFile:Le,stageGitFiles:Ze,unstageGitFile:x,unstageGitFiles:y,stageGitHunk:m,unstageGitHunk:Te,revertGitHunk:qe,isLinkedWorktree:Ge,getGitBranches:fe,deleteGitBranch:$e,deleteRemoteBranch:_e,removeRemote:h,generateCommitMessage:oe,generatePullRequestDescription:ae,listGitWorktrees:p,validateGitWorktree:T,createGitWorktree:C,deleteGitWorktree:w,validateWorktreeDirectory:ke,canonicalizeWorktreeState:Ke,createGitCommit(e,t,n){return v(e,t,n)},gitPush:ye,gitPull:ce,gitFetch:be,listGitStashes:ue,countGitStashFiles:Re,stashGitChanges:We,applyGitStash:Ve,popGitStash:Ce,dropGitStash:ve,checkoutBranch:Ie,createBranch:b,renameBranch:Ye,getGitLog(e,t){return le(e,t)},getCommitFiles:Fe,getCurrentGitIdentity:Pe,hasLocalIdentity:xe,setGitIdentity:je,getGitIdentities:se,createGitIdentity:we,updateGitIdentity:Qe,deleteGitIdentity:Oe,getRemotes:re,rebase:te,abortRebase:Ee,continueRebase:Ue,merge:Se,abortMerge:Ne,continueMerge:g,checkoutCommit:Xe,cherryPick:Me,revertCommit:De,resetToCommit:Be,stash:_,stashPop:ze,getConflictDetails:ne,worktree:{list:p,validate:T,bootstrapStatus:ge,preview:he,create:C,remove:w}}),j=e=>e.replace(/\\/g,`/`),ut=(e,t)=>({directory:j(t?.directory||t?.path||e),entries:(Array.isArray(t?.entries)?t.entries:[]).filter(e=>!!(e&&typeof e.name==`string`&&typeof e.path==`string`)).map(e=>({name:e.name,path:j(e.path),isDirectory:!!e.isDirectory}))}),M=(e,t)=>{let n=t||e?.();return n?{"x-opencode-directory":n}:void 0},dt=({getDirectory:e})=>({async listDirectory(t,n){let r=j(t),i=new URLSearchParams;r&&i.set(`path`,r),n?.respectGitignore&&i.set(`respectGitignore`,`true`);let a=await u(`/api/fs/list`,{query:i,headers:M(e)});if(!a.ok){let e=await a.json().catch(()=>({error:a.statusText}));throw Error(e.error||`Failed to list directory`)}return ut(r,await a.json())},async search(t){let n=new URLSearchParams,r=j(t.directory);r&&n.set(`directory`,r),n.set(`query`,t.query),n.set(`dirs`,`false`),n.set(`type`,`file`),typeof t.maxResults==`number`&&Number.isFinite(t.maxResults)&&n.set(`limit`,String(t.maxResults));let i=await u(`/api/find/file`,{query:n,headers:M(e)});if(!i.ok){let e=await i.json().catch(()=>({error:i.statusText}));throw Error(e.error||`Failed to search files`)}let a=await i.json();return(Array.isArray(a)?a:[]).map(e=>({path:j(`${r}/${e}`),preview:[j(e)]}))},async createDirectory(t){let n=j(t),r=await u(`/api/fs/mkdir`,{method:`POST`,headers:{"Content-Type":`application/json`,...M(e)},body:JSON.stringify({path:n})});if(!r.ok){let e=await r.json().catch(()=>({error:r.statusText}));throw Error(e.error||`Failed to create directory`)}let i=await r.json();return{success:!!i?.success,path:typeof i?.path==`string`?j(i.path):n}},async statFile(t,n){let r=j(t),i=new URLSearchParams({path:r});n?.allowOutsideWorkspace&&i.set(`allowOutsideWorkspace`,`true`),n?.outsideFileGrant&&i.set(`outsideFileGrant`,n.outsideFileGrant);let a=await u(`/api/fs/stat`,{query:i,headers:M(e,n?.directory)});if(!a.ok){let e=await a.json().catch(()=>({error:a.statusText}));throw Error(e.error||`Failed to stat file`)}let o=await a.json().catch(()=>({}));return{path:typeof o.path==`string`?j(o.path):r,isFile:!!o.isFile,size:typeof o.size==`number`?o.size:0,mtimeMs:typeof o.mtimeMs==`number`?o.mtimeMs:void 0,isBinary:typeof o.isBinary==`boolean`?o.isBinary:void 0}},async readFile(t,n){let r=j(t),i=new URLSearchParams({path:r});n?.allowOutsideWorkspace&&i.set(`allowOutsideWorkspace`,`true`),n?.outsideFileGrant&&i.set(`outsideFileGrant`,n.outsideFileGrant),n?.optional&&i.set(`optional`,`true`);let a=await u(`/api/fs/read`,{query:i,cache:n?.optional?`no-store`:`default`,headers:M(e,n?.directory)});if(!a.ok){let e=await a.json().catch(()=>({error:a.statusText}));throw Error(e.error||`Failed to read file`)}let o=await a.text();if(n?.optional){let e=a.headers.get(`x-openchamber-file-exists`);if(e!==`true`&&e!==`false`)throw Error(`Optional file read requires an existence response header`);return{content:o,path:r,exists:e===`true`}}return{content:o,path:r,exists:!0}},async writeFile(t,n){let r=j(t),i=await u(`/api/fs/write`,{method:`POST`,headers:{"Content-Type":`application/json`,...M(e)},body:JSON.stringify({path:r,content:n})});if(!i.ok){let e=await i.json().catch(()=>({error:i.statusText}));throw Error(e.error||`Failed to write file`)}let a=await i.json().catch(()=>({}));return{success:!!a.success,path:typeof a.path==`string`?j(a.path):r}},async delete(t){let n=j(t),r=await u(`/api/fs/delete`,{method:`POST`,headers:{"Content-Type":`application/json`,...M(e)},body:JSON.stringify({path:n})});if(!r.ok){let e=await r.json().catch(()=>({error:r.statusText}));throw Error(e.error||`Failed to delete file`)}return{success:!!(await r.json().catch(()=>({}))).success}},async rename(t,n){let r=await u(`/api/fs/rename`,{method:`POST`,headers:{"Content-Type":`application/json`,...M(e)},body:JSON.stringify({oldPath:t,newPath:n})});if(!r.ok){let e=await r.json().catch(()=>({error:r.statusText}));throw Error(e.error||`Failed to rename file`)}let i=await r.json().catch(()=>({}));return{success:!!i.success,path:typeof i.path==`string`?j(i.path):n}},async revealPath(t){let n=await u(`/api/fs/reveal`,{method:`POST`,headers:{"Content-Type":`application/json`,...M(e)},body:JSON.stringify({path:j(t)})});if(!n.ok){let e=await n.json().catch(()=>({error:n.statusText}));throw Error(e.error||`Failed to reveal path`)}return{success:!!(await n.json().catch(()=>({}))).success}},async downloadFile(t){let n=j(t),r=await u(`/api/fs/raw`,{query:{path:n,download:!0},headers:M(e)});if(!r.ok)throw Error(`Download failed (${r.status})`);let i=await r.blob(),a=URL.createObjectURL(i),o=document.createElement(`a`);o.href=a,o.download=n.split(`/`).pop()||`file`,document.body.appendChild(o),o.click(),document.body.removeChild(o),setTimeout(()=>URL.revokeObjectURL(a),100)}}),N=`/api/config/settings`,ft=`/api/config/reload`,P=e=>!e||typeof e!=`object`?{}:e,pt=()=>({async load(){let e=await u(N,{method:`GET`,headers:{Accept:`application/json`}});if(!e.ok)throw Error(`Failed to load settings: ${e.statusText}`);return{settings:P(await e.json().catch(()=>({}))),source:`web`}},async save(e){let t=await u(N,{method:`PUT`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify(e)});if(!t.ok){let e=await t.json().catch(()=>({error:t.statusText}));throw Error(e.error||`Failed to save settings`)}return P(await t.json().catch(()=>({})))},async restartOpenCode(){let e=await u(ft,{method:`POST`});if(!e.ok){let t=await e.json().catch(()=>({error:e.statusText}));throw Error(t.error||`Failed to restart OpenCode`)}return{restarted:!0}}}),mt=()=>({async requestDirectoryAccess(e){return{success:!0,path:e.path}},async startAccessingDirectory(e){return{success:!0}},async stopAccessingDirectory(e){return{success:!0}}}),ht=1500,F=5e3,gt=`openchamber-notification-claim:`,I=new Map,_t=()=>typeof document>`u`||document.visibilityState===`visible`&&document.hasFocus(),vt=e=>(typeof e?.tag==`string`?e.tag.trim():``)||[e?.sessionId,e?.kind,e?.title,e?.body].filter(e=>typeof e==`string`&&e.trim().length>0).map(e=>e.trim()).join(`|`),yt=e=>{for(let[t,n]of I)e-n>F&&I.delete(t)},bt=e=>{let t=vt(e);if(!t)return!0;let n=Date.now();if(yt(n),n-(I.get(t)??0)<F)return!1;try{if(typeof window<`u`&&window.localStorage){let e=`${gt}${t}`,r=Number(window.localStorage.getItem(e)??`0`);if(Number.isFinite(r)&&n-r<F)return I.set(t,r),!1;Number.isFinite(r)&&r>0&&window.localStorage.removeItem(e),window.localStorage.setItem(e,String(n))}}catch{}return I.set(t,n),!0},L=async()=>{if(typeof navigator>`u`||!(`serviceWorker`in navigator))return null;let e=null;try{e=await navigator.serviceWorker.getRegistration()??null}catch{e=null}if(e?.active)return e;if(!e)return null;try{return await Promise.race([navigator.serviceWorker.ready,new Promise(e=>{setTimeout(()=>e(null),ht)})])??e}catch{return e}},xt=async e=>{let t=await L();if(!t||typeof t.showNotification!=`function`)return!1;try{return await t.showNotification(e?.title??`OpenChamber`,{body:e?.body,tag:e?.tag}),!0}catch(e){return console.warn(`Failed to send notification via service worker`,e),!1}},St=async()=>{let e=await L();if(!e||!(`pushManager`in e)||!e.pushManager)return!1;try{return!!await e.pushManager.getSubscription()}catch{return!1}},Ct=async e=>{if(e?.requireHidden&&typeof document<`u`&&document.hasFocus())return!0;if(typeof Notification>`u`)return!1;if(Notification.permission==="default"&&await Notification.requestPermission()!==`granted`||Notification.permission!==`granted`)return console.warn(`Notification permission not granted`),!1;if(!_t()&&await St()||!bt(e))return!0;try{return await xt(e)||new Notification(e?.title??`OpenChamber`,{body:e?.body,tag:e?.tag}),!0}catch(e){return console.warn(`Failed to send notification`,e),!1}},wt=async e=>{if(typeof window>`u`)return!1;let t=window.__OPENCHAMBER_DESKTOP__;if(!t?.invoke)return!1;try{return await t.invoke(`desktop_notify`,{payload:{title:e?.title,body:e?.body,tag:e?.tag,kind:e?.kind,sessionId:e?.sessionId,directory:e?.directory,requireHidden:e?.requireHidden}}),!0}catch(e){return console.warn(`Failed to send native notification (desktop)`,e),!1}},Tt=()=>({async notifyAgentCompletion(e){return await wt(e)||await Ct(e)},canNotify:()=>typeof window<`u`&&window.__OPENCHAMBER_DESKTOP__?.invoke?!0:typeof Notification<`u`&&Notification.permission===`granted`}),Et=()=>({async getAvailableTools(){let e=await u(`/api/experimental/tool/ids`);if(!e.ok)throw Error(`Tools API returned ${e.status} ${e.statusText}`);let t=await e.json();if(!Array.isArray(t))throw Error(`Tools API returned invalid data format`);return t.filter(e=>typeof e==`string`&&e!==`invalid`).sort()}}),R=async(e,t)=>{try{let n=await u(e,{...t,credentials:`include`,headers:{Accept:`application/json`,...t?.headers??{}}});return n.ok?await n.json():null}catch{return null}},Dt=()=>({async getVapidPublicKey(){return R(`/api/push/vapid-public-key`)},async subscribe(e){return R(`/api/push/subscribe`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})},async unsubscribe(e){return R(`/api/push/subscribe`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})},async setVisibility(e){return R(`/api/push/visibility`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e),keepalive:!0})},async registerApnsToken(e){return R(`/api/push/apns-token`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})},async unregisterApnsToken(e){return R(`/api/push/apns-token`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})},async registerLiveActivityToken(e){return R(`/api/push/live-activity-token`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})},async unregisterLiveActivityToken(e){return R(`/api/push/live-activity-token`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})}}),z=async e=>await e.json().catch(()=>null),Ot=({urls:e})=>({async authStatus(){let e=await u(`/api/github/auth/status`,{method:`GET`,headers:{Accept:`application/json`}}),t=await z(e);if(!e.ok||!t)throw Error(t?.error||e.statusText||`Failed to load GitHub status`);return t},async authStart(){let e=await u(`/api/github/auth/start`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({})}),t=await z(e);if(!e.ok||!t||!(`deviceCode`in t))throw Error(t?.error||e.statusText||`Failed to start GitHub auth`);return t},async authComplete(e){let t=await u(`/api/github/auth/complete`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({deviceCode:e})}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to complete GitHub auth`);return n},async authDisconnect(){let e=await u(`/api/github/auth`,{method:`DELETE`,headers:{Accept:`application/json`}}),t=await z(e);if(!e.ok)throw Error(t?.error||e.statusText||`Failed to disconnect GitHub`);return{removed:!!t?.removed}},async authActivate(e){let t=await u(`/api/github/auth/activate`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({accountId:e})}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to activate GitHub account`);return n},async authSetGhCliDisabled(e){let t=await u(`/api/github/auth/gh-cli`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({disabled:e})}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to update gh CLI setting`);return{disabled:!!n.disabled}},async me(){let e=await u(`/api/github/me`,{method:`GET`,headers:{Accept:`application/json`}}),t=await z(e);if(!e.ok||!t)throw Error(t?.error||e.statusText||`Failed to fetch GitHub user`);return t},async prStatus(e,t,n,r){let i=new URLSearchParams({directory:e,branch:t,...n?{remote:n}:{},...r?.force?{force:`true`}:{}}),a=await u(`/api/github/pr/status?${i.toString()}`,{method:`GET`,headers:{Accept:`application/json`}}),o=await z(a);if(!a.ok||!o)throw Error(o?.error||a.statusText||`Failed to load PR status`);return o},async prCreate(e){let t=await u(`/api/github/pr/create`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify(e)}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to create PR`);return n},async prUpdate(e){let t=await u(`/api/github/pr/update`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify(e)}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to update PR`);return n},async prMerge(e){let t=await u(`/api/github/pr/merge`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify(e)}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to merge PR`);return n},async prReady(e){let t=await u(`/api/github/pr/ready`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify(e)}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to mark PR ready`);return n},async repoUpstream(e){let t=await u(`/api/github/repo/upstream?directory=${encodeURIComponent(e)}`,{method:`GET`,headers:{Accept:`application/json`}}),n=await z(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to detect upstream repo`);return n},async repoBranches(e,t){let n=await u(`/api/github/repo/branches?owner=${encodeURIComponent(e)}&repo=${encodeURIComponent(t)}`,{method:`GET`,headers:{Accept:`application/json`}}),r=await z(n);if(!n.ok||!r)throw Error(r?.error||n.statusText||`Failed to fetch repo branches`);return r.branches??[]},async prsList(e,t){let n=t?.page??1,r=new URLSearchParams({directory:e,page:String(n)});t?.query&&r.set(`query`,t.query);let i=await u(`/api/github/pulls/list?${r.toString()}`,{method:`GET`,headers:{Accept:`application/json`}}),a=await z(i);if(!i.ok||!a)throw Error(a?.error||i.statusText||`Failed to load pull requests`);return a},async prContext(t,n,r){let i=new URLSearchParams({directory:t,number:String(n)});r?.includeDiff&&i.set(`diff`,`1`),r?.includeCheckDetails&&i.set(`checkDetails`,`1`),r?.sourceRepo?.owner&&r.sourceRepo.repo&&(i.set(`owner`,r.sourceRepo.owner),i.set(`repo`,r.sourceRepo.repo));let a=await u(e.api(`/api/github/pulls/context`,i),{method:`GET`,headers:{Accept:`application/json`}}),o=await z(a);if(!a.ok||!o)throw Error(o?.error||a.statusText||`Failed to load pull request context`);return o},async issuesList(e,t){let n=t?.page??1,r=new URLSearchParams({directory:e,page:String(n)});t?.query&&r.set(`query`,t.query);let i=await u(`/api/github/issues/list?${r.toString()}`,{method:`GET`,headers:{Accept:`application/json`}}),a=await z(i);if(!i.ok||!a)throw Error(a?.error||i.statusText||`Failed to load issues`);return a},async issueGet(t,n,r){let i=new URLSearchParams({directory:t,number:String(n)});r?.sourceRepo?.owner&&r.sourceRepo.repo&&(i.set(`owner`,r.sourceRepo.owner),i.set(`repo`,r.sourceRepo.repo));let a=await u(e.api(`/api/github/issues/get`,i),{method:`GET`,headers:{Accept:`application/json`}}),o=await z(a);if(!a.ok||!o)throw Error(o?.error||a.statusText||`Failed to load issue`);return o},async issueComments(t,n,r){let i=new URLSearchParams({directory:t,number:String(n)});r?.sourceRepo?.owner&&r.sourceRepo.repo&&(i.set(`owner`,r.sourceRepo.owner),i.set(`repo`,r.sourceRepo.repo));let a=await u(e.api(`/api/github/issues/comments`,i),{method:`GET`,headers:{Accept:`application/json`}}),o=await z(a);if(!a.ok||!o)throw Error(o?.error||a.statusText||`Failed to load issue comments`);return o}}),B=async e=>await e.json().catch(()=>null),kt=()=>({async listClients(){let e=await u(`/api/client-auth/clients`,{method:`GET`,headers:{Accept:`application/json`}}),t=await B(e);if(!e.ok||!t)throw Error(t?.error||e.statusText||`Failed to load remote clients`);return Array.isArray(t.clients)?t.clients:[]},async createClient(e={}){let t=await u(`/api/client-auth/clients`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({label:e.label??``})}),n=await B(t);if(!t.ok||!n?.client||typeof n.token!=`string`)throw Error(n?.error||t.statusText||`Failed to create remote client token`);return n},async createPairingSession(e={}){let t=await u(`/api/client-auth/pairing/sessions`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({label:e.label??``,...e.allowedClientKinds?{allowedClientKinds:e.allowedClientKinds}:{},...e.serverUrl?{serverUrl:e.serverUrl}:{},...typeof e.includeRelay==`boolean`?{includeRelay:e.includeRelay}:{},...typeof e.includeDirect==`boolean`?{includeDirect:e.includeDirect}:{},...e.relayUrl?{relayUrl:e.relayUrl}:{}})}),n=await B(t);if(!t.ok||typeof n?.pairing?.secret!=`string`||!n?.server)throw Error(n?.error||t.statusText||`Failed to create pairing session`);return n},async listPendingPairings(){let e=await u(`/api/client-auth/pairing/sessions`,{method:`GET`,headers:{Accept:`application/json`}}),t=await B(e);if(!e.ok||!t)throw Error(t?.error||e.statusText||`Failed to load pending pairings`);return Array.isArray(t.pending)?t.pending:[]},async getPairingTransports(){let e=await u(`/api/client-auth/pairing/transports`,{method:`GET`,headers:{Accept:`application/json`}}),t=await B(e);if(!e.ok||!t)throw Error(t?.error||e.statusText||`Failed to load pairing transports`);return{local:t.local??null,lan:t.lan??null,relayAvailable:t.relayAvailable===!0,...typeof t.relayUrl==`string`?{relayUrl:t.relayUrl}:{},...typeof t.relayUrlLocked==`boolean`?{relayUrlLocked:t.relayUrlLocked}:{}}},async cancelPairing(e){let t=await u(`/api/client-auth/pairing/sessions/${encodeURIComponent(e)}`,{method:`DELETE`,headers:{Accept:`application/json`}}),n=await B(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to cancel pairing`);return{cancelled:n.cancelled===!0}},async revokeClient(e){let t=await u(`/api/client-auth/clients/${encodeURIComponent(e)}`,{method:`DELETE`,headers:{Accept:`application/json`}}),n=await B(t);if(!t.ok||!n)throw Error(n?.error||t.statusText||`Failed to revoke remote client`);return n},async purgeRevokedClients(){let e=await u(`/api/client-auth/clients`,{method:`DELETE`,headers:{Accept:`application/json`}}),t=await B(e);if(!e.ok||!t)throw Error(t?.error||e.statusText||`Failed to clear revoked clients`);return t}}),V=e=>typeof e==`object`&&!!e&&!Array.isArray(e),H=e=>typeof e==`number`&&Number.isFinite(e),At=e=>{if(!V(e)||typeof e.ok!=`boolean`)return!1;if(e.ok===!0)return V(e.session)&&typeof e.session.id==`string`&&typeof e.messageID==`string`;let t=e.phase;if(typeof t!=`string`)return!1;switch(t){case`validate`:return typeof e.error==`string`&&Array.isArray(e.errors)&&e.errors.every(e=>typeof e==`string`);case`create`:return!(typeof e.error!=`string`||`status`in e&&e.status!==void 0&&!H(e.status));case`prompt`:return V(e.session)&&typeof e.session.id==`string`&&typeof e.messageID==`string`&&typeof e.ambiguous==`boolean`&&typeof e.error==`string`&&(!(`status`in e)||e.status===void 0||H(e.status));case`conflict`:return typeof e.error==`string`;case`unavailable`:return typeof e.error==`string`;case`internal`:return typeof e.error==`string`;default:return!1}},jt=()=>({async createWithPrompt(e,t){let n;try{n=await u(`/api/openchamber/conversations`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e),signal:t})}catch{throw Error(`Conversation request failed`)}let r;try{r=await n.json()}catch{throw Error(`Unexpected server response: invalid JSON`)}if(!At(r))throw Error(`Unexpected server response: result shape mismatch`);return r}}),Mt=`https://openchamber.xiaobe.top/v1/mobile/update/check`,Nt=`https://openchamber-update.vercel.app/v1/mobile/update/check`,Pt=1e4,U=`openchamber.mobile-ota.device-id.v1`,W=e=>typeof e==`string`&&e.trim().length>0?e.trim():null,G=()=>{try{let e=E;return((typeof e.getConfig==`function`?e.getConfig():void 0)??e.config)?.OpenChamberOTA??{}}catch{return{}}},Ft=e=>e===`beta`||e===`stable`?e:G().channel===`stable`?`stable`:`beta`,It=()=>{let e=G().shellApiVersion;return typeof e==`number`&&Number.isFinite(e)&&e>0?Math.floor(e):1},Lt=()=>{let e=Je();if(e===`ios`||e===`android`)return e;try{let e=E.getPlatform();if(e===`ios`||e===`android`)return e}catch{}return`android`},Rt=()=>{if(typeof window>`u`)return null;try{return W(window.localStorage.getItem(U))}catch{return null}},K=e=>{if(!(typeof window>`u`))try{window.localStorage.setItem(U,e)}catch{}},zt=async e=>{if(e)try{let t=W((await e.getDeviceId()).deviceId);if(t)return K(t),t}catch{}let t=Rt();if(t)return t;let n=f();return K(n),n},Bt=async()=>{let e=W(`2.0.0-beta.23`);if(S())try{let{App:t}=await D(async()=>{let{App:e}=await import(`./vendor-capacitor-app-PzT84wSx.js`).then(e=>e.t);return{App:e}},__vite__mapDeps([0,1,2])),n=await t.getInfo(),r=W(n.version)??e??`0.0.0`,i=W(n.build),a=i?Number.parseInt(i,10):0;return{version:r,build:Number.isFinite(a)?a:0}}catch{}return{version:e??`0.0.0`,build:0}},q=(e,t,n)=>{let r=W(e?.version),i=W(t);if(n===`beta`&&i)return i;if(r&&r!==`builtin`)return i&&i!==r&&i.startsWith(`${r}-`)?i:r;let a=W(e?.id);return a&&a!==`builtin`?a:i??`builtin`},Vt=()=>W(`2.0.0-beta.23`),Ht=async(e,t)=>{let n=Vt();if(!e)return q(null,n,t);try{return q((await e.current()).bundle,n,t)}catch{return q(null,n,t)}},Ut=()=>[Mt,Nt],Wt=(e,t,n)=>{let r=e-n();return r<=0||t<=0?null:Math.max(1,Math.ceil(r/t))};async function Gt(e,t,n,r){try{if(S())try{let{CapacitorHttp:r}=await D(async()=>{let{CapacitorHttp:e}=await import(`./vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js`).then(e=>e.i);return{CapacitorHttp:e}},__vite__mapDeps([2,1])),i=Object.fromEntries(new Headers(t.headers).entries()),a;if(typeof t.body==`string`)try{a=JSON.parse(t.body)}catch{a=t.body}let o=await Promise.race([r.request({url:e,method:t.method||`GET`,headers:i,data:a,connectTimeout:n,readTimeout:n}),new Promise(e=>{window.setTimeout(()=>e(null),n)})]);return o?{ok:o.status>=200&&o.status<300,status:o.status,json:async()=>{if(typeof o.data==`string`)try{return JSON.parse(o.data)}catch{return o.data}return o.data}}:null}catch{}let i=await r(e,{...t,signal:AbortSignal.timeout(n)});return{ok:i.ok,status:i.status,json:()=>i.json()}}catch{return null}}var J=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return!(t.status!==`ok`||t.primaryAction!==`none`&&t.primaryAction!==`apply_ota`&&t.primaryAction!==`install_native_required`||!t.ota||typeof t.ota!=`object`||!t.native||typeof t.native!=`object`||typeof t.nextCheckInSec!=`number`||!Number.isFinite(t.nextCheckInSec))},Kt=e=>{let t=e;return t.isChannelRollback===!0||t.is_channel_rollback===!0?{...e,isChannelRollback:!0}:e};async function qt(e,t,n,r){let i=await Gt(e,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(t)},n,r);if(!i?.ok)return null;let a;try{a=await i.json()}catch{return null}return J(a)?Kt(a):null}async function Jt(e={}){let t=e.updater===void 0?await A():e.updater;if(!t&&!S())throw new O;let n=e.fetchImpl??fetch,r=e.now??Date.now,i=Ft(e.channelOverride),[a,o,s]=await Promise.all([zt(t),Bt(),Ht(t,i)]),c={channel:i,platform:Lt(),deviceId:a,nativeVersion:o.version,nativeBuild:o.build,shellApiVersion:It(),currentBundleId:s},l=Ut(),u=r()+Pt,d=null;for(let[e,t]of l.entries()){let i=Wt(u,l.length-e,r);if(i===null)break;try{let e=await qt(t,c,i,n);if(e)return e;d=Error(`Mobile OTA check failed at ${t}`)}catch(e){d=e instanceof Error?e:Error(`Mobile OTA check failed`)}}throw d??Error(`Unable to check for mobile OTA updates`)}async function Yt(e){let t=await A();if(!t)return null;try{let{bundles:n}=await t.list();return n.find(t=>t.checksum===e.checksum&&t.version===e.releaseVersion&&(t.status===`success`||t.status===`pending`))?.id??null}catch{return null}}async function Xt(e){let t=await A();if(!t)throw new O;return(await t.download({url:e.url,version:e.releaseVersion,checksum:e.checksum,...e.sessionKey?{sessionKey:e.sessionKey}:{}})).id}async function Zt(){let e=await A();if(!e)throw new O;await e.reload()}async function Qt(e){let t=await A();if(!t)throw new O;await t.next({id:e})}var Y=null,X=e=>typeof e==`string`&&e.trim().length>0?e.trim():null,Z=async()=>{let e=X(`2.0.0-beta.23`);try{if(E.isNativePlatform()){let{App:t}=await D(async()=>{let{App:e}=await import(`./vendor-capacitor-app-PzT84wSx.js`).then(e=>e.t);return{App:e}},__vite__mapDeps([0,1,2]));return X((await t.getInfo()).version)??e??`unknown`}}catch{}return e??`unknown`},$t=()=>({async checkForOtaUpdate(e){if(!k()&&!E.isNativePlatform())throw new O;return Jt(e?.channelOverride?{channelOverride:e.channelOverride}:{})},async downloadOtaUpdate(e){if(!k())throw new O;let t=await Yt(e);return t?(Y=t,{skipped:!0}):(Y=await Xt(e),{skipped:!1})},async applyOtaUpdateNow(){if(!k())throw new O;await Zt()},async queueOtaUpdateForNextLaunch(){if(!k())throw new O;let e=Y;if(!e)throw Error(`No downloaded OTA bundle to queue`);await Qt(e)},async getOtaStatus(){if(!k())return{supported:!1,currentBundleId:`builtin`,nativeVersion:await Z()};let e=await A();if(!e)return{supported:!1,currentBundleId:`builtin`,nativeVersion:await Z()};try{let[t,n]=await Promise.all([e.current(),Z()]);return{supported:!0,currentBundleId:X(t.bundle?.id)||`builtin`,nativeVersion:n}}catch{return{supported:!0,currentBundleId:`builtin`,nativeVersion:await Z()}}}}),en=()=>({api:(...e)=>s().api(...e),authenticatedAsset:(...e)=>s().authenticatedAsset(...e),auth:(...e)=>s().auth(...e),health:(...e)=>s().health(...e),rawFile:(...e)=>s().rawFile(...e),sse:(...e)=>s().sse(...e),websocket:(...e)=>s().websocket(...e)}),tn=(e={})=>{let t=e.urls??o();c(t);let n=en();return{runtime:{platform:`web`,isDesktop:!1,isVSCode:!1,label:`web`},terminal:ct(),git:lt(),files:dt({urls:n,getDirectory:()=>ie.getState().currentDirectory}),settings:pt(),permissions:mt(),notifications:Tt(),github:Ot({urls:n}),push:Dt(),mobileUpdates:$t(),clientAuth:kt(),tools:Et(),conversations:jt()}},Q=(e,t)=>{if(!e||!t)return!1;try{return new URL(e).origin===new URL(t).origin}catch{return!1}},$=Promise.resolve(),nn=()=>$,rn=()=>{let o=typeof window.__OPENCHAMBER_API_BASE_URL__==`string`?window.__OPENCHAMBER_API_BASE_URL__.trim():``,s=typeof window.__OPENCHAMBER_CLIENT_TOKEN__==`string`?window.__OPENCHAMBER_CLIENT_TOKEN__.trim():``,c=typeof window.__OPENCHAMBER_LOCAL_ORIGIN__==`string`?window.__OPENCHAMBER_LOCAL_ORIGIN__.trim():``,u=l({apiBaseUrl:o||void 0,realtimeBaseUrl:o||void 0});a({apiBaseUrl:o,runtimeKey:Q(o,c)?`local`:null}),n(s||null),t(window.__OPENCHAMBER_RUNTIME_HEADERS__||null),i(o||void 0).catch(()=>{}),c&&!Q(o,c)&&Object.keys(r()).length>0&&e(c).catch(()=>{}),d();let f=window.__OPENCHAMBER_RELAY_HOST_ID__;return $=Promise.race([ee(typeof f==`string`&&f?f:void 0).catch(()=>{}),new Promise(e=>{window.setTimeout(e,1e4)})]),tn({urls:u})};export{nn as n,rn as t};