@mindstudio-ai/interface 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -649,10 +649,14 @@ interface UploadToken {
649
649
  key: string;
650
650
  /** The URL the file will be readable at once uploaded. */
651
651
  url: string;
652
- /** The scoped presigned POST the browser submits to. */
652
+ /**
653
+ * The scoped presigned POST the browser submits to, and the grant a file
654
+ * too large for one request goes up in parts with.
655
+ */
653
656
  upload: {
654
657
  url: string;
655
658
  fields: Record<string, string>;
659
+ grant?: string;
656
660
  };
657
661
  }
658
662
  /**
@@ -699,6 +703,12 @@ declare const platform: {
699
703
  * is ready to render (`url` is a stable on-domain link the app can use in
700
704
  * `<img>` / `fetch` / `<a download>`).
701
705
  *
706
+ * A large file (64 MB or more) goes up in parts, each retried on its own,
707
+ * and resumes: calling `platform.upload` again with a token for the same
708
+ * key and the same file, after a dropped connection, an aborted upload or a
709
+ * reload, sends only what hadn't arrived. To resume across a reload, mint
710
+ * the token for a key of your own (`createUploadToken({ key })`).
711
+ *
702
712
  * @example
703
713
  * ```ts
704
714
  * const token = await api.getUploadSlot({ contentType: file.type });
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{a as g,b as E,c as G,d as ie,e as de,f as u,g as K,h as m,i as F}from"./chunk-5E7SM65A.js";function se(){return new Proxy({},{get(e,a){return async(o,n)=>{let t=u(),i=t.methods[a];if(!i)throw new g(`Method "${a}" not found. Available methods: ${Object.keys(t.methods).join(", ")||"(none)"}`,"method_not_found");let r=m(`/_/methods/${i}/invoke`),l=n?.stream===!0,c=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.token}`},body:JSON.stringify({input:o??{},...l&&{stream:!0}})});if(!c.ok)throw await E(c,`Method "${a}" failed: ${c.status} ${c.statusText}`,"method_error");if(!l)return(await c.json()).output;let y=c.body.getReader(),d=new TextDecoder,s="",f,p;for(;;){let{done:w,value:S}=await y.read();if(w)break;s+=d.decode(S,{stream:!0});let A=s.split(`
2
- `);s=A.pop()??"";for(let v of A){if(!v.startsWith("data: "))continue;let I=v.slice(6);try{let h=JSON.parse(I);h.type==="token"&&n?.onToken&&h.text?n.onToken(h.text):h.type==="data"&&n?.onStreamData?n.onStreamData(h.data):h.type==="error"&&h.error?(p=h.error,n?.onStreamError&&n.onStreamError(h.error)):h.type==="done"&&(f=h.output)}catch{}}}if(f===void 0&&p!==void 0)throw new g(p,"method_error");if(f===void 0)throw new g("Stream ended without a done event","stream_incomplete");return f}}})}var O="/_/agent",le=32e3;async function k(e,a,o){let t={Authorization:`Bearer ${u().token}`};o!==void 0&&(t["Content-Type"]="application/json");let i=await fetch(m(`${O}${e}`),{method:a,headers:t,...o!==void 0&&{body:JSON.stringify(o)}});if(!i.ok)throw await E(i,`Agent chat request failed: ${i.status} ${i.statusText}`,"agent_chat_error");return await i.json()}async function U(e,a){let o=u(),n=await fetch(m(`${O}/threads/${e}/tool-results`),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify(a)});n.ok||console.warn(`[agent-chat] could not deliver client tool result: ${n.status} ${n.statusText}`)}function ce(e,a){switch(a.onEvent?.(e),e.type){case"text":a.onText?.(e.text);break;case"thinking":a.onThinking?.(e.text);break;case"thinking_complete":a.onThinkingComplete?.(e.thinking,e.signature);break;case"tool_call_start":a.onToolCallStart?.(e.id,e.name);break;case"tool_call_result":a.onToolCallResult?.(e.id,e.output);break;case"error":a.onError?.(e.error);break;case"client_tool_call":break;case"tool_use":case"tool_input_delta":case"done":break}}async function ue(e,a,o){if(!o){console.warn(`[agent-chat] client tool "${a.name}" was invoked but no handler is registered \u2014 call chat.registerClientTool()`),await U(e,{callId:a.id,error:"unhandled_client_tool"});return}try{let n=await o(a.input);if(n!==void 0){let t=JSON.stringify(n);if(t&&t.length>le){await U(e,{callId:a.id,error:"result_too_large"});return}}await U(e,{callId:a.id,result:n})}catch(n){await U(e,{callId:a.id,error:n instanceof Error?n.message:String(n)})}}function ge(){let e=new Map,a=new Map,o=n=>{let t=u().token;t&&!e.has(n)&&e.set(n,t)};return{async createThread(){let n=u().token,t=await k("/threads","POST");return n&&e.set(t.id,n),t},listThreads(n){let t=n?`?cursor=${encodeURIComponent(n)}`:"";return k(`/threads${t}`,"GET")},getThread(n){return k(`/threads/${n}`,"GET")},async updateThread(n,t){await k(`/threads/${n}`,"PATCH",{title:t})},async deleteThread(n){await k(`/threads/${n}`,"DELETE")},async claimThread(n,t){let i=t?.previousToken??e.get(n);if(!i)throw new g("No pre-login token known for this thread \u2014 pass options.previousToken.","missing_previous_token",400);await k(`/threads/${n}/claim`,"POST",{previousToken:i}),e.delete(n)},registerClientTool(n,t){return a.set(n,t),()=>{a.get(n)===t&&a.delete(n)}},sendMessage(n,t,i,r){o(n);let l=new AbortController,c=i??{};c.signal&&(c.signal.aborted?l.abort():c.signal.addEventListener("abort",()=>l.abort(),{once:!0}));let d=(async()=>{let s=u(),f=m(`${O}/threads/${n}/messages`),p=await fetch(f,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.token}`},body:JSON.stringify({content:t,...r?.attachments?.length&&{attachments:r.attachments}}),signal:l.signal});if(!p.ok)throw await E(p,`Send message failed: ${p.status} ${p.statusText}`,"agent_chat_error");let w=p.body.getReader(),S=new TextDecoder,A="",v;for(;;){let{done:I,value:h}=await w.read();if(I)break;A+=S.decode(h,{stream:!0});let H=A.split(`
3
- `);A=H.pop()??"";for(let $ of H){if(!$.startsWith("data: "))continue;let te=$.slice(6);try{let T=JSON.parse(te);if(T.type==="done"&&(v={stopReason:T.stopReason,usage:T.usage}),T.type==="client_tool_call"){let oe=a.get(T.name)??(c.onClientToolCall?re=>c.onClientToolCall(T.name,re,T.id):void 0);ue(n,T,oe)}ce(T,c)}catch{}}}if(!v)throw new g("Stream ended without a done event","stream_incomplete");return v})();return d.abort=()=>l.abort(),d}}}var me={get basePath(){return K()},async uploadFile(e,a){let o=u(),{onProgress:n,signal:t}=a??{};t?.throwIfAborted();let i=m("/_/generate-upload-request"),r=await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${o.token}`,"Content-Type":"application/json"},body:JSON.stringify({filename:e.name,contentType:e.type||"application/octet-stream"}),signal:t});if(!r.ok)throw new g(`Upload presign failed: ${r.status} ${r.statusText}`,"upload_error",r.status);let{uploadUrl:l,uploadFields:c,publicUrl:y}=await r.json(),d=new FormData;for(let[f,p]of Object.entries(c))d.append(f,p);if(d.append("file",e),n)return await V(l,d,n,t),y;let s=await fetch(l,{method:"POST",body:d,signal:t});if(!s.ok)throw new g(`File upload failed: ${s.status} ${s.statusText}`,"upload_error",s.status);return y},async upload(e,a,o){let{onProgress:n,signal:t}=o??{};t?.throwIfAborted();let i=new FormData;for(let[r,l]of Object.entries(e.upload.fields))i.append(r,l);if(i.append("file",a),n)await V(e.upload.url,i,n,t);else{let r=await fetch(e.upload.url,{method:"POST",body:i,signal:t});if(!r.ok)throw new g(`File upload failed: ${r.status} ${r.statusText}`,"upload_error",r.status)}return{key:e.key,url:e.url}}};function V(e,a,o,n){return new Promise((t,i)=>{let r=new XMLHttpRequest;if(r.upload.addEventListener("progress",l=>{l.lengthComputable&&o(l.loaded/l.total)}),r.addEventListener("load",()=>{r.status>=200&&r.status<300?(o(1),t()):i(new g(`File upload failed: ${r.status} ${r.statusText}`,"upload_error",r.status))}),r.addEventListener("error",()=>{i(new g("File upload failed: network error","upload_error"))}),r.addEventListener("abort",()=>{i(new DOMException("Upload aborted","AbortError"))}),n){if(n.aborted){i(new DOMException("Upload aborted","AbortError"));return}n.addEventListener("abort",()=>r.abort(),{once:!0})}r.open("POST",e),r.send(a)})}var _=[{code:"AF",dialCode:"+93",name:"Afghanistan",flag:"\u{1F1E6}\u{1F1EB}"},{code:"AL",dialCode:"+355",name:"Albania",flag:"\u{1F1E6}\u{1F1F1}"},{code:"DZ",dialCode:"+213",name:"Algeria",flag:"\u{1F1E9}\u{1F1FF}"},{code:"AD",dialCode:"+376",name:"Andorra",flag:"\u{1F1E6}\u{1F1E9}"},{code:"AO",dialCode:"+244",name:"Angola",flag:"\u{1F1E6}\u{1F1F4}"},{code:"AG",dialCode:"+1",name:"Antigua and Barbuda",flag:"\u{1F1E6}\u{1F1EC}"},{code:"AR",dialCode:"+54",name:"Argentina",flag:"\u{1F1E6}\u{1F1F7}"},{code:"AM",dialCode:"+374",name:"Armenia",flag:"\u{1F1E6}\u{1F1F2}"},{code:"AU",dialCode:"+61",name:"Australia",flag:"\u{1F1E6}\u{1F1FA}"},{code:"AT",dialCode:"+43",name:"Austria",flag:"\u{1F1E6}\u{1F1F9}"},{code:"AZ",dialCode:"+994",name:"Azerbaijan",flag:"\u{1F1E6}\u{1F1FF}"},{code:"BS",dialCode:"+1",name:"Bahamas",flag:"\u{1F1E7}\u{1F1F8}"},{code:"BH",dialCode:"+973",name:"Bahrain",flag:"\u{1F1E7}\u{1F1ED}"},{code:"BD",dialCode:"+880",name:"Bangladesh",flag:"\u{1F1E7}\u{1F1E9}"},{code:"BB",dialCode:"+1",name:"Barbados",flag:"\u{1F1E7}\u{1F1E7}"},{code:"BY",dialCode:"+375",name:"Belarus",flag:"\u{1F1E7}\u{1F1FE}"},{code:"BE",dialCode:"+32",name:"Belgium",flag:"\u{1F1E7}\u{1F1EA}"},{code:"BZ",dialCode:"+501",name:"Belize",flag:"\u{1F1E7}\u{1F1FF}"},{code:"BJ",dialCode:"+229",name:"Benin",flag:"\u{1F1E7}\u{1F1EF}"},{code:"BT",dialCode:"+975",name:"Bhutan",flag:"\u{1F1E7}\u{1F1F9}"},{code:"BO",dialCode:"+591",name:"Bolivia",flag:"\u{1F1E7}\u{1F1F4}"},{code:"BA",dialCode:"+387",name:"Bosnia and Herzegovina",flag:"\u{1F1E7}\u{1F1E6}"},{code:"BW",dialCode:"+267",name:"Botswana",flag:"\u{1F1E7}\u{1F1FC}"},{code:"BR",dialCode:"+55",name:"Brazil",flag:"\u{1F1E7}\u{1F1F7}"},{code:"BN",dialCode:"+673",name:"Brunei",flag:"\u{1F1E7}\u{1F1F3}"},{code:"BG",dialCode:"+359",name:"Bulgaria",flag:"\u{1F1E7}\u{1F1EC}"},{code:"BF",dialCode:"+226",name:"Burkina Faso",flag:"\u{1F1E7}\u{1F1EB}"},{code:"BI",dialCode:"+257",name:"Burundi",flag:"\u{1F1E7}\u{1F1EE}"},{code:"CV",dialCode:"+238",name:"Cabo Verde",flag:"\u{1F1E8}\u{1F1FB}"},{code:"KH",dialCode:"+855",name:"Cambodia",flag:"\u{1F1F0}\u{1F1ED}"},{code:"CM",dialCode:"+237",name:"Cameroon",flag:"\u{1F1E8}\u{1F1F2}"},{code:"CA",dialCode:"+1",name:"Canada",flag:"\u{1F1E8}\u{1F1E6}"},{code:"CF",dialCode:"+236",name:"Central African Republic",flag:"\u{1F1E8}\u{1F1EB}"},{code:"TD",dialCode:"+235",name:"Chad",flag:"\u{1F1F9}\u{1F1E9}"},{code:"CL",dialCode:"+56",name:"Chile",flag:"\u{1F1E8}\u{1F1F1}"},{code:"CN",dialCode:"+86",name:"China",flag:"\u{1F1E8}\u{1F1F3}"},{code:"CO",dialCode:"+57",name:"Colombia",flag:"\u{1F1E8}\u{1F1F4}"},{code:"KM",dialCode:"+269",name:"Comoros",flag:"\u{1F1F0}\u{1F1F2}"},{code:"CG",dialCode:"+242",name:"Congo",flag:"\u{1F1E8}\u{1F1EC}"},{code:"CD",dialCode:"+243",name:"Congo (DRC)",flag:"\u{1F1E8}\u{1F1E9}"},{code:"CR",dialCode:"+506",name:"Costa Rica",flag:"\u{1F1E8}\u{1F1F7}"},{code:"HR",dialCode:"+385",name:"Croatia",flag:"\u{1F1ED}\u{1F1F7}"},{code:"CU",dialCode:"+53",name:"Cuba",flag:"\u{1F1E8}\u{1F1FA}"},{code:"CY",dialCode:"+357",name:"Cyprus",flag:"\u{1F1E8}\u{1F1FE}"},{code:"CZ",dialCode:"+420",name:"Czechia",flag:"\u{1F1E8}\u{1F1FF}"},{code:"CI",dialCode:"+225",name:"Cote d'Ivoire",flag:"\u{1F1E8}\u{1F1EE}"},{code:"DK",dialCode:"+45",name:"Denmark",flag:"\u{1F1E9}\u{1F1F0}"},{code:"DJ",dialCode:"+253",name:"Djibouti",flag:"\u{1F1E9}\u{1F1EF}"},{code:"DM",dialCode:"+1",name:"Dominica",flag:"\u{1F1E9}\u{1F1F2}"},{code:"DO",dialCode:"+1",name:"Dominican Republic",flag:"\u{1F1E9}\u{1F1F4}"},{code:"EC",dialCode:"+593",name:"Ecuador",flag:"\u{1F1EA}\u{1F1E8}"},{code:"EG",dialCode:"+20",name:"Egypt",flag:"\u{1F1EA}\u{1F1EC}"},{code:"SV",dialCode:"+503",name:"El Salvador",flag:"\u{1F1F8}\u{1F1FB}"},{code:"GQ",dialCode:"+240",name:"Equatorial Guinea",flag:"\u{1F1EC}\u{1F1F6}"},{code:"ER",dialCode:"+291",name:"Eritrea",flag:"\u{1F1EA}\u{1F1F7}"},{code:"EE",dialCode:"+372",name:"Estonia",flag:"\u{1F1EA}\u{1F1EA}"},{code:"SZ",dialCode:"+268",name:"Eswatini",flag:"\u{1F1F8}\u{1F1FF}"},{code:"ET",dialCode:"+251",name:"Ethiopia",flag:"\u{1F1EA}\u{1F1F9}"},{code:"FJ",dialCode:"+679",name:"Fiji",flag:"\u{1F1EB}\u{1F1EF}"},{code:"FI",dialCode:"+358",name:"Finland",flag:"\u{1F1EB}\u{1F1EE}"},{code:"FR",dialCode:"+33",name:"France",flag:"\u{1F1EB}\u{1F1F7}"},{code:"GA",dialCode:"+241",name:"Gabon",flag:"\u{1F1EC}\u{1F1E6}"},{code:"GM",dialCode:"+220",name:"Gambia",flag:"\u{1F1EC}\u{1F1F2}"},{code:"GE",dialCode:"+995",name:"Georgia",flag:"\u{1F1EC}\u{1F1EA}"},{code:"DE",dialCode:"+49",name:"Germany",flag:"\u{1F1E9}\u{1F1EA}"},{code:"GH",dialCode:"+233",name:"Ghana",flag:"\u{1F1EC}\u{1F1ED}"},{code:"GR",dialCode:"+30",name:"Greece",flag:"\u{1F1EC}\u{1F1F7}"},{code:"GD",dialCode:"+1",name:"Grenada",flag:"\u{1F1EC}\u{1F1E9}"},{code:"GT",dialCode:"+502",name:"Guatemala",flag:"\u{1F1EC}\u{1F1F9}"},{code:"GN",dialCode:"+224",name:"Guinea",flag:"\u{1F1EC}\u{1F1F3}"},{code:"GW",dialCode:"+245",name:"Guinea-Bissau",flag:"\u{1F1EC}\u{1F1FC}"},{code:"GY",dialCode:"+592",name:"Guyana",flag:"\u{1F1EC}\u{1F1FE}"},{code:"HT",dialCode:"+509",name:"Haiti",flag:"\u{1F1ED}\u{1F1F9}"},{code:"HN",dialCode:"+504",name:"Honduras",flag:"\u{1F1ED}\u{1F1F3}"},{code:"HK",dialCode:"+852",name:"Hong Kong",flag:"\u{1F1ED}\u{1F1F0}"},{code:"HU",dialCode:"+36",name:"Hungary",flag:"\u{1F1ED}\u{1F1FA}"},{code:"IS",dialCode:"+354",name:"Iceland",flag:"\u{1F1EE}\u{1F1F8}"},{code:"IN",dialCode:"+91",name:"India",flag:"\u{1F1EE}\u{1F1F3}"},{code:"ID",dialCode:"+62",name:"Indonesia",flag:"\u{1F1EE}\u{1F1E9}"},{code:"IR",dialCode:"+98",name:"Iran",flag:"\u{1F1EE}\u{1F1F7}"},{code:"IQ",dialCode:"+964",name:"Iraq",flag:"\u{1F1EE}\u{1F1F6}"},{code:"IE",dialCode:"+353",name:"Ireland",flag:"\u{1F1EE}\u{1F1EA}"},{code:"IL",dialCode:"+972",name:"Israel",flag:"\u{1F1EE}\u{1F1F1}"},{code:"IT",dialCode:"+39",name:"Italy",flag:"\u{1F1EE}\u{1F1F9}"},{code:"JM",dialCode:"+1",name:"Jamaica",flag:"\u{1F1EF}\u{1F1F2}"},{code:"JP",dialCode:"+81",name:"Japan",flag:"\u{1F1EF}\u{1F1F5}"},{code:"JO",dialCode:"+962",name:"Jordan",flag:"\u{1F1EF}\u{1F1F4}"},{code:"KZ",dialCode:"+7",name:"Kazakhstan",flag:"\u{1F1F0}\u{1F1FF}"},{code:"KE",dialCode:"+254",name:"Kenya",flag:"\u{1F1F0}\u{1F1EA}"},{code:"KI",dialCode:"+686",name:"Kiribati",flag:"\u{1F1F0}\u{1F1EE}"},{code:"KW",dialCode:"+965",name:"Kuwait",flag:"\u{1F1F0}\u{1F1FC}"},{code:"KG",dialCode:"+996",name:"Kyrgyzstan",flag:"\u{1F1F0}\u{1F1EC}"},{code:"LA",dialCode:"+856",name:"Laos",flag:"\u{1F1F1}\u{1F1E6}"},{code:"LV",dialCode:"+371",name:"Latvia",flag:"\u{1F1F1}\u{1F1FB}"},{code:"LB",dialCode:"+961",name:"Lebanon",flag:"\u{1F1F1}\u{1F1E7}"},{code:"LS",dialCode:"+266",name:"Lesotho",flag:"\u{1F1F1}\u{1F1F8}"},{code:"LR",dialCode:"+231",name:"Liberia",flag:"\u{1F1F1}\u{1F1F7}"},{code:"LY",dialCode:"+218",name:"Libya",flag:"\u{1F1F1}\u{1F1FE}"},{code:"LI",dialCode:"+423",name:"Liechtenstein",flag:"\u{1F1F1}\u{1F1EE}"},{code:"LT",dialCode:"+370",name:"Lithuania",flag:"\u{1F1F1}\u{1F1F9}"},{code:"LU",dialCode:"+352",name:"Luxembourg",flag:"\u{1F1F1}\u{1F1FA}"},{code:"MO",dialCode:"+853",name:"Macao",flag:"\u{1F1F2}\u{1F1F4}"},{code:"MG",dialCode:"+261",name:"Madagascar",flag:"\u{1F1F2}\u{1F1EC}"},{code:"MW",dialCode:"+265",name:"Malawi",flag:"\u{1F1F2}\u{1F1FC}"},{code:"MY",dialCode:"+60",name:"Malaysia",flag:"\u{1F1F2}\u{1F1FE}"},{code:"MV",dialCode:"+960",name:"Maldives",flag:"\u{1F1F2}\u{1F1FB}"},{code:"ML",dialCode:"+223",name:"Mali",flag:"\u{1F1F2}\u{1F1F1}"},{code:"MT",dialCode:"+356",name:"Malta",flag:"\u{1F1F2}\u{1F1F9}"},{code:"MH",dialCode:"+692",name:"Marshall Islands",flag:"\u{1F1F2}\u{1F1ED}"},{code:"MR",dialCode:"+222",name:"Mauritania",flag:"\u{1F1F2}\u{1F1F7}"},{code:"MU",dialCode:"+230",name:"Mauritius",flag:"\u{1F1F2}\u{1F1FA}"},{code:"MX",dialCode:"+52",name:"Mexico",flag:"\u{1F1F2}\u{1F1FD}"},{code:"FM",dialCode:"+691",name:"Micronesia",flag:"\u{1F1EB}\u{1F1F2}"},{code:"MD",dialCode:"+373",name:"Moldova",flag:"\u{1F1F2}\u{1F1E9}"},{code:"MC",dialCode:"+377",name:"Monaco",flag:"\u{1F1F2}\u{1F1E8}"},{code:"MN",dialCode:"+976",name:"Mongolia",flag:"\u{1F1F2}\u{1F1F3}"},{code:"ME",dialCode:"+382",name:"Montenegro",flag:"\u{1F1F2}\u{1F1EA}"},{code:"MA",dialCode:"+212",name:"Morocco",flag:"\u{1F1F2}\u{1F1E6}"},{code:"MZ",dialCode:"+258",name:"Mozambique",flag:"\u{1F1F2}\u{1F1FF}"},{code:"MM",dialCode:"+95",name:"Myanmar",flag:"\u{1F1F2}\u{1F1F2}"},{code:"NA",dialCode:"+264",name:"Namibia",flag:"\u{1F1F3}\u{1F1E6}"},{code:"NR",dialCode:"+674",name:"Nauru",flag:"\u{1F1F3}\u{1F1F7}"},{code:"NP",dialCode:"+977",name:"Nepal",flag:"\u{1F1F3}\u{1F1F5}"},{code:"NL",dialCode:"+31",name:"Netherlands",flag:"\u{1F1F3}\u{1F1F1}"},{code:"NZ",dialCode:"+64",name:"New Zealand",flag:"\u{1F1F3}\u{1F1FF}"},{code:"NI",dialCode:"+505",name:"Nicaragua",flag:"\u{1F1F3}\u{1F1EE}"},{code:"NE",dialCode:"+227",name:"Niger",flag:"\u{1F1F3}\u{1F1EA}"},{code:"NG",dialCode:"+234",name:"Nigeria",flag:"\u{1F1F3}\u{1F1EC}"},{code:"KP",dialCode:"+850",name:"North Korea",flag:"\u{1F1F0}\u{1F1F5}"},{code:"MK",dialCode:"+389",name:"North Macedonia",flag:"\u{1F1F2}\u{1F1F0}"},{code:"NO",dialCode:"+47",name:"Norway",flag:"\u{1F1F3}\u{1F1F4}"},{code:"OM",dialCode:"+968",name:"Oman",flag:"\u{1F1F4}\u{1F1F2}"},{code:"PK",dialCode:"+92",name:"Pakistan",flag:"\u{1F1F5}\u{1F1F0}"},{code:"PW",dialCode:"+680",name:"Palau",flag:"\u{1F1F5}\u{1F1FC}"},{code:"PS",dialCode:"+970",name:"Palestine",flag:"\u{1F1F5}\u{1F1F8}"},{code:"PA",dialCode:"+507",name:"Panama",flag:"\u{1F1F5}\u{1F1E6}"},{code:"PG",dialCode:"+675",name:"Papua New Guinea",flag:"\u{1F1F5}\u{1F1EC}"},{code:"PY",dialCode:"+595",name:"Paraguay",flag:"\u{1F1F5}\u{1F1FE}"},{code:"PE",dialCode:"+51",name:"Peru",flag:"\u{1F1F5}\u{1F1EA}"},{code:"PH",dialCode:"+63",name:"Philippines",flag:"\u{1F1F5}\u{1F1ED}"},{code:"PL",dialCode:"+48",name:"Poland",flag:"\u{1F1F5}\u{1F1F1}"},{code:"PT",dialCode:"+351",name:"Portugal",flag:"\u{1F1F5}\u{1F1F9}"},{code:"PR",dialCode:"+1",name:"Puerto Rico",flag:"\u{1F1F5}\u{1F1F7}"},{code:"QA",dialCode:"+974",name:"Qatar",flag:"\u{1F1F6}\u{1F1E6}"},{code:"RO",dialCode:"+40",name:"Romania",flag:"\u{1F1F7}\u{1F1F4}"},{code:"RU",dialCode:"+7",name:"Russia",flag:"\u{1F1F7}\u{1F1FA}"},{code:"RW",dialCode:"+250",name:"Rwanda",flag:"\u{1F1F7}\u{1F1FC}"},{code:"KN",dialCode:"+1",name:"Saint Kitts and Nevis",flag:"\u{1F1F0}\u{1F1F3}"},{code:"LC",dialCode:"+1",name:"Saint Lucia",flag:"\u{1F1F1}\u{1F1E8}"},{code:"VC",dialCode:"+1",name:"Saint Vincent and the Grenadines",flag:"\u{1F1FB}\u{1F1E8}"},{code:"WS",dialCode:"+685",name:"Samoa",flag:"\u{1F1FC}\u{1F1F8}"},{code:"SM",dialCode:"+378",name:"San Marino",flag:"\u{1F1F8}\u{1F1F2}"},{code:"ST",dialCode:"+239",name:"Sao Tome and Principe",flag:"\u{1F1F8}\u{1F1F9}"},{code:"SA",dialCode:"+966",name:"Saudi Arabia",flag:"\u{1F1F8}\u{1F1E6}"},{code:"SN",dialCode:"+221",name:"Senegal",flag:"\u{1F1F8}\u{1F1F3}"},{code:"RS",dialCode:"+381",name:"Serbia",flag:"\u{1F1F7}\u{1F1F8}"},{code:"SC",dialCode:"+248",name:"Seychelles",flag:"\u{1F1F8}\u{1F1E8}"},{code:"SL",dialCode:"+232",name:"Sierra Leone",flag:"\u{1F1F8}\u{1F1F1}"},{code:"SG",dialCode:"+65",name:"Singapore",flag:"\u{1F1F8}\u{1F1EC}"},{code:"SK",dialCode:"+421",name:"Slovakia",flag:"\u{1F1F8}\u{1F1F0}"},{code:"SI",dialCode:"+386",name:"Slovenia",flag:"\u{1F1F8}\u{1F1EE}"},{code:"SB",dialCode:"+677",name:"Solomon Islands",flag:"\u{1F1F8}\u{1F1E7}"},{code:"SO",dialCode:"+252",name:"Somalia",flag:"\u{1F1F8}\u{1F1F4}"},{code:"ZA",dialCode:"+27",name:"South Africa",flag:"\u{1F1FF}\u{1F1E6}"},{code:"KR",dialCode:"+82",name:"South Korea",flag:"\u{1F1F0}\u{1F1F7}"},{code:"SS",dialCode:"+211",name:"South Sudan",flag:"\u{1F1F8}\u{1F1F8}"},{code:"ES",dialCode:"+34",name:"Spain",flag:"\u{1F1EA}\u{1F1F8}"},{code:"LK",dialCode:"+94",name:"Sri Lanka",flag:"\u{1F1F1}\u{1F1F0}"},{code:"SD",dialCode:"+249",name:"Sudan",flag:"\u{1F1F8}\u{1F1E9}"},{code:"SR",dialCode:"+597",name:"Suriname",flag:"\u{1F1F8}\u{1F1F7}"},{code:"SE",dialCode:"+46",name:"Sweden",flag:"\u{1F1F8}\u{1F1EA}"},{code:"CH",dialCode:"+41",name:"Switzerland",flag:"\u{1F1E8}\u{1F1ED}"},{code:"SY",dialCode:"+963",name:"Syria",flag:"\u{1F1F8}\u{1F1FE}"},{code:"TW",dialCode:"+886",name:"Taiwan",flag:"\u{1F1F9}\u{1F1FC}"},{code:"TJ",dialCode:"+992",name:"Tajikistan",flag:"\u{1F1F9}\u{1F1EF}"},{code:"TZ",dialCode:"+255",name:"Tanzania",flag:"\u{1F1F9}\u{1F1FF}"},{code:"TH",dialCode:"+66",name:"Thailand",flag:"\u{1F1F9}\u{1F1ED}"},{code:"TL",dialCode:"+670",name:"Timor-Leste",flag:"\u{1F1F9}\u{1F1F1}"},{code:"TG",dialCode:"+228",name:"Togo",flag:"\u{1F1F9}\u{1F1EC}"},{code:"TO",dialCode:"+676",name:"Tonga",flag:"\u{1F1F9}\u{1F1F4}"},{code:"TT",dialCode:"+1",name:"Trinidad and Tobago",flag:"\u{1F1F9}\u{1F1F9}"},{code:"TN",dialCode:"+216",name:"Tunisia",flag:"\u{1F1F9}\u{1F1F3}"},{code:"TR",dialCode:"+90",name:"Turkey",flag:"\u{1F1F9}\u{1F1F7}"},{code:"TM",dialCode:"+993",name:"Turkmenistan",flag:"\u{1F1F9}\u{1F1F2}"},{code:"TV",dialCode:"+688",name:"Tuvalu",flag:"\u{1F1F9}\u{1F1FB}"},{code:"UG",dialCode:"+256",name:"Uganda",flag:"\u{1F1FA}\u{1F1EC}"},{code:"UA",dialCode:"+380",name:"Ukraine",flag:"\u{1F1FA}\u{1F1E6}"},{code:"AE",dialCode:"+971",name:"United Arab Emirates",flag:"\u{1F1E6}\u{1F1EA}"},{code:"GB",dialCode:"+44",name:"United Kingdom",flag:"\u{1F1EC}\u{1F1E7}"},{code:"US",dialCode:"+1",name:"United States",flag:"\u{1F1FA}\u{1F1F8}"},{code:"UY",dialCode:"+598",name:"Uruguay",flag:"\u{1F1FA}\u{1F1FE}"},{code:"UZ",dialCode:"+998",name:"Uzbekistan",flag:"\u{1F1FA}\u{1F1FF}"},{code:"VU",dialCode:"+678",name:"Vanuatu",flag:"\u{1F1FB}\u{1F1FA}"},{code:"VA",dialCode:"+39",name:"Vatican City",flag:"\u{1F1FB}\u{1F1E6}"},{code:"VE",dialCode:"+58",name:"Venezuela",flag:"\u{1F1FB}\u{1F1EA}"},{code:"VN",dialCode:"+84",name:"Vietnam",flag:"\u{1F1FB}\u{1F1F3}"},{code:"YE",dialCode:"+967",name:"Yemen",flag:"\u{1F1FE}\u{1F1EA}"},{code:"ZM",dialCode:"+260",name:"Zambia",flag:"\u{1F1FF}\u{1F1F2}"},{code:"ZW",dialCode:"+263",name:"Zimbabwe",flag:"\u{1F1FF}\u{1F1FC}"}],fe=new Map(_.map(e=>[e.code,e])),pe={"America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Anchorage":"US","Pacific/Honolulu":"US","America/Phoenix":"US","America/Indiana/Indianapolis":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Edmonton":"CA","America/Winnipeg":"CA","America/Halifax":"CA","America/St_Johns":"CA","Europe/London":"GB","Europe/Dublin":"IE","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Warsaw":"PL","Europe/Prague":"CZ","Europe/Bucharest":"RO","Europe/Budapest":"HU","Europe/Athens":"GR","Europe/Istanbul":"TR","Europe/Moscow":"RU","Europe/Kiev":"UA","Europe/Lisbon":"PT","Europe/Belgrade":"RS","Europe/Zagreb":"HR","Europe/Bratislava":"SK","Europe/Ljubljana":"SI","Europe/Tallinn":"EE","Europe/Riga":"LV","Europe/Vilnius":"LT","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Singapore":"SG","Asia/Kolkata":"IN","Asia/Karachi":"PK","Asia/Dhaka":"BD","Asia/Bangkok":"TH","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Kuala_Lumpur":"MY","Asia/Ho_Chi_Minh":"VN","Asia/Riyadh":"SA","Asia/Dubai":"AE","Asia/Tehran":"IR","Asia/Baghdad":"IQ","Asia/Beirut":"LB","Asia/Jerusalem":"IL","Asia/Amman":"JO","Asia/Almaty":"KZ","Asia/Tashkent":"UZ","Asia/Colombo":"LK","Asia/Kathmandu":"NP","Asia/Yangon":"MM","Australia/Sydney":"AU","Australia/Melbourne":"AU","Australia/Brisbane":"AU","Australia/Perth":"AU","Australia/Adelaide":"AU","Pacific/Auckland":"NZ","Pacific/Fiji":"FJ","Africa/Cairo":"EG","Africa/Lagos":"NG","Africa/Nairobi":"KE","Africa/Johannesburg":"ZA","Africa/Casablanca":"MA","Africa/Accra":"GH","Africa/Addis_Ababa":"ET","Africa/Dar_es_Salaam":"TZ","Africa/Kampala":"UG","America/Mexico_City":"MX","America/Bogota":"CO","America/Lima":"PE","America/Santiago":"CL","America/Buenos_Aires":"AR","America/Sao_Paulo":"BR","America/Caracas":"VE","America/Guatemala":"GT","America/Havana":"CU","America/Panama":"PA","America/Jamaica":"JM"};function J(){try{let e=Intl.DateTimeFormat().resolvedOptions().timeZone;return pe[e]??"US"}catch{return"US"}}function W(e){if(!e.startsWith("+"))return e;let a=e.slice(1);if(a.startsWith("1")&&a.length===11){let o=a.slice(1,4),n=a.slice(4,7),t=a.slice(7);return`+1 (${o}) ${n}-${t}`}for(let o=4;o>=1;o--){let n="+"+a.slice(0,o),t=_.find(i=>i.dialCode===n);if(t){let i=a.slice(o);return`${t.dialCode} ${i}`}}return e}function j(e,a){let o=e.replace(/\D/g,""),n=fe.get(a);if(!n)return"+"+o;let t=n.dialCode.slice(1);return o.startsWith(t)?"+"+o:n.dialCode+o}function Z(e){return/^\+[1-9]\d{6,14}$/.test(e)}function z(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}function X(){try{return window.top!==window.self}catch{return!0}}async function C(e,a,o){let t={Authorization:`Bearer ${u().token}`},i=a==="POST"?{...o,embedded:X()}:o;i!==void 0&&(t["Content-Type"]="application/json");let r=await fetch(m(e),{method:a,headers:t,...i!==void 0&&{body:JSON.stringify(i)}});if(!r.ok)throw await E(r,`Auth request failed: ${r.status} ${r.statusText}`,"auth_error");return await r.json()}var B="__ms_remy_state",x="ms_popup",Q="ms:remy:callback",Y=!1,P=ye();function ye(){if(typeof window>"u")return!1;try{let e=new URL(window.location.href).searchParams;return e.has("code")&&e.get(x)!=="1"}catch{return!1}}function Ae(){let e=globalThis.crypto;if(e&&typeof e.randomUUID=="function")return e.randomUUID();let a=new Uint8Array(16);return e?.getRandomValues?.(a),Array.from(a,o=>o.toString(16).padStart(2,"0")).join("")}function we(e){e.searchParams.delete("code"),e.searchParams.delete("state");try{window.history.replaceState(window.history.state,"",e.toString())}catch{}}var R=new Set;function b(e){F({token:e.token,user:e.user,methods:e.methods,visitorId:e.visitorId}),R.forEach(a=>a(e.user))}function M(e){if(!e.user)throw new g("Verification succeeded but no user was returned","auth_error");return e.user}function q(e){let a=u();a.user&&(Object.assign(a.user,e),R.forEach(o=>o(a.user)))}function L(){try{return u().user}catch{return null}}function ee(){let e=L();R.forEach(a=>a(e))}function ae(){P||(P=!0,ee())}function N(){P&&(P=!1,ee())}var Se=520,Te=600;function Ee(e,a){let o=window.screenX,n=window.screenY,t=window.outerWidth||window.innerWidth||window.screen.width,i=window.outerHeight||window.innerHeight||window.screen.height,r=Math.min(e,t),l=Math.min(a,i),c=Math.max(0,Math.round(o+(t-r)/2)),y=Math.max(0,Math.round(n+(i-l)/2));return`popup=yes,width=${Math.round(r)},height=${Math.round(l)},left=${c},top=${y}`}function be(e,a){let o=new URL(e,window.location.href);o.searchParams.set(x,"1");let n=`${m("/_/auth/remy/start")}?${new URLSearchParams({redirect_uri:o.toString(),state:a}).toString()}`,t=window.open(n,"ms-remy-signin",Ee(Se,Te));return t?(ae(),new Promise((i,r)=>{let l=window.location.origin,c=!1,y=!1,d=w=>{if(!c){c=!0,window.removeEventListener("message",s),clearInterval(f),clearTimeout(p),N();try{t.closed||t.close()}catch{}w()}},s=w=>{if(c||y||w.origin!==l||w.source!==t)return;let S=w.data;if(!(!S||S.type!==Q||S.state!==a)){if(y=!0,!S.code){d(()=>r(new g("Sign-in returned no code.","auth_error")));return}C("/_/auth/remy/exchange","POST",{code:S.code}).then(A=>{P=!1,b(A),d(()=>i(M(A)))}).catch(A=>d(()=>r(A)))}};window.addEventListener("message",s);let f=setInterval(()=>{t.closed&&!y&&d(()=>i(null))},400),p=setTimeout(()=>d(()=>r(new g("Sign-in timed out.","signin_timeout"))),300*1e3)})):Promise.reject(new g("The sign-in popup was blocked. Allow popups for this site and try again.","popup_blocked"))}function D(){if(typeof window>"u")return!1;let e;try{e=new URL(window.location.href).searchParams}catch{return!1}if(e.get(x)!=="1")return!1;let a=e.get("code");if(!a)return!1;if(Y)return!0;let o=window.opener;if(!o||o===window)return!1;try{o.postMessage({type:Q,code:a,state:e.get("state")??void 0},window.location.origin)}catch{return!1}Y=!0;try{setTimeout(()=>window.close(),0)}catch{}return!0}var Pe={get currentUser(){return u().user},getCurrentUser(){return u().user},isAuthenticated(){return u().user!==null},get authStatus(){return P?"authenticating":L()?"authenticated":"unauthenticated"},get currentVisitorId(){return u().visitorId??null},getCurrentVisitorId(){return u().visitorId??null},sendEmailCode(e){return C("/_/auth/email/send","POST",{email:e})},async verifyEmailCode(e,a){let o=await C("/_/auth/email/verify","POST",{verificationId:e,code:a});return b(o),M(o)},sendSmsCode(e){return C("/_/auth/sms/send","POST",{phone:e})},async verifySmsCode(e,a){let o=await C("/_/auth/sms/verify","POST",{verificationId:e,code:a});return b(o),M(o)},signInWithRemy(e){let a=e?.mode??"auto",o=a==="popup"||a==="auto"&&X(),n=e?.redirectUri??window.location.href,t=e?.state??Ae();if(o)return be(n,t);try{sessionStorage.setItem(B,t)}catch{}let i=new URLSearchParams({redirect_uri:n,state:t});return window.location.assign(`${m("/_/auth/remy/start")}?${i.toString()}`),new Promise(()=>{})},async handleRemyRedirect(){if(typeof window>"u")return null;let e=new URL(window.location.href),a=e.searchParams.get("code");if(!a)return null;if(e.searchParams.get(x)==="1")return D(),null;let o=e.searchParams.get("state");we(e);let n=null;try{n=sessionStorage.getItem(B),sessionStorage.removeItem(B)}catch{}if(ae(),o!==null&&o!==n)throw N(),new g("Sign-in state mismatch \u2014 possible CSRF or a stale redirect. Please try again.","invalid_state");try{let t=await C("/_/auth/remy/exchange","POST",{code:a});return P=!1,b(t),M(t)}catch(t){throw N(),t}},async requestEmailChange(e){await C("/_/auth/email/change","POST",{newEmail:e})},async confirmEmailChange(e,a){let o=await C("/_/auth/email/change/confirm","POST",{newEmail:e,code:a});return b(o),M(o)},async requestPhoneChange(e){await C("/_/auth/phone/change","POST",{newPhone:e})},async confirmPhoneChange(e,a){let o=await C("/_/auth/phone/change/confirm","POST",{newPhone:e,code:a});return b(o),M(o)},async logout(){let e=await C("/_/auth/logout","POST",{});b(e)},async createApiKey(){let e=await C("/_/auth/api-key/create","POST",{});return q({apiKey:e.apiKey}),{key:e.key}},async revokeApiKey(){await C("/_/auth/api-key/revoke","POST",{}),q({apiKey:null})},onAuthStateChanged(e){return R.add(e),e(L()),()=>{R.delete(e)}},phone:{countries:_,detectCountry:J,format:W,toE164:j,isValid:Z},email:{isValid:z}};var ne="/_/events",ve=40,ke=50;function Me(e){let a=null,o=G({prepare:async()=>{if(!a)try{a=await e.getToken()}catch(d){return e.onError?.(d instanceof Error?d:new Error(String(d))),"stop"}return{url:m(ne),headers:{Authorization:`Bearer ${a}`}}},onStatus:d=>(d===401&&(a=null),"retry"),onOpen:()=>{e.onConnect?.()},onLine:d=>{if(!d.startsWith("data: "))return;let s;try{s=JSON.parse(d.slice(6))}catch{return}if(s?.type==="grant_expired"){a=null;return}if(s?.type==="events_dropped"){e.onGap?.(typeof s.count=="number"?s.count:0);return}typeof s?.channel=="string"&&e.onEvent({id:s.id,channel:s.channel,data:s.data,ts:s.ts})}});o.start();let n=[],t=null,i=0,r=!1,l=!1,c=async()=>{if(t=null,r||n.length===0)return;if(!a||Date.now()<i){n=[];return}let d=n;n=[];try{let s=await fetch(m(ne),{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify({events:d})});if(s.status===401){a=null;return}if(s.status===429){let f=parseInt(s.headers.get("Retry-After")??"",10);i=Date.now()+(Number.isFinite(f)&&f>0?f*1e3:1e3);return}s.status===403&&!l&&(l=!0,e.onError?.(new Error("events.publish refused: the grant does not carry publish capability for these channels \u2014 mint it with `events.grant(channels, { publish: [...] })`.")))}catch{}};return{close:()=>{r=!0,t&&(clearTimeout(t),t=null),n=[],o.stop()},get state(){let d=o.state;return d==="open"?"open":d==="stopped"?"closed":"connecting"},publish:(d,s)=>{if(!r){if(n.push({channels:d,data:s}),n.length>=ke){t&&clearTimeout(t),c();return}t||(t=setTimeout(()=>{c()},ve))}}}}var Re={connect:Me};if(typeof window<"u"&&!D()&&globalThis.__MINDSTUDIO__)try{u()}catch{}export{g as MindStudioInterfaceError,ie as analytics,Pe as auth,ge as createAgentChatClient,se as createClient,Re as events,me as platform,de as telemetry};
1
+ import{a as m,b as k,c as j,d as fe,e as pe,f as p,g as Z,h as C,i as Y}from"./chunk-5E7SM65A.js";function he(){return new Proxy({},{get(e,t){return async(o,a)=>{let n=p(),i=n.methods[t];if(!i)throw new m(`Method "${t}" not found. Available methods: ${Object.keys(n.methods).join(", ")||"(none)"}`,"method_not_found");let r=C(`/_/methods/${i}/invoke`),d=a?.stream===!0,c=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify({input:o??{},...d&&{stream:!0}})});if(!c.ok)throw await k(c,`Method "${t}" failed: ${c.status} ${c.statusText}`,"method_error");if(!d)return(await c.json()).output;let h=c.body.getReader(),s=new TextDecoder,l="",f,y;for(;;){let{done:b,value:P}=await h.read();if(b)break;l+=s.decode(P,{stream:!0});let u=l.split(`
2
+ `);l=u.pop()??"";for(let A of u){if(!A.startsWith("data: "))continue;let E=A.slice(6);try{let g=JSON.parse(E);g.type==="token"&&a?.onToken&&g.text?a.onToken(g.text):g.type==="data"&&a?.onStreamData?a.onStreamData(g.data):g.type==="error"&&g.error?(y=g.error,a?.onStreamError&&a.onStreamError(g.error)):g.type==="done"&&(f=g.output)}catch{}}}if(f===void 0&&y!==void 0)throw new m(y,"method_error");if(f===void 0)throw new m("Stream ended without a done event","stream_incomplete");return f}}})}var H="/_/agent",Ce=32e3;async function U(e,t,o){let n={Authorization:`Bearer ${p().token}`};o!==void 0&&(n["Content-Type"]="application/json");let i=await fetch(C(`${H}${e}`),{method:t,headers:n,...o!==void 0&&{body:JSON.stringify(o)}});if(!i.ok)throw await k(i,`Agent chat request failed: ${i.status} ${i.statusText}`,"agent_chat_error");return await i.json()}async function L(e,t){let o=p(),a=await fetch(C(`${H}/threads/${e}/tool-results`),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify(t)});a.ok||console.warn(`[agent-chat] could not deliver client tool result: ${a.status} ${a.statusText}`)}function ye(e,t){switch(t.onEvent?.(e),e.type){case"text":t.onText?.(e.text);break;case"thinking":t.onThinking?.(e.text);break;case"thinking_complete":t.onThinkingComplete?.(e.thinking,e.signature);break;case"tool_call_start":t.onToolCallStart?.(e.id,e.name);break;case"tool_call_result":t.onToolCallResult?.(e.id,e.output);break;case"error":t.onError?.(e.error);break;case"client_tool_call":break;case"tool_use":case"tool_input_delta":case"done":break}}async function we(e,t,o){if(!o){console.warn(`[agent-chat] client tool "${t.name}" was invoked but no handler is registered \u2014 call chat.registerClientTool()`),await L(e,{callId:t.id,error:"unhandled_client_tool"});return}try{let a=await o(t.input);if(a!==void 0){let n=JSON.stringify(a);if(n&&n.length>Ce){await L(e,{callId:t.id,error:"result_too_large"});return}}await L(e,{callId:t.id,result:a})}catch(a){await L(e,{callId:t.id,error:a instanceof Error?a.message:String(a)})}}function Ae(){let e=new Map,t=new Map,o=a=>{let n=p().token;n&&!e.has(a)&&e.set(a,n)};return{async createThread(){let a=p().token,n=await U("/threads","POST");return a&&e.set(n.id,a),n},listThreads(a){let n=a?`?cursor=${encodeURIComponent(a)}`:"";return U(`/threads${n}`,"GET")},getThread(a){return U(`/threads/${a}`,"GET")},async updateThread(a,n){await U(`/threads/${a}`,"PATCH",{title:n})},async deleteThread(a){await U(`/threads/${a}`,"DELETE")},async claimThread(a,n){let i=n?.previousToken??e.get(a);if(!i)throw new m("No pre-login token known for this thread \u2014 pass options.previousToken.","missing_previous_token",400);await U(`/threads/${a}/claim`,"POST",{previousToken:i}),e.delete(a)},registerClientTool(a,n){return t.set(a,n),()=>{t.get(a)===n&&t.delete(a)}},sendMessage(a,n,i,r){o(a);let d=new AbortController,c=i??{};c.signal&&(c.signal.aborted?d.abort():c.signal.addEventListener("abort",()=>d.abort(),{once:!0}));let s=(async()=>{let l=p(),f=C(`${H}/threads/${a}/messages`),y=await fetch(f,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l.token}`},body:JSON.stringify({content:n,...r?.attachments?.length&&{attachments:r.attachments}}),signal:d.signal});if(!y.ok)throw await k(y,`Send message failed: ${y.status} ${y.statusText}`,"agent_chat_error");let b=y.body.getReader(),P=new TextDecoder,u="",A;for(;;){let{done:E,value:g}=await b.read();if(E)break;u+=P.decode(g,{stream:!0});let w=u.split(`
3
+ `);u=w.pop()??"";for(let M of w){if(!M.startsWith("data: "))continue;let v=M.slice(6);try{let T=JSON.parse(v);if(T.type==="done"&&(A={stopReason:T.stopReason,usage:T.usage}),T.type==="client_tool_call"){let D=t.get(T.name)??(c.onClientToolCall?me=>c.onClientToolCall(T.name,me,T.id):void 0);we(a,T,D)}ye(T,c)}catch{}}}if(!A)throw new m("Stream ended without a done event","stream_incomplete");return A})();return s.abort=()=>d.abort(),s}}}var X=64*1024*1024,Te=4,q=6,G=50,Se=5,be=6e4,Ee="mindstudio:upload:",K=(e,t)=>new Promise((o,a)=>{let n=setTimeout(o,e);t?.addEventListener("abort",()=>{clearTimeout(n),a(new DOMException("Upload aborted","AbortError"))},{once:!0})});async function B(e,t,o){let a=await fetch(C(`/_/uploads/${e}`),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:o});if(!a.ok)throw await k(a,`File upload failed: ${e} ${a.status}`,"upload_error");return await a.json()}var x={key:(e,t)=>`${Ee}${e.url}:${t.name}:${t.size}:${t.lastModified}`,read(e){try{return globalThis.localStorage?.getItem(e)??null}catch{return null}},write(e,t){try{globalThis.localStorage?.setItem(e,t)}catch{}},forget(e){try{globalThis.localStorage?.removeItem(e)}catch{}}},F=e=>e instanceof m&&(e.code==="upload_restart"||e.code==="invalid_upload_token");function Pe(e,t,o,a){return new Promise((n,i)=>{let r=new XMLHttpRequest;if(r.upload.addEventListener("progress",d=>o(d.loaded)),r.addEventListener("load",()=>n(r.status)),r.addEventListener("error",()=>n(0)),r.addEventListener("abort",()=>i(new DOMException("Upload aborted","AbortError"))),a){if(a.aborted){i(new DOMException("Upload aborted","AbortError"));return}a.addEventListener("abort",()=>r.abort(),{once:!0})}r.open("PUT",e),r.send(t)})}async function Q(e,t,o){let{onProgress:a,signal:n}=o,i=x.key(e,t);for(let r=0;;r+=1){let d=x.read(i),c=0,h=new Map;if(d)try{let s=await B("status",{session:d},n);c=s.partSize;for(let{partNumber:l,size:f}of s.parts)h.set(l,f)}catch(s){if(!F(s))throw s;x.forget(i),d=null}if(!d){if(!e.upload.grant)throw new m("This upload token has no grant for a large file.","upload_error");let s=await B("start",{grant:e.upload.grant,size:t.size},n);d=s.session,c=s.partSize,x.write(i,d)}try{await ve(d,t,c,h,a,n),await ke(d,n)}catch(s){if(F(s)&&r===0){x.forget(i);continue}throw s}return x.forget(i),a?.(1),{key:e.key,url:e.url}}}async function ve(e,t,o,a,n,i){let r=Math.max(1,Math.ceil(t.size/o)),d=u=>u<r?o:t.size-o*(r-1),c=[],h=0;for(let u=1;u<=r;u+=1)a.get(u)===d(u)?h+=d(u):c.push(u);let s=new Map,l=()=>{if(n&&t.size>0){let u=h;for(let A of s.values())u+=A;n(Math.min(u/t.size,.999))}};l();let f=new Map,y=(u,A)=>{let E=Math.floor((u-1)/G);A&&f.delete(E);let g=f.get(E);if(!g){let w=E*G+1,M=Math.min(r,w+G-1);g=B("parts",{session:e,parts:Array.from({length:M-w+1},(v,T)=>w+T)},i).then(v=>new Map(v.urls.map(({partNumber:T,url:D})=>[T,D]))),g.catch(()=>f.delete(E)),f.set(E,g)}return g.then(w=>w.get(u))},b=async u=>{let A=(u-1)*o,E=t.slice(A,A+d(u)),g=-1;for(let w=1;w<=q;w+=1){let M;try{M=await y(u,g===403)}catch(v){if(F(v)||v instanceof DOMException&&v.name==="AbortError")throw v;g=0,await K(1e3*2**(w-1),i);continue}if(g=await Pe(M,E,v=>{s.set(u,v),l()},i),s.delete(u),g>=200&&g<300){h+=E.size,l();return}if(g>=400&&g<500&&g!==403)break;w<q&&await K(1e3*2**(w-1),i)}throw new m(`File upload failed: part ${u} of ${r}${g>0?` (${g})`:": network error"}`,"upload_error",g>0?g:void 0)},P=Array.from({length:Math.min(Te,c.length)},async()=>{for(;c.length>0;)await b(c.shift())});await Promise.all(P)}async function ke(e,t){for(let o=1;;o+=1)try{await B("complete",{session:e},t);return}catch(a){if(!(a instanceof m&&a.status===503&&o<Se))throw a;await K(be,t)}}var Me={get basePath(){return Z()},async uploadFile(e,t){let o=p(),{onProgress:a,signal:n}=t??{};n?.throwIfAborted();let i=C("/_/generate-upload-request"),r=await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${o.token}`,"Content-Type":"application/json"},body:JSON.stringify({filename:e.name,contentType:e.type||"application/octet-stream"}),signal:n});if(!r.ok)throw new m(`Upload presign failed: ${r.status} ${r.statusText}`,"upload_error",r.status);let{uploadUrl:d,uploadFields:c,publicUrl:h}=await r.json(),s=new FormData;for(let[f,y]of Object.entries(c))s.append(f,y);if(s.append("file",e),a)return await ee(d,s,a,n),h;let l=await fetch(d,{method:"POST",body:s,signal:n});if(!l.ok)throw new m(`File upload failed: ${l.status} ${l.statusText}`,"upload_error",l.status);return h},async upload(e,t,o){let{onProgress:a,signal:n}=o??{};if(n?.throwIfAborted(),e.upload.grant&&t.size>=X)return Q(e,t,{onProgress:a,signal:n});let i=new FormData;for(let[r,d]of Object.entries(e.upload.fields))i.append(r,d);if(i.append("file",t),a)await ee(e.upload.url,i,a,n);else{let r=await fetch(e.upload.url,{method:"POST",body:i,signal:n});if(!r.ok)throw new m(`File upload failed: ${r.status} ${r.statusText}`,"upload_error",r.status)}return{key:e.key,url:e.url}}};function ee(e,t,o,a){return new Promise((n,i)=>{let r=new XMLHttpRequest;if(r.upload.addEventListener("progress",d=>{d.lengthComputable&&o(d.loaded/d.total)}),r.addEventListener("load",()=>{r.status>=200&&r.status<300?(o(1),n()):i(new m(`File upload failed: ${r.status} ${r.statusText}`,"upload_error",r.status))}),r.addEventListener("error",()=>{i(new m("File upload failed: network error","upload_error"))}),r.addEventListener("abort",()=>{i(new DOMException("Upload aborted","AbortError"))}),a){if(a.aborted){i(new DOMException("Upload aborted","AbortError"));return}a.addEventListener("abort",()=>r.abort(),{once:!0})}r.open("POST",e),r.send(t)})}var $=[{code:"AF",dialCode:"+93",name:"Afghanistan",flag:"\u{1F1E6}\u{1F1EB}"},{code:"AL",dialCode:"+355",name:"Albania",flag:"\u{1F1E6}\u{1F1F1}"},{code:"DZ",dialCode:"+213",name:"Algeria",flag:"\u{1F1E9}\u{1F1FF}"},{code:"AD",dialCode:"+376",name:"Andorra",flag:"\u{1F1E6}\u{1F1E9}"},{code:"AO",dialCode:"+244",name:"Angola",flag:"\u{1F1E6}\u{1F1F4}"},{code:"AG",dialCode:"+1",name:"Antigua and Barbuda",flag:"\u{1F1E6}\u{1F1EC}"},{code:"AR",dialCode:"+54",name:"Argentina",flag:"\u{1F1E6}\u{1F1F7}"},{code:"AM",dialCode:"+374",name:"Armenia",flag:"\u{1F1E6}\u{1F1F2}"},{code:"AU",dialCode:"+61",name:"Australia",flag:"\u{1F1E6}\u{1F1FA}"},{code:"AT",dialCode:"+43",name:"Austria",flag:"\u{1F1E6}\u{1F1F9}"},{code:"AZ",dialCode:"+994",name:"Azerbaijan",flag:"\u{1F1E6}\u{1F1FF}"},{code:"BS",dialCode:"+1",name:"Bahamas",flag:"\u{1F1E7}\u{1F1F8}"},{code:"BH",dialCode:"+973",name:"Bahrain",flag:"\u{1F1E7}\u{1F1ED}"},{code:"BD",dialCode:"+880",name:"Bangladesh",flag:"\u{1F1E7}\u{1F1E9}"},{code:"BB",dialCode:"+1",name:"Barbados",flag:"\u{1F1E7}\u{1F1E7}"},{code:"BY",dialCode:"+375",name:"Belarus",flag:"\u{1F1E7}\u{1F1FE}"},{code:"BE",dialCode:"+32",name:"Belgium",flag:"\u{1F1E7}\u{1F1EA}"},{code:"BZ",dialCode:"+501",name:"Belize",flag:"\u{1F1E7}\u{1F1FF}"},{code:"BJ",dialCode:"+229",name:"Benin",flag:"\u{1F1E7}\u{1F1EF}"},{code:"BT",dialCode:"+975",name:"Bhutan",flag:"\u{1F1E7}\u{1F1F9}"},{code:"BO",dialCode:"+591",name:"Bolivia",flag:"\u{1F1E7}\u{1F1F4}"},{code:"BA",dialCode:"+387",name:"Bosnia and Herzegovina",flag:"\u{1F1E7}\u{1F1E6}"},{code:"BW",dialCode:"+267",name:"Botswana",flag:"\u{1F1E7}\u{1F1FC}"},{code:"BR",dialCode:"+55",name:"Brazil",flag:"\u{1F1E7}\u{1F1F7}"},{code:"BN",dialCode:"+673",name:"Brunei",flag:"\u{1F1E7}\u{1F1F3}"},{code:"BG",dialCode:"+359",name:"Bulgaria",flag:"\u{1F1E7}\u{1F1EC}"},{code:"BF",dialCode:"+226",name:"Burkina Faso",flag:"\u{1F1E7}\u{1F1EB}"},{code:"BI",dialCode:"+257",name:"Burundi",flag:"\u{1F1E7}\u{1F1EE}"},{code:"CV",dialCode:"+238",name:"Cabo Verde",flag:"\u{1F1E8}\u{1F1FB}"},{code:"KH",dialCode:"+855",name:"Cambodia",flag:"\u{1F1F0}\u{1F1ED}"},{code:"CM",dialCode:"+237",name:"Cameroon",flag:"\u{1F1E8}\u{1F1F2}"},{code:"CA",dialCode:"+1",name:"Canada",flag:"\u{1F1E8}\u{1F1E6}"},{code:"CF",dialCode:"+236",name:"Central African Republic",flag:"\u{1F1E8}\u{1F1EB}"},{code:"TD",dialCode:"+235",name:"Chad",flag:"\u{1F1F9}\u{1F1E9}"},{code:"CL",dialCode:"+56",name:"Chile",flag:"\u{1F1E8}\u{1F1F1}"},{code:"CN",dialCode:"+86",name:"China",flag:"\u{1F1E8}\u{1F1F3}"},{code:"CO",dialCode:"+57",name:"Colombia",flag:"\u{1F1E8}\u{1F1F4}"},{code:"KM",dialCode:"+269",name:"Comoros",flag:"\u{1F1F0}\u{1F1F2}"},{code:"CG",dialCode:"+242",name:"Congo",flag:"\u{1F1E8}\u{1F1EC}"},{code:"CD",dialCode:"+243",name:"Congo (DRC)",flag:"\u{1F1E8}\u{1F1E9}"},{code:"CR",dialCode:"+506",name:"Costa Rica",flag:"\u{1F1E8}\u{1F1F7}"},{code:"HR",dialCode:"+385",name:"Croatia",flag:"\u{1F1ED}\u{1F1F7}"},{code:"CU",dialCode:"+53",name:"Cuba",flag:"\u{1F1E8}\u{1F1FA}"},{code:"CY",dialCode:"+357",name:"Cyprus",flag:"\u{1F1E8}\u{1F1FE}"},{code:"CZ",dialCode:"+420",name:"Czechia",flag:"\u{1F1E8}\u{1F1FF}"},{code:"CI",dialCode:"+225",name:"Cote d'Ivoire",flag:"\u{1F1E8}\u{1F1EE}"},{code:"DK",dialCode:"+45",name:"Denmark",flag:"\u{1F1E9}\u{1F1F0}"},{code:"DJ",dialCode:"+253",name:"Djibouti",flag:"\u{1F1E9}\u{1F1EF}"},{code:"DM",dialCode:"+1",name:"Dominica",flag:"\u{1F1E9}\u{1F1F2}"},{code:"DO",dialCode:"+1",name:"Dominican Republic",flag:"\u{1F1E9}\u{1F1F4}"},{code:"EC",dialCode:"+593",name:"Ecuador",flag:"\u{1F1EA}\u{1F1E8}"},{code:"EG",dialCode:"+20",name:"Egypt",flag:"\u{1F1EA}\u{1F1EC}"},{code:"SV",dialCode:"+503",name:"El Salvador",flag:"\u{1F1F8}\u{1F1FB}"},{code:"GQ",dialCode:"+240",name:"Equatorial Guinea",flag:"\u{1F1EC}\u{1F1F6}"},{code:"ER",dialCode:"+291",name:"Eritrea",flag:"\u{1F1EA}\u{1F1F7}"},{code:"EE",dialCode:"+372",name:"Estonia",flag:"\u{1F1EA}\u{1F1EA}"},{code:"SZ",dialCode:"+268",name:"Eswatini",flag:"\u{1F1F8}\u{1F1FF}"},{code:"ET",dialCode:"+251",name:"Ethiopia",flag:"\u{1F1EA}\u{1F1F9}"},{code:"FJ",dialCode:"+679",name:"Fiji",flag:"\u{1F1EB}\u{1F1EF}"},{code:"FI",dialCode:"+358",name:"Finland",flag:"\u{1F1EB}\u{1F1EE}"},{code:"FR",dialCode:"+33",name:"France",flag:"\u{1F1EB}\u{1F1F7}"},{code:"GA",dialCode:"+241",name:"Gabon",flag:"\u{1F1EC}\u{1F1E6}"},{code:"GM",dialCode:"+220",name:"Gambia",flag:"\u{1F1EC}\u{1F1F2}"},{code:"GE",dialCode:"+995",name:"Georgia",flag:"\u{1F1EC}\u{1F1EA}"},{code:"DE",dialCode:"+49",name:"Germany",flag:"\u{1F1E9}\u{1F1EA}"},{code:"GH",dialCode:"+233",name:"Ghana",flag:"\u{1F1EC}\u{1F1ED}"},{code:"GR",dialCode:"+30",name:"Greece",flag:"\u{1F1EC}\u{1F1F7}"},{code:"GD",dialCode:"+1",name:"Grenada",flag:"\u{1F1EC}\u{1F1E9}"},{code:"GT",dialCode:"+502",name:"Guatemala",flag:"\u{1F1EC}\u{1F1F9}"},{code:"GN",dialCode:"+224",name:"Guinea",flag:"\u{1F1EC}\u{1F1F3}"},{code:"GW",dialCode:"+245",name:"Guinea-Bissau",flag:"\u{1F1EC}\u{1F1FC}"},{code:"GY",dialCode:"+592",name:"Guyana",flag:"\u{1F1EC}\u{1F1FE}"},{code:"HT",dialCode:"+509",name:"Haiti",flag:"\u{1F1ED}\u{1F1F9}"},{code:"HN",dialCode:"+504",name:"Honduras",flag:"\u{1F1ED}\u{1F1F3}"},{code:"HK",dialCode:"+852",name:"Hong Kong",flag:"\u{1F1ED}\u{1F1F0}"},{code:"HU",dialCode:"+36",name:"Hungary",flag:"\u{1F1ED}\u{1F1FA}"},{code:"IS",dialCode:"+354",name:"Iceland",flag:"\u{1F1EE}\u{1F1F8}"},{code:"IN",dialCode:"+91",name:"India",flag:"\u{1F1EE}\u{1F1F3}"},{code:"ID",dialCode:"+62",name:"Indonesia",flag:"\u{1F1EE}\u{1F1E9}"},{code:"IR",dialCode:"+98",name:"Iran",flag:"\u{1F1EE}\u{1F1F7}"},{code:"IQ",dialCode:"+964",name:"Iraq",flag:"\u{1F1EE}\u{1F1F6}"},{code:"IE",dialCode:"+353",name:"Ireland",flag:"\u{1F1EE}\u{1F1EA}"},{code:"IL",dialCode:"+972",name:"Israel",flag:"\u{1F1EE}\u{1F1F1}"},{code:"IT",dialCode:"+39",name:"Italy",flag:"\u{1F1EE}\u{1F1F9}"},{code:"JM",dialCode:"+1",name:"Jamaica",flag:"\u{1F1EF}\u{1F1F2}"},{code:"JP",dialCode:"+81",name:"Japan",flag:"\u{1F1EF}\u{1F1F5}"},{code:"JO",dialCode:"+962",name:"Jordan",flag:"\u{1F1EF}\u{1F1F4}"},{code:"KZ",dialCode:"+7",name:"Kazakhstan",flag:"\u{1F1F0}\u{1F1FF}"},{code:"KE",dialCode:"+254",name:"Kenya",flag:"\u{1F1F0}\u{1F1EA}"},{code:"KI",dialCode:"+686",name:"Kiribati",flag:"\u{1F1F0}\u{1F1EE}"},{code:"KW",dialCode:"+965",name:"Kuwait",flag:"\u{1F1F0}\u{1F1FC}"},{code:"KG",dialCode:"+996",name:"Kyrgyzstan",flag:"\u{1F1F0}\u{1F1EC}"},{code:"LA",dialCode:"+856",name:"Laos",flag:"\u{1F1F1}\u{1F1E6}"},{code:"LV",dialCode:"+371",name:"Latvia",flag:"\u{1F1F1}\u{1F1FB}"},{code:"LB",dialCode:"+961",name:"Lebanon",flag:"\u{1F1F1}\u{1F1E7}"},{code:"LS",dialCode:"+266",name:"Lesotho",flag:"\u{1F1F1}\u{1F1F8}"},{code:"LR",dialCode:"+231",name:"Liberia",flag:"\u{1F1F1}\u{1F1F7}"},{code:"LY",dialCode:"+218",name:"Libya",flag:"\u{1F1F1}\u{1F1FE}"},{code:"LI",dialCode:"+423",name:"Liechtenstein",flag:"\u{1F1F1}\u{1F1EE}"},{code:"LT",dialCode:"+370",name:"Lithuania",flag:"\u{1F1F1}\u{1F1F9}"},{code:"LU",dialCode:"+352",name:"Luxembourg",flag:"\u{1F1F1}\u{1F1FA}"},{code:"MO",dialCode:"+853",name:"Macao",flag:"\u{1F1F2}\u{1F1F4}"},{code:"MG",dialCode:"+261",name:"Madagascar",flag:"\u{1F1F2}\u{1F1EC}"},{code:"MW",dialCode:"+265",name:"Malawi",flag:"\u{1F1F2}\u{1F1FC}"},{code:"MY",dialCode:"+60",name:"Malaysia",flag:"\u{1F1F2}\u{1F1FE}"},{code:"MV",dialCode:"+960",name:"Maldives",flag:"\u{1F1F2}\u{1F1FB}"},{code:"ML",dialCode:"+223",name:"Mali",flag:"\u{1F1F2}\u{1F1F1}"},{code:"MT",dialCode:"+356",name:"Malta",flag:"\u{1F1F2}\u{1F1F9}"},{code:"MH",dialCode:"+692",name:"Marshall Islands",flag:"\u{1F1F2}\u{1F1ED}"},{code:"MR",dialCode:"+222",name:"Mauritania",flag:"\u{1F1F2}\u{1F1F7}"},{code:"MU",dialCode:"+230",name:"Mauritius",flag:"\u{1F1F2}\u{1F1FA}"},{code:"MX",dialCode:"+52",name:"Mexico",flag:"\u{1F1F2}\u{1F1FD}"},{code:"FM",dialCode:"+691",name:"Micronesia",flag:"\u{1F1EB}\u{1F1F2}"},{code:"MD",dialCode:"+373",name:"Moldova",flag:"\u{1F1F2}\u{1F1E9}"},{code:"MC",dialCode:"+377",name:"Monaco",flag:"\u{1F1F2}\u{1F1E8}"},{code:"MN",dialCode:"+976",name:"Mongolia",flag:"\u{1F1F2}\u{1F1F3}"},{code:"ME",dialCode:"+382",name:"Montenegro",flag:"\u{1F1F2}\u{1F1EA}"},{code:"MA",dialCode:"+212",name:"Morocco",flag:"\u{1F1F2}\u{1F1E6}"},{code:"MZ",dialCode:"+258",name:"Mozambique",flag:"\u{1F1F2}\u{1F1FF}"},{code:"MM",dialCode:"+95",name:"Myanmar",flag:"\u{1F1F2}\u{1F1F2}"},{code:"NA",dialCode:"+264",name:"Namibia",flag:"\u{1F1F3}\u{1F1E6}"},{code:"NR",dialCode:"+674",name:"Nauru",flag:"\u{1F1F3}\u{1F1F7}"},{code:"NP",dialCode:"+977",name:"Nepal",flag:"\u{1F1F3}\u{1F1F5}"},{code:"NL",dialCode:"+31",name:"Netherlands",flag:"\u{1F1F3}\u{1F1F1}"},{code:"NZ",dialCode:"+64",name:"New Zealand",flag:"\u{1F1F3}\u{1F1FF}"},{code:"NI",dialCode:"+505",name:"Nicaragua",flag:"\u{1F1F3}\u{1F1EE}"},{code:"NE",dialCode:"+227",name:"Niger",flag:"\u{1F1F3}\u{1F1EA}"},{code:"NG",dialCode:"+234",name:"Nigeria",flag:"\u{1F1F3}\u{1F1EC}"},{code:"KP",dialCode:"+850",name:"North Korea",flag:"\u{1F1F0}\u{1F1F5}"},{code:"MK",dialCode:"+389",name:"North Macedonia",flag:"\u{1F1F2}\u{1F1F0}"},{code:"NO",dialCode:"+47",name:"Norway",flag:"\u{1F1F3}\u{1F1F4}"},{code:"OM",dialCode:"+968",name:"Oman",flag:"\u{1F1F4}\u{1F1F2}"},{code:"PK",dialCode:"+92",name:"Pakistan",flag:"\u{1F1F5}\u{1F1F0}"},{code:"PW",dialCode:"+680",name:"Palau",flag:"\u{1F1F5}\u{1F1FC}"},{code:"PS",dialCode:"+970",name:"Palestine",flag:"\u{1F1F5}\u{1F1F8}"},{code:"PA",dialCode:"+507",name:"Panama",flag:"\u{1F1F5}\u{1F1E6}"},{code:"PG",dialCode:"+675",name:"Papua New Guinea",flag:"\u{1F1F5}\u{1F1EC}"},{code:"PY",dialCode:"+595",name:"Paraguay",flag:"\u{1F1F5}\u{1F1FE}"},{code:"PE",dialCode:"+51",name:"Peru",flag:"\u{1F1F5}\u{1F1EA}"},{code:"PH",dialCode:"+63",name:"Philippines",flag:"\u{1F1F5}\u{1F1ED}"},{code:"PL",dialCode:"+48",name:"Poland",flag:"\u{1F1F5}\u{1F1F1}"},{code:"PT",dialCode:"+351",name:"Portugal",flag:"\u{1F1F5}\u{1F1F9}"},{code:"PR",dialCode:"+1",name:"Puerto Rico",flag:"\u{1F1F5}\u{1F1F7}"},{code:"QA",dialCode:"+974",name:"Qatar",flag:"\u{1F1F6}\u{1F1E6}"},{code:"RO",dialCode:"+40",name:"Romania",flag:"\u{1F1F7}\u{1F1F4}"},{code:"RU",dialCode:"+7",name:"Russia",flag:"\u{1F1F7}\u{1F1FA}"},{code:"RW",dialCode:"+250",name:"Rwanda",flag:"\u{1F1F7}\u{1F1FC}"},{code:"KN",dialCode:"+1",name:"Saint Kitts and Nevis",flag:"\u{1F1F0}\u{1F1F3}"},{code:"LC",dialCode:"+1",name:"Saint Lucia",flag:"\u{1F1F1}\u{1F1E8}"},{code:"VC",dialCode:"+1",name:"Saint Vincent and the Grenadines",flag:"\u{1F1FB}\u{1F1E8}"},{code:"WS",dialCode:"+685",name:"Samoa",flag:"\u{1F1FC}\u{1F1F8}"},{code:"SM",dialCode:"+378",name:"San Marino",flag:"\u{1F1F8}\u{1F1F2}"},{code:"ST",dialCode:"+239",name:"Sao Tome and Principe",flag:"\u{1F1F8}\u{1F1F9}"},{code:"SA",dialCode:"+966",name:"Saudi Arabia",flag:"\u{1F1F8}\u{1F1E6}"},{code:"SN",dialCode:"+221",name:"Senegal",flag:"\u{1F1F8}\u{1F1F3}"},{code:"RS",dialCode:"+381",name:"Serbia",flag:"\u{1F1F7}\u{1F1F8}"},{code:"SC",dialCode:"+248",name:"Seychelles",flag:"\u{1F1F8}\u{1F1E8}"},{code:"SL",dialCode:"+232",name:"Sierra Leone",flag:"\u{1F1F8}\u{1F1F1}"},{code:"SG",dialCode:"+65",name:"Singapore",flag:"\u{1F1F8}\u{1F1EC}"},{code:"SK",dialCode:"+421",name:"Slovakia",flag:"\u{1F1F8}\u{1F1F0}"},{code:"SI",dialCode:"+386",name:"Slovenia",flag:"\u{1F1F8}\u{1F1EE}"},{code:"SB",dialCode:"+677",name:"Solomon Islands",flag:"\u{1F1F8}\u{1F1E7}"},{code:"SO",dialCode:"+252",name:"Somalia",flag:"\u{1F1F8}\u{1F1F4}"},{code:"ZA",dialCode:"+27",name:"South Africa",flag:"\u{1F1FF}\u{1F1E6}"},{code:"KR",dialCode:"+82",name:"South Korea",flag:"\u{1F1F0}\u{1F1F7}"},{code:"SS",dialCode:"+211",name:"South Sudan",flag:"\u{1F1F8}\u{1F1F8}"},{code:"ES",dialCode:"+34",name:"Spain",flag:"\u{1F1EA}\u{1F1F8}"},{code:"LK",dialCode:"+94",name:"Sri Lanka",flag:"\u{1F1F1}\u{1F1F0}"},{code:"SD",dialCode:"+249",name:"Sudan",flag:"\u{1F1F8}\u{1F1E9}"},{code:"SR",dialCode:"+597",name:"Suriname",flag:"\u{1F1F8}\u{1F1F7}"},{code:"SE",dialCode:"+46",name:"Sweden",flag:"\u{1F1F8}\u{1F1EA}"},{code:"CH",dialCode:"+41",name:"Switzerland",flag:"\u{1F1E8}\u{1F1ED}"},{code:"SY",dialCode:"+963",name:"Syria",flag:"\u{1F1F8}\u{1F1FE}"},{code:"TW",dialCode:"+886",name:"Taiwan",flag:"\u{1F1F9}\u{1F1FC}"},{code:"TJ",dialCode:"+992",name:"Tajikistan",flag:"\u{1F1F9}\u{1F1EF}"},{code:"TZ",dialCode:"+255",name:"Tanzania",flag:"\u{1F1F9}\u{1F1FF}"},{code:"TH",dialCode:"+66",name:"Thailand",flag:"\u{1F1F9}\u{1F1ED}"},{code:"TL",dialCode:"+670",name:"Timor-Leste",flag:"\u{1F1F9}\u{1F1F1}"},{code:"TG",dialCode:"+228",name:"Togo",flag:"\u{1F1F9}\u{1F1EC}"},{code:"TO",dialCode:"+676",name:"Tonga",flag:"\u{1F1F9}\u{1F1F4}"},{code:"TT",dialCode:"+1",name:"Trinidad and Tobago",flag:"\u{1F1F9}\u{1F1F9}"},{code:"TN",dialCode:"+216",name:"Tunisia",flag:"\u{1F1F9}\u{1F1F3}"},{code:"TR",dialCode:"+90",name:"Turkey",flag:"\u{1F1F9}\u{1F1F7}"},{code:"TM",dialCode:"+993",name:"Turkmenistan",flag:"\u{1F1F9}\u{1F1F2}"},{code:"TV",dialCode:"+688",name:"Tuvalu",flag:"\u{1F1F9}\u{1F1FB}"},{code:"UG",dialCode:"+256",name:"Uganda",flag:"\u{1F1FA}\u{1F1EC}"},{code:"UA",dialCode:"+380",name:"Ukraine",flag:"\u{1F1FA}\u{1F1E6}"},{code:"AE",dialCode:"+971",name:"United Arab Emirates",flag:"\u{1F1E6}\u{1F1EA}"},{code:"GB",dialCode:"+44",name:"United Kingdom",flag:"\u{1F1EC}\u{1F1E7}"},{code:"US",dialCode:"+1",name:"United States",flag:"\u{1F1FA}\u{1F1F8}"},{code:"UY",dialCode:"+598",name:"Uruguay",flag:"\u{1F1FA}\u{1F1FE}"},{code:"UZ",dialCode:"+998",name:"Uzbekistan",flag:"\u{1F1FA}\u{1F1FF}"},{code:"VU",dialCode:"+678",name:"Vanuatu",flag:"\u{1F1FB}\u{1F1FA}"},{code:"VA",dialCode:"+39",name:"Vatican City",flag:"\u{1F1FB}\u{1F1E6}"},{code:"VE",dialCode:"+58",name:"Venezuela",flag:"\u{1F1FB}\u{1F1EA}"},{code:"VN",dialCode:"+84",name:"Vietnam",flag:"\u{1F1FB}\u{1F1F3}"},{code:"YE",dialCode:"+967",name:"Yemen",flag:"\u{1F1FE}\u{1F1EA}"},{code:"ZM",dialCode:"+260",name:"Zambia",flag:"\u{1F1FF}\u{1F1F2}"},{code:"ZW",dialCode:"+263",name:"Zimbabwe",flag:"\u{1F1FF}\u{1F1FC}"}],Re=new Map($.map(e=>[e.code,e])),_e={"America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Anchorage":"US","Pacific/Honolulu":"US","America/Phoenix":"US","America/Indiana/Indianapolis":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Edmonton":"CA","America/Winnipeg":"CA","America/Halifax":"CA","America/St_Johns":"CA","Europe/London":"GB","Europe/Dublin":"IE","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Warsaw":"PL","Europe/Prague":"CZ","Europe/Bucharest":"RO","Europe/Budapest":"HU","Europe/Athens":"GR","Europe/Istanbul":"TR","Europe/Moscow":"RU","Europe/Kiev":"UA","Europe/Lisbon":"PT","Europe/Belgrade":"RS","Europe/Zagreb":"HR","Europe/Bratislava":"SK","Europe/Ljubljana":"SI","Europe/Tallinn":"EE","Europe/Riga":"LV","Europe/Vilnius":"LT","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Singapore":"SG","Asia/Kolkata":"IN","Asia/Karachi":"PK","Asia/Dhaka":"BD","Asia/Bangkok":"TH","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Kuala_Lumpur":"MY","Asia/Ho_Chi_Minh":"VN","Asia/Riyadh":"SA","Asia/Dubai":"AE","Asia/Tehran":"IR","Asia/Baghdad":"IQ","Asia/Beirut":"LB","Asia/Jerusalem":"IL","Asia/Amman":"JO","Asia/Almaty":"KZ","Asia/Tashkent":"UZ","Asia/Colombo":"LK","Asia/Kathmandu":"NP","Asia/Yangon":"MM","Australia/Sydney":"AU","Australia/Melbourne":"AU","Australia/Brisbane":"AU","Australia/Perth":"AU","Australia/Adelaide":"AU","Pacific/Auckland":"NZ","Pacific/Fiji":"FJ","Africa/Cairo":"EG","Africa/Lagos":"NG","Africa/Nairobi":"KE","Africa/Johannesburg":"ZA","Africa/Casablanca":"MA","Africa/Accra":"GH","Africa/Addis_Ababa":"ET","Africa/Dar_es_Salaam":"TZ","Africa/Kampala":"UG","America/Mexico_City":"MX","America/Bogota":"CO","America/Lima":"PE","America/Santiago":"CL","America/Buenos_Aires":"AR","America/Sao_Paulo":"BR","America/Caracas":"VE","America/Guatemala":"GT","America/Havana":"CU","America/Panama":"PA","America/Jamaica":"JM"};function te(){try{let e=Intl.DateTimeFormat().resolvedOptions().timeZone;return _e[e]??"US"}catch{return"US"}}function ae(e){if(!e.startsWith("+"))return e;let t=e.slice(1);if(t.startsWith("1")&&t.length===11){let o=t.slice(1,4),a=t.slice(4,7),n=t.slice(7);return`+1 (${o}) ${a}-${n}`}for(let o=4;o>=1;o--){let a="+"+t.slice(0,o),n=$.find(i=>i.dialCode===a);if(n){let i=t.slice(o);return`${n.dialCode} ${i}`}}return e}function ne(e,t){let o=e.replace(/\D/g,""),a=Re.get(t);if(!a)return"+"+o;let n=a.dialCode.slice(1);return o.startsWith(n)?"+"+o:a.dialCode+o}function oe(e){return/^\+[1-9]\d{6,14}$/.test(e)}function re(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}function de(){try{return window.top!==window.self}catch{return!0}}async function S(e,t,o){let n={Authorization:`Bearer ${p().token}`},i=t==="POST"?{...o,embedded:de()}:o;i!==void 0&&(n["Content-Type"]="application/json");let r=await fetch(C(e),{method:t,headers:n,...i!==void 0&&{body:JSON.stringify(i)}});if(!r.ok)throw await k(r,`Auth request failed: ${r.status} ${r.statusText}`,"auth_error");return await r.json()}var z="__ms_remy_state",N="ms_popup",le="ms:remy:callback",ie=!1,_=Ie();function Ie(){if(typeof window>"u")return!1;try{let e=new URL(window.location.href).searchParams;return e.has("code")&&e.get(N)!=="1"}catch{return!1}}function Oe(){let e=globalThis.crypto;if(e&&typeof e.randomUUID=="function")return e.randomUUID();let t=new Uint8Array(16);return e?.getRandomValues?.(t),Array.from(t,o=>o.toString(16).padStart(2,"0")).join("")}function Le(e){e.searchParams.delete("code"),e.searchParams.delete("state");try{window.history.replaceState(window.history.state,"",e.toString())}catch{}}var O=new Set;function R(e){Y({token:e.token,user:e.user,methods:e.methods,visitorId:e.visitorId}),O.forEach(t=>t(e.user))}function I(e){if(!e.user)throw new m("Verification succeeded but no user was returned","auth_error");return e.user}function se(e){let t=p();t.user&&(Object.assign(t.user,e),O.forEach(o=>o(t.user)))}function V(){try{return p().user}catch{return null}}function ce(){let e=V();O.forEach(t=>t(e))}function ue(){_||(_=!0,ce())}function J(){_&&(_=!1,ce())}var Be=520,$e=600;function Ne(e,t){let o=window.screenX,a=window.screenY,n=window.outerWidth||window.innerWidth||window.screen.width,i=window.outerHeight||window.innerHeight||window.screen.height,r=Math.min(e,n),d=Math.min(t,i),c=Math.max(0,Math.round(o+(n-r)/2)),h=Math.max(0,Math.round(a+(i-d)/2));return`popup=yes,width=${Math.round(r)},height=${Math.round(d)},left=${c},top=${h}`}function De(e,t){let o=new URL(e,window.location.href);o.searchParams.set(N,"1");let a=`${C("/_/auth/remy/start")}?${new URLSearchParams({redirect_uri:o.toString(),state:t}).toString()}`,n=window.open(a,"ms-remy-signin",Ne(Be,$e));return n?(ue(),new Promise((i,r)=>{let d=window.location.origin,c=!1,h=!1,s=b=>{if(!c){c=!0,window.removeEventListener("message",l),clearInterval(f),clearTimeout(y),J();try{n.closed||n.close()}catch{}b()}},l=b=>{if(c||h||b.origin!==d||b.source!==n)return;let P=b.data;if(!(!P||P.type!==le||P.state!==t)){if(h=!0,!P.code){s(()=>r(new m("Sign-in returned no code.","auth_error")));return}S("/_/auth/remy/exchange","POST",{code:P.code}).then(u=>{_=!1,R(u),s(()=>i(I(u)))}).catch(u=>s(()=>r(u)))}};window.addEventListener("message",l);let f=setInterval(()=>{n.closed&&!h&&s(()=>i(null))},400),y=setTimeout(()=>s(()=>r(new m("Sign-in timed out.","signin_timeout"))),300*1e3)})):Promise.reject(new m("The sign-in popup was blocked. Allow popups for this site and try again.","popup_blocked"))}function W(){if(typeof window>"u")return!1;let e;try{e=new URL(window.location.href).searchParams}catch{return!1}if(e.get(N)!=="1")return!1;let t=e.get("code");if(!t)return!1;if(ie)return!0;let o=window.opener;if(!o||o===window)return!1;try{o.postMessage({type:le,code:t,state:e.get("state")??void 0},window.location.origin)}catch{return!1}ie=!0;try{setTimeout(()=>window.close(),0)}catch{}return!0}var He={get currentUser(){return p().user},getCurrentUser(){return p().user},isAuthenticated(){return p().user!==null},get authStatus(){return _?"authenticating":V()?"authenticated":"unauthenticated"},get currentVisitorId(){return p().visitorId??null},getCurrentVisitorId(){return p().visitorId??null},sendEmailCode(e){return S("/_/auth/email/send","POST",{email:e})},async verifyEmailCode(e,t){let o=await S("/_/auth/email/verify","POST",{verificationId:e,code:t});return R(o),I(o)},sendSmsCode(e){return S("/_/auth/sms/send","POST",{phone:e})},async verifySmsCode(e,t){let o=await S("/_/auth/sms/verify","POST",{verificationId:e,code:t});return R(o),I(o)},signInWithRemy(e){let t=e?.mode??"auto",o=t==="popup"||t==="auto"&&de(),a=e?.redirectUri??window.location.href,n=e?.state??Oe();if(o)return De(a,n);try{sessionStorage.setItem(z,n)}catch{}let i=new URLSearchParams({redirect_uri:a,state:n});return window.location.assign(`${C("/_/auth/remy/start")}?${i.toString()}`),new Promise(()=>{})},async handleRemyRedirect(){if(typeof window>"u")return null;let e=new URL(window.location.href),t=e.searchParams.get("code");if(!t)return null;if(e.searchParams.get(N)==="1")return W(),null;let o=e.searchParams.get("state");Le(e);let a=null;try{a=sessionStorage.getItem(z),sessionStorage.removeItem(z)}catch{}if(ue(),o!==null&&o!==a)throw J(),new m("Sign-in state mismatch \u2014 possible CSRF or a stale redirect. Please try again.","invalid_state");try{let n=await S("/_/auth/remy/exchange","POST",{code:t});return _=!1,R(n),I(n)}catch(n){throw J(),n}},async requestEmailChange(e){await S("/_/auth/email/change","POST",{newEmail:e})},async confirmEmailChange(e,t){let o=await S("/_/auth/email/change/confirm","POST",{newEmail:e,code:t});return R(o),I(o)},async requestPhoneChange(e){await S("/_/auth/phone/change","POST",{newPhone:e})},async confirmPhoneChange(e,t){let o=await S("/_/auth/phone/change/confirm","POST",{newPhone:e,code:t});return R(o),I(o)},async logout(){let e=await S("/_/auth/logout","POST",{});R(e)},async createApiKey(){let e=await S("/_/auth/api-key/create","POST",{});return se({apiKey:e.apiKey}),{key:e.key}},async revokeApiKey(){await S("/_/auth/api-key/revoke","POST",{}),se({apiKey:null})},onAuthStateChanged(e){return O.add(e),e(V()),()=>{O.delete(e)}},phone:{countries:$,detectCountry:te,format:ae,toE164:ne,isValid:oe},email:{isValid:re}};var ge="/_/events",Ge=40,Ke=50;function Fe(e){let t=null,o=j({prepare:async()=>{if(!t)try{t=await e.getToken()}catch(s){return e.onError?.(s instanceof Error?s:new Error(String(s))),"stop"}return{url:C(ge),headers:{Authorization:`Bearer ${t}`}}},onStatus:s=>(s===401&&(t=null),"retry"),onOpen:()=>{e.onConnect?.()},onLine:s=>{if(!s.startsWith("data: "))return;let l;try{l=JSON.parse(s.slice(6))}catch{return}if(l?.type==="grant_expired"){t=null;return}if(l?.type==="events_dropped"){e.onGap?.(typeof l.count=="number"?l.count:0);return}typeof l?.channel=="string"&&e.onEvent({id:l.id,channel:l.channel,data:l.data,ts:l.ts})}});o.start();let a=[],n=null,i=0,r=!1,d=!1,c=async()=>{if(n=null,r||a.length===0)return;if(!t||Date.now()<i){a=[];return}let s=a;a=[];try{let l=await fetch(C(ge),{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({events:s})});if(l.status===401){t=null;return}if(l.status===429){let f=parseInt(l.headers.get("Retry-After")??"",10);i=Date.now()+(Number.isFinite(f)&&f>0?f*1e3:1e3);return}l.status===403&&!d&&(d=!0,e.onError?.(new Error("events.publish refused: the grant does not carry publish capability for these channels \u2014 mint it with `events.grant(channels, { publish: [...] })`.")))}catch{}};return{close:()=>{r=!0,n&&(clearTimeout(n),n=null),a=[],o.stop()},get state(){let s=o.state;return s==="open"?"open":s==="stopped"?"closed":"connecting"},publish:(s,l)=>{if(!r){if(a.push({channels:s,data:l}),a.length>=Ke){n&&clearTimeout(n),c();return}n||(n=setTimeout(()=>{c()},Ge))}}}}var ze={connect:Fe};if(typeof window<"u"&&!W()&&globalThis.__MINDSTUDIO__)try{p()}catch{}export{m as MindStudioInterfaceError,fe as analytics,He as auth,Ae as createAgentChatClient,he as createClient,ze as events,Me as platform,pe as telemetry};
4
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/agent-chat.ts","../src/platform.ts","../src/auth-phone.ts","../src/auth-email.ts","../src/auth.ts","../src/events.ts","../src/index.ts"],"sourcesContent":["/**\n * Method RPC client — typed function calls to backend methods.\n *\n * `createClient()` returns a Proxy where every property access creates\n * an async function that invokes the corresponding backend method via HTTP.\n * The method name matches the method's `export` field in `app.json`.\n *\n * ## How it works\n *\n * ```\n * api.submitVendorRequest({ name: 'Acme' })\n * │\n * ├─ Look up method ID: config.methods[\"submitVendorRequest\"]\n * │ → \"submit-vendor-request\"\n * │\n * ├─ POST /_/methods/submit-vendor-request/invoke\n * │ Authorization: Bearer {session token}\n * │ Body: { input: { name: \"Acme\" } }\n * │\n * └─ Returns response.output (or throws on error)\n * ```\n *\n * The method registry (export name → method ID mapping) is embedded in\n * `window.__MINDSTUDIO__.methods` at page load time — no extra HTTP call\n * needed to discover methods.\n *\n * ## Streaming\n *\n * Methods can optionally stream output via SSE. Pass `{ stream: true }` as\n * the second argument along with callbacks for the events you care about:\n *\n * ```ts\n * // LLM token text\n * const result = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n *\n * // Structured progress events from `stream({...})` on the backend\n * const order = await api.processOrder({ id: 'abc' }, {\n * stream: true,\n * onStreamData: (event) => setStatus(event),\n * });\n * ```\n *\n * **Important:** The `text` value passed to `onToken` is the accumulated\n * response so far (not a delta). Replace your display content each time —\n * do not append. See {@link InvokeOptions.onToken} for details.\n *\n * Each `onStreamData` payload is independent (no accumulation) — it's the\n * raw object passed to `stream({...})` on the backend.\n *\n * ## Type safety\n *\n * Since the frontend and backend live in the same project, you can import\n * types directly:\n *\n * ```ts\n * import type { SubmitVendorInput, SubmitVendorOutput }\n * from '../../backend/src/submitVendorRequest';\n *\n * interface AppMethods {\n * submitVendorRequest(input: SubmitVendorInput): Promise<SubmitVendorOutput>;\n * getDashboard(): Promise<GetDashboardOutput>;\n * }\n *\n * const api = createClient<AppMethods>();\n * const result = await api.submitVendorRequest({ name: 'Acme' }); // fully typed\n * ```\n *\n * If no type parameter is provided, methods accept `any` input and return `any`.\n */\n\nimport { getConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\n\n/**\n * Options for method invocation. Pass as the optional second argument to\n * any method on the client returned by {@link createClient}.\n *\n * When `stream` is `true`, the SDK sends `{ stream: true }` in the request\n * body and parses the response as an SSE (Server-Sent Events) stream.\n * Four event types are handled:\n *\n * | SSE `type` | Payload field | SDK action |\n * |------------|---------------|------------|\n * | `token` | `text` | Calls {@link onToken} with accumulated text |\n * | `data` | `data` | Calls {@link onStreamData} with the raw payload |\n * | `error` | `error` | Calls {@link onStreamError} with the message |\n * | `done` | `output` | Captures as the method's return value |\n *\n * The method's `Promise` resolves with the final `output` from the `done`\n * event — the same shape you'd get without streaming.\n */\nexport interface InvokeOptions {\n /**\n * Enable SSE streaming for LLM token output.\n *\n * When `true`, the request body includes `{ stream: true }` and the\n * response is parsed as an SSE stream instead of a single JSON payload.\n * The method still returns a `Promise` that resolves with the final\n * output once the stream completes.\n *\n * @default false\n */\n stream?: boolean;\n\n /**\n * Called with the accumulated response text each time a new token arrives.\n *\n * **Important:** `text` is the full response so far, not a delta/chunk.\n * Your frontend should **replace** (not append to) its display content\n * with this value each time the callback fires.\n *\n * This design avoids edge cases where the platform sanitizes or\n * reformats JSON mid-stream, causing earlier portions of the text to\n * change between tokens.\n *\n * @example\n * ```ts\n * const result = await api.generate({ prompt: 'Hello' }, {\n * stream: true,\n * onToken: (text) => {\n * // Replace — do NOT append\n * responseElement.textContent = text;\n * },\n * });\n * ```\n */\n onToken?: (text: string) => void;\n\n /**\n * Called when the backend method emits a structured event via\n * `stream({...})` (object payload).\n *\n * Receives the raw object passed to `stream()` on the backend —\n * not accumulated, not parsed, not wrapped. Use this for status\n * updates, progress, intermediate results, or any structured\n * streaming UI that isn't LLM token text.\n *\n * Different from {@link onToken}, which receives accumulated text from\n * `stream('text')` calls.\n *\n * @example\n * ```ts\n * await api.processOrder({ id: 'abc' }, {\n * stream: true,\n * onStreamData: (event) => {\n * // event is whatever the backend passed to stream({...})\n * setStatus(event as { status: string; progress: number });\n * },\n * });\n * ```\n */\n onStreamData?: (payload: unknown) => void;\n\n /**\n * Called if a stream-level error event arrives.\n *\n * If the stream then closes without a `done` event, the method rejects\n * with a `MindStudioInterfaceError` containing the error message (code\n * `method_error`). If a `done` event does arrive after the error, the\n * method resolves with the final output — in that case this callback\n * is just for logging or showing a transient warning.\n *\n * If the stream ends without either a `done` or `error` event, the\n * method rejects with `stream_incomplete`.\n */\n onStreamError?: (error: string) => void;\n}\n\n/**\n * Default client type when no generic parameter is provided.\n * Every property returns an async function accepting optional input\n * and optional {@link InvokeOptions} for streaming.\n */\ntype DefaultMethodClient = Record<\n string,\n (input?: Record<string, unknown>, options?: InvokeOptions) => Promise<unknown>\n>;\n\n/**\n * Create a typed RPC client for calling backend methods.\n *\n * @typeParam T - Optional interface mapping method names to their signatures.\n * If omitted, all methods accept `any` and return `Promise<any>`.\n *\n * @returns A Proxy object where each property is an async function that\n * invokes the corresponding backend method.\n *\n * @example\n * ```ts\n * import { createClient } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n *\n * // Each method maps to a backend method export\n * const result = await api.submitVendorRequest({ name: 'Acme' });\n * const dashboard = await api.getDashboard();\n *\n * // Stream LLM output — onToken receives accumulated text (replace, don't append)\n * const streamed = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n * // `streamed` is the same final output you'd get without streaming\n * ```\n */\nexport function createClient<T = DefaultMethodClient>(): T {\n return new Proxy({} as Record<string, unknown> as T & object, {\n get(_, methodName: string) {\n return async (input?: unknown, options?: InvokeOptions) => {\n const config = getConfig();\n\n const methodId = config.methods[methodName];\n if (!methodId) {\n throw new MindStudioInterfaceError(\n `Method \"${methodName}\" not found. Available methods: ${Object.keys(config.methods).join(', ') || '(none)'}`,\n 'method_not_found',\n );\n }\n\n const url = withBase(`/_/methods/${methodId}/invoke`);\n const wantsStream = options?.stream === true;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n input: input ?? {},\n ...(wantsStream && { stream: true }),\n }),\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Method \"${methodName}\" failed: ${res.status} ${res.statusText}`,\n 'method_error',\n );\n }\n\n // Non-streaming: unchanged behavior\n if (!wantsStream) {\n const body = (await res.json()) as { output?: unknown };\n return body.output;\n }\n\n // Streaming: parse SSE events from response body\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let finalOutput: unknown;\n let streamError: string | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as {\n type: string;\n text?: string;\n error?: string;\n output?: unknown;\n data?: unknown;\n };\n\n if (event.type === 'token' && options?.onToken && event.text) {\n options.onToken(event.text);\n } else if (event.type === 'data' && options?.onStreamData) {\n options.onStreamData(event.data);\n } else if (event.type === 'error' && event.error) {\n streamError = event.error;\n if (options?.onStreamError) {\n options.onStreamError(event.error);\n }\n } else if (event.type === 'done') {\n finalOutput = event.output;\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n // If the stream errored and didn't produce a final output,\n // surface the error message from the error event.\n if (finalOutput === undefined && streamError !== undefined) {\n throw new MindStudioInterfaceError(streamError, 'method_error');\n }\n\n if (finalOutput === undefined) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return finalOutput;\n };\n },\n });\n}\n","/**\n * Agent chat client — thread-based conversations with AI agents.\n *\n * `createAgentChatClient()` returns an object with methods for thread\n * lifecycle (create, list, get, rename, delete) and message streaming.\n * The agent runs server-side with access to the app's methods as tools.\n *\n * ## How it works\n *\n * ```\n * chat.sendMessage(threadId, 'Help me prep for my review', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * })\n * │\n * ├─ POST /_/agent/threads/{threadId}/messages\n * │ Authorization: Bearer {session token}\n * │ Body: { content: \"Help me prep for my review\" }\n * │\n * ├─ Response: SSE stream\n * │ data: {\"type\":\"text\",\"text\":\"I'll create...\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_start\",\"id\":\"call_abc\",\"name\":\"create-todo\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_result\",\"id\":\"call_abc\",\"output\":{...},\"ts\":1711...}\n * │ data: {\"type\":\"text\",\"text\":\"Done — added it.\",\"ts\":1711...}\n * │ data: {\"type\":\"done\",\"stopReason\":\"end_turn\",\"usage\":{...},\"ts\":1711...}\n * │\n * └─ Promise resolves with { stopReason, usage }\n * ```\n *\n * ## Client tools\n *\n * A tool declared in `agent.md` with `target: \"client\"` runs in this browser\n * instead of the app's backend. Register a handler and its return value goes\n * back to the agent as the tool result:\n *\n * ```\n * ├─ data: {\"type\":\"client_tool_call\",\"id\":\"call_x\",\"name\":\"pickFile\",\"input\":{...}}\n * │\n * ├─ your handler runs (the agent is waiting)\n * │\n * └─ POST /_/agent/threads/{threadId}/tool-results\n * Body: { callId: \"call_x\", result: { path: \"/invoices/q3.pdf\" } }\n * ```\n *\n * ## Stateless design\n *\n * This client is a thin wrapper over HTTP endpoints. Apart from client-tool\n * handlers, it does not manage local state — your React/framework layer owns\n * the thread list, message array, and UI state.\n *\n * ## Streaming callbacks\n *\n * `sendMessage` accepts named callbacks for common events (onText,\n * onThinking, onToolCallStart, etc.) plus a catch-all `onEvent` that\n * receives every SSE event as a discriminated union. Both fire for the\n * same event — use named callbacks for convenience, `onEvent` for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * // Thread lifecycle\n * const thread = await chat.createThread();\n * const { threads } = await chat.listThreads();\n * const full = await chat.getThread(thread.id);\n *\n * // Send a message with streaming\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n *\n * // Abort mid-stream\n * response.abort();\n * ```\n */\n\nimport { getConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Thread types\n// ---------------------------------------------------------------------------\n\n/** Thread summary returned from create and list endpoints. */\nexport interface ThreadSummary {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title, auto-generated on first message. */\n title: string | null;\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** Full thread with message history, returned from get endpoint. */\nexport interface Thread {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title. */\n title: string | null;\n /** Full conversation history. */\n messages: Message[];\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** A tool call requested by the assistant. */\nexport interface ToolCall {\n /** Tool call ID, used to match results. */\n id: string;\n /** Method name. */\n name: string;\n /** Input arguments. */\n input: Record<string, unknown>;\n}\n\n/** A single message in a thread's conversation history. */\nexport interface Message {\n /** `\"user\"` for human messages, `\"assistant\"` for agent responses. */\n role: 'user' | 'assistant';\n /** Message text content. For tool results, this is JSON-stringified output. */\n content: string;\n /**\n * MindStudio CDN URLs attached to this message (user messages only).\n *\n * Images (`i.mscdn.ai`) are sent to the model as vision input.\n * Documents (`f.mscdn.ai`, `files.mindstudio-cdn.com`) have their text\n * extracted server-side and included in the model context. The original\n * URLs are preserved in thread history for the frontend to render.\n */\n attachments?: string[];\n /** Tool calls the assistant requested (assistant messages only). */\n toolCalls?: ToolCall[];\n /** ID of the tool call this message is a result for (tool result messages only). */\n toolCallId?: string;\n /** Whether this tool result represents an error. */\n isToolError?: boolean;\n}\n\n/** Paginated thread list response. */\nexport interface ThreadListPage {\n /** Threads for this page. */\n threads: ThreadSummary[];\n /** Cursor for the next page, or `null` if this is the last page. */\n nextCursor: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// SSE event types\n// ---------------------------------------------------------------------------\n\n/** Assistant text delta — append to your display string, do not replace. */\nexport interface AgentTextEvent {\n type: 'text';\n text: string;\n ts: number;\n}\n\n/** Model thinking text (extended thinking). */\nexport interface AgentThinkingEvent {\n type: 'thinking';\n text: string;\n ts: number;\n}\n\n/** Thinking complete with full text and signature. */\nexport interface AgentThinkingCompleteEvent {\n type: 'thinking_complete';\n thinking: string;\n signature: string;\n ts: number;\n}\n\n/** Model requested a tool call (includes parsed input). */\nexport interface AgentToolUseEvent {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n ts: number;\n}\n\n/** Streaming delta for tool call input JSON. */\nexport interface AgentToolInputDeltaEvent {\n type: 'tool_input_delta';\n id: string;\n name: string;\n delta: string;\n ts: number;\n}\n\n/** Agent began executing a tool call. */\nexport interface AgentToolCallStartEvent {\n type: 'tool_call_start';\n id: string;\n name: string;\n ts: number;\n}\n\n/** Tool call returned a result. */\nexport interface AgentToolCallResultEvent {\n type: 'tool_call_result';\n id: string;\n /**\n * What the agent was given. When the method's result was over the agent's\n * bound (`maxToolOutputChars`), this is the record the agent got instead:\n * `{ truncated, totalChars, shownChars, content }`, `content` being the\n * beginning of the result's JSON.\n */\n output: unknown;\n /** Set when the result was cut before the agent saw it. */\n truncated?: true;\n ts: number;\n}\n\n/**\n * The agent invoked a client tool (agent.md `target: \"client\"`) — an action\n * that happens in this browser (open a sheet, pick a file, confirm something).\n * The agent is holding its turn until the SDK answers, which it always does:\n * with your handler's return value, with an error if it threw, or with\n * `unhandled_client_tool` if nothing is registered for the name.\n */\nexport interface AgentClientToolCallEvent {\n type: 'client_tool_call';\n id: string;\n name: string;\n input: unknown;\n /** How long the agent will wait for an answer before giving up on it. */\n timeoutMs?: number;\n ts: number;\n}\n\n/** Stream complete. */\nexport interface AgentDoneEvent {\n type: 'done';\n stopReason: string;\n usage: { inputTokens: number; outputTokens: number };\n ts: number;\n}\n\n/** Stream-level error. */\nexport interface AgentErrorEvent {\n type: 'error';\n error: string;\n ts: number;\n}\n\n/** Discriminated union of all SSE events from the agent chat stream. */\nexport type AgentChatEvent =\n | AgentTextEvent\n | AgentThinkingEvent\n | AgentThinkingCompleteEvent\n | AgentToolUseEvent\n | AgentToolInputDeltaEvent\n | AgentToolCallStartEvent\n | AgentToolCallResultEvent\n | AgentClientToolCallEvent\n | AgentDoneEvent\n | AgentErrorEvent;\n\n// ---------------------------------------------------------------------------\n// Callbacks and result types\n// ---------------------------------------------------------------------------\n\n/**\n * Callbacks for `sendMessage` streaming.\n *\n * Named callbacks fire for common events. The catch-all `onEvent` fires\n * for every event including those with named callbacks — use it for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n */\nexport interface SendMessageCallbacks {\n /**\n * Called with each text delta as the assistant responds.\n *\n * **Important:** `text` is a delta (new chunk), not the accumulated\n * response. Append it to your display string — do not replace.\n *\n * @example\n * ```ts\n * onText: (delta) => setMessage((prev) => prev + delta)\n * ```\n */\n onText?: (text: string) => void;\n\n /** Called with model thinking text (extended thinking). */\n onThinking?: (text: string) => void;\n\n /** Called when thinking is complete. */\n onThinkingComplete?: (thinking: string, signature: string) => void;\n\n /** Called when a tool call begins executing. */\n onToolCallStart?: (id: string, name: string) => void;\n\n /** Called when a tool call produces a result. */\n onToolCallResult?: (id: string, output: unknown) => void;\n\n /**\n * Called when the agent invokes a client tool (agent.md `target: \"client\"`)\n * and no handler is registered for its name — run the on-screen action here.\n * Whatever you return becomes the tool result; return nothing and the agent\n * is told the action ran.\n *\n * {@link AgentChatClient.registerClientTool} is the better home for anything\n * beyond a one-off, since handlers there survive across messages and are\n * keyed by tool name.\n */\n onClientToolCall?: (\n name: string,\n input: unknown,\n id: string,\n ) => unknown | Promise<unknown>;\n\n /** Called on a stream-level error event. */\n onError?: (error: string) => void;\n\n /** Called for every SSE event (including ones with named callbacks). */\n onEvent?: (event: AgentChatEvent) => void;\n\n /** AbortSignal to cancel the stream. */\n signal?: AbortSignal;\n}\n\n/** Options for `sendMessage`. */\nexport interface SendMessageOptions {\n /**\n * MindStudio CDN URLs to attach to the message.\n *\n * Images are sent to the model as vision input (one per message).\n * Documents have their text extracted server-side and included in context.\n *\n * Upload files first via {@link platform.upload} (with a backend-minted\n * upload token) and pass the returned URLs.\n *\n * @example\n * ```ts\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { url } = await platform.upload(token, file);\n * chat.sendMessage(threadId, 'What is this?', callbacks, {\n * attachments: [url],\n * });\n * ```\n */\n attachments?: string[];\n}\n\n/** Resolved value of `sendMessage` — extracted from the `done` event. */\nexport interface SendMessageResult {\n /** Why the agent stopped (`\"end_turn\"`, `\"max_tokens\"`, etc.). */\n stopReason: string;\n /** Token usage for the full agent loop. */\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/** A Promise with an `abort()` method for cancelling the stream. */\nexport type AbortablePromise<T> = Promise<T> & {\n /** Cancel the in-flight stream. The promise rejects with `AbortError`. */\n abort: () => void;\n};\n\n// ---------------------------------------------------------------------------\n// Client interface\n// ---------------------------------------------------------------------------\n\n/** Agent chat client returned by {@link createAgentChatClient}. */\nexport interface AgentChatClient {\n /** Create a new conversation thread. */\n createThread(): Promise<ThreadSummary>;\n\n /** List threads with cursor-based pagination (50 per page). */\n listThreads(cursor?: string): Promise<ThreadListPage>;\n\n /** Get a thread with its full message history. */\n getThread(threadId: string): Promise<Thread>;\n\n /** Update a thread's title. */\n updateThread(threadId: string, title: string): Promise<void>;\n\n /** Delete a thread (soft delete). */\n deleteThread(threadId: string): Promise<void>;\n\n /**\n * Claim an anonymous thread after in-app verification (progressive auth).\n * Login replaces the app's session token, which makes threads started\n * anonymously unreachable — claiming re-binds the thread to the signed-in\n * user so the conversation survives the login. Call it right after your\n * verification succeeds, for each anonymous thread you want to keep.\n *\n * Ownership is proven with the token the thread was created/used under:\n * the client remembers it automatically for threads touched in this page\n * session; pass `previousToken` explicitly for threads from an earlier\n * page load (e.g. a token you persisted before login).\n * Rejects with `already_identified` if the thread already has an owner.\n */\n claimThread(\n threadId: string,\n options?: { previousToken?: string },\n ): Promise<void>;\n\n /**\n * Register a handler for a client tool — a tool declared in `agent.md` with\n * `target: \"client\"`, whose effect lives in this browser (open a sheet, pick\n * a file, confirm an action). When the agent invokes it, the handler runs and\n * its return value goes back as the tool result, so the agent knows what\n * happened rather than assuming.\n *\n * The agent waits while your handler runs — long enough for a person to read\n * something and decide — so a handler may resolve from a dialog's Save button\n * as readily as it can return immediately. Throwing returns an error the\n * agent reasons about; a tool with no handler answers instantly with\n * `unhandled_client_tool` rather than leaving the agent hanging.\n *\n * One handler per tool name (a later registration replaces the earlier one);\n * returns an unregister function.\n *\n * @example\n * ```ts\n * chat.registerClientTool('pickFile', async ({ prompt }) => {\n * const file = await openFilePicker(prompt);\n * return file ? { path: file.path } : { cancelled: true };\n * });\n * ```\n */\n registerClientTool(\n name: string,\n handler: (input: unknown) => unknown | Promise<unknown>,\n ): () => void;\n\n /**\n * Send a message and stream the agent's response.\n *\n * Returns an {@link AbortablePromise} that resolves with\n * `{ stopReason, usage }` when the stream completes. Call `.abort()`\n * on the returned promise to cancel mid-stream.\n *\n * @param options.attachments - URLs to attach.\n * Images are sent as vision input.\n * Documents have text extracted into context.\n * Upload files first via {@link platform.upload} and pass the returned URLs.\n */\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult>;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nconst AGENT_BASE = '/_/agent';\n\n/**\n * Matches the platform's own cap on a client-tool result. Caught here so an\n * oversize return value reports itself instead of being rejected at the door.\n */\nconst CLIENT_TOOL_RESULT_MAX_CHARS = 32_000;\n\nasync function request<T>(\n path: string,\n method: string,\n body?: unknown,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(withBase(`${AGENT_BASE}${path}`), {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Agent chat request failed: ${res.status} ${res.statusText}`,\n 'agent_chat_error',\n );\n }\n\n return (await res.json()) as T;\n}\n\n/**\n * Answer one client-tool invocation. Fire-and-forget by design: the agent is\n * the one waiting, and a failure to deliver leaves it to its own timeout —\n * there is nothing useful for the page to do about it beyond saying so.\n */\nasync function postToolResult(\n threadId: string,\n body: { callId: string; result?: unknown; error?: string },\n): Promise<void> {\n const config = getConfig();\n const res = await fetch(\n withBase(`${AGENT_BASE}/threads/${threadId}/tool-results`),\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify(body),\n },\n );\n if (!res.ok) {\n console.warn(\n `[agent-chat] could not deliver client tool result: ${res.status} ${res.statusText}`,\n );\n }\n}\n\nfunction dispatchEvent(\n event: AgentChatEvent,\n callbacks: SendMessageCallbacks,\n): void {\n // Catch-all fires for every event\n callbacks.onEvent?.(event);\n\n // Named callbacks\n switch (event.type) {\n case 'text':\n callbacks.onText?.(event.text);\n break;\n case 'thinking':\n callbacks.onThinking?.(event.text);\n break;\n case 'thinking_complete':\n callbacks.onThinkingComplete?.(event.thinking, event.signature);\n break;\n case 'tool_call_start':\n callbacks.onToolCallStart?.(event.id, event.name);\n break;\n case 'tool_call_result':\n callbacks.onToolCallResult?.(event.id, event.output);\n break;\n case 'error':\n callbacks.onError?.(event.error);\n break;\n // Deliberately no named callback: whoever answers a client tool call also\n // runs it (see runClientTool), so dispatching here too would run the\n // action twice.\n case 'client_tool_call':\n break;\n // No named callback; `onEvent` above has already seen them.\n case 'tool_use':\n case 'tool_input_delta':\n case 'done':\n break;\n }\n}\n\ntype ClientToolHandler = (input: unknown) => unknown | Promise<unknown>;\n\n/**\n * Run one client-tool invocation and answer it. Every path answers — a missing\n * handler, a thrown handler and an oversize result included — because the agent\n * is holding its turn on this call and silence costs it the full timeout.\n */\nasync function runClientTool(\n threadId: string,\n event: AgentClientToolCallEvent,\n handler: ClientToolHandler | undefined,\n): Promise<void> {\n if (!handler) {\n console.warn(\n `[agent-chat] client tool \"${event.name}\" was invoked but no handler is registered — call chat.registerClientTool()`,\n );\n await postToolResult(threadId, {\n callId: event.id,\n error: 'unhandled_client_tool',\n });\n return;\n }\n\n try {\n const result = await handler(event.input);\n if (result !== undefined) {\n const serialized = JSON.stringify(result);\n if (serialized && serialized.length > CLIENT_TOOL_RESULT_MAX_CHARS) {\n await postToolResult(threadId, {\n callId: event.id,\n error: 'result_too_large',\n });\n return;\n }\n }\n await postToolResult(threadId, { callId: event.id, result });\n } catch (err) {\n await postToolResult(threadId, {\n callId: event.id,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an agent chat client for thread-based conversations.\n *\n * The client is stateless — each method is an independent HTTP request.\n * Your application layer owns thread state, message arrays, and UI updates.\n *\n * @returns An {@link AgentChatClient} with methods for thread CRUD and\n * message streaming.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * const thread = await chat.createThread();\n *\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n * ```\n */\nexport function createAgentChatClient(): AgentChatClient {\n // Mint-time bearer per thread, for claimThread's ownership proof — after an\n // in-app login the app's config token is REPLACED (a fresh interface\n // session), so the pre-login token must be remembered to claim with.\n const threadTokens = new Map<string, string>();\n const clientToolHandlers = new Map<string, ClientToolHandler>();\n const rememberToken = (threadId: string) => {\n const token = getConfig().token;\n if (token && !threadTokens.has(threadId)) {\n threadTokens.set(threadId, token);\n }\n };\n\n return {\n async createThread() {\n const token = getConfig().token;\n const thread = await request<ThreadSummary>('/threads', 'POST');\n if (token) {\n threadTokens.set(thread.id, token);\n }\n return thread;\n },\n\n listThreads(cursor?: string) {\n const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';\n return request<ThreadListPage>(`/threads${query}`, 'GET');\n },\n\n getThread(threadId: string) {\n return request<Thread>(`/threads/${threadId}`, 'GET');\n },\n\n async updateThread(threadId: string, title: string) {\n await request(`/threads/${threadId}`, 'PATCH', { title });\n },\n\n async deleteThread(threadId: string) {\n await request(`/threads/${threadId}`, 'DELETE');\n },\n\n async claimThread(threadId: string, options?: { previousToken?: string }) {\n const previousToken =\n options?.previousToken ?? threadTokens.get(threadId);\n if (!previousToken) {\n throw new MindStudioInterfaceError(\n 'No pre-login token known for this thread — pass options.previousToken.',\n 'missing_previous_token',\n 400,\n );\n }\n await request(`/threads/${threadId}/claim`, 'POST', { previousToken });\n threadTokens.delete(threadId);\n },\n\n registerClientTool(name: string, handler: ClientToolHandler) {\n clientToolHandlers.set(name, handler);\n return () => {\n if (clientToolHandlers.get(name) === handler) {\n clientToolHandlers.delete(name);\n }\n };\n },\n\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult> {\n rememberToken(threadId);\n const controller = new AbortController();\n const cb = callbacks ?? {};\n\n // Wire caller's signal to our internal controller\n if (cb.signal) {\n if (cb.signal.aborted) {\n controller.abort();\n } else {\n cb.signal.addEventListener('abort', () => controller.abort(), {\n once: true,\n });\n }\n }\n\n const promise = (async (): Promise<SendMessageResult> => {\n const config = getConfig();\n const url = withBase(`${AGENT_BASE}/threads/${threadId}/messages`);\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n content,\n ...(options?.attachments?.length && {\n attachments: options.attachments,\n }),\n }),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Send message failed: ${res.status} ${res.statusText}`,\n 'agent_chat_error',\n );\n }\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let result: SendMessageResult | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as AgentChatEvent;\n\n if (event.type === 'done') {\n result = {\n stopReason: event.stopReason,\n usage: event.usage,\n };\n }\n\n if (event.type === 'client_tool_call') {\n // Detached on purpose: the handler may take as long as the\n // agent's patience allows, and this loop has to keep draining\n // the stream (keepalives included) while it does.\n const handler =\n clientToolHandlers.get(event.name) ??\n (cb.onClientToolCall\n ? (input: unknown) =>\n cb.onClientToolCall!(event.name, input, event.id)\n : undefined);\n void runClientTool(threadId, event, handler);\n }\n\n dispatchEvent(event, cb);\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n if (!result) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return result;\n })();\n\n const abortable = promise as AbortablePromise<SendMessageResult>;\n abortable.abort = () => controller.abort();\n return abortable;\n },\n };\n}\n","/**\n * Platform actions — client-direct file uploads.\n *\n * The app's backend mints an upload token (`Store.createUploadToken` in\n * `@mindstudio-ai/agent`); the browser then uploads the bytes straight to\n * storage with {@link platform.upload}. The file lands in the app's own file\n * store (private by default, served on the app's domain).\n *\n * @example\n * ```ts\n * import { createClient, platform } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n * const file = document.querySelector('input[type=file]').files[0];\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { key, url } = await platform.upload(token, file, {\n * onProgress: (fraction) => console.log(`${Math.round(fraction * 100)}%`),\n * });\n * ```\n */\n\nimport { getBasePath, getConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n/**\n * Options for `platform.uploadFile()`.\n */\nexport interface UploadFileOptions {\n /** Called with upload progress as a fraction from 0 to 1. */\n onProgress?: (fraction: number) => void;\n\n /** AbortSignal to cancel the upload. */\n signal?: AbortSignal;\n}\n\n/**\n * An upload token minted by an app's backend (`Store.createUploadToken` in\n * `@mindstudio-ai/agent`) and passed to {@link platform.upload}. Opaque — carry\n * it from the backend response straight into `platform.upload(token, file)`.\n */\nexport interface UploadToken {\n /** The object key the upload lands at. */\n key: string;\n /** The URL the file will be readable at once uploaded. */\n url: string;\n /** The scoped presigned POST the browser submits to. */\n upload: { url: string; fields: Record<string, string> };\n}\n\n/**\n * The platform namespace — file upload actions + serving context.\n */\nexport const platform = {\n /**\n * The mount prefix this app is served under on the current host, or ''\n * when serving at the root of its own subdomain/custom host. Use it as the\n * router basename so routes resolve wherever the app is mounted:\n *\n * ```ts\n * createBrowserRouter(routes, { basename: platform.basePath || '/' });\n * ```\n *\n * The SDK already prefixes all its own platform calls with it.\n */\n get basePath(): string {\n return getBasePath();\n },\n\n /**\n * Upload a file to the MindStudio CDN.\n *\n * Requests a presigned upload URL from the platform, then uploads\n * the file directly to S3. Returns the public CDN URL.\n *\n * @deprecated Use {@link platform.upload} with a backend-minted upload token\n * (`Store.createUploadToken`) instead. That uploads into the app's own file\n * store (private by default, on the app's domain); this legacy path uploads\n * to the shared, world-readable account media CDN.\n *\n * @param file - The File to upload\n * @param options - Optional progress callback and abort signal\n * @returns CDN URL of the uploaded file\n *\n * @example\n * ```ts\n * const url = await platform.uploadFile(file);\n * ```\n */\n async uploadFile(file: File, options?: UploadFileOptions): Promise<string> {\n const config = getConfig();\n const { onProgress, signal } = options ?? {};\n\n // Check if already aborted\n signal?.throwIfAborted();\n\n // Step 1: Get presigned upload URL\n const presignUrl = withBase('/_/generate-upload-request');\n const presignRes = await fetch(presignUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n filename: file.name,\n contentType: file.type || 'application/octet-stream',\n }),\n signal,\n });\n\n if (!presignRes.ok) {\n throw new MindStudioInterfaceError(\n `Upload presign failed: ${presignRes.status} ${presignRes.statusText}`,\n 'upload_error',\n presignRes.status,\n );\n }\n\n const { uploadUrl, uploadFields, publicUrl } =\n (await presignRes.json()) as {\n uploadUrl: string;\n uploadFields: Record<string, string>;\n publicUrl: string;\n };\n\n // Step 2: Upload directly to S3\n const formData = new FormData();\n for (const [key, value] of Object.entries(uploadFields)) {\n formData.append(key, value);\n }\n formData.append('file', file); // must be last\n\n // Use XHR for upload progress (fetch doesn't support it)\n if (onProgress) {\n await xhrUpload(uploadUrl, formData, onProgress, signal);\n return publicUrl;\n }\n\n const uploadRes = await fetch(uploadUrl, {\n method: 'POST',\n body: formData,\n signal,\n });\n\n if (!uploadRes.ok) {\n throw new MindStudioInterfaceError(\n `File upload failed: ${uploadRes.status} ${uploadRes.statusText}`,\n 'upload_error',\n uploadRes.status,\n );\n }\n\n return publicUrl;\n },\n\n /**\n * Upload a file with an {@link UploadToken} minted by the app's backend\n * (`Store.createUploadToken`). The file is POSTed **directly to storage** —\n * the bytes never pass through the platform — and the returned `{ key, url }`\n * is ready to render (`url` is a stable on-domain link the app can use in\n * `<img>` / `fetch` / `<a download>`).\n *\n * @example\n * ```ts\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { url } = await platform.upload(token, file, {\n * onProgress: (f) => setProgress(f),\n * });\n * ```\n */\n async upload(\n token: UploadToken,\n file: File,\n options?: UploadFileOptions,\n ): Promise<{ key: string; url: string }> {\n const { onProgress, signal } = options ?? {};\n signal?.throwIfAborted();\n\n const formData = new FormData();\n for (const [key, value] of Object.entries(token.upload.fields)) {\n formData.append(key, value);\n }\n formData.append('file', file); // must be last\n\n if (onProgress) {\n await xhrUpload(token.upload.url, formData, onProgress, signal);\n } else {\n const res = await fetch(token.upload.url, {\n method: 'POST',\n body: formData,\n signal,\n });\n if (!res.ok) {\n throw new MindStudioInterfaceError(\n `File upload failed: ${res.status} ${res.statusText}`,\n 'upload_error',\n res.status,\n );\n }\n }\n\n return { key: token.key, url: token.url };\n },\n};\n\n/**\n * Upload via XMLHttpRequest to get progress events.\n * Falls back from fetch because fetch upload streams don't support progress.\n */\nfunction xhrUpload(\n url: string,\n formData: FormData,\n onProgress: (fraction: number) => void,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress(e.loaded / e.total);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress(1);\n resolve();\n } else {\n reject(\n new MindStudioInterfaceError(\n `File upload failed: ${xhr.status} ${xhr.statusText}`,\n 'upload_error',\n xhr.status,\n ),\n );\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(\n new MindStudioInterfaceError(\n 'File upload failed: network error',\n 'upload_error',\n ),\n );\n });\n\n xhr.addEventListener('abort', () => {\n reject(new DOMException('Upload aborted', 'AbortError'));\n });\n\n // Wire up abort signal\n if (signal) {\n if (signal.aborted) {\n reject(new DOMException('Upload aborted', 'AbortError'));\n return;\n }\n signal.addEventListener('abort', () => xhr.abort(), { once: true });\n }\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n}\n","/**\n * Phone number utilities for auth flows.\n *\n * Provides country data, E.164 conversion, formatting, and validation.\n * No external dependencies — designed for login/signup UIs that need\n * a country code picker and phone number formatting.\n */\n\n// ---------------------------------------------------------------------------\n// Country data\n// ---------------------------------------------------------------------------\n\n/** A country with its dial code and flag emoji. */\nexport interface Country {\n /** ISO 3166-1 alpha-2 code (e.g. \"US\"). */\n code: string;\n /** International dial code with + prefix (e.g. \"+1\"). */\n dialCode: string;\n /** Country name in English. */\n name: string;\n /** Flag emoji. */\n flag: string;\n}\n\n/** All countries with dial codes, sorted alphabetically by name. */\nexport const countries: readonly Country[] = [\n { code: 'AF', dialCode: '+93', name: 'Afghanistan', flag: '🇦🇫' },\n { code: 'AL', dialCode: '+355', name: 'Albania', flag: '🇦🇱' },\n { code: 'DZ', dialCode: '+213', name: 'Algeria', flag: '🇩🇿' },\n { code: 'AD', dialCode: '+376', name: 'Andorra', flag: '🇦🇩' },\n { code: 'AO', dialCode: '+244', name: 'Angola', flag: '🇦🇴' },\n { code: 'AG', dialCode: '+1', name: 'Antigua and Barbuda', flag: '🇦🇬' },\n { code: 'AR', dialCode: '+54', name: 'Argentina', flag: '🇦🇷' },\n { code: 'AM', dialCode: '+374', name: 'Armenia', flag: '🇦🇲' },\n { code: 'AU', dialCode: '+61', name: 'Australia', flag: '🇦🇺' },\n { code: 'AT', dialCode: '+43', name: 'Austria', flag: '🇦🇹' },\n { code: 'AZ', dialCode: '+994', name: 'Azerbaijan', flag: '🇦🇿' },\n { code: 'BS', dialCode: '+1', name: 'Bahamas', flag: '🇧🇸' },\n { code: 'BH', dialCode: '+973', name: 'Bahrain', flag: '🇧🇭' },\n { code: 'BD', dialCode: '+880', name: 'Bangladesh', flag: '🇧🇩' },\n { code: 'BB', dialCode: '+1', name: 'Barbados', flag: '🇧🇧' },\n { code: 'BY', dialCode: '+375', name: 'Belarus', flag: '🇧🇾' },\n { code: 'BE', dialCode: '+32', name: 'Belgium', flag: '🇧🇪' },\n { code: 'BZ', dialCode: '+501', name: 'Belize', flag: '🇧🇿' },\n { code: 'BJ', dialCode: '+229', name: 'Benin', flag: '🇧🇯' },\n { code: 'BT', dialCode: '+975', name: 'Bhutan', flag: '🇧🇹' },\n { code: 'BO', dialCode: '+591', name: 'Bolivia', flag: '🇧🇴' },\n { code: 'BA', dialCode: '+387', name: 'Bosnia and Herzegovina', flag: '🇧🇦' },\n { code: 'BW', dialCode: '+267', name: 'Botswana', flag: '🇧🇼' },\n { code: 'BR', dialCode: '+55', name: 'Brazil', flag: '🇧🇷' },\n { code: 'BN', dialCode: '+673', name: 'Brunei', flag: '🇧🇳' },\n { code: 'BG', dialCode: '+359', name: 'Bulgaria', flag: '🇧🇬' },\n { code: 'BF', dialCode: '+226', name: 'Burkina Faso', flag: '🇧🇫' },\n { code: 'BI', dialCode: '+257', name: 'Burundi', flag: '🇧🇮' },\n { code: 'CV', dialCode: '+238', name: 'Cabo Verde', flag: '🇨🇻' },\n { code: 'KH', dialCode: '+855', name: 'Cambodia', flag: '🇰🇭' },\n { code: 'CM', dialCode: '+237', name: 'Cameroon', flag: '🇨🇲' },\n { code: 'CA', dialCode: '+1', name: 'Canada', flag: '🇨🇦' },\n {\n code: 'CF',\n dialCode: '+236',\n name: 'Central African Republic',\n flag: '🇨🇫',\n },\n { code: 'TD', dialCode: '+235', name: 'Chad', flag: '🇹🇩' },\n { code: 'CL', dialCode: '+56', name: 'Chile', flag: '🇨🇱' },\n { code: 'CN', dialCode: '+86', name: 'China', flag: '🇨🇳' },\n { code: 'CO', dialCode: '+57', name: 'Colombia', flag: '🇨🇴' },\n { code: 'KM', dialCode: '+269', name: 'Comoros', flag: '🇰🇲' },\n { code: 'CG', dialCode: '+242', name: 'Congo', flag: '🇨🇬' },\n { code: 'CD', dialCode: '+243', name: 'Congo (DRC)', flag: '🇨🇩' },\n { code: 'CR', dialCode: '+506', name: 'Costa Rica', flag: '🇨🇷' },\n { code: 'HR', dialCode: '+385', name: 'Croatia', flag: '🇭🇷' },\n { code: 'CU', dialCode: '+53', name: 'Cuba', flag: '🇨🇺' },\n { code: 'CY', dialCode: '+357', name: 'Cyprus', flag: '🇨🇾' },\n { code: 'CZ', dialCode: '+420', name: 'Czechia', flag: '🇨🇿' },\n { code: 'CI', dialCode: '+225', name: \"Cote d'Ivoire\", flag: '🇨🇮' },\n { code: 'DK', dialCode: '+45', name: 'Denmark', flag: '🇩🇰' },\n { code: 'DJ', dialCode: '+253', name: 'Djibouti', flag: '🇩🇯' },\n { code: 'DM', dialCode: '+1', name: 'Dominica', flag: '🇩🇲' },\n { code: 'DO', dialCode: '+1', name: 'Dominican Republic', flag: '🇩🇴' },\n { code: 'EC', dialCode: '+593', name: 'Ecuador', flag: '🇪🇨' },\n { code: 'EG', dialCode: '+20', name: 'Egypt', flag: '🇪🇬' },\n { code: 'SV', dialCode: '+503', name: 'El Salvador', flag: '🇸🇻' },\n { code: 'GQ', dialCode: '+240', name: 'Equatorial Guinea', flag: '🇬🇶' },\n { code: 'ER', dialCode: '+291', name: 'Eritrea', flag: '🇪🇷' },\n { code: 'EE', dialCode: '+372', name: 'Estonia', flag: '🇪🇪' },\n { code: 'SZ', dialCode: '+268', name: 'Eswatini', flag: '🇸🇿' },\n { code: 'ET', dialCode: '+251', name: 'Ethiopia', flag: '🇪🇹' },\n { code: 'FJ', dialCode: '+679', name: 'Fiji', flag: '🇫🇯' },\n { code: 'FI', dialCode: '+358', name: 'Finland', flag: '🇫🇮' },\n { code: 'FR', dialCode: '+33', name: 'France', flag: '🇫🇷' },\n { code: 'GA', dialCode: '+241', name: 'Gabon', flag: '🇬🇦' },\n { code: 'GM', dialCode: '+220', name: 'Gambia', flag: '🇬🇲' },\n { code: 'GE', dialCode: '+995', name: 'Georgia', flag: '🇬🇪' },\n { code: 'DE', dialCode: '+49', name: 'Germany', flag: '🇩🇪' },\n { code: 'GH', dialCode: '+233', name: 'Ghana', flag: '🇬🇭' },\n { code: 'GR', dialCode: '+30', name: 'Greece', flag: '🇬🇷' },\n { code: 'GD', dialCode: '+1', name: 'Grenada', flag: '🇬🇩' },\n { code: 'GT', dialCode: '+502', name: 'Guatemala', flag: '🇬🇹' },\n { code: 'GN', dialCode: '+224', name: 'Guinea', flag: '🇬🇳' },\n { code: 'GW', dialCode: '+245', name: 'Guinea-Bissau', flag: '🇬🇼' },\n { code: 'GY', dialCode: '+592', name: 'Guyana', flag: '🇬🇾' },\n { code: 'HT', dialCode: '+509', name: 'Haiti', flag: '🇭🇹' },\n { code: 'HN', dialCode: '+504', name: 'Honduras', flag: '🇭🇳' },\n { code: 'HK', dialCode: '+852', name: 'Hong Kong', flag: '🇭🇰' },\n { code: 'HU', dialCode: '+36', name: 'Hungary', flag: '🇭🇺' },\n { code: 'IS', dialCode: '+354', name: 'Iceland', flag: '🇮🇸' },\n { code: 'IN', dialCode: '+91', name: 'India', flag: '🇮🇳' },\n { code: 'ID', dialCode: '+62', name: 'Indonesia', flag: '🇮🇩' },\n { code: 'IR', dialCode: '+98', name: 'Iran', flag: '🇮🇷' },\n { code: 'IQ', dialCode: '+964', name: 'Iraq', flag: '🇮🇶' },\n { code: 'IE', dialCode: '+353', name: 'Ireland', flag: '🇮🇪' },\n { code: 'IL', dialCode: '+972', name: 'Israel', flag: '🇮🇱' },\n { code: 'IT', dialCode: '+39', name: 'Italy', flag: '🇮🇹' },\n { code: 'JM', dialCode: '+1', name: 'Jamaica', flag: '🇯🇲' },\n { code: 'JP', dialCode: '+81', name: 'Japan', flag: '🇯🇵' },\n { code: 'JO', dialCode: '+962', name: 'Jordan', flag: '🇯🇴' },\n { code: 'KZ', dialCode: '+7', name: 'Kazakhstan', flag: '🇰🇿' },\n { code: 'KE', dialCode: '+254', name: 'Kenya', flag: '🇰🇪' },\n { code: 'KI', dialCode: '+686', name: 'Kiribati', flag: '🇰🇮' },\n { code: 'KW', dialCode: '+965', name: 'Kuwait', flag: '🇰🇼' },\n { code: 'KG', dialCode: '+996', name: 'Kyrgyzstan', flag: '🇰🇬' },\n { code: 'LA', dialCode: '+856', name: 'Laos', flag: '🇱🇦' },\n { code: 'LV', dialCode: '+371', name: 'Latvia', flag: '🇱🇻' },\n { code: 'LB', dialCode: '+961', name: 'Lebanon', flag: '🇱🇧' },\n { code: 'LS', dialCode: '+266', name: 'Lesotho', flag: '🇱🇸' },\n { code: 'LR', dialCode: '+231', name: 'Liberia', flag: '🇱🇷' },\n { code: 'LY', dialCode: '+218', name: 'Libya', flag: '🇱🇾' },\n { code: 'LI', dialCode: '+423', name: 'Liechtenstein', flag: '🇱🇮' },\n { code: 'LT', dialCode: '+370', name: 'Lithuania', flag: '🇱🇹' },\n { code: 'LU', dialCode: '+352', name: 'Luxembourg', flag: '🇱🇺' },\n { code: 'MO', dialCode: '+853', name: 'Macao', flag: '🇲🇴' },\n { code: 'MG', dialCode: '+261', name: 'Madagascar', flag: '🇲🇬' },\n { code: 'MW', dialCode: '+265', name: 'Malawi', flag: '🇲🇼' },\n { code: 'MY', dialCode: '+60', name: 'Malaysia', flag: '🇲🇾' },\n { code: 'MV', dialCode: '+960', name: 'Maldives', flag: '🇲🇻' },\n { code: 'ML', dialCode: '+223', name: 'Mali', flag: '🇲🇱' },\n { code: 'MT', dialCode: '+356', name: 'Malta', flag: '🇲🇹' },\n { code: 'MH', dialCode: '+692', name: 'Marshall Islands', flag: '🇲🇭' },\n { code: 'MR', dialCode: '+222', name: 'Mauritania', flag: '🇲🇷' },\n { code: 'MU', dialCode: '+230', name: 'Mauritius', flag: '🇲🇺' },\n { code: 'MX', dialCode: '+52', name: 'Mexico', flag: '🇲🇽' },\n { code: 'FM', dialCode: '+691', name: 'Micronesia', flag: '🇫🇲' },\n { code: 'MD', dialCode: '+373', name: 'Moldova', flag: '🇲🇩' },\n { code: 'MC', dialCode: '+377', name: 'Monaco', flag: '🇲🇨' },\n { code: 'MN', dialCode: '+976', name: 'Mongolia', flag: '🇲🇳' },\n { code: 'ME', dialCode: '+382', name: 'Montenegro', flag: '🇲🇪' },\n { code: 'MA', dialCode: '+212', name: 'Morocco', flag: '🇲🇦' },\n { code: 'MZ', dialCode: '+258', name: 'Mozambique', flag: '🇲🇿' },\n { code: 'MM', dialCode: '+95', name: 'Myanmar', flag: '🇲🇲' },\n { code: 'NA', dialCode: '+264', name: 'Namibia', flag: '🇳🇦' },\n { code: 'NR', dialCode: '+674', name: 'Nauru', flag: '🇳🇷' },\n { code: 'NP', dialCode: '+977', name: 'Nepal', flag: '🇳🇵' },\n { code: 'NL', dialCode: '+31', name: 'Netherlands', flag: '🇳🇱' },\n { code: 'NZ', dialCode: '+64', name: 'New Zealand', flag: '🇳🇿' },\n { code: 'NI', dialCode: '+505', name: 'Nicaragua', flag: '🇳🇮' },\n { code: 'NE', dialCode: '+227', name: 'Niger', flag: '🇳🇪' },\n { code: 'NG', dialCode: '+234', name: 'Nigeria', flag: '🇳🇬' },\n { code: 'KP', dialCode: '+850', name: 'North Korea', flag: '🇰🇵' },\n { code: 'MK', dialCode: '+389', name: 'North Macedonia', flag: '🇲🇰' },\n { code: 'NO', dialCode: '+47', name: 'Norway', flag: '🇳🇴' },\n { code: 'OM', dialCode: '+968', name: 'Oman', flag: '🇴🇲' },\n { code: 'PK', dialCode: '+92', name: 'Pakistan', flag: '🇵🇰' },\n { code: 'PW', dialCode: '+680', name: 'Palau', flag: '🇵🇼' },\n { code: 'PS', dialCode: '+970', name: 'Palestine', flag: '🇵🇸' },\n { code: 'PA', dialCode: '+507', name: 'Panama', flag: '🇵🇦' },\n { code: 'PG', dialCode: '+675', name: 'Papua New Guinea', flag: '🇵🇬' },\n { code: 'PY', dialCode: '+595', name: 'Paraguay', flag: '🇵🇾' },\n { code: 'PE', dialCode: '+51', name: 'Peru', flag: '🇵🇪' },\n { code: 'PH', dialCode: '+63', name: 'Philippines', flag: '🇵🇭' },\n { code: 'PL', dialCode: '+48', name: 'Poland', flag: '🇵🇱' },\n { code: 'PT', dialCode: '+351', name: 'Portugal', flag: '🇵🇹' },\n { code: 'PR', dialCode: '+1', name: 'Puerto Rico', flag: '🇵🇷' },\n { code: 'QA', dialCode: '+974', name: 'Qatar', flag: '🇶🇦' },\n { code: 'RO', dialCode: '+40', name: 'Romania', flag: '🇷🇴' },\n { code: 'RU', dialCode: '+7', name: 'Russia', flag: '🇷🇺' },\n { code: 'RW', dialCode: '+250', name: 'Rwanda', flag: '🇷🇼' },\n { code: 'KN', dialCode: '+1', name: 'Saint Kitts and Nevis', flag: '🇰🇳' },\n { code: 'LC', dialCode: '+1', name: 'Saint Lucia', flag: '🇱🇨' },\n {\n code: 'VC',\n dialCode: '+1',\n name: 'Saint Vincent and the Grenadines',\n flag: '🇻🇨',\n },\n { code: 'WS', dialCode: '+685', name: 'Samoa', flag: '🇼🇸' },\n { code: 'SM', dialCode: '+378', name: 'San Marino', flag: '🇸🇲' },\n { code: 'ST', dialCode: '+239', name: 'Sao Tome and Principe', flag: '🇸🇹' },\n { code: 'SA', dialCode: '+966', name: 'Saudi Arabia', flag: '🇸🇦' },\n { code: 'SN', dialCode: '+221', name: 'Senegal', flag: '🇸🇳' },\n { code: 'RS', dialCode: '+381', name: 'Serbia', flag: '🇷🇸' },\n { code: 'SC', dialCode: '+248', name: 'Seychelles', flag: '🇸🇨' },\n { code: 'SL', dialCode: '+232', name: 'Sierra Leone', flag: '🇸🇱' },\n { code: 'SG', dialCode: '+65', name: 'Singapore', flag: '🇸🇬' },\n { code: 'SK', dialCode: '+421', name: 'Slovakia', flag: '🇸🇰' },\n { code: 'SI', dialCode: '+386', name: 'Slovenia', flag: '🇸🇮' },\n { code: 'SB', dialCode: '+677', name: 'Solomon Islands', flag: '🇸🇧' },\n { code: 'SO', dialCode: '+252', name: 'Somalia', flag: '🇸🇴' },\n { code: 'ZA', dialCode: '+27', name: 'South Africa', flag: '🇿🇦' },\n { code: 'KR', dialCode: '+82', name: 'South Korea', flag: '🇰🇷' },\n { code: 'SS', dialCode: '+211', name: 'South Sudan', flag: '🇸🇸' },\n { code: 'ES', dialCode: '+34', name: 'Spain', flag: '🇪🇸' },\n { code: 'LK', dialCode: '+94', name: 'Sri Lanka', flag: '🇱🇰' },\n { code: 'SD', dialCode: '+249', name: 'Sudan', flag: '🇸🇩' },\n { code: 'SR', dialCode: '+597', name: 'Suriname', flag: '🇸🇷' },\n { code: 'SE', dialCode: '+46', name: 'Sweden', flag: '🇸🇪' },\n { code: 'CH', dialCode: '+41', name: 'Switzerland', flag: '🇨🇭' },\n { code: 'SY', dialCode: '+963', name: 'Syria', flag: '🇸🇾' },\n { code: 'TW', dialCode: '+886', name: 'Taiwan', flag: '🇹🇼' },\n { code: 'TJ', dialCode: '+992', name: 'Tajikistan', flag: '🇹🇯' },\n { code: 'TZ', dialCode: '+255', name: 'Tanzania', flag: '🇹🇿' },\n { code: 'TH', dialCode: '+66', name: 'Thailand', flag: '🇹🇭' },\n { code: 'TL', dialCode: '+670', name: 'Timor-Leste', flag: '🇹🇱' },\n { code: 'TG', dialCode: '+228', name: 'Togo', flag: '🇹🇬' },\n { code: 'TO', dialCode: '+676', name: 'Tonga', flag: '🇹🇴' },\n { code: 'TT', dialCode: '+1', name: 'Trinidad and Tobago', flag: '🇹🇹' },\n { code: 'TN', dialCode: '+216', name: 'Tunisia', flag: '🇹🇳' },\n { code: 'TR', dialCode: '+90', name: 'Turkey', flag: '🇹🇷' },\n { code: 'TM', dialCode: '+993', name: 'Turkmenistan', flag: '🇹🇲' },\n { code: 'TV', dialCode: '+688', name: 'Tuvalu', flag: '🇹🇻' },\n { code: 'UG', dialCode: '+256', name: 'Uganda', flag: '🇺🇬' },\n { code: 'UA', dialCode: '+380', name: 'Ukraine', flag: '🇺🇦' },\n { code: 'AE', dialCode: '+971', name: 'United Arab Emirates', flag: '🇦🇪' },\n { code: 'GB', dialCode: '+44', name: 'United Kingdom', flag: '🇬🇧' },\n { code: 'US', dialCode: '+1', name: 'United States', flag: '🇺🇸' },\n { code: 'UY', dialCode: '+598', name: 'Uruguay', flag: '🇺🇾' },\n { code: 'UZ', dialCode: '+998', name: 'Uzbekistan', flag: '🇺🇿' },\n { code: 'VU', dialCode: '+678', name: 'Vanuatu', flag: '🇻🇺' },\n { code: 'VA', dialCode: '+39', name: 'Vatican City', flag: '🇻🇦' },\n { code: 'VE', dialCode: '+58', name: 'Venezuela', flag: '🇻🇪' },\n { code: 'VN', dialCode: '+84', name: 'Vietnam', flag: '🇻🇳' },\n { code: 'YE', dialCode: '+967', name: 'Yemen', flag: '🇾🇪' },\n { code: 'ZM', dialCode: '+260', name: 'Zambia', flag: '🇿🇲' },\n { code: 'ZW', dialCode: '+263', name: 'Zimbabwe', flag: '🇿🇼' },\n];\n\n// ---------------------------------------------------------------------------\n// Country lookup helpers\n// ---------------------------------------------------------------------------\n\nconst byCode = new Map(countries.map((c) => [c.code, c]));\n\n/** Common IANA timezone → country code mapping. */\nconst timezoneToCountry: Record<string, string> = {\n 'America/New_York': 'US',\n 'America/Chicago': 'US',\n 'America/Denver': 'US',\n 'America/Los_Angeles': 'US',\n 'America/Anchorage': 'US',\n 'Pacific/Honolulu': 'US',\n 'America/Phoenix': 'US',\n 'America/Indiana/Indianapolis': 'US',\n 'America/Toronto': 'CA',\n 'America/Vancouver': 'CA',\n 'America/Edmonton': 'CA',\n 'America/Winnipeg': 'CA',\n 'America/Halifax': 'CA',\n 'America/St_Johns': 'CA',\n 'Europe/London': 'GB',\n 'Europe/Dublin': 'IE',\n 'Europe/Paris': 'FR',\n 'Europe/Berlin': 'DE',\n 'Europe/Madrid': 'ES',\n 'Europe/Rome': 'IT',\n 'Europe/Amsterdam': 'NL',\n 'Europe/Brussels': 'BE',\n 'Europe/Zurich': 'CH',\n 'Europe/Vienna': 'AT',\n 'Europe/Stockholm': 'SE',\n 'Europe/Oslo': 'NO',\n 'Europe/Copenhagen': 'DK',\n 'Europe/Helsinki': 'FI',\n 'Europe/Warsaw': 'PL',\n 'Europe/Prague': 'CZ',\n 'Europe/Bucharest': 'RO',\n 'Europe/Budapest': 'HU',\n 'Europe/Athens': 'GR',\n 'Europe/Istanbul': 'TR',\n 'Europe/Moscow': 'RU',\n 'Europe/Kiev': 'UA',\n 'Europe/Lisbon': 'PT',\n 'Europe/Belgrade': 'RS',\n 'Europe/Zagreb': 'HR',\n 'Europe/Bratislava': 'SK',\n 'Europe/Ljubljana': 'SI',\n 'Europe/Tallinn': 'EE',\n 'Europe/Riga': 'LV',\n 'Europe/Vilnius': 'LT',\n 'Asia/Tokyo': 'JP',\n 'Asia/Seoul': 'KR',\n 'Asia/Shanghai': 'CN',\n 'Asia/Hong_Kong': 'HK',\n 'Asia/Taipei': 'TW',\n 'Asia/Singapore': 'SG',\n 'Asia/Kolkata': 'IN',\n 'Asia/Karachi': 'PK',\n 'Asia/Dhaka': 'BD',\n 'Asia/Bangkok': 'TH',\n 'Asia/Jakarta': 'ID',\n 'Asia/Manila': 'PH',\n 'Asia/Kuala_Lumpur': 'MY',\n 'Asia/Ho_Chi_Minh': 'VN',\n 'Asia/Riyadh': 'SA',\n 'Asia/Dubai': 'AE',\n 'Asia/Tehran': 'IR',\n 'Asia/Baghdad': 'IQ',\n 'Asia/Beirut': 'LB',\n 'Asia/Jerusalem': 'IL',\n 'Asia/Amman': 'JO',\n 'Asia/Almaty': 'KZ',\n 'Asia/Tashkent': 'UZ',\n 'Asia/Colombo': 'LK',\n 'Asia/Kathmandu': 'NP',\n 'Asia/Yangon': 'MM',\n 'Australia/Sydney': 'AU',\n 'Australia/Melbourne': 'AU',\n 'Australia/Brisbane': 'AU',\n 'Australia/Perth': 'AU',\n 'Australia/Adelaide': 'AU',\n 'Pacific/Auckland': 'NZ',\n 'Pacific/Fiji': 'FJ',\n 'Africa/Cairo': 'EG',\n 'Africa/Lagos': 'NG',\n 'Africa/Nairobi': 'KE',\n 'Africa/Johannesburg': 'ZA',\n 'Africa/Casablanca': 'MA',\n 'Africa/Accra': 'GH',\n 'Africa/Addis_Ababa': 'ET',\n 'Africa/Dar_es_Salaam': 'TZ',\n 'Africa/Kampala': 'UG',\n 'America/Mexico_City': 'MX',\n 'America/Bogota': 'CO',\n 'America/Lima': 'PE',\n 'America/Santiago': 'CL',\n 'America/Buenos_Aires': 'AR',\n 'America/Sao_Paulo': 'BR',\n 'America/Caracas': 'VE',\n 'America/Guatemala': 'GT',\n 'America/Havana': 'CU',\n 'America/Panama': 'PA',\n 'America/Jamaica': 'JM',\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Detect the user's country from their timezone.\n * Falls back to `'US'` if the timezone can't be mapped.\n */\nexport function detectCountry(): string {\n try {\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n return timezoneToCountry[tz] ?? 'US';\n } catch {\n return 'US';\n }\n}\n\n/**\n * Format an E.164 phone number for display.\n *\n * US/CA numbers: `+1 (555) 123-4567`\n * Others: `+{dialCode} {rest}`\n */\nexport function format(e164: string): string {\n if (!e164.startsWith('+')) {\n return e164;\n }\n\n const digits = e164.slice(1);\n\n // US/CA: +1 followed by 10 digits\n if (digits.startsWith('1') && digits.length === 11) {\n const area = digits.slice(1, 4);\n const prefix = digits.slice(4, 7);\n const line = digits.slice(7);\n return `+1 (${area}) ${prefix}-${line}`;\n }\n\n // Find matching dial code (try longest match first)\n for (let len = 4; len >= 1; len--) {\n const candidate = '+' + digits.slice(0, len);\n const match = countries.find((c) => c.dialCode === candidate);\n if (match) {\n const rest = digits.slice(len);\n return `${match.dialCode} ${rest}`;\n }\n }\n\n return e164;\n}\n\n/**\n * Convert a national phone number to E.164 format.\n *\n * @param national - Phone number with or without formatting (e.g. \"(555) 123-4567\")\n * @param countryCode - ISO 3166-1 alpha-2 code (e.g. \"US\")\n * @returns E.164 formatted number (e.g. \"+15551234567\")\n */\nexport function toE164(national: string, countryCode: string): string {\n const digits = national.replace(/\\D/g, '');\n const country = byCode.get(countryCode);\n if (!country) {\n return '+' + digits;\n }\n const dialDigits = country.dialCode.slice(1); // remove +\n // If the number already starts with the dial code, don't double-add\n if (digits.startsWith(dialDigits)) {\n return '+' + digits;\n }\n return country.dialCode + digits;\n}\n\n/**\n * Check if a string is a valid E.164 phone number.\n * Must start with + followed by 7-15 digits.\n */\nexport function isValid(phone: string): boolean {\n return /^\\+[1-9]\\d{6,14}$/.test(phone);\n}\n","/**\n * Email validation helper for auth flows.\n */\n\n/** Basic email format check. Not RFC 5322 exhaustive. */\nexport function isValid(email: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n}\n","/**\n * Auth — app-managed authentication for MindStudio v2 apps.\n *\n * Provides verification code flows (email + SMS), session state\n * management, and phone/email validation helpers. The platform\n * handles code delivery, cookie management, and user storage —\n * developers build their own login UI using these methods.\n *\n * ## How it works\n *\n * ```\n * // 1. Send a verification code\n * const { verificationId } = await auth.sendEmailCode('user@example.com');\n *\n * // 2. User enters the code in your UI\n * const user = await auth.verifyEmailCode(verificationId, '123456');\n *\n * // 3. Session is now active — all SDK calls use the authenticated token\n * const result = await api.getDashboard(); // uses authenticated session\n * auth.getCurrentUser(); // { id, email, phone, roles, createdAt }\n * ```\n *\n * Verify, confirm, and logout methods update `window.__MINDSTUDIO__`\n * in-place so all downstream calls (method invocation, agent chat,\n * uploads) immediately use the new session. No page refresh needed.\n *\n * @example\n * ```tsx\n * import { auth } from '@mindstudio-ai/interface';\n *\n * function LoginPage() {\n * const [email, setEmail] = useState('');\n * const [verificationId, setVerificationId] = useState('');\n * const [code, setCode] = useState('');\n *\n * const handleSend = async () => {\n * const { verificationId } = await auth.sendEmailCode(email);\n * setVerificationId(verificationId);\n * };\n *\n * const handleVerify = async () => {\n * await auth.verifyEmailCode(verificationId, code);\n * // Session updated in-place — navigate to your app\n * window.location.href = '/dashboard';\n * };\n * }\n * ```\n */\n\nimport { getConfig, updateConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\nimport type { AppUser, AuthSessionBundle } from './types.js';\nimport * as phoneHelpers from './auth-phone.js';\nimport * as emailHelpers from './auth-email.js';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** True when the SDK is running inside a (cross-origin) iframe. */\nfunction isEmbedded(): boolean {\n try {\n return window.top !== window.self;\n } catch {\n // Cross-origin restrictions on reading window.top → we're definitely framed.\n return true;\n }\n}\n\nasync function authFetch<T>(\n path: string,\n method: 'GET' | 'POST',\n body?: Record<string, unknown>,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n // Every auth POST carries the framing hint, injected here at the transport\n // so no cookie-setting endpoint (verify, change-confirm, exchange, logout)\n // can miss it. The server can't detect framing itself — these are\n // same-origin fetches — and it needs to know: a framed sign-in must set the\n // `__ms_auth` cookie `SameSite=None; Partitioned` or the browser discards\n // it, breaking header-less reads (private `file.url` in <img>/fetch) and\n // session restore on reload. Endpoints that don't set cookies ignore the\n // extra field.\n const payload =\n method === 'POST' ? { ...body, embedded: isEmbedded() } : body;\n\n if (payload !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(withBase(path), {\n method,\n headers,\n ...(payload !== undefined && { body: JSON.stringify(payload) }),\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Auth request failed: ${res.status} ${res.statusText}`,\n 'auth_error',\n );\n }\n\n return (await res.json()) as T;\n}\n\n// sessionStorage key holding the CSRF `state` for an in-flight top-level\n// \"Sign in with Remy\" round-trip. Set by signInWithRemy(), validated + cleared\n// by handleRemyRedirect(). (The embedded/popup flow keeps `state` in the\n// opener's memory instead — see signInWithRemyPopup.)\nconst REMY_STATE_KEY = '__ms_remy_state';\n\n// Embedded (iframe) flow: a query marker on the popup callback URL so the SDK\n// instance that loads in the popup relays + closes instead of redeeming, and\n// the postMessage discriminator the opener matches on.\nconst REMY_POPUP_MARKER = 'ms_popup';\nconst REMY_POPUP_MESSAGE = 'ms:remy:callback';\n\n// The popup document forwards its one-time code to the opener exactly once,\n// whichever entry point (the import-time fast path or handleRemyRedirect())\n// runs the relay. Flipped after the first successful post — see\n// maybeRelayRemyPopupCallback.\nlet remyPopupForwarded = false;\n\n/**\n * Whether a delegated (\"Sign in with Remy\") handshake is completing right now.\n *\n * Set **synchronously at module load** when the page is a cold-load redirect\n * return (a `?code` in the URL that isn't a popup callback) — before any app\n * render — and cleared when the exchange settles. This is the stable signal\n * apps read via {@link Auth.authStatus}; the `?code` URL param can't serve that\n * role because handleRemyRedirect() strips it at the *start* of the exchange.\n * Also toggled around the embedded popup exchange (see signInWithRemyPopup).\n */\nlet redirectPending = detectColdLoadRedirect();\n\n/** True when this page load is a cold-load delegated redirect return. */\nfunction detectColdLoadRedirect(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n try {\n const params = new URL(window.location.href).searchParams;\n // A popup-callback document (`?ms_popup=1`) relays + closes; it never\n // redeems here, so it isn't \"authenticating\" from the app's perspective.\n return params.has('code') && params.get(REMY_POPUP_MARKER) !== '1';\n } catch {\n return false;\n }\n}\n\n/** Generate an opaque, unguessable CSRF state value. */\nfunction generateState(): string {\n const c = globalThis.crypto;\n if (c && typeof c.randomUUID === 'function') {\n return c.randomUUID();\n }\n const bytes = new Uint8Array(16);\n c?.getRandomValues?.(bytes);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** Strip our own `code`/`state` params from the URL without a reload. */\nfunction stripRemyRedirectParams(url: URL): void {\n url.searchParams.delete('code');\n url.searchParams.delete('state');\n try {\n window.history.replaceState(window.history.state, '', url.toString());\n } catch {\n // history unavailable (e.g. non-browser) — nothing to clean\n }\n}\n\nconst authListeners: Set<(user: AppUser | null) => void> = new Set();\n\nfunction applySession(bundle: AuthSessionBundle): void {\n updateConfig({\n token: bundle.token,\n user: bundle.user,\n methods: bundle.methods,\n visitorId: bundle.visitorId,\n });\n authListeners.forEach((cb) => cb(bundle.user));\n}\n\nfunction requireUser(bundle: AuthSessionBundle): AppUser {\n if (!bundle.user) {\n throw new MindStudioInterfaceError(\n 'Verification succeeded but no user was returned',\n 'auth_error',\n );\n }\n return bundle.user;\n}\n\nfunction updateUserAndNotify(update: Partial<AppUser>): void {\n const config = getConfig();\n if (config.user) {\n Object.assign(config.user, update);\n authListeners.forEach((cb) => cb(config.user));\n }\n}\n\n/** The current user, or `null` — never throws if the bootstrap is absent. */\nfunction safeCurrentUser(): AppUser | null {\n try {\n return getConfig().user;\n } catch {\n return null;\n }\n}\n\n/** Fire every auth listener with the current user (for auth-state changes). */\nfunction notifyAuthListeners(): void {\n const user = safeCurrentUser();\n authListeners.forEach((cb) => cb(user));\n}\n\n/** Enter the 'authenticating' state and notify subscribers (idempotent). */\nfunction beginAuthenticating(): void {\n if (redirectPending) {\n return;\n }\n redirectPending = true;\n notifyAuthListeners();\n}\n\n/** Leave the 'authenticating' state and notify subscribers (idempotent). */\nfunction endAuthenticating(): void {\n if (!redirectPending) {\n return;\n }\n redirectPending = false;\n notifyAuthListeners();\n}\n\n// Sign-in popup size — compact and roughly square. (The old 480×720 opened tall\n// and, with no left/top, pinned to the top-left corner.)\nconst REMY_POPUP_WIDTH = 520;\nconst REMY_POPUP_HEIGHT = 600;\n\n/**\n * A `window.open` features string for a popup of the given size, centered over\n * the current browser window (dual-monitor-safe — centers on the window the\n * user is actually looking at, not the primary screen), clamped to fit.\n */\nfunction centeredPopupFeatures(width: number, height: number): string {\n const baseLeft = window.screenX;\n const baseTop = window.screenY;\n const viewW = window.outerWidth || window.innerWidth || window.screen.width;\n const viewH =\n window.outerHeight || window.innerHeight || window.screen.height;\n\n const w = Math.min(width, viewW);\n const h = Math.min(height, viewH);\n const left = Math.max(0, Math.round(baseLeft + (viewW - w) / 2));\n const top = Math.max(0, Math.round(baseTop + (viewH - h) / 2));\n\n return `popup=yes,width=${Math.round(w)},height=${Math.round(h)},left=${left},top=${top}`;\n}\n\n/**\n * Run the \"Sign in with Remy\" handshake in a top-level popup — used when the\n * app is embedded (a cross-origin iframe like the dev IDE preview). The\n * authorize page hard-denies framing, so it can only render as a first-party\n * top-level document. The popup relays its one-time code back to this window\n * (the opener) via postMessage; the exchange runs *here* so this context — the\n * one that must end up authenticated — owns the session. The `ms_iface_` token\n * from the response body is the source of truth (held in memory), so it works\n * regardless of whether the third-party `__ms_auth` cookie survives.\n */\nfunction signInWithRemyPopup(\n redirectUri: string,\n state: string,\n): Promise<AppUser | null> {\n // Tag the callback so the SDK instance that loads in the popup relays +\n // closes rather than redeeming in place.\n const callbackUrl = new URL(redirectUri, window.location.href);\n callbackUrl.searchParams.set(REMY_POPUP_MARKER, '1');\n\n const startUrl = `${withBase('/_/auth/remy/start')}?${new URLSearchParams({\n redirect_uri: callbackUrl.toString(),\n state,\n }).toString()}`;\n\n // MUST open synchronously on the user's click — awaiting first spends the\n // gesture and the browser blocks the popup.\n const popup = window.open(\n startUrl,\n 'ms-remy-signin',\n centeredPopupFeatures(REMY_POPUP_WIDTH, REMY_POPUP_HEIGHT),\n );\n if (!popup) {\n return Promise.reject(\n new MindStudioInterfaceError(\n 'The sign-in popup was blocked. Allow popups for this site and try again.',\n 'popup_blocked',\n ),\n );\n }\n\n // The opener is now completing a handshake — reflected by auth.authStatus\n // until the exchange settles (or the popup is cancelled/times out).\n beginAuthenticating();\n\n return new Promise<AppUser | null>((resolve, reject) => {\n const appOrigin = window.location.origin;\n let settled = false;\n // Set synchronously when the first valid message is accepted — guards\n // against a duplicate/replayed post starting a second exchange, and stops\n // a later popup.closed from reading as \"cancel\" mid-exchange.\n let accepted = false;\n\n const finish = (fn: () => void): void => {\n if (settled) {\n return;\n }\n settled = true;\n window.removeEventListener('message', onMessage);\n clearInterval(pollTimer);\n clearTimeout(timeoutId);\n // Clear the 'authenticating' state (no-op on success — the exchange\n // already cleared it before applySession; clears + notifies on\n // cancel/timeout/error so the app leaves the \"Completing sign-in…\" UI).\n endAuthenticating();\n try {\n if (!popup.closed) {\n popup.close();\n }\n } catch {\n // ignore — closing a cross-context window can throw in rare cases\n }\n fn();\n };\n\n const onMessage = (event: MessageEvent): void => {\n // Once we've committed to the first valid message, ignore everything\n // else — a duplicate/replayed post must never start a second redemption.\n if (settled || accepted) {\n return;\n }\n // Hygiene: our exact origin, the window we opened, our shape, our state.\n if (event.origin !== appOrigin || event.source !== popup) {\n return;\n }\n const data = event.data as {\n type?: string;\n code?: string;\n state?: string;\n } | null;\n if (!data || data.type !== REMY_POPUP_MESSAGE || data.state !== state) {\n return; // not ours / stale / cross-talk / possible CSRF\n }\n\n // Commit synchronously, before the exchange await: this is the single\n // redemption, and a later popup.closed must not read as \"cancel\".\n accepted = true;\n\n if (!data.code) {\n finish(() =>\n reject(\n new MindStudioInterfaceError(\n 'Sign-in returned no code.',\n 'auth_error',\n ),\n ),\n );\n return;\n }\n\n // Redeem in the opener: this is the context that must be authenticated.\n authFetch<AuthSessionBundle>('/_/auth/remy/exchange', 'POST', {\n code: data.code,\n })\n .then((bundle) => {\n // Clear the flag before applySession's notify so subscribers see\n // 'authenticated' directly — never a stray 'unauthenticated' frame.\n redirectPending = false;\n applySession(bundle);\n finish(() => resolve(requireUser(bundle)));\n })\n .catch((err: unknown) => finish(() => reject(err)));\n };\n\n window.addEventListener('message', onMessage);\n\n // User closed the popup before finishing → cancelled (unless we've already\n // accepted the callback message and are mid-exchange).\n const pollTimer = setInterval(() => {\n if (popup.closed && !accepted) {\n finish(() => resolve(null));\n }\n }, 400);\n\n // Backstop for a popup that never closes or posts (e.g. stuck on an error).\n const timeoutId = setTimeout(\n () =>\n finish(() =>\n reject(\n new MindStudioInterfaceError(\n 'Sign-in timed out.',\n 'signin_timeout',\n ),\n ),\n ),\n 5 * 60 * 1000,\n );\n });\n}\n\n/**\n * If this window is the throwaway \"Sign in with Remy\" popup, relay the\n * one-time code to the opener and let it close us; returns `true` when it\n * handled the callback. Safe and synchronous — invoked both at SDK import\n * (before telemetry installs, so the popup fires no phantom pageview/presence)\n * and as a branch of {@link Auth.handleRemyRedirect}.\n *\n * @internal Not part of the public API surface.\n */\nexport function maybeRelayRemyPopupCallback(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n let params: URLSearchParams;\n try {\n params = new URL(window.location.href).searchParams;\n } catch {\n return false;\n }\n if (params.get(REMY_POPUP_MARKER) !== '1') {\n return false;\n }\n const code = params.get('code');\n if (!code) {\n return false;\n }\n\n // Idempotent: forward the code once per popup document. Both the import-time\n // fast path and handleRemyRedirect() call this on load; without the guard an\n // app following the documented \"call handleRemyRedirect() on load\" runs a\n // second forward inside the popup and the opener redeems the one-time code\n // twice (the second 400s). Return `true` — it's still the \"handled\" path.\n if (remyPopupForwarded) {\n return true;\n }\n\n const opener = window.opener as Window | null;\n if (!opener || opener === window) {\n // No channel back to the app — e.g. COOP severed the opener. Redeeming\n // here would authenticate the throwaway popup, which the storage-\n // partitioned iframe can't use, so we deliberately do nothing.\n return false;\n }\n\n try {\n opener.postMessage(\n {\n type: REMY_POPUP_MESSAGE,\n code,\n state: params.get('state') ?? undefined,\n },\n window.location.origin,\n );\n } catch {\n return false;\n }\n\n remyPopupForwarded = true;\n\n // The opener closes us after the exchange; self-close on the next tick is a\n // backstop in case it isn't listening.\n try {\n setTimeout(() => window.close(), 0);\n } catch {\n // ignore\n }\n return true;\n}\n\n/**\n * The high-level authentication state, from {@link Auth.authStatus}.\n *\n * - `'authenticating'` — a \"Sign in with Remy\" handshake is completing right\n * now: a cold-load redirect return (top-level or dashboard launch) is being\n * redeemed, or an embedded popup exchange is in flight. Render a\n * \"Completing sign-in…\" state, not the logged-out UI.\n * - `'authenticated'` — a user is signed in ({@link Auth.currentUser} is set).\n * - `'unauthenticated'` — no session and nothing in progress.\n */\nexport type AuthStatus = 'authenticating' | 'authenticated' | 'unauthenticated';\n\n/**\n * Options for {@link Auth.signInWithRemy}.\n */\nexport interface SignInWithRemyOptions {\n /**\n * The same-origin URL the platform should return the browser to after\n * resolving access. Must be an origin the app actually serves on (the\n * backend validates it against the app's allow-list).\n *\n * @default `window.location.href` — the user lands back where they started.\n */\n redirectUri?: string;\n\n /**\n * CSRF token round-tripped through the redirect. Provide your own to\n * correlate the return with app state; otherwise an opaque random value\n * is generated. Validated on return by {@link Auth.handleRemyRedirect}\n * (top-level flow) or by the opener (embedded/popup flow).\n */\n state?: string;\n\n /**\n * How to run the handshake.\n *\n * - `'auto'` (default) — full-page redirect when the app is top-level; a\n * popup when it's embedded in a cross-origin iframe (the authorize page\n * can't be framed, so a redirect there would fail).\n * - `'popup'` — always use a popup.\n * - `'redirect'` — always use a full-page redirect.\n *\n * @default 'auto'\n */\n mode?: 'auto' | 'popup' | 'redirect';\n}\n\n// ---------------------------------------------------------------------------\n// Auth interface\n// ---------------------------------------------------------------------------\n\n/**\n * The auth namespace — authentication flows, state, and helpers.\n *\n * Auth methods throw {@link MindStudioInterfaceError} on failure.\n * Common error codes:\n *\n * | Code | Status | Meaning |\n * |------|--------|---------|\n * | `rate_limited` | 429 | Too many code requests (max 5 per 15 min per identifier) |\n * | `invalid_code` | 400 | Wrong verification code |\n * | `verification_expired` | 400 | Code expired (10 min TTL) — request a new one |\n * | `max_attempts_exceeded` | 400 | Too many incorrect attempts (max 3) — request a new code |\n * | `not_authenticated` | 401 | Auth cookie missing (change/logout endpoints) |\n * | `invalid_session` | 401 | Auth cookie expired or invalid |\n */\nexport interface Auth {\n // -- State --\n\n /** The current authenticated user, or `null` if not authenticated. */\n readonly currentUser: AppUser | null;\n\n /** Get the current authenticated user, or `null` if not authenticated. */\n getCurrentUser(): AppUser | null;\n\n /** Whether the current session is authenticated. */\n isAuthenticated(): boolean;\n\n /**\n * The high-level auth state — `'authenticating'`, `'authenticated'`, or\n * `'unauthenticated'`. Synchronous and safe to read in render.\n *\n * The key case is `'authenticating'`: it's `true` from **page load** through\n * settle whenever a \"Sign in with Remy\" handshake is completing (a cold-load\n * redirect return being redeemed, or an embedded popup exchange in flight).\n * Read this to show a \"Completing sign-in…\" state instead of the logged-out\n * UI — the `?code` URL param can't be used for that (it's stripped at the\n * start of the exchange), and the immediate `null` from\n * {@link onAuthStateChanged} during this window is expected.\n *\n * `onAuthStateChanged` fires on every transition, so re-read this there.\n */\n readonly authStatus: AuthStatus;\n\n /**\n * The stable per-browser, per-app visitor identifier, or `null` if\n * unavailable (e.g. cached bootstrap from before this field existed).\n *\n * Opaque string backed by a server-set HttpOnly cookie. For authed\n * sessions this is the user's platform user ID; for guests it's a\n * per-browser UUID. Persists ~1 year; refreshed on each page load.\n * Updates in-place on login/logout transitions.\n */\n readonly currentVisitorId: string | null;\n\n /** Get the current visitor ID, or `null` if unavailable. */\n getCurrentVisitorId(): string | null;\n\n // -- Email code flow --\n\n /** Send a 6-digit verification code to an email address. */\n sendEmailCode(email: string): Promise<{ verificationId: string }>;\n\n /**\n * Verify an email code. On success, updates the session in-place\n * and returns the authenticated user.\n */\n verifyEmailCode(verificationId: string, code: string): Promise<AppUser>;\n\n // -- SMS code flow --\n\n /** Send a 6-digit verification code via SMS. Phone must be E.164. */\n sendSmsCode(phone: string): Promise<{ verificationId: string }>;\n\n /**\n * Verify an SMS code. On success, updates the session in-place\n * and returns the authenticated user.\n */\n verifySmsCode(verificationId: string, code: string): Promise<AppUser>;\n\n // -- Sign in with Remy (platform-delegated) --\n\n /**\n * Start the \"Sign in with Remy\" flow. The platform — not the app — decides\n * who the person is and whether they're allowed (think \"Sign in with\n * Google\"). Auto-detects the context:\n *\n * - **Top-level** (production, standalone tab): redirects the browser to the\n * platform and bounces back to `redirectUri` with a one-time code that\n * {@link handleRemyRedirect} redeems on return. **The page navigates away**,\n * so the returned promise never settles.\n * - **Embedded** (cross-origin iframe, e.g. the dev IDE preview): opens a\n * top-level popup for the handshake (the authorize page can't be framed).\n * The returned promise resolves with the user on success, `null` if the\n * user closes the popup, or rejects (`popup_blocked` / `invalid_state` /\n * `signin_timeout` / exchange error). `onAuthStateChanged` also fires on\n * success.\n *\n * Must be called from a user gesture (e.g. a click) so the popup isn't\n * blocked. Override the auto-detection with `options.mode`.\n *\n * @example\n * ```ts\n * // \"Continue with {Org}\" button — works top-level or embedded:\n * <button onClick={() => auth.signInWithRemy()}>Continue with Acme</button>\n *\n * // Await the result in an embedded app:\n * const user = await auth.signInWithRemy();\n * if (user) navigate('/dashboard');\n * ```\n */\n signInWithRemy(options?: SignInWithRemyOptions): Promise<AppUser | null>;\n\n /**\n * Complete a \"Sign in with Remy\" redirect. Call once on app load — it's a\n * no-op (resolves `null`) when there's no code to redeem, so it's safe to\n * run on every mount.\n *\n * Handles both entry points:\n * - **SP-initiated** (returned from {@link signInWithRemy}) — validates the\n * round-tripped CSRF `state` against the value stashed at start.\n * - **IdP-initiated** (opened from the Remy dashboard) — a `?code=` lands on\n * first paint with no `state`; the single-use, app-bound code is the\n * control, so it's redeemed directly.\n *\n * Inside the embedded/popup document it's also a safe no-op: the popup boots\n * the same bundle, but the code is relayed to the opener exactly once\n * (idempotent — the import-time fast path already forwarded it), and this\n * call resolves `null` without redeeming. So the documented \"call on load\"\n * guidance holds everywhere — no need to special-case the popup.\n *\n * On success, updates the session in-place (fires `onAuthStateChanged`),\n * strips `code`/`state` from the URL, and returns the authenticated user.\n *\n * @returns The authenticated user, or `null` if there was no code to redeem.\n * @throws `invalid_state` if a returned `state` doesn't match the stashed one.\n *\n * @example\n * ```ts\n * useEffect(() => { auth.handleRemyRedirect(); }, []);\n * useEffect(() => auth.onAuthStateChanged(setUser), []);\n * ```\n */\n handleRemyRedirect(): Promise<AppUser | null>;\n\n // -- Email/phone change (requires authentication) --\n\n /** Request an email change. Sends a code to the new email. */\n requestEmailChange(newEmail: string): Promise<void>;\n\n /** Confirm an email change with the verification code. */\n confirmEmailChange(newEmail: string, code: string): Promise<AppUser>;\n\n /** Request a phone change. Sends a code to the new phone (E.164). */\n requestPhoneChange(newPhone: string): Promise<void>;\n\n /** Confirm a phone change with the verification code. */\n confirmPhoneChange(newPhone: string, code: string): Promise<AppUser>;\n\n // -- Session --\n\n /** Log out. Clears the cookie and updates the session to unauthenticated. */\n logout(): Promise<void>;\n\n // -- API keys --\n\n /**\n * Generate an API key for the current user. Returns the full key\n * (shown once). The user's `apiKey` field updates to the masked\n * value and `onAuthStateChanged` fires.\n *\n * @throws `not_authenticated` (401) if no session\n * @throws `not_supported` (400) if api-key auth is not enabled\n */\n createApiKey(): Promise<{ key: string }>;\n\n /**\n * Revoke the current user's API key. The user's `apiKey` field\n * becomes `null` and `onAuthStateChanged` fires.\n *\n * @throws `not_authenticated` (401) if no session\n */\n revokeApiKey(): Promise<void>;\n\n /**\n * Subscribe to auth state changes. Fires immediately with the current user,\n * then again on every transition — verify / confirm / logout, **and** each\n * `authStatus` change (a \"Sign in with Remy\" handshake starting or settling).\n *\n * During `'authenticating'` the callback may fire with `null` (no session\n * yet) — that's expected; read {@link authStatus} to tell it apart from a\n * logged-out visit rather than rendering the login UI.\n *\n * @returns An unsubscribe function.\n *\n * @example\n * ```ts\n * // React hook — track both the user and the status.\n * function useAuth() {\n * const [user, setUser] = useState(auth.currentUser);\n * const [status, setStatus] = useState(auth.authStatus);\n * useEffect(() => auth.onAuthStateChanged(() => {\n * setUser(auth.currentUser);\n * setStatus(auth.authStatus);\n * }), []);\n * return { user, status };\n * }\n * ```\n */\n onAuthStateChanged(callback: (user: AppUser | null) => void): () => void;\n\n // -- Helpers --\n\n /** Phone number utilities — countries, formatting, validation. */\n phone: {\n /** All countries with dial codes, sorted alphabetically. */\n countries: readonly phoneHelpers.Country[];\n /** Detect the user's country from their timezone. Falls back to `'US'`. */\n detectCountry(): string;\n /** Format an E.164 number for display (e.g. `+1 (555) 123-4567`). */\n format(e164: string): string;\n /** Convert a national number to E.164 (e.g. `('5551234567', 'US') → '+15551234567'`). */\n toE164(national: string, countryCode: string): string;\n /** Check if a string is a valid E.164 phone number. */\n isValid(phone: string): boolean;\n };\n\n /** Email validation. */\n email: {\n /** Basic email format check. */\n isValid(email: string): boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport const auth: Auth = {\n // -- State --\n\n get currentUser() {\n return getConfig().user;\n },\n\n getCurrentUser() {\n return getConfig().user;\n },\n\n isAuthenticated() {\n return getConfig().user !== null;\n },\n\n get authStatus() {\n if (redirectPending) {\n return 'authenticating';\n }\n return safeCurrentUser() ? 'authenticated' : 'unauthenticated';\n },\n\n get currentVisitorId() {\n return getConfig().visitorId ?? null;\n },\n\n getCurrentVisitorId() {\n return getConfig().visitorId ?? null;\n },\n\n // -- Email code flow --\n\n sendEmailCode(email: string) {\n return authFetch<{ verificationId: string }>('/_/auth/email/send', 'POST', {\n email,\n });\n },\n\n async verifyEmailCode(verificationId: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/email/verify',\n 'POST',\n { verificationId, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- SMS code flow --\n\n sendSmsCode(phone: string) {\n return authFetch<{ verificationId: string }>('/_/auth/sms/send', 'POST', {\n phone,\n });\n },\n\n async verifySmsCode(verificationId: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/sms/verify',\n 'POST',\n { verificationId, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- Sign in with Remy --\n\n signInWithRemy(options?: SignInWithRemyOptions): Promise<AppUser | null> {\n const mode = options?.mode ?? 'auto';\n const usePopup = mode === 'popup' || (mode === 'auto' && isEmbedded());\n const redirectUri = options?.redirectUri ?? window.location.href;\n const state = options?.state ?? generateState();\n\n if (usePopup) {\n // window.open must happen synchronously on the gesture — do it first.\n return signInWithRemyPopup(redirectUri, state);\n }\n\n // Top-level full-page redirect. The opener/popup handshake isn't needed,\n // so the CSRF `state` is stashed for handleRemyRedirect() to validate.\n try {\n sessionStorage.setItem(REMY_STATE_KEY, state);\n } catch {\n // sessionStorage unavailable (private mode / disabled) — proceed\n // without the CSRF stash; the one-time, app-bound code still gates.\n }\n\n const params = new URLSearchParams({ redirect_uri: redirectUri, state });\n window.location.assign(\n `${withBase('/_/auth/remy/start')}?${params.toString()}`,\n );\n\n // The page is navigating away — nothing after this resolves.\n return new Promise<AppUser | null>(() => {});\n },\n\n async handleRemyRedirect() {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const url = new URL(window.location.href);\n const code = url.searchParams.get('code');\n if (!code) {\n return null;\n }\n\n // Embedded/popup callback: relay the code to the opener and let it close\n // us — never redeem here (the opener owns the session). The import-time\n // fast path usually handles this already; this is the fallback for apps\n // that call handleRemyRedirect() directly.\n if (url.searchParams.get(REMY_POPUP_MARKER) === '1') {\n maybeRelayRemyPopupCallback();\n return null;\n }\n\n const returnedState = url.searchParams.get('state');\n\n // We're committed to consuming this redirect — strip the params up front\n // so a refresh never retries a spent (or failed) one-time code.\n stripRemyRedirectParams(url);\n\n let storedState: string | null = null;\n try {\n storedState = sessionStorage.getItem(REMY_STATE_KEY);\n sessionStorage.removeItem(REMY_STATE_KEY);\n } catch {\n // sessionStorage unavailable — CSRF check degrades to redeem-only.\n }\n\n // We're redeeming now — reflect it in auth.authStatus. Idempotent: module\n // load already set this for a cold-load return; this also covers callers\n // that reach here without the eager path having run.\n beginAuthenticating();\n\n // SP-initiated returns round-trip a `state` that must match what we\n // stashed. IdP-initiated launches carry no `state` — the single-use,\n // app-bound, short-lived code is the control there.\n if (returnedState !== null && returnedState !== storedState) {\n endAuthenticating();\n throw new MindStudioInterfaceError(\n 'Sign-in state mismatch — possible CSRF or a stale redirect. Please try again.',\n 'invalid_state',\n );\n }\n\n try {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/remy/exchange',\n 'POST',\n { code },\n );\n // Clear the flag before applySession's notify so subscribers transition\n // straight to 'authenticated' — no intermediate 'unauthenticated' frame.\n redirectPending = false;\n applySession(bundle);\n return requireUser(bundle);\n } catch (err) {\n // Clear + notify so the app leaves the \"Completing sign-in…\" state.\n endAuthenticating();\n throw err;\n }\n },\n\n // -- Email/phone change --\n\n async requestEmailChange(newEmail: string) {\n await authFetch('/_/auth/email/change', 'POST', { newEmail });\n },\n\n async confirmEmailChange(newEmail: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/email/change/confirm',\n 'POST',\n { newEmail, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n async requestPhoneChange(newPhone: string) {\n await authFetch('/_/auth/phone/change', 'POST', { newPhone });\n },\n\n async confirmPhoneChange(newPhone: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/phone/change/confirm',\n 'POST',\n { newPhone, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- Session --\n\n async logout() {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/logout',\n 'POST',\n {},\n );\n applySession(bundle);\n },\n\n async createApiKey() {\n const result = await authFetch<{ key: string; apiKey: string }>(\n '/_/auth/api-key/create',\n 'POST',\n {},\n );\n updateUserAndNotify({ apiKey: result.apiKey });\n return { key: result.key };\n },\n\n async revokeApiKey() {\n await authFetch('/_/auth/api-key/revoke', 'POST', {});\n updateUserAndNotify({ apiKey: null });\n },\n\n onAuthStateChanged(callback: (user: AppUser | null) => void) {\n authListeners.add(callback);\n callback(safeCurrentUser());\n return () => {\n authListeners.delete(callback);\n };\n },\n\n // -- Helpers --\n\n phone: {\n countries: phoneHelpers.countries,\n detectCountry: phoneHelpers.detectCountry,\n format: phoneHelpers.format,\n toE164: phoneHelpers.toE164,\n isValid: phoneHelpers.isValid,\n },\n\n email: {\n isValid: emailHelpers.isValid,\n },\n};\n","/**\n * App events client — server→client realtime.\n *\n * Backend code publishes to named channels (`events.publish` in\n * `@mindstudio-ai/agent`); this client holds a platform-served SSE and\n * delivers matching publishes as they happen. Authorization is a **grant**\n * minted by one of the app's own backend methods — which is why `connect`\n * takes a token *provider*, not a token: grants expire on purpose (expiry is\n * the revocation window), and the SDK re-mints through your method on every\n * expiry, re-running your auth checks.\n *\n * ```ts\n * import { createClient, events } from '@mindstudio-ai/interface';\n * const api = createClient();\n *\n * const sub = events.connect({\n * getToken: () => api.watchJobs().then((r) => r.token),\n * onEvent: (e) => {\n * if (e.channel.startsWith('jobs:')) refreshJob(e.data);\n * },\n * onConnect: () => refetchJobs(), // reconcile — see below\n * });\n *\n * // later (component unmount, page change):\n * sub.close();\n * ```\n *\n * ## Reconcile on connect\n *\n * Events are at-most-once nudges: nothing is buffered while you're\n * disconnected and nothing is replayed when you reconnect. `onConnect` fires\n * on EVERY (re)connect — network drop, grant expiry, server deploy — and is\n * where you refetch current state. Subscribe for speed, reconcile for truth;\n * a subscriber without an `onConnect` refetch will silently miss whatever\n * happened while it was away.\n *\n * ## Lifecycle\n *\n * - Grant expiry (`grant_expired` frame, every TTL window) → the SDK calls\n * `getToken` again and reconnects. Invisible to the app beyond `onConnect`.\n * - Network drop → reconnect with backoff, reusing the still-valid token (no\n * method invocation wasted).\n * - `getToken` throwing → terminal: your own method refused (logged out, role\n * revoked), so the SDK calls `onError` and closes rather than retrying an\n * authorization refusal in a loop. Call `connect` again when your app's\n * auth state changes.\n */\n\nimport { withBase } from './config.js';\nimport { createSseLoop, type SseLoop } from './sse-loop.js';\n\n/** One published event, delivered on a channel the grant names. */\nexport interface AppEvent {\n /**\n * Platform-stamped publish id. One publish = one id; if your grant covers\n * several of the published channels you receive it once per channel, so the\n * dedupe key is `id + channel`.\n */\n id: string;\n channel: string;\n data: unknown;\n /** Publish time, milliseconds since epoch. */\n ts: number;\n}\n\nexport interface EventsConnectOptions {\n /**\n * Return a subscribe token — typically by invoking the backend method that\n * mints the grant (`events.grant` in the agent SDK) after its own auth\n * checks. Called on first connect and again after every grant expiry.\n * Throwing is terminal: `onError` fires and the subscription closes.\n */\n getToken: () => Promise<string> | string;\n /** A published event on one of the grant's channels. */\n onEvent: (event: AppEvent) => void;\n /**\n * Fires on every (re)connect. Refetch current state here — events are\n * at-most-once, so this is the correctness half of the contract.\n */\n onConnect?: () => void;\n /**\n * The server dropped `count` events for this connection because it couldn't\n * keep up (socket backpressure). Treat it like a reconnect: refetch current\n * state, same as `onConnect`. Without a handler the gap is silent — the\n * at-most-once contract already requires reconcile-on-connect, so this is\n * an extra hint, not a new obligation.\n */\n onGap?: (count: number) => void;\n /** Terminal failures only (a `getToken` refusal). Reconnects are silent. */\n onError?: (error: Error) => void;\n}\n\nexport interface EventsSubscription {\n /** Stop the stream and all reconnection. Idempotent. */\n close(): void;\n /** `connecting` | `open` | `closed` */\n readonly state: 'connecting' | 'open' | 'closed';\n /**\n * Publish an ephemeral event on channels the grant carries **publish**\n * capability for (`events.grant(channels, { publish: [...] })` in the\n * backend SDK). This is the client-direct fast path — no backend method\n * runs; the platform fans out directly, so it's the right shape for\n * cursors, typing indicators, and live stroke batches.\n *\n * Fire-and-forget with automatic coalescing: call it per input event (per\n * mousemove is fine) and the SDK batches to ~40ms flushes. Delivery is\n * at-most-once and NOTHING here is retried — a stale cursor is worse than a\n * missing one. Payloads cap at 8k serialized; put a client `seq` in the\n * payload if receivers need cross-batch ordering. Truth still lives in your\n * database: commit durable state through a method, signal through this.\n */\n publish(channels: string | string[], data: unknown): void;\n}\n\nconst EVENTS_ENDPOINT = '/_/events';\n\n// Client-publish coalescing: flush cadence and the per-batch ceiling (kept\n// under the platform's 100-event batch cap). 40ms caps sustained flushes at\n// 25 POSTs/sec — headroom inside the platform's 30 batches/sec rate box; a\n// faster timer (25ms) measured out at ~30/sec and grazed the box.\nconst PUBLISH_FLUSH_MS = 40;\nconst PUBLISH_MAX_BATCH = 50;\n\nfunction connect(options: EventsConnectOptions): EventsSubscription {\n // The cached grant. Reused across network-drop reconnects (still valid, no\n // method call wasted); cleared when the server says it's dead — a\n // grant_expired frame, or a 401 on connect — so the next attempt re-mints.\n let token: string | null = null;\n\n const loop: SseLoop = createSseLoop({\n prepare: async () => {\n if (!token) {\n try {\n token = await options.getToken();\n } catch (err) {\n options.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n return 'stop';\n }\n }\n return {\n url: withBase(EVENTS_ENDPOINT),\n headers: { Authorization: `Bearer ${token}` },\n };\n },\n onStatus: (status) => {\n if (status === 401) {\n // Grant expired or evicted between frames — re-mint on the retry.\n token = null;\n }\n return 'retry';\n },\n onOpen: () => {\n options.onConnect?.();\n },\n onLine: (line) => {\n if (!line.startsWith('data: ')) {\n return; // keepalive comments, blank frame separators\n }\n let parsed: any;\n try {\n parsed = JSON.parse(line.slice(6));\n } catch {\n return; // malformed frame — skip, matching the agent-chat reader\n }\n if (parsed?.type === 'grant_expired') {\n // Expected every TTL window. Clear the token; the server ends the\n // stream right after this frame and the loop's reconnect re-mints.\n token = null;\n return;\n }\n if (parsed?.type === 'events_dropped') {\n options.onGap?.(typeof parsed.count === 'number' ? parsed.count : 0);\n return;\n }\n if (typeof parsed?.channel === 'string') {\n options.onEvent({\n id: parsed.id,\n channel: parsed.channel,\n data: parsed.data,\n ts: parsed.ts,\n });\n }\n },\n });\n\n loop.start();\n\n //////////////////////////////////////////////////////////////////////////////\n // Client publish — fire-and-forget, coalesced. Failure policy is the\n // at-most-once contract applied upstream: ephemeral signals are dropped on\n // any failure, never queued or retried.\n //////////////////////////////////////////////////////////////////////////////\n\n let pending: Array<{ channels: string | string[]; data: unknown }> = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let pausedUntil = 0; // 429 backoff\n let closed = false;\n let publishRefusalSurfaced = false;\n\n const flush = async () => {\n flushTimer = null;\n if (closed || pending.length === 0) {\n return;\n }\n if (!token || Date.now() < pausedUntil) {\n // Not connected (no grant yet) or rate-boxed: drop. A cursor position\n // from before the connection existed has no value after it.\n pending = [];\n return;\n }\n const events = pending;\n pending = [];\n try {\n const res = await fetch(withBase(EVENTS_ENDPOINT), {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events }),\n });\n if (res.status === 401) {\n // Grant died between frames — clear it so the SSE loop's reconnect\n // re-mints; the dropped batch is already stale.\n token = null;\n return;\n }\n if (res.status === 429) {\n const retryAfterSec = parseInt(\n res.headers.get('Retry-After') ?? '',\n 10,\n );\n pausedUntil =\n Date.now() +\n (Number.isFinite(retryAfterSec) && retryAfterSec > 0\n ? retryAfterSec * 1000\n : 1000);\n return;\n }\n if (res.status === 403 && !publishRefusalSurfaced) {\n // The grant has no publish capability (or not for these channels) — a\n // programming error in the minting method, not a transient. Surface\n // once, keep dropping quietly after.\n publishRefusalSurfaced = true;\n options.onError?.(\n new Error(\n 'events.publish refused: the grant does not carry publish ' +\n 'capability for these channels — mint it with ' +\n '`events.grant(channels, { publish: [...] })`.',\n ),\n );\n }\n } catch {\n // Network blip — drop, at-most-once.\n }\n };\n\n const publish = (channels: string | string[], data: unknown) => {\n if (closed) {\n return;\n }\n pending.push({ channels, data });\n if (pending.length >= PUBLISH_MAX_BATCH) {\n if (flushTimer) {\n clearTimeout(flushTimer);\n }\n void flush();\n return;\n }\n if (!flushTimer) {\n flushTimer = setTimeout(() => void flush(), PUBLISH_FLUSH_MS);\n }\n };\n\n return {\n close: () => {\n closed = true;\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n pending = [];\n loop.stop();\n },\n get state() {\n const s = loop.state;\n return s === 'open' ? 'open' : s === 'stopped' ? 'closed' : 'connecting';\n },\n publish,\n };\n}\n\n/**\n * The app-events client. `connect` opens a managed subscription — reconnects,\n * grant re-minting and backoff are handled internally; the app supplies the\n * token source and the handlers.\n */\nexport const events = { connect };\n","/**\n * @mindstudio-ai/interface — Frontend SDK for MindStudio v2 app interfaces.\n *\n * Provides typed RPC to backend methods, file uploads, authentication,\n * agent chat conversations, and analytics. Runs inside web interfaces with\n * credentials injected by the platform via `window.__MINDSTUDIO__`.\n *\n * ## Exports\n *\n * - `createClient()` — typed method RPC client\n * - `createAgentChatClient()` — thread-based agent conversations\n * - `platform` — file upload actions\n * - `auth` — authentication flows, user state, and validation helpers\n * - `analytics` — custom event tracking\n * - `telemetry` — crash reporting: React root hooks + manual capture\n *\n * Voice sessions live on the `./voice` subpath (`createVoiceClient()` from\n * `@mindstudio-ai/interface/voice`) — deliberately not re-exported here, so\n * the core entry stays free of the realtime media dependency.\n *\n * @example\n * ```ts\n * import {\n * createClient,\n * createAgentChatClient,\n * platform,\n * auth,\n * } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n * const chat = createAgentChatClient();\n *\n * // Check auth state\n * if (!auth.isAuthenticated()) {\n * const { verificationId } = await auth.sendEmailCode('user@example.com');\n * await auth.verifyEmailCode(verificationId, code);\n * }\n *\n * // Call backend methods (uses authenticated session)\n * const dashboard = await api.getDashboard();\n *\n * // Upload a file (client-direct to the app's file store)\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { url } = await platform.upload(token, file);\n *\n * // Current user\n * const user = auth.getCurrentUser();\n * ```\n */\n\nexport { createClient, type InvokeOptions } from './client.js';\nexport {\n createAgentChatClient,\n type AgentChatClient,\n type AgentChatEvent,\n type SendMessageCallbacks,\n type SendMessageOptions,\n type SendMessageResult,\n type AbortablePromise,\n type Thread,\n type ThreadSummary,\n type ThreadListPage,\n type Message,\n} from './agent-chat.js';\nexport {\n platform,\n type UploadFileOptions,\n type UploadToken,\n} from './platform.js';\nexport {\n auth,\n type Auth,\n type AuthStatus,\n type SignInWithRemyOptions,\n} from './auth.js';\nexport {\n analytics,\n type AnalyticsClient,\n type CustomEventProps,\n} from './telemetry-analytics.js';\nexport {\n telemetry,\n type Telemetry,\n type CaptureOptions,\n type ErrorMechanism,\n type ReactErrorHandlerOptions,\n type ReactErrorInfo,\n} from './telemetry-errors.js';\nexport {\n events,\n type AppEvent,\n type EventsConnectOptions,\n type EventsSubscription,\n} from './events.js';\nexport { MindStudioInterfaceError } from './errors.js';\nexport type { BootstrapConfig, AppUser } from './types.js';\nexport type { Country } from './auth-phone.js';\nexport type { Breadcrumb } from './telemetry-breadcrumbs.js';\n\n// ---------------------------------------------------------------------------\n// Eager telemetry init\n//\n// When `window.__MINDSTUDIO__` is present at import time, trigger `getConfig()`\n// so telemetry surfaces (errors, analytics, presence) install at page load\n// rather than on first SDK use. Without this, an app that creates an `api`\n// proxy at the module top but only invokes it from user interactions would\n// have no telemetry until the user clicks something.\n//\n// SYNCHRONOUS, deliberately. This ran in `setTimeout(…, 0)`, which lost the\n// race it did not look like it was in: `createRoot().render()` schedules React's\n// first render through the Scheduler's MessageChannel, and that task runs before\n// a queued timer. Every first-paint crash — the most common kind there is —\n// happened before the error listeners existed. Analytics install stays deferred\n// (in config.ts), where a tick costs nothing.\n//\n// In environments without the bootstrap (SSR/Node, tests, dev without platform\n// context), the guard skips this and the SDK falls back to lazy init — first\n// SDK method call triggers `getConfig()` exactly as before.\n// ---------------------------------------------------------------------------\n\nimport { getConfig } from './config.js';\nimport { maybeRelayRemyPopupCallback } from './auth.js';\n\nif (typeof window !== 'undefined') {\n // If this window is the throwaway \"Sign in with Remy\" popup callback, relay\n // the code to the opener and close — synchronously, *before* installing\n // telemetry, so the popup never fires a phantom pageview / presence\n // connection. When it's not a popup callback this is a cheap no-op.\n const relayed = maybeRelayRemyPopupCallback();\n\n if (!relayed && (globalThis as Record<string, unknown>).__MINDSTUDIO__) {\n try {\n getConfig();\n } catch {\n // bootstrap reads can still happen lazily on first SDK call\n }\n }\n}\n"],"mappings":"kGAgNO,SAASA,IAA2C,CACzD,OAAO,IAAI,MAAM,CAAC,EAA4C,CAC5D,IAAIC,EAAGC,EAAoB,CACzB,MAAO,OAAOC,EAAiBC,IAA4B,CACzD,IAAMC,EAASC,EAAU,EAEnBC,EAAWF,EAAO,QAAQH,CAAU,EAC1C,GAAI,CAACK,EACH,MAAM,IAAIC,EACR,WAAWN,CAAU,mCAAmC,OAAO,KAAKG,EAAO,OAAO,EAAE,KAAK,IAAI,GAAK,QAAQ,GAC1G,kBACF,EAGF,IAAMI,EAAMC,EAAS,cAAcH,CAAQ,SAAS,EAC9CI,EAAcP,GAAS,SAAW,GAElCQ,EAAM,MAAM,MAAMH,EAAK,CAC3B,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,cAAe,UAAUJ,EAAO,KAAK,EACvC,EACA,KAAM,KAAK,UAAU,CACnB,MAAOF,GAAS,CAAC,EACjB,GAAIQ,GAAe,CAAE,OAAQ,EAAK,CACpC,CAAC,CACH,CAAC,EAED,GAAI,CAACC,EAAI,GACP,MAAM,MAAMC,EACVD,EACA,WAAWV,CAAU,aAAaU,EAAI,MAAM,IAAIA,EAAI,UAAU,GAC9D,cACF,EAIF,GAAI,CAACD,EAEH,OADc,MAAMC,EAAI,KAAK,GACjB,OAId,IAAME,EAASF,EAAI,KAAM,UAAU,EAC7BG,EAAU,IAAI,YAChBC,EAAS,GACTC,EACAC,EAEJ,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAMN,EAAO,KAAK,EAC1C,GAAIK,EACF,MAGFH,GAAUD,EAAQ,OAAOK,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAMC,EAAQL,EAAO,MAAM;AAAA,CAAI,EAC/BA,EAASK,EAAM,IAAI,GAAK,GAExB,QAAWC,KAAQD,EAAO,CACxB,GAAI,CAACC,EAAK,WAAW,QAAQ,EAC3B,SAEF,IAAMC,EAAOD,EAAK,MAAM,CAAC,EACzB,GAAI,CACF,IAAME,EAAQ,KAAK,MAAMD,CAAI,EAQzBC,EAAM,OAAS,SAAWpB,GAAS,SAAWoB,EAAM,KACtDpB,EAAQ,QAAQoB,EAAM,IAAI,EACjBA,EAAM,OAAS,QAAUpB,GAAS,aAC3CA,EAAQ,aAAaoB,EAAM,IAAI,EACtBA,EAAM,OAAS,SAAWA,EAAM,OACzCN,EAAcM,EAAM,MAChBpB,GAAS,eACXA,EAAQ,cAAcoB,EAAM,KAAK,GAE1BA,EAAM,OAAS,SACxBP,EAAcO,EAAM,OAExB,MAAQ,CAER,CACF,CACF,CAIA,GAAIP,IAAgB,QAAaC,IAAgB,OAC/C,MAAM,IAAIV,EAAyBU,EAAa,cAAc,EAGhE,GAAID,IAAgB,OAClB,MAAM,IAAIT,EACR,oCACA,mBACF,EAGF,OAAOS,CACT,CACF,CACF,CAAC,CACH,CCkJA,IAAMQ,EAAa,WAMbC,GAA+B,KAErC,eAAeC,EACbC,EACAC,EACAC,EACY,CAEZ,IAAMC,EAAkC,CACtC,cAAe,UAFFC,EAAU,EAES,KAAK,EACvC,EAEIF,IAAS,SACXC,EAAQ,cAAc,EAAI,oBAG5B,IAAME,EAAM,MAAM,MAAMC,EAAS,GAAGT,CAAU,GAAGG,CAAI,EAAE,EAAG,CACxD,OAAAC,EACA,QAAAE,EACA,GAAID,IAAS,QAAa,CAAE,KAAM,KAAK,UAAUA,CAAI,CAAE,CACzD,CAAC,EAED,GAAI,CAACG,EAAI,GACP,MAAM,MAAME,EACVF,EACA,8BAA8BA,EAAI,MAAM,IAAIA,EAAI,UAAU,GAC1D,kBACF,EAGF,OAAQ,MAAMA,EAAI,KAAK,CACzB,CAOA,eAAeG,EACbC,EACAP,EACe,CACf,IAAMQ,EAASN,EAAU,EACnBC,EAAM,MAAM,MAChBC,EAAS,GAAGT,CAAU,YAAYY,CAAQ,eAAe,EACzD,CACE,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,cAAe,UAAUC,EAAO,KAAK,EACvC,EACA,KAAM,KAAK,UAAUR,CAAI,CAC3B,CACF,EACKG,EAAI,IACP,QAAQ,KACN,sDAAsDA,EAAI,MAAM,IAAIA,EAAI,UAAU,EACpF,CAEJ,CAEA,SAASM,GACPC,EACAC,EACM,CAKN,OAHAA,EAAU,UAAUD,CAAK,EAGjBA,EAAM,KAAM,CAClB,IAAK,OACHC,EAAU,SAASD,EAAM,IAAI,EAC7B,MACF,IAAK,WACHC,EAAU,aAAaD,EAAM,IAAI,EACjC,MACF,IAAK,oBACHC,EAAU,qBAAqBD,EAAM,SAAUA,EAAM,SAAS,EAC9D,MACF,IAAK,kBACHC,EAAU,kBAAkBD,EAAM,GAAIA,EAAM,IAAI,EAChD,MACF,IAAK,mBACHC,EAAU,mBAAmBD,EAAM,GAAIA,EAAM,MAAM,EACnD,MACF,IAAK,QACHC,EAAU,UAAUD,EAAM,KAAK,EAC/B,MAIF,IAAK,mBACH,MAEF,IAAK,WACL,IAAK,mBACL,IAAK,OACH,KACJ,CACF,CASA,eAAeE,GACbL,EACAG,EACAG,EACe,CACf,GAAI,CAACA,EAAS,CACZ,QAAQ,KACN,6BAA6BH,EAAM,IAAI,kFACzC,EACA,MAAMJ,EAAeC,EAAU,CAC7B,OAAQG,EAAM,GACd,MAAO,uBACT,CAAC,EACD,MACF,CAEA,GAAI,CACF,IAAMI,EAAS,MAAMD,EAAQH,EAAM,KAAK,EACxC,GAAII,IAAW,OAAW,CACxB,IAAMC,EAAa,KAAK,UAAUD,CAAM,EACxC,GAAIC,GAAcA,EAAW,OAASnB,GAA8B,CAClE,MAAMU,EAAeC,EAAU,CAC7B,OAAQG,EAAM,GACd,MAAO,kBACT,CAAC,EACD,MACF,CACF,CACA,MAAMJ,EAAeC,EAAU,CAAE,OAAQG,EAAM,GAAI,OAAAI,CAAO,CAAC,CAC7D,OAASE,EAAK,CACZ,MAAMV,EAAeC,EAAU,CAC7B,OAAQG,EAAM,GACd,MAAOM,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CACxD,CAAC,CACH,CACF,CAgCO,SAASC,IAAyC,CAIvD,IAAMC,EAAe,IAAI,IACnBC,EAAqB,IAAI,IACzBC,EAAiBb,GAAqB,CAC1C,IAAMc,EAAQnB,EAAU,EAAE,MACtBmB,GAAS,CAACH,EAAa,IAAIX,CAAQ,GACrCW,EAAa,IAAIX,EAAUc,CAAK,CAEpC,EAEA,MAAO,CACL,MAAM,cAAe,CACnB,IAAMA,EAAQnB,EAAU,EAAE,MACpBoB,EAAS,MAAMzB,EAAuB,WAAY,MAAM,EAC9D,OAAIwB,GACFH,EAAa,IAAII,EAAO,GAAID,CAAK,EAE5BC,CACT,EAEA,YAAYC,EAAiB,CAC3B,IAAMC,EAAQD,EAAS,WAAW,mBAAmBA,CAAM,CAAC,GAAK,GACjE,OAAO1B,EAAwB,WAAW2B,CAAK,GAAI,KAAK,CAC1D,EAEA,UAAUjB,EAAkB,CAC1B,OAAOV,EAAgB,YAAYU,CAAQ,GAAI,KAAK,CACtD,EAEA,MAAM,aAAaA,EAAkBkB,EAAe,CAClD,MAAM5B,EAAQ,YAAYU,CAAQ,GAAI,QAAS,CAAE,MAAAkB,CAAM,CAAC,CAC1D,EAEA,MAAM,aAAalB,EAAkB,CACnC,MAAMV,EAAQ,YAAYU,CAAQ,GAAI,QAAQ,CAChD,EAEA,MAAM,YAAYA,EAAkBmB,EAAsC,CACxE,IAAMC,EACJD,GAAS,eAAiBR,EAAa,IAAIX,CAAQ,EACrD,GAAI,CAACoB,EACH,MAAM,IAAIC,EACR,8EACA,yBACA,GACF,EAEF,MAAM/B,EAAQ,YAAYU,CAAQ,SAAU,OAAQ,CAAE,cAAAoB,CAAc,CAAC,EACrET,EAAa,OAAOX,CAAQ,CAC9B,EAEA,mBAAmBsB,EAAchB,EAA4B,CAC3D,OAAAM,EAAmB,IAAIU,EAAMhB,CAAO,EAC7B,IAAM,CACPM,EAAmB,IAAIU,CAAI,IAAMhB,GACnCM,EAAmB,OAAOU,CAAI,CAElC,CACF,EAEA,YACEtB,EACAuB,EACAnB,EACAe,EACqC,CACrCN,EAAcb,CAAQ,EACtB,IAAMwB,EAAa,IAAI,gBACjBC,EAAKrB,GAAa,CAAC,EAGrBqB,EAAG,SACDA,EAAG,OAAO,QACZD,EAAW,MAAM,EAEjBC,EAAG,OAAO,iBAAiB,QAAS,IAAMD,EAAW,MAAM,EAAG,CAC5D,KAAM,EACR,CAAC,GA2FL,IAAME,GAvFW,SAAwC,CACvD,IAAMzB,EAASN,EAAU,EACnBgC,EAAM9B,EAAS,GAAGT,CAAU,YAAYY,CAAQ,WAAW,EAE3DJ,EAAM,MAAM,MAAM+B,EAAK,CAC3B,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,cAAe,UAAU1B,EAAO,KAAK,EACvC,EACA,KAAM,KAAK,UAAU,CACnB,QAAAsB,EACA,GAAIJ,GAAS,aAAa,QAAU,CAClC,YAAaA,EAAQ,WACvB,CACF,CAAC,EACD,OAAQK,EAAW,MACrB,CAAC,EAED,GAAI,CAAC5B,EAAI,GACP,MAAM,MAAME,EACVF,EACA,wBAAwBA,EAAI,MAAM,IAAIA,EAAI,UAAU,GACpD,kBACF,EAGF,IAAMgC,EAAShC,EAAI,KAAM,UAAU,EAC7BiC,EAAU,IAAI,YAChBC,EAAS,GACTvB,EAEJ,OAAa,CACX,GAAM,CAAE,KAAAwB,EAAM,MAAAC,CAAM,EAAI,MAAMJ,EAAO,KAAK,EAC1C,GAAIG,EACF,MAGFD,GAAUD,EAAQ,OAAOG,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAMC,EAAQH,EAAO,MAAM;AAAA,CAAI,EAC/BA,EAASG,EAAM,IAAI,GAAK,GAExB,QAAWC,KAAQD,EAAO,CACxB,GAAI,CAACC,EAAK,WAAW,QAAQ,EAC3B,SAEF,IAAMC,GAAOD,EAAK,MAAM,CAAC,EACzB,GAAI,CACF,IAAM/B,EAAQ,KAAK,MAAMgC,EAAI,EAS7B,GAPIhC,EAAM,OAAS,SACjBI,EAAS,CACP,WAAYJ,EAAM,WAClB,MAAOA,EAAM,KACf,GAGEA,EAAM,OAAS,mBAAoB,CAIrC,IAAMG,GACJM,EAAmB,IAAIT,EAAM,IAAI,IAChCsB,EAAG,iBACCW,IACCX,EAAG,iBAAkBtB,EAAM,KAAMiC,GAAOjC,EAAM,EAAE,EAClD,QACDE,GAAcL,EAAUG,EAAOG,EAAO,CAC7C,CAEAJ,GAAcC,EAAOsB,CAAE,CACzB,MAAQ,CAER,CACF,CACF,CAEA,GAAI,CAAClB,EACH,MAAM,IAAIc,EACR,oCACA,mBACF,EAGF,OAAOd,CACT,GAAG,EAGH,OAAAmB,EAAU,MAAQ,IAAMF,EAAW,MAAM,EAClCE,CACT,CACF,CACF,CChwBO,IAAMW,GAAW,CAYtB,IAAI,UAAmB,CACrB,OAAOC,EAAY,CACrB,EAsBA,MAAM,WAAWC,EAAYC,EAA8C,CACzE,IAAMC,EAASC,EAAU,EACnB,CAAE,WAAAC,EAAY,OAAAC,CAAO,EAAIJ,GAAW,CAAC,EAG3CI,GAAQ,eAAe,EAGvB,IAAMC,EAAaC,EAAS,4BAA4B,EAClDC,EAAa,MAAM,MAAMF,EAAY,CACzC,OAAQ,OACR,QAAS,CACP,cAAe,UAAUJ,EAAO,KAAK,GACrC,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CACnB,SAAUF,EAAK,KACf,YAAaA,EAAK,MAAQ,0BAC5B,CAAC,EACD,OAAAK,CACF,CAAC,EAED,GAAI,CAACG,EAAW,GACd,MAAM,IAAIC,EACR,0BAA0BD,EAAW,MAAM,IAAIA,EAAW,UAAU,GACpE,eACAA,EAAW,MACb,EAGF,GAAM,CAAE,UAAAE,EAAW,aAAAC,EAAc,UAAAC,CAAU,EACxC,MAAMJ,EAAW,KAAK,EAOnBK,EAAW,IAAI,SACrB,OAAW,CAACC,EAAKC,CAAK,IAAK,OAAO,QAAQJ,CAAY,EACpDE,EAAS,OAAOC,EAAKC,CAAK,EAK5B,GAHAF,EAAS,OAAO,OAAQb,CAAI,EAGxBI,EACF,aAAMY,EAAUN,EAAWG,EAAUT,EAAYC,CAAM,EAChDO,EAGT,IAAMK,EAAY,MAAM,MAAMP,EAAW,CACvC,OAAQ,OACR,KAAMG,EACN,OAAAR,CACF,CAAC,EAED,GAAI,CAACY,EAAU,GACb,MAAM,IAAIR,EACR,uBAAuBQ,EAAU,MAAM,IAAIA,EAAU,UAAU,GAC/D,eACAA,EAAU,MACZ,EAGF,OAAOL,CACT,EAiBA,MAAM,OACJM,EACAlB,EACAC,EACuC,CACvC,GAAM,CAAE,WAAAG,EAAY,OAAAC,CAAO,EAAIJ,GAAW,CAAC,EAC3CI,GAAQ,eAAe,EAEvB,IAAMQ,EAAW,IAAI,SACrB,OAAW,CAACC,EAAKC,CAAK,IAAK,OAAO,QAAQG,EAAM,OAAO,MAAM,EAC3DL,EAAS,OAAOC,EAAKC,CAAK,EAI5B,GAFAF,EAAS,OAAO,OAAQb,CAAI,EAExBI,EACF,MAAMY,EAAUE,EAAM,OAAO,IAAKL,EAAUT,EAAYC,CAAM,MACzD,CACL,IAAMc,EAAM,MAAM,MAAMD,EAAM,OAAO,IAAK,CACxC,OAAQ,OACR,KAAML,EACN,OAAAR,CACF,CAAC,EACD,GAAI,CAACc,EAAI,GACP,MAAM,IAAIV,EACR,uBAAuBU,EAAI,MAAM,IAAIA,EAAI,UAAU,GACnD,eACAA,EAAI,MACN,CAEJ,CAEA,MAAO,CAAE,IAAKD,EAAM,IAAK,IAAKA,EAAM,GAAI,CAC1C,CACF,EAMA,SAASF,EACPI,EACAP,EACAT,EACAC,EACe,CACf,OAAO,IAAI,QAAc,CAACgB,EAASC,IAAW,CAC5C,IAAMC,EAAM,IAAI,eAqChB,GAnCAA,EAAI,OAAO,iBAAiB,WAAaC,GAAM,CACzCA,EAAE,kBACJpB,EAAWoB,EAAE,OAASA,EAAE,KAAK,CAEjC,CAAC,EAEDD,EAAI,iBAAiB,OAAQ,IAAM,CAC7BA,EAAI,QAAU,KAAOA,EAAI,OAAS,KACpCnB,EAAW,CAAC,EACZiB,EAAQ,GAERC,EACE,IAAIb,EACF,uBAAuBc,EAAI,MAAM,IAAIA,EAAI,UAAU,GACnD,eACAA,EAAI,MACN,CACF,CAEJ,CAAC,EAEDA,EAAI,iBAAiB,QAAS,IAAM,CAClCD,EACE,IAAIb,EACF,oCACA,cACF,CACF,CACF,CAAC,EAEDc,EAAI,iBAAiB,QAAS,IAAM,CAClCD,EAAO,IAAI,aAAa,iBAAkB,YAAY,CAAC,CACzD,CAAC,EAGGjB,EAAQ,CACV,GAAIA,EAAO,QAAS,CAClBiB,EAAO,IAAI,aAAa,iBAAkB,YAAY,CAAC,EACvD,MACF,CACAjB,EAAO,iBAAiB,QAAS,IAAMkB,EAAI,MAAM,EAAG,CAAE,KAAM,EAAK,CAAC,CACpE,CAEAA,EAAI,KAAK,OAAQH,CAAG,EACpBG,EAAI,KAAKV,CAAQ,CACnB,CAAC,CACH,CC/OO,IAAMY,EAAgC,CAC3C,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,cAAe,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,sBAAuB,KAAM,oBAAO,EACxE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,YAAa,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,YAAa,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,UAAW,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,WAAY,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,yBAA0B,KAAM,oBAAO,EAC7E,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,eAAgB,KAAM,oBAAO,EACnE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,SAAU,KAAM,oBAAO,EAC3D,CACE,KAAM,KACN,SAAU,OACV,KAAM,2BACN,KAAM,oBACR,EACA,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,WAAY,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,cAAe,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,OAAQ,KAAM,oBAAO,EAC1D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,gBAAiB,KAAM,oBAAO,EACpE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,WAAY,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,qBAAsB,KAAM,oBAAO,EACvE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,cAAe,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,oBAAqB,KAAM,oBAAO,EACxE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,UAAW,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,YAAa,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,gBAAiB,KAAM,oBAAO,EACpE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,YAAa,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,YAAa,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,OAAQ,KAAM,oBAAO,EAC1D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,UAAW,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,aAAc,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,gBAAiB,KAAM,oBAAO,EACpE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,YAAa,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,WAAY,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,mBAAoB,KAAM,oBAAO,EACvE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,YAAa,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,cAAe,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,cAAe,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,YAAa,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,cAAe,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,kBAAmB,KAAM,oBAAO,EACtE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,WAAY,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,YAAa,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,mBAAoB,KAAM,oBAAO,EACvE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,OAAQ,KAAM,oBAAO,EAC1D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,cAAe,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,cAAe,KAAM,oBAAO,EAChE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,SAAU,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,wBAAyB,KAAM,oBAAO,EAC1E,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,cAAe,KAAM,oBAAO,EAChE,CACE,KAAM,KACN,SAAU,KACV,KAAM,mCACN,KAAM,oBACR,EACA,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,wBAAyB,KAAM,oBAAO,EAC5E,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,eAAgB,KAAM,oBAAO,EACnE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,eAAgB,KAAM,oBAAO,EACnE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,YAAa,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,kBAAmB,KAAM,oBAAO,EACtE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,eAAgB,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,cAAe,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,cAAe,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,QAAS,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,YAAa,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,cAAe,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,WAAY,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,cAAe,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,OAAQ,KAAM,oBAAO,EAC3D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,sBAAuB,KAAM,oBAAO,EACxE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,SAAU,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,eAAgB,KAAM,oBAAO,EACnE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,uBAAwB,KAAM,oBAAO,EAC3E,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,iBAAkB,KAAM,oBAAO,EACpE,CAAE,KAAM,KAAM,SAAU,KAAM,KAAM,gBAAiB,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,aAAc,KAAM,oBAAO,EACjE,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,UAAW,KAAM,oBAAO,EAC9D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,eAAgB,KAAM,oBAAO,EAClE,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,YAAa,KAAM,oBAAO,EAC/D,CAAE,KAAM,KAAM,SAAU,MAAO,KAAM,UAAW,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,QAAS,KAAM,oBAAO,EAC5D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,SAAU,KAAM,oBAAO,EAC7D,CAAE,KAAM,KAAM,SAAU,OAAQ,KAAM,WAAY,KAAM,oBAAO,CACjE,EAMMC,GAAS,IAAI,IAAID,EAAU,IAAKE,GAAM,CAACA,EAAE,KAAMA,CAAC,CAAC,CAAC,EAGlDC,GAA4C,CAChD,mBAAoB,KACpB,kBAAmB,KACnB,iBAAkB,KAClB,sBAAuB,KACvB,oBAAqB,KACrB,mBAAoB,KACpB,kBAAmB,KACnB,+BAAgC,KAChC,kBAAmB,KACnB,oBAAqB,KACrB,mBAAoB,KACpB,mBAAoB,KACpB,kBAAmB,KACnB,mBAAoB,KACpB,gBAAiB,KACjB,gBAAiB,KACjB,eAAgB,KAChB,gBAAiB,KACjB,gBAAiB,KACjB,cAAe,KACf,mBAAoB,KACpB,kBAAmB,KACnB,gBAAiB,KACjB,gBAAiB,KACjB,mBAAoB,KACpB,cAAe,KACf,oBAAqB,KACrB,kBAAmB,KACnB,gBAAiB,KACjB,gBAAiB,KACjB,mBAAoB,KACpB,kBAAmB,KACnB,gBAAiB,KACjB,kBAAmB,KACnB,gBAAiB,KACjB,cAAe,KACf,gBAAiB,KACjB,kBAAmB,KACnB,gBAAiB,KACjB,oBAAqB,KACrB,mBAAoB,KACpB,iBAAkB,KAClB,cAAe,KACf,iBAAkB,KAClB,aAAc,KACd,aAAc,KACd,gBAAiB,KACjB,iBAAkB,KAClB,cAAe,KACf,iBAAkB,KAClB,eAAgB,KAChB,eAAgB,KAChB,aAAc,KACd,eAAgB,KAChB,eAAgB,KAChB,cAAe,KACf,oBAAqB,KACrB,mBAAoB,KACpB,cAAe,KACf,aAAc,KACd,cAAe,KACf,eAAgB,KAChB,cAAe,KACf,iBAAkB,KAClB,aAAc,KACd,cAAe,KACf,gBAAiB,KACjB,eAAgB,KAChB,iBAAkB,KAClB,cAAe,KACf,mBAAoB,KACpB,sBAAuB,KACvB,qBAAsB,KACtB,kBAAmB,KACnB,qBAAsB,KACtB,mBAAoB,KACpB,eAAgB,KAChB,eAAgB,KAChB,eAAgB,KAChB,iBAAkB,KAClB,sBAAuB,KACvB,oBAAqB,KACrB,eAAgB,KAChB,qBAAsB,KACtB,uBAAwB,KACxB,iBAAkB,KAClB,sBAAuB,KACvB,iBAAkB,KAClB,eAAgB,KAChB,mBAAoB,KACpB,uBAAwB,KACxB,oBAAqB,KACrB,kBAAmB,KACnB,oBAAqB,KACrB,iBAAkB,KAClB,iBAAkB,KAClB,kBAAmB,IACrB,EAUO,SAASC,GAAwB,CACtC,GAAI,CACF,IAAMC,EAAK,KAAK,eAAe,EAAE,gBAAgB,EAAE,SACnD,OAAOF,GAAkBE,CAAE,GAAK,IAClC,MAAQ,CACN,MAAO,IACT,CACF,CAQO,SAASC,EAAOC,EAAsB,CAC3C,GAAI,CAACA,EAAK,WAAW,GAAG,EACtB,OAAOA,EAGT,IAAMC,EAASD,EAAK,MAAM,CAAC,EAG3B,GAAIC,EAAO,WAAW,GAAG,GAAKA,EAAO,SAAW,GAAI,CAClD,IAAMC,EAAOD,EAAO,MAAM,EAAG,CAAC,EACxBE,EAASF,EAAO,MAAM,EAAG,CAAC,EAC1BG,EAAOH,EAAO,MAAM,CAAC,EAC3B,MAAO,OAAOC,CAAI,KAAKC,CAAM,IAAIC,CAAI,EACvC,CAGA,QAASC,EAAM,EAAGA,GAAO,EAAGA,IAAO,CACjC,IAAMC,EAAY,IAAML,EAAO,MAAM,EAAGI,CAAG,EACrCE,EAAQd,EAAU,KAAME,GAAMA,EAAE,WAAaW,CAAS,EAC5D,GAAIC,EAAO,CACT,IAAMC,EAAOP,EAAO,MAAMI,CAAG,EAC7B,MAAO,GAAGE,EAAM,QAAQ,IAAIC,CAAI,EAClC,CACF,CAEA,OAAOR,CACT,CASO,SAASS,EAAOC,EAAkBC,EAA6B,CACpE,IAAMV,EAASS,EAAS,QAAQ,MAAO,EAAE,EACnCE,EAAUlB,GAAO,IAAIiB,CAAW,EACtC,GAAI,CAACC,EACH,MAAO,IAAMX,EAEf,IAAMY,EAAaD,EAAQ,SAAS,MAAM,CAAC,EAE3C,OAAIX,EAAO,WAAWY,CAAU,EACvB,IAAMZ,EAERW,EAAQ,SAAWX,CAC5B,CAMO,SAASa,EAAQC,EAAwB,CAC9C,MAAO,oBAAoB,KAAKA,CAAK,CACvC,CCjaO,SAASC,EAAQC,EAAwB,CAC9C,MAAO,6BAA6B,KAAKA,CAAK,CAChD,CCqDA,SAASC,GAAsB,CAC7B,GAAI,CACF,OAAO,OAAO,MAAQ,OAAO,IAC/B,MAAQ,CAEN,MAAO,EACT,CACF,CAEA,eAAeC,EACbC,EACAC,EACAC,EACY,CAEZ,IAAMC,EAAkC,CACtC,cAAe,UAFFC,EAAU,EAES,KAAK,EACvC,EAUMC,EACJJ,IAAW,OAAS,CAAE,GAAGC,EAAM,SAAUJ,EAAW,CAAE,EAAII,EAExDG,IAAY,SACdF,EAAQ,cAAc,EAAI,oBAG5B,IAAMG,EAAM,MAAM,MAAMC,EAASP,CAAI,EAAG,CACtC,OAAAC,EACA,QAAAE,EACA,GAAIE,IAAY,QAAa,CAAE,KAAM,KAAK,UAAUA,CAAO,CAAE,CAC/D,CAAC,EAED,GAAI,CAACC,EAAI,GACP,MAAM,MAAME,EACVF,EACA,wBAAwBA,EAAI,MAAM,IAAIA,EAAI,UAAU,GACpD,YACF,EAGF,OAAQ,MAAMA,EAAI,KAAK,CACzB,CAMA,IAAMG,EAAiB,kBAKjBC,EAAoB,WACpBC,EAAqB,mBAMvBC,EAAqB,GAYrBC,EAAkBC,GAAuB,EAG7C,SAASA,IAAkC,CACzC,GAAI,OAAO,OAAW,IACpB,MAAO,GAET,GAAI,CACF,IAAMC,EAAS,IAAI,IAAI,OAAO,SAAS,IAAI,EAAE,aAG7C,OAAOA,EAAO,IAAI,MAAM,GAAKA,EAAO,IAAIL,CAAiB,IAAM,GACjE,MAAQ,CACN,MAAO,EACT,CACF,CAGA,SAASM,IAAwB,CAC/B,IAAMC,EAAI,WAAW,OACrB,GAAIA,GAAK,OAAOA,EAAE,YAAe,WAC/B,OAAOA,EAAE,WAAW,EAEtB,IAAMC,EAAQ,IAAI,WAAW,EAAE,EAC/B,OAAAD,GAAG,kBAAkBC,CAAK,EACnB,MAAM,KAAKA,EAAQC,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAC1E,CAGA,SAASC,GAAwBC,EAAgB,CAC/CA,EAAI,aAAa,OAAO,MAAM,EAC9BA,EAAI,aAAa,OAAO,OAAO,EAC/B,GAAI,CACF,OAAO,QAAQ,aAAa,OAAO,QAAQ,MAAO,GAAIA,EAAI,SAAS,CAAC,CACtE,MAAQ,CAER,CACF,CAEA,IAAMC,EAAqD,IAAI,IAE/D,SAASC,EAAaC,EAAiC,CACrDC,EAAa,CACX,MAAOD,EAAO,MACd,KAAMA,EAAO,KACb,QAASA,EAAO,QAChB,UAAWA,EAAO,SACpB,CAAC,EACDF,EAAc,QAASI,GAAOA,EAAGF,EAAO,IAAI,CAAC,CAC/C,CAEA,SAASG,EAAYH,EAAoC,CACvD,GAAI,CAACA,EAAO,KACV,MAAM,IAAII,EACR,kDACA,YACF,EAEF,OAAOJ,EAAO,IAChB,CAEA,SAASK,EAAoBC,EAAgC,CAC3D,IAAMC,EAAS3B,EAAU,EACrB2B,EAAO,OACT,OAAO,OAAOA,EAAO,KAAMD,CAAM,EACjCR,EAAc,QAASI,GAAOA,EAAGK,EAAO,IAAI,CAAC,EAEjD,CAGA,SAASC,GAAkC,CACzC,GAAI,CACF,OAAO5B,EAAU,EAAE,IACrB,MAAQ,CACN,OAAO,IACT,CACF,CAGA,SAAS6B,IAA4B,CACnC,IAAMC,EAAOF,EAAgB,EAC7BV,EAAc,QAASI,GAAOA,EAAGQ,CAAI,CAAC,CACxC,CAGA,SAASC,IAA4B,CAC/BtB,IAGJA,EAAkB,GAClBoB,GAAoB,EACtB,CAGA,SAASG,GAA0B,CAC5BvB,IAGLA,EAAkB,GAClBoB,GAAoB,EACtB,CAIA,IAAMI,GAAmB,IACnBC,GAAoB,IAO1B,SAASC,GAAsBC,EAAeC,EAAwB,CACpE,IAAMC,EAAW,OAAO,QAClBC,EAAU,OAAO,QACjBC,EAAQ,OAAO,YAAc,OAAO,YAAc,OAAO,OAAO,MAChEC,EACJ,OAAO,aAAe,OAAO,aAAe,OAAO,OAAO,OAEtDC,EAAI,KAAK,IAAIN,EAAOI,CAAK,EACzBG,EAAI,KAAK,IAAIN,EAAQI,CAAK,EAC1BG,EAAO,KAAK,IAAI,EAAG,KAAK,MAAMN,GAAYE,EAAQE,GAAK,CAAC,CAAC,EACzDG,EAAM,KAAK,IAAI,EAAG,KAAK,MAAMN,GAAWE,EAAQE,GAAK,CAAC,CAAC,EAE7D,MAAO,mBAAmB,KAAK,MAAMD,CAAC,CAAC,WAAW,KAAK,MAAMC,CAAC,CAAC,SAASC,CAAI,QAAQC,CAAG,EACzF,CAYA,SAASC,GACPC,EACAC,EACyB,CAGzB,IAAMC,EAAc,IAAI,IAAIF,EAAa,OAAO,SAAS,IAAI,EAC7DE,EAAY,aAAa,IAAI3C,EAAmB,GAAG,EAEnD,IAAM4C,EAAW,GAAG/C,EAAS,oBAAoB,CAAC,IAAI,IAAI,gBAAgB,CACxE,aAAc8C,EAAY,SAAS,EACnC,MAAAD,CACF,CAAC,EAAE,SAAS,CAAC,GAIPG,EAAQ,OAAO,KACnBD,EACA,iBACAf,GAAsBF,GAAkBC,EAAiB,CAC3D,EACA,OAAKiB,GAWLpB,GAAoB,EAEb,IAAI,QAAwB,CAACqB,EAASC,IAAW,CACtD,IAAMC,EAAY,OAAO,SAAS,OAC9BC,EAAU,GAIVC,EAAW,GAETC,EAAUC,GAAyB,CACvC,GAAI,CAAAH,EAGJ,CAAAA,EAAU,GACV,OAAO,oBAAoB,UAAWI,CAAS,EAC/C,cAAcC,CAAS,EACvB,aAAaC,CAAS,EAItB7B,EAAkB,EAClB,GAAI,CACGmB,EAAM,QACTA,EAAM,MAAM,CAEhB,MAAQ,CAER,CACAO,EAAG,EACL,EAEMC,EAAaG,GAA8B,CAO/C,GAJIP,GAAWC,GAIXM,EAAM,SAAWR,GAAaQ,EAAM,SAAWX,EACjD,OAEF,IAAMY,EAAOD,EAAM,KAKnB,GAAI,GAACC,GAAQA,EAAK,OAASxD,GAAsBwD,EAAK,QAAUf,GAQhE,IAFAQ,EAAW,GAEP,CAACO,EAAK,KAAM,CACdN,EAAO,IACLJ,EACE,IAAI7B,EACF,4BACA,YACF,CACF,CACF,EACA,MACF,CAGA7B,EAA6B,wBAAyB,OAAQ,CAC5D,KAAMoE,EAAK,IACb,CAAC,EACE,KAAM3C,GAAW,CAGhBX,EAAkB,GAClBU,EAAaC,CAAM,EACnBqC,EAAO,IAAML,EAAQ7B,EAAYH,CAAM,CAAC,CAAC,CAC3C,CAAC,EACA,MAAO4C,GAAiBP,EAAO,IAAMJ,EAAOW,CAAG,CAAC,CAAC,EACtD,EAEA,OAAO,iBAAiB,UAAWL,CAAS,EAI5C,IAAMC,EAAY,YAAY,IAAM,CAC9BT,EAAM,QAAU,CAACK,GACnBC,EAAO,IAAML,EAAQ,IAAI,CAAC,CAE9B,EAAG,GAAG,EAGAS,EAAY,WAChB,IACEJ,EAAO,IACLJ,EACE,IAAI7B,EACF,qBACA,gBACF,CACF,CACF,EACF,IAAS,GACX,CACF,CAAC,GAlHQ,QAAQ,OACb,IAAIA,EACF,2EACA,eACF,CACF,CA8GJ,CAWO,SAASyC,GAAuC,CACrD,GAAI,OAAO,OAAW,IACpB,MAAO,GAGT,IAAItD,EACJ,GAAI,CACFA,EAAS,IAAI,IAAI,OAAO,SAAS,IAAI,EAAE,YACzC,MAAQ,CACN,MAAO,EACT,CACA,GAAIA,EAAO,IAAIL,CAAiB,IAAM,IACpC,MAAO,GAET,IAAM4D,EAAOvD,EAAO,IAAI,MAAM,EAC9B,GAAI,CAACuD,EACH,MAAO,GAQT,GAAI1D,EACF,MAAO,GAGT,IAAM2D,EAAS,OAAO,OACtB,GAAI,CAACA,GAAUA,IAAW,OAIxB,MAAO,GAGT,GAAI,CACFA,EAAO,YACL,CACE,KAAM5D,EACN,KAAA2D,EACA,MAAOvD,EAAO,IAAI,OAAO,GAAK,MAChC,EACA,OAAO,SAAS,MAClB,CACF,MAAQ,CACN,MAAO,EACT,CAEAH,EAAqB,GAIrB,GAAI,CACF,WAAW,IAAM,OAAO,MAAM,EAAG,CAAC,CACpC,MAAQ,CAER,CACA,MAAO,EACT,CAkSO,IAAM4D,GAAa,CAGxB,IAAI,aAAc,CAChB,OAAOpE,EAAU,EAAE,IACrB,EAEA,gBAAiB,CACf,OAAOA,EAAU,EAAE,IACrB,EAEA,iBAAkB,CAChB,OAAOA,EAAU,EAAE,OAAS,IAC9B,EAEA,IAAI,YAAa,CACf,OAAIS,EACK,iBAEFmB,EAAgB,EAAI,gBAAkB,iBAC/C,EAEA,IAAI,kBAAmB,CACrB,OAAO5B,EAAU,EAAE,WAAa,IAClC,EAEA,qBAAsB,CACpB,OAAOA,EAAU,EAAE,WAAa,IAClC,EAIA,cAAcqE,EAAe,CAC3B,OAAO1E,EAAsC,qBAAsB,OAAQ,CACzE,MAAA0E,CACF,CAAC,CACH,EAEA,MAAM,gBAAgBC,EAAwBJ,EAAc,CAC1D,IAAM9C,EAAS,MAAMzB,EACnB,uBACA,OACA,CAAE,eAAA2E,EAAgB,KAAAJ,CAAK,CACzB,EACA,OAAA/C,EAAaC,CAAM,EACZG,EAAYH,CAAM,CAC3B,EAIA,YAAYmD,EAAe,CACzB,OAAO5E,EAAsC,mBAAoB,OAAQ,CACvE,MAAA4E,CACF,CAAC,CACH,EAEA,MAAM,cAAcD,EAAwBJ,EAAc,CACxD,IAAM9C,EAAS,MAAMzB,EACnB,qBACA,OACA,CAAE,eAAA2E,EAAgB,KAAAJ,CAAK,CACzB,EACA,OAAA/C,EAAaC,CAAM,EACZG,EAAYH,CAAM,CAC3B,EAIA,eAAeoD,EAA0D,CACvE,IAAMC,EAAOD,GAAS,MAAQ,OACxBE,EAAWD,IAAS,SAAYA,IAAS,QAAU/E,EAAW,EAC9DqD,EAAcyB,GAAS,aAAe,OAAO,SAAS,KACtDxB,EAAQwB,GAAS,OAAS5D,GAAc,EAE9C,GAAI8D,EAEF,OAAO5B,GAAoBC,EAAaC,CAAK,EAK/C,GAAI,CACF,eAAe,QAAQ3C,EAAgB2C,CAAK,CAC9C,MAAQ,CAGR,CAEA,IAAMrC,EAAS,IAAI,gBAAgB,CAAE,aAAcoC,EAAa,MAAAC,CAAM,CAAC,EACvE,cAAO,SAAS,OACd,GAAG7C,EAAS,oBAAoB,CAAC,IAAIQ,EAAO,SAAS,CAAC,EACxD,EAGO,IAAI,QAAwB,IAAM,CAAC,CAAC,CAC7C,EAEA,MAAM,oBAAqB,CACzB,GAAI,OAAO,OAAW,IACpB,OAAO,KAGT,IAAMM,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EAClCiD,EAAOjD,EAAI,aAAa,IAAI,MAAM,EACxC,GAAI,CAACiD,EACH,OAAO,KAOT,GAAIjD,EAAI,aAAa,IAAIX,CAAiB,IAAM,IAC9C,OAAA2D,EAA4B,EACrB,KAGT,IAAMU,EAAgB1D,EAAI,aAAa,IAAI,OAAO,EAIlDD,GAAwBC,CAAG,EAE3B,IAAI2D,EAA6B,KACjC,GAAI,CACFA,EAAc,eAAe,QAAQvE,CAAc,EACnD,eAAe,WAAWA,CAAc,CAC1C,MAAQ,CAER,CAUA,GALA0B,GAAoB,EAKhB4C,IAAkB,MAAQA,IAAkBC,EAC9C,MAAA5C,EAAkB,EACZ,IAAIR,EACR,qFACA,eACF,EAGF,GAAI,CACF,IAAMJ,EAAS,MAAMzB,EACnB,wBACA,OACA,CAAE,KAAAuE,CAAK,CACT,EAGA,OAAAzD,EAAkB,GAClBU,EAAaC,CAAM,EACZG,EAAYH,CAAM,CAC3B,OAAS4C,EAAK,CAEZ,MAAAhC,EAAkB,EACZgC,CACR,CACF,EAIA,MAAM,mBAAmBa,EAAkB,CACzC,MAAMlF,EAAU,uBAAwB,OAAQ,CAAE,SAAAkF,CAAS,CAAC,CAC9D,EAEA,MAAM,mBAAmBA,EAAkBX,EAAc,CACvD,IAAM9C,EAAS,MAAMzB,EACnB,+BACA,OACA,CAAE,SAAAkF,EAAU,KAAAX,CAAK,CACnB,EACA,OAAA/C,EAAaC,CAAM,EACZG,EAAYH,CAAM,CAC3B,EAEA,MAAM,mBAAmB0D,EAAkB,CACzC,MAAMnF,EAAU,uBAAwB,OAAQ,CAAE,SAAAmF,CAAS,CAAC,CAC9D,EAEA,MAAM,mBAAmBA,EAAkBZ,EAAc,CACvD,IAAM9C,EAAS,MAAMzB,EACnB,+BACA,OACA,CAAE,SAAAmF,EAAU,KAAAZ,CAAK,CACnB,EACA,OAAA/C,EAAaC,CAAM,EACZG,EAAYH,CAAM,CAC3B,EAIA,MAAM,QAAS,CACb,IAAMA,EAAS,MAAMzB,EACnB,iBACA,OACA,CAAC,CACH,EACAwB,EAAaC,CAAM,CACrB,EAEA,MAAM,cAAe,CACnB,IAAM2D,EAAS,MAAMpF,EACnB,yBACA,OACA,CAAC,CACH,EACA,OAAA8B,EAAoB,CAAE,OAAQsD,EAAO,MAAO,CAAC,EACtC,CAAE,IAAKA,EAAO,GAAI,CAC3B,EAEA,MAAM,cAAe,CACnB,MAAMpF,EAAU,yBAA0B,OAAQ,CAAC,CAAC,EACpD8B,EAAoB,CAAE,OAAQ,IAAK,CAAC,CACtC,EAEA,mBAAmBuD,EAA0C,CAC3D,OAAA9D,EAAc,IAAI8D,CAAQ,EAC1BA,EAASpD,EAAgB,CAAC,EACnB,IAAM,CACXV,EAAc,OAAO8D,CAAQ,CAC/B,CACF,EAIA,MAAO,CACL,UAAwBC,EACxB,cAA4BC,EAC5B,OAAqBC,EACrB,OAAqBC,EACrB,QAAsBC,CACxB,EAEA,MAAO,CACL,QAAsBA,CACxB,CACF,ECr4BA,IAAMC,GAAkB,YAMlBC,GAAmB,GACnBC,GAAoB,GAE1B,SAASC,GAAQC,EAAmD,CAIlE,IAAIC,EAAuB,KAErBC,EAAgBC,EAAc,CAClC,QAAS,SAAY,CACnB,GAAI,CAACF,EACH,GAAI,CACFA,EAAQ,MAAMD,EAAQ,SAAS,CACjC,OAASI,EAAK,CACZ,OAAAJ,EAAQ,UACNI,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,CACpD,EACO,MACT,CAEF,MAAO,CACL,IAAKC,EAAST,EAAe,EAC7B,QAAS,CAAE,cAAe,UAAUK,CAAK,EAAG,CAC9C,CACF,EACA,SAAWK,IACLA,IAAW,MAEbL,EAAQ,MAEH,SAET,OAAQ,IAAM,CACZD,EAAQ,YAAY,CACtB,EACA,OAASO,GAAS,CAChB,GAAI,CAACA,EAAK,WAAW,QAAQ,EAC3B,OAEF,IAAIC,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMD,EAAK,MAAM,CAAC,CAAC,CACnC,MAAQ,CACN,MACF,CACA,GAAIC,GAAQ,OAAS,gBAAiB,CAGpCP,EAAQ,KACR,MACF,CACA,GAAIO,GAAQ,OAAS,iBAAkB,CACrCR,EAAQ,QAAQ,OAAOQ,EAAO,OAAU,SAAWA,EAAO,MAAQ,CAAC,EACnE,MACF,CACI,OAAOA,GAAQ,SAAY,UAC7BR,EAAQ,QAAQ,CACd,GAAIQ,EAAO,GACX,QAASA,EAAO,QAChB,KAAMA,EAAO,KACb,GAAIA,EAAO,EACb,CAAC,CAEL,CACF,CAAC,EAEDN,EAAK,MAAM,EAQX,IAAIO,EAAiE,CAAC,EAClEC,EAAmD,KACnDC,EAAc,EACdC,EAAS,GACTC,EAAyB,GAEvBC,EAAQ,SAAY,CAExB,GADAJ,EAAa,KACTE,GAAUH,EAAQ,SAAW,EAC/B,OAEF,GAAI,CAACR,GAAS,KAAK,IAAI,EAAIU,EAAa,CAGtCF,EAAU,CAAC,EACX,MACF,CACA,IAAMM,EAASN,EACfA,EAAU,CAAC,EACX,GAAI,CACF,IAAMO,EAAM,MAAM,MAAMX,EAAST,EAAe,EAAG,CACjD,OAAQ,OACR,QAAS,CACP,cAAe,UAAUK,CAAK,GAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAE,OAAAc,CAAO,CAAC,CACjC,CAAC,EACD,GAAIC,EAAI,SAAW,IAAK,CAGtBf,EAAQ,KACR,MACF,CACA,GAAIe,EAAI,SAAW,IAAK,CACtB,IAAMC,EAAgB,SACpBD,EAAI,QAAQ,IAAI,aAAa,GAAK,GAClC,EACF,EACAL,EACE,KAAK,IAAI,GACR,OAAO,SAASM,CAAa,GAAKA,EAAgB,EAC/CA,EAAgB,IAChB,KACN,MACF,CACID,EAAI,SAAW,KAAO,CAACH,IAIzBA,EAAyB,GACzBb,EAAQ,UACN,IAAI,MACF,0JAGF,CACF,EAEJ,MAAQ,CAER,CACF,EAmBA,MAAO,CACL,MAAO,IAAM,CACXY,EAAS,GACLF,IACF,aAAaA,CAAU,EACvBA,EAAa,MAEfD,EAAU,CAAC,EACXP,EAAK,KAAK,CACZ,EACA,IAAI,OAAQ,CACV,IAAMgB,EAAIhB,EAAK,MACf,OAAOgB,IAAM,OAAS,OAASA,IAAM,UAAY,SAAW,YAC9D,EACA,QA/Bc,CAACC,EAA6BC,IAAkB,CAC9D,GAAI,CAAAR,EAIJ,IADAH,EAAQ,KAAK,CAAE,SAAAU,EAAU,KAAAC,CAAK,CAAC,EAC3BX,EAAQ,QAAUX,GAAmB,CACnCY,GACF,aAAaA,CAAU,EAEpBI,EAAM,EACX,MACF,CACKJ,IACHA,EAAa,WAAW,IAAG,CAAQI,EAAM,GAAGjB,EAAgB,GAEhE,CAiBA,CACF,CAOO,IAAMkB,GAAS,CAAE,QAAAhB,EAAQ,EChLhC,GAAI,OAAO,OAAW,KAOhB,CAFYsB,EAA4B,GAE3B,WAAuC,eACtD,GAAI,CACFC,EAAU,CACZ,MAAQ,CAER","names":["createClient","_","methodName","input","options","config","getConfig","methodId","MindStudioInterfaceError","url","withBase","wantsStream","res","errorFromResponse","reader","decoder","buffer","finalOutput","streamError","done","value","lines","line","json","event","AGENT_BASE","CLIENT_TOOL_RESULT_MAX_CHARS","request","path","method","body","headers","getConfig","res","withBase","errorFromResponse","postToolResult","threadId","config","dispatchEvent","event","callbacks","runClientTool","handler","result","serialized","err","createAgentChatClient","threadTokens","clientToolHandlers","rememberToken","token","thread","cursor","query","title","options","previousToken","MindStudioInterfaceError","name","content","controller","cb","abortable","url","reader","decoder","buffer","done","value","lines","line","json","input","platform","getBasePath","file","options","config","getConfig","onProgress","signal","presignUrl","withBase","presignRes","MindStudioInterfaceError","uploadUrl","uploadFields","publicUrl","formData","key","value","xhrUpload","uploadRes","token","res","url","resolve","reject","xhr","e","countries","byCode","c","timezoneToCountry","detectCountry","tz","format","e164","digits","area","prefix","line","len","candidate","match","rest","toE164","national","countryCode","country","dialDigits","isValid","phone","isValid","email","isEmbedded","authFetch","path","method","body","headers","getConfig","payload","res","withBase","errorFromResponse","REMY_STATE_KEY","REMY_POPUP_MARKER","REMY_POPUP_MESSAGE","remyPopupForwarded","redirectPending","detectColdLoadRedirect","params","generateState","c","bytes","b","stripRemyRedirectParams","url","authListeners","applySession","bundle","updateConfig","cb","requireUser","MindStudioInterfaceError","updateUserAndNotify","update","config","safeCurrentUser","notifyAuthListeners","user","beginAuthenticating","endAuthenticating","REMY_POPUP_WIDTH","REMY_POPUP_HEIGHT","centeredPopupFeatures","width","height","baseLeft","baseTop","viewW","viewH","w","h","left","top","signInWithRemyPopup","redirectUri","state","callbackUrl","startUrl","popup","resolve","reject","appOrigin","settled","accepted","finish","fn","onMessage","pollTimer","timeoutId","event","data","err","maybeRelayRemyPopupCallback","code","opener","auth","email","verificationId","phone","options","mode","usePopup","returnedState","storedState","newEmail","newPhone","result","callback","countries","detectCountry","format","toE164","isValid","EVENTS_ENDPOINT","PUBLISH_FLUSH_MS","PUBLISH_MAX_BATCH","connect","options","token","loop","createSseLoop","err","withBase","status","line","parsed","pending","flushTimer","pausedUntil","closed","publishRefusalSurfaced","flush","events","res","retryAfterSec","s","channels","data","maybeRelayRemyPopupCallback","getConfig"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/agent-chat.ts","../src/multipart-upload.ts","../src/platform.ts","../src/auth-phone.ts","../src/auth-email.ts","../src/auth.ts","../src/events.ts","../src/index.ts"],"sourcesContent":["/**\n * Method RPC client — typed function calls to backend methods.\n *\n * `createClient()` returns a Proxy where every property access creates\n * an async function that invokes the corresponding backend method via HTTP.\n * The method name matches the method's `export` field in `app.json`.\n *\n * ## How it works\n *\n * ```\n * api.submitVendorRequest({ name: 'Acme' })\n * │\n * ├─ Look up method ID: config.methods[\"submitVendorRequest\"]\n * │ → \"submit-vendor-request\"\n * │\n * ├─ POST /_/methods/submit-vendor-request/invoke\n * │ Authorization: Bearer {session token}\n * │ Body: { input: { name: \"Acme\" } }\n * │\n * └─ Returns response.output (or throws on error)\n * ```\n *\n * The method registry (export name → method ID mapping) is embedded in\n * `window.__MINDSTUDIO__.methods` at page load time — no extra HTTP call\n * needed to discover methods.\n *\n * ## Streaming\n *\n * Methods can optionally stream output via SSE. Pass `{ stream: true }` as\n * the second argument along with callbacks for the events you care about:\n *\n * ```ts\n * // LLM token text\n * const result = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n *\n * // Structured progress events from `stream({...})` on the backend\n * const order = await api.processOrder({ id: 'abc' }, {\n * stream: true,\n * onStreamData: (event) => setStatus(event),\n * });\n * ```\n *\n * **Important:** The `text` value passed to `onToken` is the accumulated\n * response so far (not a delta). Replace your display content each time —\n * do not append. See {@link InvokeOptions.onToken} for details.\n *\n * Each `onStreamData` payload is independent (no accumulation) — it's the\n * raw object passed to `stream({...})` on the backend.\n *\n * ## Type safety\n *\n * Since the frontend and backend live in the same project, you can import\n * types directly:\n *\n * ```ts\n * import type { SubmitVendorInput, SubmitVendorOutput }\n * from '../../backend/src/submitVendorRequest';\n *\n * interface AppMethods {\n * submitVendorRequest(input: SubmitVendorInput): Promise<SubmitVendorOutput>;\n * getDashboard(): Promise<GetDashboardOutput>;\n * }\n *\n * const api = createClient<AppMethods>();\n * const result = await api.submitVendorRequest({ name: 'Acme' }); // fully typed\n * ```\n *\n * If no type parameter is provided, methods accept `any` input and return `any`.\n */\n\nimport { getConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\n\n/**\n * Options for method invocation. Pass as the optional second argument to\n * any method on the client returned by {@link createClient}.\n *\n * When `stream` is `true`, the SDK sends `{ stream: true }` in the request\n * body and parses the response as an SSE (Server-Sent Events) stream.\n * Four event types are handled:\n *\n * | SSE `type` | Payload field | SDK action |\n * |------------|---------------|------------|\n * | `token` | `text` | Calls {@link onToken} with accumulated text |\n * | `data` | `data` | Calls {@link onStreamData} with the raw payload |\n * | `error` | `error` | Calls {@link onStreamError} with the message |\n * | `done` | `output` | Captures as the method's return value |\n *\n * The method's `Promise` resolves with the final `output` from the `done`\n * event — the same shape you'd get without streaming.\n */\nexport interface InvokeOptions {\n /**\n * Enable SSE streaming for LLM token output.\n *\n * When `true`, the request body includes `{ stream: true }` and the\n * response is parsed as an SSE stream instead of a single JSON payload.\n * The method still returns a `Promise` that resolves with the final\n * output once the stream completes.\n *\n * @default false\n */\n stream?: boolean;\n\n /**\n * Called with the accumulated response text each time a new token arrives.\n *\n * **Important:** `text` is the full response so far, not a delta/chunk.\n * Your frontend should **replace** (not append to) its display content\n * with this value each time the callback fires.\n *\n * This design avoids edge cases where the platform sanitizes or\n * reformats JSON mid-stream, causing earlier portions of the text to\n * change between tokens.\n *\n * @example\n * ```ts\n * const result = await api.generate({ prompt: 'Hello' }, {\n * stream: true,\n * onToken: (text) => {\n * // Replace — do NOT append\n * responseElement.textContent = text;\n * },\n * });\n * ```\n */\n onToken?: (text: string) => void;\n\n /**\n * Called when the backend method emits a structured event via\n * `stream({...})` (object payload).\n *\n * Receives the raw object passed to `stream()` on the backend —\n * not accumulated, not parsed, not wrapped. Use this for status\n * updates, progress, intermediate results, or any structured\n * streaming UI that isn't LLM token text.\n *\n * Different from {@link onToken}, which receives accumulated text from\n * `stream('text')` calls.\n *\n * @example\n * ```ts\n * await api.processOrder({ id: 'abc' }, {\n * stream: true,\n * onStreamData: (event) => {\n * // event is whatever the backend passed to stream({...})\n * setStatus(event as { status: string; progress: number });\n * },\n * });\n * ```\n */\n onStreamData?: (payload: unknown) => void;\n\n /**\n * Called if a stream-level error event arrives.\n *\n * If the stream then closes without a `done` event, the method rejects\n * with a `MindStudioInterfaceError` containing the error message (code\n * `method_error`). If a `done` event does arrive after the error, the\n * method resolves with the final output — in that case this callback\n * is just for logging or showing a transient warning.\n *\n * If the stream ends without either a `done` or `error` event, the\n * method rejects with `stream_incomplete`.\n */\n onStreamError?: (error: string) => void;\n}\n\n/**\n * Default client type when no generic parameter is provided.\n * Every property returns an async function accepting optional input\n * and optional {@link InvokeOptions} for streaming.\n */\ntype DefaultMethodClient = Record<\n string,\n (input?: Record<string, unknown>, options?: InvokeOptions) => Promise<unknown>\n>;\n\n/**\n * Create a typed RPC client for calling backend methods.\n *\n * @typeParam T - Optional interface mapping method names to their signatures.\n * If omitted, all methods accept `any` and return `Promise<any>`.\n *\n * @returns A Proxy object where each property is an async function that\n * invokes the corresponding backend method.\n *\n * @example\n * ```ts\n * import { createClient } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n *\n * // Each method maps to a backend method export\n * const result = await api.submitVendorRequest({ name: 'Acme' });\n * const dashboard = await api.getDashboard();\n *\n * // Stream LLM output — onToken receives accumulated text (replace, don't append)\n * const streamed = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n * // `streamed` is the same final output you'd get without streaming\n * ```\n */\nexport function createClient<T = DefaultMethodClient>(): T {\n return new Proxy({} as Record<string, unknown> as T & object, {\n get(_, methodName: string) {\n return async (input?: unknown, options?: InvokeOptions) => {\n const config = getConfig();\n\n const methodId = config.methods[methodName];\n if (!methodId) {\n throw new MindStudioInterfaceError(\n `Method \"${methodName}\" not found. Available methods: ${Object.keys(config.methods).join(', ') || '(none)'}`,\n 'method_not_found',\n );\n }\n\n const url = withBase(`/_/methods/${methodId}/invoke`);\n const wantsStream = options?.stream === true;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n input: input ?? {},\n ...(wantsStream && { stream: true }),\n }),\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Method \"${methodName}\" failed: ${res.status} ${res.statusText}`,\n 'method_error',\n );\n }\n\n // Non-streaming: unchanged behavior\n if (!wantsStream) {\n const body = (await res.json()) as { output?: unknown };\n return body.output;\n }\n\n // Streaming: parse SSE events from response body\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let finalOutput: unknown;\n let streamError: string | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as {\n type: string;\n text?: string;\n error?: string;\n output?: unknown;\n data?: unknown;\n };\n\n if (event.type === 'token' && options?.onToken && event.text) {\n options.onToken(event.text);\n } else if (event.type === 'data' && options?.onStreamData) {\n options.onStreamData(event.data);\n } else if (event.type === 'error' && event.error) {\n streamError = event.error;\n if (options?.onStreamError) {\n options.onStreamError(event.error);\n }\n } else if (event.type === 'done') {\n finalOutput = event.output;\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n // If the stream errored and didn't produce a final output,\n // surface the error message from the error event.\n if (finalOutput === undefined && streamError !== undefined) {\n throw new MindStudioInterfaceError(streamError, 'method_error');\n }\n\n if (finalOutput === undefined) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return finalOutput;\n };\n },\n });\n}\n","/**\n * Agent chat client — thread-based conversations with AI agents.\n *\n * `createAgentChatClient()` returns an object with methods for thread\n * lifecycle (create, list, get, rename, delete) and message streaming.\n * The agent runs server-side with access to the app's methods as tools.\n *\n * ## How it works\n *\n * ```\n * chat.sendMessage(threadId, 'Help me prep for my review', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * })\n * │\n * ├─ POST /_/agent/threads/{threadId}/messages\n * │ Authorization: Bearer {session token}\n * │ Body: { content: \"Help me prep for my review\" }\n * │\n * ├─ Response: SSE stream\n * │ data: {\"type\":\"text\",\"text\":\"I'll create...\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_start\",\"id\":\"call_abc\",\"name\":\"create-todo\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_result\",\"id\":\"call_abc\",\"output\":{...},\"ts\":1711...}\n * │ data: {\"type\":\"text\",\"text\":\"Done — added it.\",\"ts\":1711...}\n * │ data: {\"type\":\"done\",\"stopReason\":\"end_turn\",\"usage\":{...},\"ts\":1711...}\n * │\n * └─ Promise resolves with { stopReason, usage }\n * ```\n *\n * ## Client tools\n *\n * A tool declared in `agent.md` with `target: \"client\"` runs in this browser\n * instead of the app's backend. Register a handler and its return value goes\n * back to the agent as the tool result:\n *\n * ```\n * ├─ data: {\"type\":\"client_tool_call\",\"id\":\"call_x\",\"name\":\"pickFile\",\"input\":{...}}\n * │\n * ├─ your handler runs (the agent is waiting)\n * │\n * └─ POST /_/agent/threads/{threadId}/tool-results\n * Body: { callId: \"call_x\", result: { path: \"/invoices/q3.pdf\" } }\n * ```\n *\n * ## Stateless design\n *\n * This client is a thin wrapper over HTTP endpoints. Apart from client-tool\n * handlers, it does not manage local state — your React/framework layer owns\n * the thread list, message array, and UI state.\n *\n * ## Streaming callbacks\n *\n * `sendMessage` accepts named callbacks for common events (onText,\n * onThinking, onToolCallStart, etc.) plus a catch-all `onEvent` that\n * receives every SSE event as a discriminated union. Both fire for the\n * same event — use named callbacks for convenience, `onEvent` for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * // Thread lifecycle\n * const thread = await chat.createThread();\n * const { threads } = await chat.listThreads();\n * const full = await chat.getThread(thread.id);\n *\n * // Send a message with streaming\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n *\n * // Abort mid-stream\n * response.abort();\n * ```\n */\n\nimport { getConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Thread types\n// ---------------------------------------------------------------------------\n\n/** Thread summary returned from create and list endpoints. */\nexport interface ThreadSummary {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title, auto-generated on first message. */\n title: string | null;\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** Full thread with message history, returned from get endpoint. */\nexport interface Thread {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title. */\n title: string | null;\n /** Full conversation history. */\n messages: Message[];\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** A tool call requested by the assistant. */\nexport interface ToolCall {\n /** Tool call ID, used to match results. */\n id: string;\n /** Method name. */\n name: string;\n /** Input arguments. */\n input: Record<string, unknown>;\n}\n\n/** A single message in a thread's conversation history. */\nexport interface Message {\n /** `\"user\"` for human messages, `\"assistant\"` for agent responses. */\n role: 'user' | 'assistant';\n /** Message text content. For tool results, this is JSON-stringified output. */\n content: string;\n /**\n * MindStudio CDN URLs attached to this message (user messages only).\n *\n * Images (`i.mscdn.ai`) are sent to the model as vision input.\n * Documents (`f.mscdn.ai`, `files.mindstudio-cdn.com`) have their text\n * extracted server-side and included in the model context. The original\n * URLs are preserved in thread history for the frontend to render.\n */\n attachments?: string[];\n /** Tool calls the assistant requested (assistant messages only). */\n toolCalls?: ToolCall[];\n /** ID of the tool call this message is a result for (tool result messages only). */\n toolCallId?: string;\n /** Whether this tool result represents an error. */\n isToolError?: boolean;\n}\n\n/** Paginated thread list response. */\nexport interface ThreadListPage {\n /** Threads for this page. */\n threads: ThreadSummary[];\n /** Cursor for the next page, or `null` if this is the last page. */\n nextCursor: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// SSE event types\n// ---------------------------------------------------------------------------\n\n/** Assistant text delta — append to your display string, do not replace. */\nexport interface AgentTextEvent {\n type: 'text';\n text: string;\n ts: number;\n}\n\n/** Model thinking text (extended thinking). */\nexport interface AgentThinkingEvent {\n type: 'thinking';\n text: string;\n ts: number;\n}\n\n/** Thinking complete with full text and signature. */\nexport interface AgentThinkingCompleteEvent {\n type: 'thinking_complete';\n thinking: string;\n signature: string;\n ts: number;\n}\n\n/** Model requested a tool call (includes parsed input). */\nexport interface AgentToolUseEvent {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n ts: number;\n}\n\n/** Streaming delta for tool call input JSON. */\nexport interface AgentToolInputDeltaEvent {\n type: 'tool_input_delta';\n id: string;\n name: string;\n delta: string;\n ts: number;\n}\n\n/** Agent began executing a tool call. */\nexport interface AgentToolCallStartEvent {\n type: 'tool_call_start';\n id: string;\n name: string;\n ts: number;\n}\n\n/** Tool call returned a result. */\nexport interface AgentToolCallResultEvent {\n type: 'tool_call_result';\n id: string;\n /**\n * What the agent was given. When the method's result was over the agent's\n * bound (`maxToolOutputChars`), this is the record the agent got instead:\n * `{ truncated, totalChars, shownChars, content }`, `content` being the\n * beginning of the result's JSON.\n */\n output: unknown;\n /** Set when the result was cut before the agent saw it. */\n truncated?: true;\n ts: number;\n}\n\n/**\n * The agent invoked a client tool (agent.md `target: \"client\"`) — an action\n * that happens in this browser (open a sheet, pick a file, confirm something).\n * The agent is holding its turn until the SDK answers, which it always does:\n * with your handler's return value, with an error if it threw, or with\n * `unhandled_client_tool` if nothing is registered for the name.\n */\nexport interface AgentClientToolCallEvent {\n type: 'client_tool_call';\n id: string;\n name: string;\n input: unknown;\n /** How long the agent will wait for an answer before giving up on it. */\n timeoutMs?: number;\n ts: number;\n}\n\n/** Stream complete. */\nexport interface AgentDoneEvent {\n type: 'done';\n stopReason: string;\n usage: { inputTokens: number; outputTokens: number };\n ts: number;\n}\n\n/** Stream-level error. */\nexport interface AgentErrorEvent {\n type: 'error';\n error: string;\n ts: number;\n}\n\n/** Discriminated union of all SSE events from the agent chat stream. */\nexport type AgentChatEvent =\n | AgentTextEvent\n | AgentThinkingEvent\n | AgentThinkingCompleteEvent\n | AgentToolUseEvent\n | AgentToolInputDeltaEvent\n | AgentToolCallStartEvent\n | AgentToolCallResultEvent\n | AgentClientToolCallEvent\n | AgentDoneEvent\n | AgentErrorEvent;\n\n// ---------------------------------------------------------------------------\n// Callbacks and result types\n// ---------------------------------------------------------------------------\n\n/**\n * Callbacks for `sendMessage` streaming.\n *\n * Named callbacks fire for common events. The catch-all `onEvent` fires\n * for every event including those with named callbacks — use it for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n */\nexport interface SendMessageCallbacks {\n /**\n * Called with each text delta as the assistant responds.\n *\n * **Important:** `text` is a delta (new chunk), not the accumulated\n * response. Append it to your display string — do not replace.\n *\n * @example\n * ```ts\n * onText: (delta) => setMessage((prev) => prev + delta)\n * ```\n */\n onText?: (text: string) => void;\n\n /** Called with model thinking text (extended thinking). */\n onThinking?: (text: string) => void;\n\n /** Called when thinking is complete. */\n onThinkingComplete?: (thinking: string, signature: string) => void;\n\n /** Called when a tool call begins executing. */\n onToolCallStart?: (id: string, name: string) => void;\n\n /** Called when a tool call produces a result. */\n onToolCallResult?: (id: string, output: unknown) => void;\n\n /**\n * Called when the agent invokes a client tool (agent.md `target: \"client\"`)\n * and no handler is registered for its name — run the on-screen action here.\n * Whatever you return becomes the tool result; return nothing and the agent\n * is told the action ran.\n *\n * {@link AgentChatClient.registerClientTool} is the better home for anything\n * beyond a one-off, since handlers there survive across messages and are\n * keyed by tool name.\n */\n onClientToolCall?: (\n name: string,\n input: unknown,\n id: string,\n ) => unknown | Promise<unknown>;\n\n /** Called on a stream-level error event. */\n onError?: (error: string) => void;\n\n /** Called for every SSE event (including ones with named callbacks). */\n onEvent?: (event: AgentChatEvent) => void;\n\n /** AbortSignal to cancel the stream. */\n signal?: AbortSignal;\n}\n\n/** Options for `sendMessage`. */\nexport interface SendMessageOptions {\n /**\n * MindStudio CDN URLs to attach to the message.\n *\n * Images are sent to the model as vision input (one per message).\n * Documents have their text extracted server-side and included in context.\n *\n * Upload files first via {@link platform.upload} (with a backend-minted\n * upload token) and pass the returned URLs.\n *\n * @example\n * ```ts\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { url } = await platform.upload(token, file);\n * chat.sendMessage(threadId, 'What is this?', callbacks, {\n * attachments: [url],\n * });\n * ```\n */\n attachments?: string[];\n}\n\n/** Resolved value of `sendMessage` — extracted from the `done` event. */\nexport interface SendMessageResult {\n /** Why the agent stopped (`\"end_turn\"`, `\"max_tokens\"`, etc.). */\n stopReason: string;\n /** Token usage for the full agent loop. */\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/** A Promise with an `abort()` method for cancelling the stream. */\nexport type AbortablePromise<T> = Promise<T> & {\n /** Cancel the in-flight stream. The promise rejects with `AbortError`. */\n abort: () => void;\n};\n\n// ---------------------------------------------------------------------------\n// Client interface\n// ---------------------------------------------------------------------------\n\n/** Agent chat client returned by {@link createAgentChatClient}. */\nexport interface AgentChatClient {\n /** Create a new conversation thread. */\n createThread(): Promise<ThreadSummary>;\n\n /** List threads with cursor-based pagination (50 per page). */\n listThreads(cursor?: string): Promise<ThreadListPage>;\n\n /** Get a thread with its full message history. */\n getThread(threadId: string): Promise<Thread>;\n\n /** Update a thread's title. */\n updateThread(threadId: string, title: string): Promise<void>;\n\n /** Delete a thread (soft delete). */\n deleteThread(threadId: string): Promise<void>;\n\n /**\n * Claim an anonymous thread after in-app verification (progressive auth).\n * Login replaces the app's session token, which makes threads started\n * anonymously unreachable — claiming re-binds the thread to the signed-in\n * user so the conversation survives the login. Call it right after your\n * verification succeeds, for each anonymous thread you want to keep.\n *\n * Ownership is proven with the token the thread was created/used under:\n * the client remembers it automatically for threads touched in this page\n * session; pass `previousToken` explicitly for threads from an earlier\n * page load (e.g. a token you persisted before login).\n * Rejects with `already_identified` if the thread already has an owner.\n */\n claimThread(\n threadId: string,\n options?: { previousToken?: string },\n ): Promise<void>;\n\n /**\n * Register a handler for a client tool — a tool declared in `agent.md` with\n * `target: \"client\"`, whose effect lives in this browser (open a sheet, pick\n * a file, confirm an action). When the agent invokes it, the handler runs and\n * its return value goes back as the tool result, so the agent knows what\n * happened rather than assuming.\n *\n * The agent waits while your handler runs — long enough for a person to read\n * something and decide — so a handler may resolve from a dialog's Save button\n * as readily as it can return immediately. Throwing returns an error the\n * agent reasons about; a tool with no handler answers instantly with\n * `unhandled_client_tool` rather than leaving the agent hanging.\n *\n * One handler per tool name (a later registration replaces the earlier one);\n * returns an unregister function.\n *\n * @example\n * ```ts\n * chat.registerClientTool('pickFile', async ({ prompt }) => {\n * const file = await openFilePicker(prompt);\n * return file ? { path: file.path } : { cancelled: true };\n * });\n * ```\n */\n registerClientTool(\n name: string,\n handler: (input: unknown) => unknown | Promise<unknown>,\n ): () => void;\n\n /**\n * Send a message and stream the agent's response.\n *\n * Returns an {@link AbortablePromise} that resolves with\n * `{ stopReason, usage }` when the stream completes. Call `.abort()`\n * on the returned promise to cancel mid-stream.\n *\n * @param options.attachments - URLs to attach.\n * Images are sent as vision input.\n * Documents have text extracted into context.\n * Upload files first via {@link platform.upload} and pass the returned URLs.\n */\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult>;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nconst AGENT_BASE = '/_/agent';\n\n/**\n * Matches the platform's own cap on a client-tool result. Caught here so an\n * oversize return value reports itself instead of being rejected at the door.\n */\nconst CLIENT_TOOL_RESULT_MAX_CHARS = 32_000;\n\nasync function request<T>(\n path: string,\n method: string,\n body?: unknown,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(withBase(`${AGENT_BASE}${path}`), {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Agent chat request failed: ${res.status} ${res.statusText}`,\n 'agent_chat_error',\n );\n }\n\n return (await res.json()) as T;\n}\n\n/**\n * Answer one client-tool invocation. Fire-and-forget by design: the agent is\n * the one waiting, and a failure to deliver leaves it to its own timeout —\n * there is nothing useful for the page to do about it beyond saying so.\n */\nasync function postToolResult(\n threadId: string,\n body: { callId: string; result?: unknown; error?: string },\n): Promise<void> {\n const config = getConfig();\n const res = await fetch(\n withBase(`${AGENT_BASE}/threads/${threadId}/tool-results`),\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify(body),\n },\n );\n if (!res.ok) {\n console.warn(\n `[agent-chat] could not deliver client tool result: ${res.status} ${res.statusText}`,\n );\n }\n}\n\nfunction dispatchEvent(\n event: AgentChatEvent,\n callbacks: SendMessageCallbacks,\n): void {\n // Catch-all fires for every event\n callbacks.onEvent?.(event);\n\n // Named callbacks\n switch (event.type) {\n case 'text':\n callbacks.onText?.(event.text);\n break;\n case 'thinking':\n callbacks.onThinking?.(event.text);\n break;\n case 'thinking_complete':\n callbacks.onThinkingComplete?.(event.thinking, event.signature);\n break;\n case 'tool_call_start':\n callbacks.onToolCallStart?.(event.id, event.name);\n break;\n case 'tool_call_result':\n callbacks.onToolCallResult?.(event.id, event.output);\n break;\n case 'error':\n callbacks.onError?.(event.error);\n break;\n // Deliberately no named callback: whoever answers a client tool call also\n // runs it (see runClientTool), so dispatching here too would run the\n // action twice.\n case 'client_tool_call':\n break;\n // No named callback; `onEvent` above has already seen them.\n case 'tool_use':\n case 'tool_input_delta':\n case 'done':\n break;\n }\n}\n\ntype ClientToolHandler = (input: unknown) => unknown | Promise<unknown>;\n\n/**\n * Run one client-tool invocation and answer it. Every path answers — a missing\n * handler, a thrown handler and an oversize result included — because the agent\n * is holding its turn on this call and silence costs it the full timeout.\n */\nasync function runClientTool(\n threadId: string,\n event: AgentClientToolCallEvent,\n handler: ClientToolHandler | undefined,\n): Promise<void> {\n if (!handler) {\n console.warn(\n `[agent-chat] client tool \"${event.name}\" was invoked but no handler is registered — call chat.registerClientTool()`,\n );\n await postToolResult(threadId, {\n callId: event.id,\n error: 'unhandled_client_tool',\n });\n return;\n }\n\n try {\n const result = await handler(event.input);\n if (result !== undefined) {\n const serialized = JSON.stringify(result);\n if (serialized && serialized.length > CLIENT_TOOL_RESULT_MAX_CHARS) {\n await postToolResult(threadId, {\n callId: event.id,\n error: 'result_too_large',\n });\n return;\n }\n }\n await postToolResult(threadId, { callId: event.id, result });\n } catch (err) {\n await postToolResult(threadId, {\n callId: event.id,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an agent chat client for thread-based conversations.\n *\n * The client is stateless — each method is an independent HTTP request.\n * Your application layer owns thread state, message arrays, and UI updates.\n *\n * @returns An {@link AgentChatClient} with methods for thread CRUD and\n * message streaming.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * const thread = await chat.createThread();\n *\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n * ```\n */\nexport function createAgentChatClient(): AgentChatClient {\n // Mint-time bearer per thread, for claimThread's ownership proof — after an\n // in-app login the app's config token is REPLACED (a fresh interface\n // session), so the pre-login token must be remembered to claim with.\n const threadTokens = new Map<string, string>();\n const clientToolHandlers = new Map<string, ClientToolHandler>();\n const rememberToken = (threadId: string) => {\n const token = getConfig().token;\n if (token && !threadTokens.has(threadId)) {\n threadTokens.set(threadId, token);\n }\n };\n\n return {\n async createThread() {\n const token = getConfig().token;\n const thread = await request<ThreadSummary>('/threads', 'POST');\n if (token) {\n threadTokens.set(thread.id, token);\n }\n return thread;\n },\n\n listThreads(cursor?: string) {\n const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';\n return request<ThreadListPage>(`/threads${query}`, 'GET');\n },\n\n getThread(threadId: string) {\n return request<Thread>(`/threads/${threadId}`, 'GET');\n },\n\n async updateThread(threadId: string, title: string) {\n await request(`/threads/${threadId}`, 'PATCH', { title });\n },\n\n async deleteThread(threadId: string) {\n await request(`/threads/${threadId}`, 'DELETE');\n },\n\n async claimThread(threadId: string, options?: { previousToken?: string }) {\n const previousToken =\n options?.previousToken ?? threadTokens.get(threadId);\n if (!previousToken) {\n throw new MindStudioInterfaceError(\n 'No pre-login token known for this thread — pass options.previousToken.',\n 'missing_previous_token',\n 400,\n );\n }\n await request(`/threads/${threadId}/claim`, 'POST', { previousToken });\n threadTokens.delete(threadId);\n },\n\n registerClientTool(name: string, handler: ClientToolHandler) {\n clientToolHandlers.set(name, handler);\n return () => {\n if (clientToolHandlers.get(name) === handler) {\n clientToolHandlers.delete(name);\n }\n };\n },\n\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult> {\n rememberToken(threadId);\n const controller = new AbortController();\n const cb = callbacks ?? {};\n\n // Wire caller's signal to our internal controller\n if (cb.signal) {\n if (cb.signal.aborted) {\n controller.abort();\n } else {\n cb.signal.addEventListener('abort', () => controller.abort(), {\n once: true,\n });\n }\n }\n\n const promise = (async (): Promise<SendMessageResult> => {\n const config = getConfig();\n const url = withBase(`${AGENT_BASE}/threads/${threadId}/messages`);\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n content,\n ...(options?.attachments?.length && {\n attachments: options.attachments,\n }),\n }),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Send message failed: ${res.status} ${res.statusText}`,\n 'agent_chat_error',\n );\n }\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let result: SendMessageResult | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as AgentChatEvent;\n\n if (event.type === 'done') {\n result = {\n stopReason: event.stopReason,\n usage: event.usage,\n };\n }\n\n if (event.type === 'client_tool_call') {\n // Detached on purpose: the handler may take as long as the\n // agent's patience allows, and this loop has to keep draining\n // the stream (keepalives included) while it does.\n const handler =\n clientToolHandlers.get(event.name) ??\n (cb.onClientToolCall\n ? (input: unknown) =>\n cb.onClientToolCall!(event.name, input, event.id)\n : undefined);\n void runClientTool(threadId, event, handler);\n }\n\n dispatchEvent(event, cb);\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n if (!result) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return result;\n })();\n\n const abortable = promise as AbortablePromise<SendMessageResult>;\n abortable.abort = () => controller.abort();\n return abortable;\n },\n };\n}\n","/**\n * A large file's upload in parts, behind `platform.upload`.\n *\n * An upload token carries a grant beside its form. For a file of\n * `MULTIPART_THRESHOLD` or more, `platform.upload` presents it to the app's\n * `/_/uploads/start`, which answers with a session; each part then goes\n * straight to storage over a URL the session asks for (`/_/uploads/parts`), a\n * few at a time, each retried on its own, and `/_/uploads/complete` joins them.\n *\n * The session is kept in localStorage under the token's file URL and the\n * file's name, size and modified time, so calling `platform.upload` again\n * with a token for the same key and the same file (after a dropped\n * connection, an aborted upload or a reload) sends only the parts that\n * haven't arrived (`/_/uploads/status`).\n */\n\nimport { withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\n\n/** A file this size or larger goes up in parts, when its token has a grant. */\nexport const MULTIPART_THRESHOLD = 64 * 1024 * 1024;\n\n/** Parts in flight at once. */\nconst PART_CONCURRENCY = 4;\n\n/** Attempts per part, the first included. */\nconst PART_ATTEMPTS = 6;\n\n/** Part URLs asked for at a time. */\nconst PART_URL_BATCH = 50;\n\n/**\n * Attempts to join the parts while the app is briefly locked (its data\n * moving), a minute apart. Past them the upload throws, and calling\n * `platform.upload` again finishes it.\n */\nconst COMPLETE_ATTEMPTS = 5;\nconst COMPLETE_RETRY_MS = 60_000;\n\nconst RESUME_PREFIX = 'mindstudio:upload:';\n\ninterface Token {\n key: string;\n url: string;\n upload: { grant?: string };\n}\n\nconst sleep = (ms: number, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(new DOMException('Upload aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n\nasync function call<T>(\n op: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<T> {\n const res = await fetch(withBase(`/_/uploads/${op}`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal,\n });\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `File upload failed: ${op} ${res.status}`,\n 'upload_error',\n );\n }\n return (await res.json()) as T;\n}\n\n// localStorage can be missing or refuse (a sandboxed frame, a full quota);\n// without it an upload still works, it just can't resume after a reload.\nconst resume = {\n key: (token: Token, file: File) =>\n `${RESUME_PREFIX}${token.url}:${file.name}:${file.size}:${file.lastModified}`,\n read(key: string): string | null {\n try {\n return globalThis.localStorage?.getItem(key) ?? null;\n } catch {\n return null;\n }\n },\n write(key: string, session: string) {\n try {\n globalThis.localStorage?.setItem(key, session);\n } catch {\n // Not resumable after a reload; nothing else changes.\n }\n },\n forget(key: string) {\n try {\n globalThis.localStorage?.removeItem(key);\n } catch {\n // As above.\n }\n },\n};\n\n/** The session's own state that ends it: start again with a fresh one. */\nconst isRestart = (err: unknown) =>\n err instanceof MindStudioInterfaceError &&\n (err.code === 'upload_restart' || err.code === 'invalid_upload_token');\n\n/** One part, PUT with progress; resolves with the response status. */\nfunction putPart(\n url: string,\n bytes: Blob,\n onLoaded: (loaded: number) => void,\n signal?: AbortSignal,\n): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener('progress', (e) => onLoaded(e.loaded));\n xhr.addEventListener('load', () => resolve(xhr.status));\n xhr.addEventListener('error', () => resolve(0));\n xhr.addEventListener('abort', () =>\n reject(new DOMException('Upload aborted', 'AbortError')),\n );\n if (signal) {\n if (signal.aborted) {\n reject(new DOMException('Upload aborted', 'AbortError'));\n return;\n }\n signal.addEventListener('abort', () => xhr.abort(), { once: true });\n }\n xhr.open('PUT', url);\n xhr.send(bytes);\n });\n}\n\n/**\n * Upload `file` in parts with `token`'s grant, resuming a session this\n * browser kept for the same key and file.\n */\nexport async function uploadInParts(\n token: Token,\n file: File,\n options: { onProgress?: (fraction: number) => void; signal?: AbortSignal },\n): Promise<{ key: string; url: string }> {\n const { onProgress, signal } = options;\n const resumeKey = resume.key(token, file);\n\n // At most one fresh start after a session the platform no longer has.\n for (let restarts = 0; ; restarts += 1) {\n let session = resume.read(resumeKey);\n let partSize = 0;\n const arrived = new Map<number, number>();\n if (session) {\n try {\n const status = await call<{\n parts: { partNumber: number; size: number }[];\n partSize: number;\n }>('status', { session }, signal);\n partSize = status.partSize;\n for (const { partNumber, size } of status.parts) {\n arrived.set(partNumber, size);\n }\n } catch (err) {\n if (!isRestart(err)) {\n throw err;\n }\n resume.forget(resumeKey);\n session = null;\n }\n }\n if (!session) {\n if (!token.upload.grant) {\n throw new MindStudioInterfaceError(\n 'This upload token has no grant for a large file.',\n 'upload_error',\n );\n }\n const started = await call<{ session: string; partSize: number }>(\n 'start',\n { grant: token.upload.grant, size: file.size },\n signal,\n );\n session = started.session;\n partSize = started.partSize;\n resume.write(resumeKey, session);\n }\n\n try {\n await sendParts(session, file, partSize, arrived, onProgress, signal);\n await complete(session, signal);\n } catch (err) {\n if (isRestart(err) && restarts === 0) {\n resume.forget(resumeKey);\n continue;\n }\n throw err;\n }\n resume.forget(resumeKey);\n onProgress?.(1);\n return { key: token.key, url: token.url };\n }\n}\n\nasync function sendParts(\n session: string,\n file: File,\n partSize: number,\n arrived: Map<number, number>,\n onProgress: ((fraction: number) => void) | undefined,\n signal: AbortSignal | undefined,\n): Promise<void> {\n const partCount = Math.max(1, Math.ceil(file.size / partSize));\n const sizeOf = (n: number) =>\n n < partCount ? partSize : file.size - partSize * (partCount - 1);\n\n // A part that arrived whole is done; anything else goes (again).\n const todo: number[] = [];\n let doneBytes = 0;\n for (let n = 1; n <= partCount; n += 1) {\n if (arrived.get(n) === sizeOf(n)) {\n doneBytes += sizeOf(n);\n } else {\n todo.push(n);\n }\n }\n const loading = new Map<number, number>();\n const report = () => {\n if (onProgress && file.size > 0) {\n let loaded = doneBytes;\n for (const bytes of loading.values()) {\n loaded += bytes;\n }\n onProgress(Math.min(loaded / file.size, 0.999));\n }\n };\n report();\n\n const batches = new Map<number, Promise<Map<number, string>>>();\n const urlFor = (n: number, fresh: boolean): Promise<string> => {\n const batch = Math.floor((n - 1) / PART_URL_BATCH);\n if (fresh) {\n batches.delete(batch);\n }\n let urls = batches.get(batch);\n if (!urls) {\n const first = batch * PART_URL_BATCH + 1;\n const last = Math.min(partCount, first + PART_URL_BATCH - 1);\n urls = call<{ urls: { partNumber: number; url: string }[] }>(\n 'parts',\n {\n session,\n parts: Array.from({ length: last - first + 1 }, (_, i) => first + i),\n },\n signal,\n ).then(\n (res) =>\n new Map(res.urls.map(({ partNumber, url }) => [partNumber, url])),\n );\n // A batch that failed is asked again by the next part that needs it.\n urls.catch(() => batches.delete(batch));\n batches.set(batch, urls);\n }\n return urls.then((map) => map.get(n)!);\n };\n\n const sendOne = async (n: number) => {\n const start = (n - 1) * partSize;\n const bytes = file.slice(start, start + sizeOf(n));\n let status = -1;\n for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt += 1) {\n let url: string;\n try {\n url = await urlFor(n, status === 403);\n } catch (err) {\n if (\n isRestart(err) ||\n (err instanceof DOMException && err.name === 'AbortError')\n ) {\n throw err;\n }\n status = 0;\n await sleep(1000 * 2 ** (attempt - 1), signal);\n continue;\n }\n status = await putPart(\n url,\n bytes,\n (loaded) => {\n loading.set(n, loaded);\n report();\n },\n signal,\n );\n loading.delete(n);\n if (status >= 200 && status < 300) {\n doneBytes += bytes.size;\n report();\n return;\n }\n if (status >= 400 && status < 500 && status !== 403) {\n break;\n }\n if (attempt < PART_ATTEMPTS) {\n await sleep(1000 * 2 ** (attempt - 1), signal);\n }\n }\n throw new MindStudioInterfaceError(\n `File upload failed: part ${n} of ${partCount}${status > 0 ? ` (${status})` : ': network error'}`,\n 'upload_error',\n status > 0 ? status : undefined,\n );\n };\n\n const workers = Array.from(\n { length: Math.min(PART_CONCURRENCY, todo.length) },\n async () => {\n while (todo.length > 0) {\n await sendOne(todo.shift()!);\n }\n },\n );\n await Promise.all(workers);\n}\n\nasync function complete(session: string, signal?: AbortSignal): Promise<void> {\n for (let attempt = 1; ; attempt += 1) {\n try {\n await call('complete', { session }, signal);\n return;\n } catch (err) {\n const moving =\n err instanceof MindStudioInterfaceError &&\n err.status === 503 &&\n attempt < COMPLETE_ATTEMPTS;\n if (!moving) {\n throw err;\n }\n await sleep(COMPLETE_RETRY_MS, signal);\n }\n }\n}\n","/**\n * Platform actions — client-direct file uploads.\n *\n * The app's backend mints an upload token (`Store.createUploadToken` in\n * `@mindstudio-ai/agent`); the browser then uploads the bytes straight to\n * storage with {@link platform.upload}. The file lands in the app's own file\n * store (private by default, served on the app's domain).\n *\n * @example\n * ```ts\n * import { createClient, platform } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n * const file = document.querySelector('input[type=file]').files[0];\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { key, url } = await platform.upload(token, file, {\n * onProgress: (fraction) => console.log(`${Math.round(fraction * 100)}%`),\n * });\n * ```\n */\n\nimport { getBasePath, getConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\nimport { MULTIPART_THRESHOLD, uploadInParts } from './multipart-upload.js';\n\n/**\n * Options for `platform.uploadFile()`.\n */\nexport interface UploadFileOptions {\n /** Called with upload progress as a fraction from 0 to 1. */\n onProgress?: (fraction: number) => void;\n\n /** AbortSignal to cancel the upload. */\n signal?: AbortSignal;\n}\n\n/**\n * An upload token minted by an app's backend (`Store.createUploadToken` in\n * `@mindstudio-ai/agent`) and passed to {@link platform.upload}. Opaque — carry\n * it from the backend response straight into `platform.upload(token, file)`.\n */\nexport interface UploadToken {\n /** The object key the upload lands at. */\n key: string;\n /** The URL the file will be readable at once uploaded. */\n url: string;\n /**\n * The scoped presigned POST the browser submits to, and the grant a file\n * too large for one request goes up in parts with.\n */\n upload: { url: string; fields: Record<string, string>; grant?: string };\n}\n\n/**\n * The platform namespace — file upload actions + serving context.\n */\nexport const platform = {\n /**\n * The mount prefix this app is served under on the current host, or ''\n * when serving at the root of its own subdomain/custom host. Use it as the\n * router basename so routes resolve wherever the app is mounted:\n *\n * ```ts\n * createBrowserRouter(routes, { basename: platform.basePath || '/' });\n * ```\n *\n * The SDK already prefixes all its own platform calls with it.\n */\n get basePath(): string {\n return getBasePath();\n },\n\n /**\n * Upload a file to the MindStudio CDN.\n *\n * Requests a presigned upload URL from the platform, then uploads\n * the file directly to S3. Returns the public CDN URL.\n *\n * @deprecated Use {@link platform.upload} with a backend-minted upload token\n * (`Store.createUploadToken`) instead. That uploads into the app's own file\n * store (private by default, on the app's domain); this legacy path uploads\n * to the shared, world-readable account media CDN.\n *\n * @param file - The File to upload\n * @param options - Optional progress callback and abort signal\n * @returns CDN URL of the uploaded file\n *\n * @example\n * ```ts\n * const url = await platform.uploadFile(file);\n * ```\n */\n async uploadFile(file: File, options?: UploadFileOptions): Promise<string> {\n const config = getConfig();\n const { onProgress, signal } = options ?? {};\n\n // Check if already aborted\n signal?.throwIfAborted();\n\n // Step 1: Get presigned upload URL\n const presignUrl = withBase('/_/generate-upload-request');\n const presignRes = await fetch(presignUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n filename: file.name,\n contentType: file.type || 'application/octet-stream',\n }),\n signal,\n });\n\n if (!presignRes.ok) {\n throw new MindStudioInterfaceError(\n `Upload presign failed: ${presignRes.status} ${presignRes.statusText}`,\n 'upload_error',\n presignRes.status,\n );\n }\n\n const { uploadUrl, uploadFields, publicUrl } =\n (await presignRes.json()) as {\n uploadUrl: string;\n uploadFields: Record<string, string>;\n publicUrl: string;\n };\n\n // Step 2: Upload directly to S3\n const formData = new FormData();\n for (const [key, value] of Object.entries(uploadFields)) {\n formData.append(key, value);\n }\n formData.append('file', file); // must be last\n\n // Use XHR for upload progress (fetch doesn't support it)\n if (onProgress) {\n await xhrUpload(uploadUrl, formData, onProgress, signal);\n return publicUrl;\n }\n\n const uploadRes = await fetch(uploadUrl, {\n method: 'POST',\n body: formData,\n signal,\n });\n\n if (!uploadRes.ok) {\n throw new MindStudioInterfaceError(\n `File upload failed: ${uploadRes.status} ${uploadRes.statusText}`,\n 'upload_error',\n uploadRes.status,\n );\n }\n\n return publicUrl;\n },\n\n /**\n * Upload a file with an {@link UploadToken} minted by the app's backend\n * (`Store.createUploadToken`). The file is POSTed **directly to storage** —\n * the bytes never pass through the platform — and the returned `{ key, url }`\n * is ready to render (`url` is a stable on-domain link the app can use in\n * `<img>` / `fetch` / `<a download>`).\n *\n * A large file (64 MB or more) goes up in parts, each retried on its own,\n * and resumes: calling `platform.upload` again with a token for the same\n * key and the same file, after a dropped connection, an aborted upload or a\n * reload, sends only what hadn't arrived. To resume across a reload, mint\n * the token for a key of your own (`createUploadToken({ key })`).\n *\n * @example\n * ```ts\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { url } = await platform.upload(token, file, {\n * onProgress: (f) => setProgress(f),\n * });\n * ```\n */\n async upload(\n token: UploadToken,\n file: File,\n options?: UploadFileOptions,\n ): Promise<{ key: string; url: string }> {\n const { onProgress, signal } = options ?? {};\n signal?.throwIfAborted();\n\n if (token.upload.grant && file.size >= MULTIPART_THRESHOLD) {\n return uploadInParts(token, file, { onProgress, signal });\n }\n\n const formData = new FormData();\n for (const [key, value] of Object.entries(token.upload.fields)) {\n formData.append(key, value);\n }\n formData.append('file', file); // must be last\n\n if (onProgress) {\n await xhrUpload(token.upload.url, formData, onProgress, signal);\n } else {\n const res = await fetch(token.upload.url, {\n method: 'POST',\n body: formData,\n signal,\n });\n if (!res.ok) {\n throw new MindStudioInterfaceError(\n `File upload failed: ${res.status} ${res.statusText}`,\n 'upload_error',\n res.status,\n );\n }\n }\n\n return { key: token.key, url: token.url };\n },\n};\n\n/**\n * Upload via XMLHttpRequest to get progress events.\n * Falls back from fetch because fetch upload streams don't support progress.\n */\nfunction xhrUpload(\n url: string,\n formData: FormData,\n onProgress: (fraction: number) => void,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress(e.loaded / e.total);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress(1);\n resolve();\n } else {\n reject(\n new MindStudioInterfaceError(\n `File upload failed: ${xhr.status} ${xhr.statusText}`,\n 'upload_error',\n xhr.status,\n ),\n );\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(\n new MindStudioInterfaceError(\n 'File upload failed: network error',\n 'upload_error',\n ),\n );\n });\n\n xhr.addEventListener('abort', () => {\n reject(new DOMException('Upload aborted', 'AbortError'));\n });\n\n // Wire up abort signal\n if (signal) {\n if (signal.aborted) {\n reject(new DOMException('Upload aborted', 'AbortError'));\n return;\n }\n signal.addEventListener('abort', () => xhr.abort(), { once: true });\n }\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n}\n","/**\n * Phone number utilities for auth flows.\n *\n * Provides country data, E.164 conversion, formatting, and validation.\n * No external dependencies — designed for login/signup UIs that need\n * a country code picker and phone number formatting.\n */\n\n// ---------------------------------------------------------------------------\n// Country data\n// ---------------------------------------------------------------------------\n\n/** A country with its dial code and flag emoji. */\nexport interface Country {\n /** ISO 3166-1 alpha-2 code (e.g. \"US\"). */\n code: string;\n /** International dial code with + prefix (e.g. \"+1\"). */\n dialCode: string;\n /** Country name in English. */\n name: string;\n /** Flag emoji. */\n flag: string;\n}\n\n/** All countries with dial codes, sorted alphabetically by name. */\nexport const countries: readonly Country[] = [\n { code: 'AF', dialCode: '+93', name: 'Afghanistan', flag: '🇦🇫' },\n { code: 'AL', dialCode: '+355', name: 'Albania', flag: '🇦🇱' },\n { code: 'DZ', dialCode: '+213', name: 'Algeria', flag: '🇩🇿' },\n { code: 'AD', dialCode: '+376', name: 'Andorra', flag: '🇦🇩' },\n { code: 'AO', dialCode: '+244', name: 'Angola', flag: '🇦🇴' },\n { code: 'AG', dialCode: '+1', name: 'Antigua and Barbuda', flag: '🇦🇬' },\n { code: 'AR', dialCode: '+54', name: 'Argentina', flag: '🇦🇷' },\n { code: 'AM', dialCode: '+374', name: 'Armenia', flag: '🇦🇲' },\n { code: 'AU', dialCode: '+61', name: 'Australia', flag: '🇦🇺' },\n { code: 'AT', dialCode: '+43', name: 'Austria', flag: '🇦🇹' },\n { code: 'AZ', dialCode: '+994', name: 'Azerbaijan', flag: '🇦🇿' },\n { code: 'BS', dialCode: '+1', name: 'Bahamas', flag: '🇧🇸' },\n { code: 'BH', dialCode: '+973', name: 'Bahrain', flag: '🇧🇭' },\n { code: 'BD', dialCode: '+880', name: 'Bangladesh', flag: '🇧🇩' },\n { code: 'BB', dialCode: '+1', name: 'Barbados', flag: '🇧🇧' },\n { code: 'BY', dialCode: '+375', name: 'Belarus', flag: '🇧🇾' },\n { code: 'BE', dialCode: '+32', name: 'Belgium', flag: '🇧🇪' },\n { code: 'BZ', dialCode: '+501', name: 'Belize', flag: '🇧🇿' },\n { code: 'BJ', dialCode: '+229', name: 'Benin', flag: '🇧🇯' },\n { code: 'BT', dialCode: '+975', name: 'Bhutan', flag: '🇧🇹' },\n { code: 'BO', dialCode: '+591', name: 'Bolivia', flag: '🇧🇴' },\n { code: 'BA', dialCode: '+387', name: 'Bosnia and Herzegovina', flag: '🇧🇦' },\n { code: 'BW', dialCode: '+267', name: 'Botswana', flag: '🇧🇼' },\n { code: 'BR', dialCode: '+55', name: 'Brazil', flag: '🇧🇷' },\n { code: 'BN', dialCode: '+673', name: 'Brunei', flag: '🇧🇳' },\n { code: 'BG', dialCode: '+359', name: 'Bulgaria', flag: '🇧🇬' },\n { code: 'BF', dialCode: '+226', name: 'Burkina Faso', flag: '🇧🇫' },\n { code: 'BI', dialCode: '+257', name: 'Burundi', flag: '🇧🇮' },\n { code: 'CV', dialCode: '+238', name: 'Cabo Verde', flag: '🇨🇻' },\n { code: 'KH', dialCode: '+855', name: 'Cambodia', flag: '🇰🇭' },\n { code: 'CM', dialCode: '+237', name: 'Cameroon', flag: '🇨🇲' },\n { code: 'CA', dialCode: '+1', name: 'Canada', flag: '🇨🇦' },\n {\n code: 'CF',\n dialCode: '+236',\n name: 'Central African Republic',\n flag: '🇨🇫',\n },\n { code: 'TD', dialCode: '+235', name: 'Chad', flag: '🇹🇩' },\n { code: 'CL', dialCode: '+56', name: 'Chile', flag: '🇨🇱' },\n { code: 'CN', dialCode: '+86', name: 'China', flag: '🇨🇳' },\n { code: 'CO', dialCode: '+57', name: 'Colombia', flag: '🇨🇴' },\n { code: 'KM', dialCode: '+269', name: 'Comoros', flag: '🇰🇲' },\n { code: 'CG', dialCode: '+242', name: 'Congo', flag: '🇨🇬' },\n { code: 'CD', dialCode: '+243', name: 'Congo (DRC)', flag: '🇨🇩' },\n { code: 'CR', dialCode: '+506', name: 'Costa Rica', flag: '🇨🇷' },\n { code: 'HR', dialCode: '+385', name: 'Croatia', flag: '🇭🇷' },\n { code: 'CU', dialCode: '+53', name: 'Cuba', flag: '🇨🇺' },\n { code: 'CY', dialCode: '+357', name: 'Cyprus', flag: '🇨🇾' },\n { code: 'CZ', dialCode: '+420', name: 'Czechia', flag: '🇨🇿' },\n { code: 'CI', dialCode: '+225', name: \"Cote d'Ivoire\", flag: '🇨🇮' },\n { code: 'DK', dialCode: '+45', name: 'Denmark', flag: '🇩🇰' },\n { code: 'DJ', dialCode: '+253', name: 'Djibouti', flag: '🇩🇯' },\n { code: 'DM', dialCode: '+1', name: 'Dominica', flag: '🇩🇲' },\n { code: 'DO', dialCode: '+1', name: 'Dominican Republic', flag: '🇩🇴' },\n { code: 'EC', dialCode: '+593', name: 'Ecuador', flag: '🇪🇨' },\n { code: 'EG', dialCode: '+20', name: 'Egypt', flag: '🇪🇬' },\n { code: 'SV', dialCode: '+503', name: 'El Salvador', flag: '🇸🇻' },\n { code: 'GQ', dialCode: '+240', name: 'Equatorial Guinea', flag: '🇬🇶' },\n { code: 'ER', dialCode: '+291', name: 'Eritrea', flag: '🇪🇷' },\n { code: 'EE', dialCode: '+372', name: 'Estonia', flag: '🇪🇪' },\n { code: 'SZ', dialCode: '+268', name: 'Eswatini', flag: '🇸🇿' },\n { code: 'ET', dialCode: '+251', name: 'Ethiopia', flag: '🇪🇹' },\n { code: 'FJ', dialCode: '+679', name: 'Fiji', flag: '🇫🇯' },\n { code: 'FI', dialCode: '+358', name: 'Finland', flag: '🇫🇮' },\n { code: 'FR', dialCode: '+33', name: 'France', flag: '🇫🇷' },\n { code: 'GA', dialCode: '+241', name: 'Gabon', flag: '🇬🇦' },\n { code: 'GM', dialCode: '+220', name: 'Gambia', flag: '🇬🇲' },\n { code: 'GE', dialCode: '+995', name: 'Georgia', flag: '🇬🇪' },\n { code: 'DE', dialCode: '+49', name: 'Germany', flag: '🇩🇪' },\n { code: 'GH', dialCode: '+233', name: 'Ghana', flag: '🇬🇭' },\n { code: 'GR', dialCode: '+30', name: 'Greece', flag: '🇬🇷' },\n { code: 'GD', dialCode: '+1', name: 'Grenada', flag: '🇬🇩' },\n { code: 'GT', dialCode: '+502', name: 'Guatemala', flag: '🇬🇹' },\n { code: 'GN', dialCode: '+224', name: 'Guinea', flag: '🇬🇳' },\n { code: 'GW', dialCode: '+245', name: 'Guinea-Bissau', flag: '🇬🇼' },\n { code: 'GY', dialCode: '+592', name: 'Guyana', flag: '🇬🇾' },\n { code: 'HT', dialCode: '+509', name: 'Haiti', flag: '🇭🇹' },\n { code: 'HN', dialCode: '+504', name: 'Honduras', flag: '🇭🇳' },\n { code: 'HK', dialCode: '+852', name: 'Hong Kong', flag: '🇭🇰' },\n { code: 'HU', dialCode: '+36', name: 'Hungary', flag: '🇭🇺' },\n { code: 'IS', dialCode: '+354', name: 'Iceland', flag: '🇮🇸' },\n { code: 'IN', dialCode: '+91', name: 'India', flag: '🇮🇳' },\n { code: 'ID', dialCode: '+62', name: 'Indonesia', flag: '🇮🇩' },\n { code: 'IR', dialCode: '+98', name: 'Iran', flag: '🇮🇷' },\n { code: 'IQ', dialCode: '+964', name: 'Iraq', flag: '🇮🇶' },\n { code: 'IE', dialCode: '+353', name: 'Ireland', flag: '🇮🇪' },\n { code: 'IL', dialCode: '+972', name: 'Israel', flag: '🇮🇱' },\n { code: 'IT', dialCode: '+39', name: 'Italy', flag: '🇮🇹' },\n { code: 'JM', dialCode: '+1', name: 'Jamaica', flag: '🇯🇲' },\n { code: 'JP', dialCode: '+81', name: 'Japan', flag: '🇯🇵' },\n { code: 'JO', dialCode: '+962', name: 'Jordan', flag: '🇯🇴' },\n { code: 'KZ', dialCode: '+7', name: 'Kazakhstan', flag: '🇰🇿' },\n { code: 'KE', dialCode: '+254', name: 'Kenya', flag: '🇰🇪' },\n { code: 'KI', dialCode: '+686', name: 'Kiribati', flag: '🇰🇮' },\n { code: 'KW', dialCode: '+965', name: 'Kuwait', flag: '🇰🇼' },\n { code: 'KG', dialCode: '+996', name: 'Kyrgyzstan', flag: '🇰🇬' },\n { code: 'LA', dialCode: '+856', name: 'Laos', flag: '🇱🇦' },\n { code: 'LV', dialCode: '+371', name: 'Latvia', flag: '🇱🇻' },\n { code: 'LB', dialCode: '+961', name: 'Lebanon', flag: '🇱🇧' },\n { code: 'LS', dialCode: '+266', name: 'Lesotho', flag: '🇱🇸' },\n { code: 'LR', dialCode: '+231', name: 'Liberia', flag: '🇱🇷' },\n { code: 'LY', dialCode: '+218', name: 'Libya', flag: '🇱🇾' },\n { code: 'LI', dialCode: '+423', name: 'Liechtenstein', flag: '🇱🇮' },\n { code: 'LT', dialCode: '+370', name: 'Lithuania', flag: '🇱🇹' },\n { code: 'LU', dialCode: '+352', name: 'Luxembourg', flag: '🇱🇺' },\n { code: 'MO', dialCode: '+853', name: 'Macao', flag: '🇲🇴' },\n { code: 'MG', dialCode: '+261', name: 'Madagascar', flag: '🇲🇬' },\n { code: 'MW', dialCode: '+265', name: 'Malawi', flag: '🇲🇼' },\n { code: 'MY', dialCode: '+60', name: 'Malaysia', flag: '🇲🇾' },\n { code: 'MV', dialCode: '+960', name: 'Maldives', flag: '🇲🇻' },\n { code: 'ML', dialCode: '+223', name: 'Mali', flag: '🇲🇱' },\n { code: 'MT', dialCode: '+356', name: 'Malta', flag: '🇲🇹' },\n { code: 'MH', dialCode: '+692', name: 'Marshall Islands', flag: '🇲🇭' },\n { code: 'MR', dialCode: '+222', name: 'Mauritania', flag: '🇲🇷' },\n { code: 'MU', dialCode: '+230', name: 'Mauritius', flag: '🇲🇺' },\n { code: 'MX', dialCode: '+52', name: 'Mexico', flag: '🇲🇽' },\n { code: 'FM', dialCode: '+691', name: 'Micronesia', flag: '🇫🇲' },\n { code: 'MD', dialCode: '+373', name: 'Moldova', flag: '🇲🇩' },\n { code: 'MC', dialCode: '+377', name: 'Monaco', flag: '🇲🇨' },\n { code: 'MN', dialCode: '+976', name: 'Mongolia', flag: '🇲🇳' },\n { code: 'ME', dialCode: '+382', name: 'Montenegro', flag: '🇲🇪' },\n { code: 'MA', dialCode: '+212', name: 'Morocco', flag: '🇲🇦' },\n { code: 'MZ', dialCode: '+258', name: 'Mozambique', flag: '🇲🇿' },\n { code: 'MM', dialCode: '+95', name: 'Myanmar', flag: '🇲🇲' },\n { code: 'NA', dialCode: '+264', name: 'Namibia', flag: '🇳🇦' },\n { code: 'NR', dialCode: '+674', name: 'Nauru', flag: '🇳🇷' },\n { code: 'NP', dialCode: '+977', name: 'Nepal', flag: '🇳🇵' },\n { code: 'NL', dialCode: '+31', name: 'Netherlands', flag: '🇳🇱' },\n { code: 'NZ', dialCode: '+64', name: 'New Zealand', flag: '🇳🇿' },\n { code: 'NI', dialCode: '+505', name: 'Nicaragua', flag: '🇳🇮' },\n { code: 'NE', dialCode: '+227', name: 'Niger', flag: '🇳🇪' },\n { code: 'NG', dialCode: '+234', name: 'Nigeria', flag: '🇳🇬' },\n { code: 'KP', dialCode: '+850', name: 'North Korea', flag: '🇰🇵' },\n { code: 'MK', dialCode: '+389', name: 'North Macedonia', flag: '🇲🇰' },\n { code: 'NO', dialCode: '+47', name: 'Norway', flag: '🇳🇴' },\n { code: 'OM', dialCode: '+968', name: 'Oman', flag: '🇴🇲' },\n { code: 'PK', dialCode: '+92', name: 'Pakistan', flag: '🇵🇰' },\n { code: 'PW', dialCode: '+680', name: 'Palau', flag: '🇵🇼' },\n { code: 'PS', dialCode: '+970', name: 'Palestine', flag: '🇵🇸' },\n { code: 'PA', dialCode: '+507', name: 'Panama', flag: '🇵🇦' },\n { code: 'PG', dialCode: '+675', name: 'Papua New Guinea', flag: '🇵🇬' },\n { code: 'PY', dialCode: '+595', name: 'Paraguay', flag: '🇵🇾' },\n { code: 'PE', dialCode: '+51', name: 'Peru', flag: '🇵🇪' },\n { code: 'PH', dialCode: '+63', name: 'Philippines', flag: '🇵🇭' },\n { code: 'PL', dialCode: '+48', name: 'Poland', flag: '🇵🇱' },\n { code: 'PT', dialCode: '+351', name: 'Portugal', flag: '🇵🇹' },\n { code: 'PR', dialCode: '+1', name: 'Puerto Rico', flag: '🇵🇷' },\n { code: 'QA', dialCode: '+974', name: 'Qatar', flag: '🇶🇦' },\n { code: 'RO', dialCode: '+40', name: 'Romania', flag: '🇷🇴' },\n { code: 'RU', dialCode: '+7', name: 'Russia', flag: '🇷🇺' },\n { code: 'RW', dialCode: '+250', name: 'Rwanda', flag: '🇷🇼' },\n { code: 'KN', dialCode: '+1', name: 'Saint Kitts and Nevis', flag: '🇰🇳' },\n { code: 'LC', dialCode: '+1', name: 'Saint Lucia', flag: '🇱🇨' },\n {\n code: 'VC',\n dialCode: '+1',\n name: 'Saint Vincent and the Grenadines',\n flag: '🇻🇨',\n },\n { code: 'WS', dialCode: '+685', name: 'Samoa', flag: '🇼🇸' },\n { code: 'SM', dialCode: '+378', name: 'San Marino', flag: '🇸🇲' },\n { code: 'ST', dialCode: '+239', name: 'Sao Tome and Principe', flag: '🇸🇹' },\n { code: 'SA', dialCode: '+966', name: 'Saudi Arabia', flag: '🇸🇦' },\n { code: 'SN', dialCode: '+221', name: 'Senegal', flag: '🇸🇳' },\n { code: 'RS', dialCode: '+381', name: 'Serbia', flag: '🇷🇸' },\n { code: 'SC', dialCode: '+248', name: 'Seychelles', flag: '🇸🇨' },\n { code: 'SL', dialCode: '+232', name: 'Sierra Leone', flag: '🇸🇱' },\n { code: 'SG', dialCode: '+65', name: 'Singapore', flag: '🇸🇬' },\n { code: 'SK', dialCode: '+421', name: 'Slovakia', flag: '🇸🇰' },\n { code: 'SI', dialCode: '+386', name: 'Slovenia', flag: '🇸🇮' },\n { code: 'SB', dialCode: '+677', name: 'Solomon Islands', flag: '🇸🇧' },\n { code: 'SO', dialCode: '+252', name: 'Somalia', flag: '🇸🇴' },\n { code: 'ZA', dialCode: '+27', name: 'South Africa', flag: '🇿🇦' },\n { code: 'KR', dialCode: '+82', name: 'South Korea', flag: '🇰🇷' },\n { code: 'SS', dialCode: '+211', name: 'South Sudan', flag: '🇸🇸' },\n { code: 'ES', dialCode: '+34', name: 'Spain', flag: '🇪🇸' },\n { code: 'LK', dialCode: '+94', name: 'Sri Lanka', flag: '🇱🇰' },\n { code: 'SD', dialCode: '+249', name: 'Sudan', flag: '🇸🇩' },\n { code: 'SR', dialCode: '+597', name: 'Suriname', flag: '🇸🇷' },\n { code: 'SE', dialCode: '+46', name: 'Sweden', flag: '🇸🇪' },\n { code: 'CH', dialCode: '+41', name: 'Switzerland', flag: '🇨🇭' },\n { code: 'SY', dialCode: '+963', name: 'Syria', flag: '🇸🇾' },\n { code: 'TW', dialCode: '+886', name: 'Taiwan', flag: '🇹🇼' },\n { code: 'TJ', dialCode: '+992', name: 'Tajikistan', flag: '🇹🇯' },\n { code: 'TZ', dialCode: '+255', name: 'Tanzania', flag: '🇹🇿' },\n { code: 'TH', dialCode: '+66', name: 'Thailand', flag: '🇹🇭' },\n { code: 'TL', dialCode: '+670', name: 'Timor-Leste', flag: '🇹🇱' },\n { code: 'TG', dialCode: '+228', name: 'Togo', flag: '🇹🇬' },\n { code: 'TO', dialCode: '+676', name: 'Tonga', flag: '🇹🇴' },\n { code: 'TT', dialCode: '+1', name: 'Trinidad and Tobago', flag: '🇹🇹' },\n { code: 'TN', dialCode: '+216', name: 'Tunisia', flag: '🇹🇳' },\n { code: 'TR', dialCode: '+90', name: 'Turkey', flag: '🇹🇷' },\n { code: 'TM', dialCode: '+993', name: 'Turkmenistan', flag: '🇹🇲' },\n { code: 'TV', dialCode: '+688', name: 'Tuvalu', flag: '🇹🇻' },\n { code: 'UG', dialCode: '+256', name: 'Uganda', flag: '🇺🇬' },\n { code: 'UA', dialCode: '+380', name: 'Ukraine', flag: '🇺🇦' },\n { code: 'AE', dialCode: '+971', name: 'United Arab Emirates', flag: '🇦🇪' },\n { code: 'GB', dialCode: '+44', name: 'United Kingdom', flag: '🇬🇧' },\n { code: 'US', dialCode: '+1', name: 'United States', flag: '🇺🇸' },\n { code: 'UY', dialCode: '+598', name: 'Uruguay', flag: '🇺🇾' },\n { code: 'UZ', dialCode: '+998', name: 'Uzbekistan', flag: '🇺🇿' },\n { code: 'VU', dialCode: '+678', name: 'Vanuatu', flag: '🇻🇺' },\n { code: 'VA', dialCode: '+39', name: 'Vatican City', flag: '🇻🇦' },\n { code: 'VE', dialCode: '+58', name: 'Venezuela', flag: '🇻🇪' },\n { code: 'VN', dialCode: '+84', name: 'Vietnam', flag: '🇻🇳' },\n { code: 'YE', dialCode: '+967', name: 'Yemen', flag: '🇾🇪' },\n { code: 'ZM', dialCode: '+260', name: 'Zambia', flag: '🇿🇲' },\n { code: 'ZW', dialCode: '+263', name: 'Zimbabwe', flag: '🇿🇼' },\n];\n\n// ---------------------------------------------------------------------------\n// Country lookup helpers\n// ---------------------------------------------------------------------------\n\nconst byCode = new Map(countries.map((c) => [c.code, c]));\n\n/** Common IANA timezone → country code mapping. */\nconst timezoneToCountry: Record<string, string> = {\n 'America/New_York': 'US',\n 'America/Chicago': 'US',\n 'America/Denver': 'US',\n 'America/Los_Angeles': 'US',\n 'America/Anchorage': 'US',\n 'Pacific/Honolulu': 'US',\n 'America/Phoenix': 'US',\n 'America/Indiana/Indianapolis': 'US',\n 'America/Toronto': 'CA',\n 'America/Vancouver': 'CA',\n 'America/Edmonton': 'CA',\n 'America/Winnipeg': 'CA',\n 'America/Halifax': 'CA',\n 'America/St_Johns': 'CA',\n 'Europe/London': 'GB',\n 'Europe/Dublin': 'IE',\n 'Europe/Paris': 'FR',\n 'Europe/Berlin': 'DE',\n 'Europe/Madrid': 'ES',\n 'Europe/Rome': 'IT',\n 'Europe/Amsterdam': 'NL',\n 'Europe/Brussels': 'BE',\n 'Europe/Zurich': 'CH',\n 'Europe/Vienna': 'AT',\n 'Europe/Stockholm': 'SE',\n 'Europe/Oslo': 'NO',\n 'Europe/Copenhagen': 'DK',\n 'Europe/Helsinki': 'FI',\n 'Europe/Warsaw': 'PL',\n 'Europe/Prague': 'CZ',\n 'Europe/Bucharest': 'RO',\n 'Europe/Budapest': 'HU',\n 'Europe/Athens': 'GR',\n 'Europe/Istanbul': 'TR',\n 'Europe/Moscow': 'RU',\n 'Europe/Kiev': 'UA',\n 'Europe/Lisbon': 'PT',\n 'Europe/Belgrade': 'RS',\n 'Europe/Zagreb': 'HR',\n 'Europe/Bratislava': 'SK',\n 'Europe/Ljubljana': 'SI',\n 'Europe/Tallinn': 'EE',\n 'Europe/Riga': 'LV',\n 'Europe/Vilnius': 'LT',\n 'Asia/Tokyo': 'JP',\n 'Asia/Seoul': 'KR',\n 'Asia/Shanghai': 'CN',\n 'Asia/Hong_Kong': 'HK',\n 'Asia/Taipei': 'TW',\n 'Asia/Singapore': 'SG',\n 'Asia/Kolkata': 'IN',\n 'Asia/Karachi': 'PK',\n 'Asia/Dhaka': 'BD',\n 'Asia/Bangkok': 'TH',\n 'Asia/Jakarta': 'ID',\n 'Asia/Manila': 'PH',\n 'Asia/Kuala_Lumpur': 'MY',\n 'Asia/Ho_Chi_Minh': 'VN',\n 'Asia/Riyadh': 'SA',\n 'Asia/Dubai': 'AE',\n 'Asia/Tehran': 'IR',\n 'Asia/Baghdad': 'IQ',\n 'Asia/Beirut': 'LB',\n 'Asia/Jerusalem': 'IL',\n 'Asia/Amman': 'JO',\n 'Asia/Almaty': 'KZ',\n 'Asia/Tashkent': 'UZ',\n 'Asia/Colombo': 'LK',\n 'Asia/Kathmandu': 'NP',\n 'Asia/Yangon': 'MM',\n 'Australia/Sydney': 'AU',\n 'Australia/Melbourne': 'AU',\n 'Australia/Brisbane': 'AU',\n 'Australia/Perth': 'AU',\n 'Australia/Adelaide': 'AU',\n 'Pacific/Auckland': 'NZ',\n 'Pacific/Fiji': 'FJ',\n 'Africa/Cairo': 'EG',\n 'Africa/Lagos': 'NG',\n 'Africa/Nairobi': 'KE',\n 'Africa/Johannesburg': 'ZA',\n 'Africa/Casablanca': 'MA',\n 'Africa/Accra': 'GH',\n 'Africa/Addis_Ababa': 'ET',\n 'Africa/Dar_es_Salaam': 'TZ',\n 'Africa/Kampala': 'UG',\n 'America/Mexico_City': 'MX',\n 'America/Bogota': 'CO',\n 'America/Lima': 'PE',\n 'America/Santiago': 'CL',\n 'America/Buenos_Aires': 'AR',\n 'America/Sao_Paulo': 'BR',\n 'America/Caracas': 'VE',\n 'America/Guatemala': 'GT',\n 'America/Havana': 'CU',\n 'America/Panama': 'PA',\n 'America/Jamaica': 'JM',\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Detect the user's country from their timezone.\n * Falls back to `'US'` if the timezone can't be mapped.\n */\nexport function detectCountry(): string {\n try {\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n return timezoneToCountry[tz] ?? 'US';\n } catch {\n return 'US';\n }\n}\n\n/**\n * Format an E.164 phone number for display.\n *\n * US/CA numbers: `+1 (555) 123-4567`\n * Others: `+{dialCode} {rest}`\n */\nexport function format(e164: string): string {\n if (!e164.startsWith('+')) {\n return e164;\n }\n\n const digits = e164.slice(1);\n\n // US/CA: +1 followed by 10 digits\n if (digits.startsWith('1') && digits.length === 11) {\n const area = digits.slice(1, 4);\n const prefix = digits.slice(4, 7);\n const line = digits.slice(7);\n return `+1 (${area}) ${prefix}-${line}`;\n }\n\n // Find matching dial code (try longest match first)\n for (let len = 4; len >= 1; len--) {\n const candidate = '+' + digits.slice(0, len);\n const match = countries.find((c) => c.dialCode === candidate);\n if (match) {\n const rest = digits.slice(len);\n return `${match.dialCode} ${rest}`;\n }\n }\n\n return e164;\n}\n\n/**\n * Convert a national phone number to E.164 format.\n *\n * @param national - Phone number with or without formatting (e.g. \"(555) 123-4567\")\n * @param countryCode - ISO 3166-1 alpha-2 code (e.g. \"US\")\n * @returns E.164 formatted number (e.g. \"+15551234567\")\n */\nexport function toE164(national: string, countryCode: string): string {\n const digits = national.replace(/\\D/g, '');\n const country = byCode.get(countryCode);\n if (!country) {\n return '+' + digits;\n }\n const dialDigits = country.dialCode.slice(1); // remove +\n // If the number already starts with the dial code, don't double-add\n if (digits.startsWith(dialDigits)) {\n return '+' + digits;\n }\n return country.dialCode + digits;\n}\n\n/**\n * Check if a string is a valid E.164 phone number.\n * Must start with + followed by 7-15 digits.\n */\nexport function isValid(phone: string): boolean {\n return /^\\+[1-9]\\d{6,14}$/.test(phone);\n}\n","/**\n * Email validation helper for auth flows.\n */\n\n/** Basic email format check. Not RFC 5322 exhaustive. */\nexport function isValid(email: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n}\n","/**\n * Auth — app-managed authentication for MindStudio v2 apps.\n *\n * Provides verification code flows (email + SMS), session state\n * management, and phone/email validation helpers. The platform\n * handles code delivery, cookie management, and user storage —\n * developers build their own login UI using these methods.\n *\n * ## How it works\n *\n * ```\n * // 1. Send a verification code\n * const { verificationId } = await auth.sendEmailCode('user@example.com');\n *\n * // 2. User enters the code in your UI\n * const user = await auth.verifyEmailCode(verificationId, '123456');\n *\n * // 3. Session is now active — all SDK calls use the authenticated token\n * const result = await api.getDashboard(); // uses authenticated session\n * auth.getCurrentUser(); // { id, email, phone, roles, createdAt }\n * ```\n *\n * Verify, confirm, and logout methods update `window.__MINDSTUDIO__`\n * in-place so all downstream calls (method invocation, agent chat,\n * uploads) immediately use the new session. No page refresh needed.\n *\n * @example\n * ```tsx\n * import { auth } from '@mindstudio-ai/interface';\n *\n * function LoginPage() {\n * const [email, setEmail] = useState('');\n * const [verificationId, setVerificationId] = useState('');\n * const [code, setCode] = useState('');\n *\n * const handleSend = async () => {\n * const { verificationId } = await auth.sendEmailCode(email);\n * setVerificationId(verificationId);\n * };\n *\n * const handleVerify = async () => {\n * await auth.verifyEmailCode(verificationId, code);\n * // Session updated in-place — navigate to your app\n * window.location.href = '/dashboard';\n * };\n * }\n * ```\n */\n\nimport { getConfig, updateConfig, withBase } from './config.js';\nimport { MindStudioInterfaceError, errorFromResponse } from './errors.js';\nimport type { AppUser, AuthSessionBundle } from './types.js';\nimport * as phoneHelpers from './auth-phone.js';\nimport * as emailHelpers from './auth-email.js';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** True when the SDK is running inside a (cross-origin) iframe. */\nfunction isEmbedded(): boolean {\n try {\n return window.top !== window.self;\n } catch {\n // Cross-origin restrictions on reading window.top → we're definitely framed.\n return true;\n }\n}\n\nasync function authFetch<T>(\n path: string,\n method: 'GET' | 'POST',\n body?: Record<string, unknown>,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n // Every auth POST carries the framing hint, injected here at the transport\n // so no cookie-setting endpoint (verify, change-confirm, exchange, logout)\n // can miss it. The server can't detect framing itself — these are\n // same-origin fetches — and it needs to know: a framed sign-in must set the\n // `__ms_auth` cookie `SameSite=None; Partitioned` or the browser discards\n // it, breaking header-less reads (private `file.url` in <img>/fetch) and\n // session restore on reload. Endpoints that don't set cookies ignore the\n // extra field.\n const payload =\n method === 'POST' ? { ...body, embedded: isEmbedded() } : body;\n\n if (payload !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(withBase(path), {\n method,\n headers,\n ...(payload !== undefined && { body: JSON.stringify(payload) }),\n });\n\n if (!res.ok) {\n throw await errorFromResponse(\n res,\n `Auth request failed: ${res.status} ${res.statusText}`,\n 'auth_error',\n );\n }\n\n return (await res.json()) as T;\n}\n\n// sessionStorage key holding the CSRF `state` for an in-flight top-level\n// \"Sign in with Remy\" round-trip. Set by signInWithRemy(), validated + cleared\n// by handleRemyRedirect(). (The embedded/popup flow keeps `state` in the\n// opener's memory instead — see signInWithRemyPopup.)\nconst REMY_STATE_KEY = '__ms_remy_state';\n\n// Embedded (iframe) flow: a query marker on the popup callback URL so the SDK\n// instance that loads in the popup relays + closes instead of redeeming, and\n// the postMessage discriminator the opener matches on.\nconst REMY_POPUP_MARKER = 'ms_popup';\nconst REMY_POPUP_MESSAGE = 'ms:remy:callback';\n\n// The popup document forwards its one-time code to the opener exactly once,\n// whichever entry point (the import-time fast path or handleRemyRedirect())\n// runs the relay. Flipped after the first successful post — see\n// maybeRelayRemyPopupCallback.\nlet remyPopupForwarded = false;\n\n/**\n * Whether a delegated (\"Sign in with Remy\") handshake is completing right now.\n *\n * Set **synchronously at module load** when the page is a cold-load redirect\n * return (a `?code` in the URL that isn't a popup callback) — before any app\n * render — and cleared when the exchange settles. This is the stable signal\n * apps read via {@link Auth.authStatus}; the `?code` URL param can't serve that\n * role because handleRemyRedirect() strips it at the *start* of the exchange.\n * Also toggled around the embedded popup exchange (see signInWithRemyPopup).\n */\nlet redirectPending = detectColdLoadRedirect();\n\n/** True when this page load is a cold-load delegated redirect return. */\nfunction detectColdLoadRedirect(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n try {\n const params = new URL(window.location.href).searchParams;\n // A popup-callback document (`?ms_popup=1`) relays + closes; it never\n // redeems here, so it isn't \"authenticating\" from the app's perspective.\n return params.has('code') && params.get(REMY_POPUP_MARKER) !== '1';\n } catch {\n return false;\n }\n}\n\n/** Generate an opaque, unguessable CSRF state value. */\nfunction generateState(): string {\n const c = globalThis.crypto;\n if (c && typeof c.randomUUID === 'function') {\n return c.randomUUID();\n }\n const bytes = new Uint8Array(16);\n c?.getRandomValues?.(bytes);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** Strip our own `code`/`state` params from the URL without a reload. */\nfunction stripRemyRedirectParams(url: URL): void {\n url.searchParams.delete('code');\n url.searchParams.delete('state');\n try {\n window.history.replaceState(window.history.state, '', url.toString());\n } catch {\n // history unavailable (e.g. non-browser) — nothing to clean\n }\n}\n\nconst authListeners: Set<(user: AppUser | null) => void> = new Set();\n\nfunction applySession(bundle: AuthSessionBundle): void {\n updateConfig({\n token: bundle.token,\n user: bundle.user,\n methods: bundle.methods,\n visitorId: bundle.visitorId,\n });\n authListeners.forEach((cb) => cb(bundle.user));\n}\n\nfunction requireUser(bundle: AuthSessionBundle): AppUser {\n if (!bundle.user) {\n throw new MindStudioInterfaceError(\n 'Verification succeeded but no user was returned',\n 'auth_error',\n );\n }\n return bundle.user;\n}\n\nfunction updateUserAndNotify(update: Partial<AppUser>): void {\n const config = getConfig();\n if (config.user) {\n Object.assign(config.user, update);\n authListeners.forEach((cb) => cb(config.user));\n }\n}\n\n/** The current user, or `null` — never throws if the bootstrap is absent. */\nfunction safeCurrentUser(): AppUser | null {\n try {\n return getConfig().user;\n } catch {\n return null;\n }\n}\n\n/** Fire every auth listener with the current user (for auth-state changes). */\nfunction notifyAuthListeners(): void {\n const user = safeCurrentUser();\n authListeners.forEach((cb) => cb(user));\n}\n\n/** Enter the 'authenticating' state and notify subscribers (idempotent). */\nfunction beginAuthenticating(): void {\n if (redirectPending) {\n return;\n }\n redirectPending = true;\n notifyAuthListeners();\n}\n\n/** Leave the 'authenticating' state and notify subscribers (idempotent). */\nfunction endAuthenticating(): void {\n if (!redirectPending) {\n return;\n }\n redirectPending = false;\n notifyAuthListeners();\n}\n\n// Sign-in popup size — compact and roughly square. (The old 480×720 opened tall\n// and, with no left/top, pinned to the top-left corner.)\nconst REMY_POPUP_WIDTH = 520;\nconst REMY_POPUP_HEIGHT = 600;\n\n/**\n * A `window.open` features string for a popup of the given size, centered over\n * the current browser window (dual-monitor-safe — centers on the window the\n * user is actually looking at, not the primary screen), clamped to fit.\n */\nfunction centeredPopupFeatures(width: number, height: number): string {\n const baseLeft = window.screenX;\n const baseTop = window.screenY;\n const viewW = window.outerWidth || window.innerWidth || window.screen.width;\n const viewH =\n window.outerHeight || window.innerHeight || window.screen.height;\n\n const w = Math.min(width, viewW);\n const h = Math.min(height, viewH);\n const left = Math.max(0, Math.round(baseLeft + (viewW - w) / 2));\n const top = Math.max(0, Math.round(baseTop + (viewH - h) / 2));\n\n return `popup=yes,width=${Math.round(w)},height=${Math.round(h)},left=${left},top=${top}`;\n}\n\n/**\n * Run the \"Sign in with Remy\" handshake in a top-level popup — used when the\n * app is embedded (a cross-origin iframe like the dev IDE preview). The\n * authorize page hard-denies framing, so it can only render as a first-party\n * top-level document. The popup relays its one-time code back to this window\n * (the opener) via postMessage; the exchange runs *here* so this context — the\n * one that must end up authenticated — owns the session. The `ms_iface_` token\n * from the response body is the source of truth (held in memory), so it works\n * regardless of whether the third-party `__ms_auth` cookie survives.\n */\nfunction signInWithRemyPopup(\n redirectUri: string,\n state: string,\n): Promise<AppUser | null> {\n // Tag the callback so the SDK instance that loads in the popup relays +\n // closes rather than redeeming in place.\n const callbackUrl = new URL(redirectUri, window.location.href);\n callbackUrl.searchParams.set(REMY_POPUP_MARKER, '1');\n\n const startUrl = `${withBase('/_/auth/remy/start')}?${new URLSearchParams({\n redirect_uri: callbackUrl.toString(),\n state,\n }).toString()}`;\n\n // MUST open synchronously on the user's click — awaiting first spends the\n // gesture and the browser blocks the popup.\n const popup = window.open(\n startUrl,\n 'ms-remy-signin',\n centeredPopupFeatures(REMY_POPUP_WIDTH, REMY_POPUP_HEIGHT),\n );\n if (!popup) {\n return Promise.reject(\n new MindStudioInterfaceError(\n 'The sign-in popup was blocked. Allow popups for this site and try again.',\n 'popup_blocked',\n ),\n );\n }\n\n // The opener is now completing a handshake — reflected by auth.authStatus\n // until the exchange settles (or the popup is cancelled/times out).\n beginAuthenticating();\n\n return new Promise<AppUser | null>((resolve, reject) => {\n const appOrigin = window.location.origin;\n let settled = false;\n // Set synchronously when the first valid message is accepted — guards\n // against a duplicate/replayed post starting a second exchange, and stops\n // a later popup.closed from reading as \"cancel\" mid-exchange.\n let accepted = false;\n\n const finish = (fn: () => void): void => {\n if (settled) {\n return;\n }\n settled = true;\n window.removeEventListener('message', onMessage);\n clearInterval(pollTimer);\n clearTimeout(timeoutId);\n // Clear the 'authenticating' state (no-op on success — the exchange\n // already cleared it before applySession; clears + notifies on\n // cancel/timeout/error so the app leaves the \"Completing sign-in…\" UI).\n endAuthenticating();\n try {\n if (!popup.closed) {\n popup.close();\n }\n } catch {\n // ignore — closing a cross-context window can throw in rare cases\n }\n fn();\n };\n\n const onMessage = (event: MessageEvent): void => {\n // Once we've committed to the first valid message, ignore everything\n // else — a duplicate/replayed post must never start a second redemption.\n if (settled || accepted) {\n return;\n }\n // Hygiene: our exact origin, the window we opened, our shape, our state.\n if (event.origin !== appOrigin || event.source !== popup) {\n return;\n }\n const data = event.data as {\n type?: string;\n code?: string;\n state?: string;\n } | null;\n if (!data || data.type !== REMY_POPUP_MESSAGE || data.state !== state) {\n return; // not ours / stale / cross-talk / possible CSRF\n }\n\n // Commit synchronously, before the exchange await: this is the single\n // redemption, and a later popup.closed must not read as \"cancel\".\n accepted = true;\n\n if (!data.code) {\n finish(() =>\n reject(\n new MindStudioInterfaceError(\n 'Sign-in returned no code.',\n 'auth_error',\n ),\n ),\n );\n return;\n }\n\n // Redeem in the opener: this is the context that must be authenticated.\n authFetch<AuthSessionBundle>('/_/auth/remy/exchange', 'POST', {\n code: data.code,\n })\n .then((bundle) => {\n // Clear the flag before applySession's notify so subscribers see\n // 'authenticated' directly — never a stray 'unauthenticated' frame.\n redirectPending = false;\n applySession(bundle);\n finish(() => resolve(requireUser(bundle)));\n })\n .catch((err: unknown) => finish(() => reject(err)));\n };\n\n window.addEventListener('message', onMessage);\n\n // User closed the popup before finishing → cancelled (unless we've already\n // accepted the callback message and are mid-exchange).\n const pollTimer = setInterval(() => {\n if (popup.closed && !accepted) {\n finish(() => resolve(null));\n }\n }, 400);\n\n // Backstop for a popup that never closes or posts (e.g. stuck on an error).\n const timeoutId = setTimeout(\n () =>\n finish(() =>\n reject(\n new MindStudioInterfaceError(\n 'Sign-in timed out.',\n 'signin_timeout',\n ),\n ),\n ),\n 5 * 60 * 1000,\n );\n });\n}\n\n/**\n * If this window is the throwaway \"Sign in with Remy\" popup, relay the\n * one-time code to the opener and let it close us; returns `true` when it\n * handled the callback. Safe and synchronous — invoked both at SDK import\n * (before telemetry installs, so the popup fires no phantom pageview/presence)\n * and as a branch of {@link Auth.handleRemyRedirect}.\n *\n * @internal Not part of the public API surface.\n */\nexport function maybeRelayRemyPopupCallback(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n let params: URLSearchParams;\n try {\n params = new URL(window.location.href).searchParams;\n } catch {\n return false;\n }\n if (params.get(REMY_POPUP_MARKER) !== '1') {\n return false;\n }\n const code = params.get('code');\n if (!code) {\n return false;\n }\n\n // Idempotent: forward the code once per popup document. Both the import-time\n // fast path and handleRemyRedirect() call this on load; without the guard an\n // app following the documented \"call handleRemyRedirect() on load\" runs a\n // second forward inside the popup and the opener redeems the one-time code\n // twice (the second 400s). Return `true` — it's still the \"handled\" path.\n if (remyPopupForwarded) {\n return true;\n }\n\n const opener = window.opener as Window | null;\n if (!opener || opener === window) {\n // No channel back to the app — e.g. COOP severed the opener. Redeeming\n // here would authenticate the throwaway popup, which the storage-\n // partitioned iframe can't use, so we deliberately do nothing.\n return false;\n }\n\n try {\n opener.postMessage(\n {\n type: REMY_POPUP_MESSAGE,\n code,\n state: params.get('state') ?? undefined,\n },\n window.location.origin,\n );\n } catch {\n return false;\n }\n\n remyPopupForwarded = true;\n\n // The opener closes us after the exchange; self-close on the next tick is a\n // backstop in case it isn't listening.\n try {\n setTimeout(() => window.close(), 0);\n } catch {\n // ignore\n }\n return true;\n}\n\n/**\n * The high-level authentication state, from {@link Auth.authStatus}.\n *\n * - `'authenticating'` — a \"Sign in with Remy\" handshake is completing right\n * now: a cold-load redirect return (top-level or dashboard launch) is being\n * redeemed, or an embedded popup exchange is in flight. Render a\n * \"Completing sign-in…\" state, not the logged-out UI.\n * - `'authenticated'` — a user is signed in ({@link Auth.currentUser} is set).\n * - `'unauthenticated'` — no session and nothing in progress.\n */\nexport type AuthStatus = 'authenticating' | 'authenticated' | 'unauthenticated';\n\n/**\n * Options for {@link Auth.signInWithRemy}.\n */\nexport interface SignInWithRemyOptions {\n /**\n * The same-origin URL the platform should return the browser to after\n * resolving access. Must be an origin the app actually serves on (the\n * backend validates it against the app's allow-list).\n *\n * @default `window.location.href` — the user lands back where they started.\n */\n redirectUri?: string;\n\n /**\n * CSRF token round-tripped through the redirect. Provide your own to\n * correlate the return with app state; otherwise an opaque random value\n * is generated. Validated on return by {@link Auth.handleRemyRedirect}\n * (top-level flow) or by the opener (embedded/popup flow).\n */\n state?: string;\n\n /**\n * How to run the handshake.\n *\n * - `'auto'` (default) — full-page redirect when the app is top-level; a\n * popup when it's embedded in a cross-origin iframe (the authorize page\n * can't be framed, so a redirect there would fail).\n * - `'popup'` — always use a popup.\n * - `'redirect'` — always use a full-page redirect.\n *\n * @default 'auto'\n */\n mode?: 'auto' | 'popup' | 'redirect';\n}\n\n// ---------------------------------------------------------------------------\n// Auth interface\n// ---------------------------------------------------------------------------\n\n/**\n * The auth namespace — authentication flows, state, and helpers.\n *\n * Auth methods throw {@link MindStudioInterfaceError} on failure.\n * Common error codes:\n *\n * | Code | Status | Meaning |\n * |------|--------|---------|\n * | `rate_limited` | 429 | Too many code requests (max 5 per 15 min per identifier) |\n * | `invalid_code` | 400 | Wrong verification code |\n * | `verification_expired` | 400 | Code expired (10 min TTL) — request a new one |\n * | `max_attempts_exceeded` | 400 | Too many incorrect attempts (max 3) — request a new code |\n * | `not_authenticated` | 401 | Auth cookie missing (change/logout endpoints) |\n * | `invalid_session` | 401 | Auth cookie expired or invalid |\n */\nexport interface Auth {\n // -- State --\n\n /** The current authenticated user, or `null` if not authenticated. */\n readonly currentUser: AppUser | null;\n\n /** Get the current authenticated user, or `null` if not authenticated. */\n getCurrentUser(): AppUser | null;\n\n /** Whether the current session is authenticated. */\n isAuthenticated(): boolean;\n\n /**\n * The high-level auth state — `'authenticating'`, `'authenticated'`, or\n * `'unauthenticated'`. Synchronous and safe to read in render.\n *\n * The key case is `'authenticating'`: it's `true` from **page load** through\n * settle whenever a \"Sign in with Remy\" handshake is completing (a cold-load\n * redirect return being redeemed, or an embedded popup exchange in flight).\n * Read this to show a \"Completing sign-in…\" state instead of the logged-out\n * UI — the `?code` URL param can't be used for that (it's stripped at the\n * start of the exchange), and the immediate `null` from\n * {@link onAuthStateChanged} during this window is expected.\n *\n * `onAuthStateChanged` fires on every transition, so re-read this there.\n */\n readonly authStatus: AuthStatus;\n\n /**\n * The stable per-browser, per-app visitor identifier, or `null` if\n * unavailable (e.g. cached bootstrap from before this field existed).\n *\n * Opaque string backed by a server-set HttpOnly cookie. For authed\n * sessions this is the user's platform user ID; for guests it's a\n * per-browser UUID. Persists ~1 year; refreshed on each page load.\n * Updates in-place on login/logout transitions.\n */\n readonly currentVisitorId: string | null;\n\n /** Get the current visitor ID, or `null` if unavailable. */\n getCurrentVisitorId(): string | null;\n\n // -- Email code flow --\n\n /** Send a 6-digit verification code to an email address. */\n sendEmailCode(email: string): Promise<{ verificationId: string }>;\n\n /**\n * Verify an email code. On success, updates the session in-place\n * and returns the authenticated user.\n */\n verifyEmailCode(verificationId: string, code: string): Promise<AppUser>;\n\n // -- SMS code flow --\n\n /** Send a 6-digit verification code via SMS. Phone must be E.164. */\n sendSmsCode(phone: string): Promise<{ verificationId: string }>;\n\n /**\n * Verify an SMS code. On success, updates the session in-place\n * and returns the authenticated user.\n */\n verifySmsCode(verificationId: string, code: string): Promise<AppUser>;\n\n // -- Sign in with Remy (platform-delegated) --\n\n /**\n * Start the \"Sign in with Remy\" flow. The platform — not the app — decides\n * who the person is and whether they're allowed (think \"Sign in with\n * Google\"). Auto-detects the context:\n *\n * - **Top-level** (production, standalone tab): redirects the browser to the\n * platform and bounces back to `redirectUri` with a one-time code that\n * {@link handleRemyRedirect} redeems on return. **The page navigates away**,\n * so the returned promise never settles.\n * - **Embedded** (cross-origin iframe, e.g. the dev IDE preview): opens a\n * top-level popup for the handshake (the authorize page can't be framed).\n * The returned promise resolves with the user on success, `null` if the\n * user closes the popup, or rejects (`popup_blocked` / `invalid_state` /\n * `signin_timeout` / exchange error). `onAuthStateChanged` also fires on\n * success.\n *\n * Must be called from a user gesture (e.g. a click) so the popup isn't\n * blocked. Override the auto-detection with `options.mode`.\n *\n * @example\n * ```ts\n * // \"Continue with {Org}\" button — works top-level or embedded:\n * <button onClick={() => auth.signInWithRemy()}>Continue with Acme</button>\n *\n * // Await the result in an embedded app:\n * const user = await auth.signInWithRemy();\n * if (user) navigate('/dashboard');\n * ```\n */\n signInWithRemy(options?: SignInWithRemyOptions): Promise<AppUser | null>;\n\n /**\n * Complete a \"Sign in with Remy\" redirect. Call once on app load — it's a\n * no-op (resolves `null`) when there's no code to redeem, so it's safe to\n * run on every mount.\n *\n * Handles both entry points:\n * - **SP-initiated** (returned from {@link signInWithRemy}) — validates the\n * round-tripped CSRF `state` against the value stashed at start.\n * - **IdP-initiated** (opened from the Remy dashboard) — a `?code=` lands on\n * first paint with no `state`; the single-use, app-bound code is the\n * control, so it's redeemed directly.\n *\n * Inside the embedded/popup document it's also a safe no-op: the popup boots\n * the same bundle, but the code is relayed to the opener exactly once\n * (idempotent — the import-time fast path already forwarded it), and this\n * call resolves `null` without redeeming. So the documented \"call on load\"\n * guidance holds everywhere — no need to special-case the popup.\n *\n * On success, updates the session in-place (fires `onAuthStateChanged`),\n * strips `code`/`state` from the URL, and returns the authenticated user.\n *\n * @returns The authenticated user, or `null` if there was no code to redeem.\n * @throws `invalid_state` if a returned `state` doesn't match the stashed one.\n *\n * @example\n * ```ts\n * useEffect(() => { auth.handleRemyRedirect(); }, []);\n * useEffect(() => auth.onAuthStateChanged(setUser), []);\n * ```\n */\n handleRemyRedirect(): Promise<AppUser | null>;\n\n // -- Email/phone change (requires authentication) --\n\n /** Request an email change. Sends a code to the new email. */\n requestEmailChange(newEmail: string): Promise<void>;\n\n /** Confirm an email change with the verification code. */\n confirmEmailChange(newEmail: string, code: string): Promise<AppUser>;\n\n /** Request a phone change. Sends a code to the new phone (E.164). */\n requestPhoneChange(newPhone: string): Promise<void>;\n\n /** Confirm a phone change with the verification code. */\n confirmPhoneChange(newPhone: string, code: string): Promise<AppUser>;\n\n // -- Session --\n\n /** Log out. Clears the cookie and updates the session to unauthenticated. */\n logout(): Promise<void>;\n\n // -- API keys --\n\n /**\n * Generate an API key for the current user. Returns the full key\n * (shown once). The user's `apiKey` field updates to the masked\n * value and `onAuthStateChanged` fires.\n *\n * @throws `not_authenticated` (401) if no session\n * @throws `not_supported` (400) if api-key auth is not enabled\n */\n createApiKey(): Promise<{ key: string }>;\n\n /**\n * Revoke the current user's API key. The user's `apiKey` field\n * becomes `null` and `onAuthStateChanged` fires.\n *\n * @throws `not_authenticated` (401) if no session\n */\n revokeApiKey(): Promise<void>;\n\n /**\n * Subscribe to auth state changes. Fires immediately with the current user,\n * then again on every transition — verify / confirm / logout, **and** each\n * `authStatus` change (a \"Sign in with Remy\" handshake starting or settling).\n *\n * During `'authenticating'` the callback may fire with `null` (no session\n * yet) — that's expected; read {@link authStatus} to tell it apart from a\n * logged-out visit rather than rendering the login UI.\n *\n * @returns An unsubscribe function.\n *\n * @example\n * ```ts\n * // React hook — track both the user and the status.\n * function useAuth() {\n * const [user, setUser] = useState(auth.currentUser);\n * const [status, setStatus] = useState(auth.authStatus);\n * useEffect(() => auth.onAuthStateChanged(() => {\n * setUser(auth.currentUser);\n * setStatus(auth.authStatus);\n * }), []);\n * return { user, status };\n * }\n * ```\n */\n onAuthStateChanged(callback: (user: AppUser | null) => void): () => void;\n\n // -- Helpers --\n\n /** Phone number utilities — countries, formatting, validation. */\n phone: {\n /** All countries with dial codes, sorted alphabetically. */\n countries: readonly phoneHelpers.Country[];\n /** Detect the user's country from their timezone. Falls back to `'US'`. */\n detectCountry(): string;\n /** Format an E.164 number for display (e.g. `+1 (555) 123-4567`). */\n format(e164: string): string;\n /** Convert a national number to E.164 (e.g. `('5551234567', 'US') → '+15551234567'`). */\n toE164(national: string, countryCode: string): string;\n /** Check if a string is a valid E.164 phone number. */\n isValid(phone: string): boolean;\n };\n\n /** Email validation. */\n email: {\n /** Basic email format check. */\n isValid(email: string): boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport const auth: Auth = {\n // -- State --\n\n get currentUser() {\n return getConfig().user;\n },\n\n getCurrentUser() {\n return getConfig().user;\n },\n\n isAuthenticated() {\n return getConfig().user !== null;\n },\n\n get authStatus() {\n if (redirectPending) {\n return 'authenticating';\n }\n return safeCurrentUser() ? 'authenticated' : 'unauthenticated';\n },\n\n get currentVisitorId() {\n return getConfig().visitorId ?? null;\n },\n\n getCurrentVisitorId() {\n return getConfig().visitorId ?? null;\n },\n\n // -- Email code flow --\n\n sendEmailCode(email: string) {\n return authFetch<{ verificationId: string }>('/_/auth/email/send', 'POST', {\n email,\n });\n },\n\n async verifyEmailCode(verificationId: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/email/verify',\n 'POST',\n { verificationId, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- SMS code flow --\n\n sendSmsCode(phone: string) {\n return authFetch<{ verificationId: string }>('/_/auth/sms/send', 'POST', {\n phone,\n });\n },\n\n async verifySmsCode(verificationId: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/sms/verify',\n 'POST',\n { verificationId, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- Sign in with Remy --\n\n signInWithRemy(options?: SignInWithRemyOptions): Promise<AppUser | null> {\n const mode = options?.mode ?? 'auto';\n const usePopup = mode === 'popup' || (mode === 'auto' && isEmbedded());\n const redirectUri = options?.redirectUri ?? window.location.href;\n const state = options?.state ?? generateState();\n\n if (usePopup) {\n // window.open must happen synchronously on the gesture — do it first.\n return signInWithRemyPopup(redirectUri, state);\n }\n\n // Top-level full-page redirect. The opener/popup handshake isn't needed,\n // so the CSRF `state` is stashed for handleRemyRedirect() to validate.\n try {\n sessionStorage.setItem(REMY_STATE_KEY, state);\n } catch {\n // sessionStorage unavailable (private mode / disabled) — proceed\n // without the CSRF stash; the one-time, app-bound code still gates.\n }\n\n const params = new URLSearchParams({ redirect_uri: redirectUri, state });\n window.location.assign(\n `${withBase('/_/auth/remy/start')}?${params.toString()}`,\n );\n\n // The page is navigating away — nothing after this resolves.\n return new Promise<AppUser | null>(() => {});\n },\n\n async handleRemyRedirect() {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const url = new URL(window.location.href);\n const code = url.searchParams.get('code');\n if (!code) {\n return null;\n }\n\n // Embedded/popup callback: relay the code to the opener and let it close\n // us — never redeem here (the opener owns the session). The import-time\n // fast path usually handles this already; this is the fallback for apps\n // that call handleRemyRedirect() directly.\n if (url.searchParams.get(REMY_POPUP_MARKER) === '1') {\n maybeRelayRemyPopupCallback();\n return null;\n }\n\n const returnedState = url.searchParams.get('state');\n\n // We're committed to consuming this redirect — strip the params up front\n // so a refresh never retries a spent (or failed) one-time code.\n stripRemyRedirectParams(url);\n\n let storedState: string | null = null;\n try {\n storedState = sessionStorage.getItem(REMY_STATE_KEY);\n sessionStorage.removeItem(REMY_STATE_KEY);\n } catch {\n // sessionStorage unavailable — CSRF check degrades to redeem-only.\n }\n\n // We're redeeming now — reflect it in auth.authStatus. Idempotent: module\n // load already set this for a cold-load return; this also covers callers\n // that reach here without the eager path having run.\n beginAuthenticating();\n\n // SP-initiated returns round-trip a `state` that must match what we\n // stashed. IdP-initiated launches carry no `state` — the single-use,\n // app-bound, short-lived code is the control there.\n if (returnedState !== null && returnedState !== storedState) {\n endAuthenticating();\n throw new MindStudioInterfaceError(\n 'Sign-in state mismatch — possible CSRF or a stale redirect. Please try again.',\n 'invalid_state',\n );\n }\n\n try {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/remy/exchange',\n 'POST',\n { code },\n );\n // Clear the flag before applySession's notify so subscribers transition\n // straight to 'authenticated' — no intermediate 'unauthenticated' frame.\n redirectPending = false;\n applySession(bundle);\n return requireUser(bundle);\n } catch (err) {\n // Clear + notify so the app leaves the \"Completing sign-in…\" state.\n endAuthenticating();\n throw err;\n }\n },\n\n // -- Email/phone change --\n\n async requestEmailChange(newEmail: string) {\n await authFetch('/_/auth/email/change', 'POST', { newEmail });\n },\n\n async confirmEmailChange(newEmail: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/email/change/confirm',\n 'POST',\n { newEmail, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n async requestPhoneChange(newPhone: string) {\n await authFetch('/_/auth/phone/change', 'POST', { newPhone });\n },\n\n async confirmPhoneChange(newPhone: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/phone/change/confirm',\n 'POST',\n { newPhone, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- Session --\n\n async logout() {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/logout',\n 'POST',\n {},\n );\n applySession(bundle);\n },\n\n async createApiKey() {\n const result = await authFetch<{ key: string; apiKey: string }>(\n '/_/auth/api-key/create',\n 'POST',\n {},\n );\n updateUserAndNotify({ apiKey: result.apiKey });\n return { key: result.key };\n },\n\n async revokeApiKey() {\n await authFetch('/_/auth/api-key/revoke', 'POST', {});\n updateUserAndNotify({ apiKey: null });\n },\n\n onAuthStateChanged(callback: (user: AppUser | null) => void) {\n authListeners.add(callback);\n callback(safeCurrentUser());\n return () => {\n authListeners.delete(callback);\n };\n },\n\n // -- Helpers --\n\n phone: {\n countries: phoneHelpers.countries,\n detectCountry: phoneHelpers.detectCountry,\n format: phoneHelpers.format,\n toE164: phoneHelpers.toE164,\n isValid: phoneHelpers.isValid,\n },\n\n email: {\n isValid: emailHelpers.isValid,\n },\n};\n","/**\n * App events client — server→client realtime.\n *\n * Backend code publishes to named channels (`events.publish` in\n * `@mindstudio-ai/agent`); this client holds a platform-served SSE and\n * delivers matching publishes as they happen. Authorization is a **grant**\n * minted by one of the app's own backend methods — which is why `connect`\n * takes a token *provider*, not a token: grants expire on purpose (expiry is\n * the revocation window), and the SDK re-mints through your method on every\n * expiry, re-running your auth checks.\n *\n * ```ts\n * import { createClient, events } from '@mindstudio-ai/interface';\n * const api = createClient();\n *\n * const sub = events.connect({\n * getToken: () => api.watchJobs().then((r) => r.token),\n * onEvent: (e) => {\n * if (e.channel.startsWith('jobs:')) refreshJob(e.data);\n * },\n * onConnect: () => refetchJobs(), // reconcile — see below\n * });\n *\n * // later (component unmount, page change):\n * sub.close();\n * ```\n *\n * ## Reconcile on connect\n *\n * Events are at-most-once nudges: nothing is buffered while you're\n * disconnected and nothing is replayed when you reconnect. `onConnect` fires\n * on EVERY (re)connect — network drop, grant expiry, server deploy — and is\n * where you refetch current state. Subscribe for speed, reconcile for truth;\n * a subscriber without an `onConnect` refetch will silently miss whatever\n * happened while it was away.\n *\n * ## Lifecycle\n *\n * - Grant expiry (`grant_expired` frame, every TTL window) → the SDK calls\n * `getToken` again and reconnects. Invisible to the app beyond `onConnect`.\n * - Network drop → reconnect with backoff, reusing the still-valid token (no\n * method invocation wasted).\n * - `getToken` throwing → terminal: your own method refused (logged out, role\n * revoked), so the SDK calls `onError` and closes rather than retrying an\n * authorization refusal in a loop. Call `connect` again when your app's\n * auth state changes.\n */\n\nimport { withBase } from './config.js';\nimport { createSseLoop, type SseLoop } from './sse-loop.js';\n\n/** One published event, delivered on a channel the grant names. */\nexport interface AppEvent {\n /**\n * Platform-stamped publish id. One publish = one id; if your grant covers\n * several of the published channels you receive it once per channel, so the\n * dedupe key is `id + channel`.\n */\n id: string;\n channel: string;\n data: unknown;\n /** Publish time, milliseconds since epoch. */\n ts: number;\n}\n\nexport interface EventsConnectOptions {\n /**\n * Return a subscribe token — typically by invoking the backend method that\n * mints the grant (`events.grant` in the agent SDK) after its own auth\n * checks. Called on first connect and again after every grant expiry.\n * Throwing is terminal: `onError` fires and the subscription closes.\n */\n getToken: () => Promise<string> | string;\n /** A published event on one of the grant's channels. */\n onEvent: (event: AppEvent) => void;\n /**\n * Fires on every (re)connect. Refetch current state here — events are\n * at-most-once, so this is the correctness half of the contract.\n */\n onConnect?: () => void;\n /**\n * The server dropped `count` events for this connection because it couldn't\n * keep up (socket backpressure). Treat it like a reconnect: refetch current\n * state, same as `onConnect`. Without a handler the gap is silent — the\n * at-most-once contract already requires reconcile-on-connect, so this is\n * an extra hint, not a new obligation.\n */\n onGap?: (count: number) => void;\n /** Terminal failures only (a `getToken` refusal). Reconnects are silent. */\n onError?: (error: Error) => void;\n}\n\nexport interface EventsSubscription {\n /** Stop the stream and all reconnection. Idempotent. */\n close(): void;\n /** `connecting` | `open` | `closed` */\n readonly state: 'connecting' | 'open' | 'closed';\n /**\n * Publish an ephemeral event on channels the grant carries **publish**\n * capability for (`events.grant(channels, { publish: [...] })` in the\n * backend SDK). This is the client-direct fast path — no backend method\n * runs; the platform fans out directly, so it's the right shape for\n * cursors, typing indicators, and live stroke batches.\n *\n * Fire-and-forget with automatic coalescing: call it per input event (per\n * mousemove is fine) and the SDK batches to ~40ms flushes. Delivery is\n * at-most-once and NOTHING here is retried — a stale cursor is worse than a\n * missing one. Payloads cap at 8k serialized; put a client `seq` in the\n * payload if receivers need cross-batch ordering. Truth still lives in your\n * database: commit durable state through a method, signal through this.\n */\n publish(channels: string | string[], data: unknown): void;\n}\n\nconst EVENTS_ENDPOINT = '/_/events';\n\n// Client-publish coalescing: flush cadence and the per-batch ceiling (kept\n// under the platform's 100-event batch cap). 40ms caps sustained flushes at\n// 25 POSTs/sec — headroom inside the platform's 30 batches/sec rate box; a\n// faster timer (25ms) measured out at ~30/sec and grazed the box.\nconst PUBLISH_FLUSH_MS = 40;\nconst PUBLISH_MAX_BATCH = 50;\n\nfunction connect(options: EventsConnectOptions): EventsSubscription {\n // The cached grant. Reused across network-drop reconnects (still valid, no\n // method call wasted); cleared when the server says it's dead — a\n // grant_expired frame, or a 401 on connect — so the next attempt re-mints.\n let token: string | null = null;\n\n const loop: SseLoop = createSseLoop({\n prepare: async () => {\n if (!token) {\n try {\n token = await options.getToken();\n } catch (err) {\n options.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n return 'stop';\n }\n }\n return {\n url: withBase(EVENTS_ENDPOINT),\n headers: { Authorization: `Bearer ${token}` },\n };\n },\n onStatus: (status) => {\n if (status === 401) {\n // Grant expired or evicted between frames — re-mint on the retry.\n token = null;\n }\n return 'retry';\n },\n onOpen: () => {\n options.onConnect?.();\n },\n onLine: (line) => {\n if (!line.startsWith('data: ')) {\n return; // keepalive comments, blank frame separators\n }\n let parsed: any;\n try {\n parsed = JSON.parse(line.slice(6));\n } catch {\n return; // malformed frame — skip, matching the agent-chat reader\n }\n if (parsed?.type === 'grant_expired') {\n // Expected every TTL window. Clear the token; the server ends the\n // stream right after this frame and the loop's reconnect re-mints.\n token = null;\n return;\n }\n if (parsed?.type === 'events_dropped') {\n options.onGap?.(typeof parsed.count === 'number' ? parsed.count : 0);\n return;\n }\n if (typeof parsed?.channel === 'string') {\n options.onEvent({\n id: parsed.id,\n channel: parsed.channel,\n data: parsed.data,\n ts: parsed.ts,\n });\n }\n },\n });\n\n loop.start();\n\n //////////////////////////////////////////////////////////////////////////////\n // Client publish — fire-and-forget, coalesced. Failure policy is the\n // at-most-once contract applied upstream: ephemeral signals are dropped on\n // any failure, never queued or retried.\n //////////////////////////////////////////////////////////////////////////////\n\n let pending: Array<{ channels: string | string[]; data: unknown }> = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let pausedUntil = 0; // 429 backoff\n let closed = false;\n let publishRefusalSurfaced = false;\n\n const flush = async () => {\n flushTimer = null;\n if (closed || pending.length === 0) {\n return;\n }\n if (!token || Date.now() < pausedUntil) {\n // Not connected (no grant yet) or rate-boxed: drop. A cursor position\n // from before the connection existed has no value after it.\n pending = [];\n return;\n }\n const events = pending;\n pending = [];\n try {\n const res = await fetch(withBase(EVENTS_ENDPOINT), {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events }),\n });\n if (res.status === 401) {\n // Grant died between frames — clear it so the SSE loop's reconnect\n // re-mints; the dropped batch is already stale.\n token = null;\n return;\n }\n if (res.status === 429) {\n const retryAfterSec = parseInt(\n res.headers.get('Retry-After') ?? '',\n 10,\n );\n pausedUntil =\n Date.now() +\n (Number.isFinite(retryAfterSec) && retryAfterSec > 0\n ? retryAfterSec * 1000\n : 1000);\n return;\n }\n if (res.status === 403 && !publishRefusalSurfaced) {\n // The grant has no publish capability (or not for these channels) — a\n // programming error in the minting method, not a transient. Surface\n // once, keep dropping quietly after.\n publishRefusalSurfaced = true;\n options.onError?.(\n new Error(\n 'events.publish refused: the grant does not carry publish ' +\n 'capability for these channels — mint it with ' +\n '`events.grant(channels, { publish: [...] })`.',\n ),\n );\n }\n } catch {\n // Network blip — drop, at-most-once.\n }\n };\n\n const publish = (channels: string | string[], data: unknown) => {\n if (closed) {\n return;\n }\n pending.push({ channels, data });\n if (pending.length >= PUBLISH_MAX_BATCH) {\n if (flushTimer) {\n clearTimeout(flushTimer);\n }\n void flush();\n return;\n }\n if (!flushTimer) {\n flushTimer = setTimeout(() => void flush(), PUBLISH_FLUSH_MS);\n }\n };\n\n return {\n close: () => {\n closed = true;\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n pending = [];\n loop.stop();\n },\n get state() {\n const s = loop.state;\n return s === 'open' ? 'open' : s === 'stopped' ? 'closed' : 'connecting';\n },\n publish,\n };\n}\n\n/**\n * The app-events client. `connect` opens a managed subscription — reconnects,\n * grant re-minting and backoff are handled internally; the app supplies the\n * token source and the handlers.\n */\nexport const events = { connect };\n","/**\n * @mindstudio-ai/interface — Frontend SDK for MindStudio v2 app interfaces.\n *\n * Provides typed RPC to backend methods, file uploads, authentication,\n * agent chat conversations, and analytics. Runs inside web interfaces with\n * credentials injected by the platform via `window.__MINDSTUDIO__`.\n *\n * ## Exports\n *\n * - `createClient()` — typed method RPC client\n * - `createAgentChatClient()` — thread-based agent conversations\n * - `platform` — file upload actions\n * - `auth` — authentication flows, user state, and validation helpers\n * - `analytics` — custom event tracking\n * - `telemetry` — crash reporting: React root hooks + manual capture\n *\n * Voice sessions live on the `./voice` subpath (`createVoiceClient()` from\n * `@mindstudio-ai/interface/voice`) — deliberately not re-exported here, so\n * the core entry stays free of the realtime media dependency.\n *\n * @example\n * ```ts\n * import {\n * createClient,\n * createAgentChatClient,\n * platform,\n * auth,\n * } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n * const chat = createAgentChatClient();\n *\n * // Check auth state\n * if (!auth.isAuthenticated()) {\n * const { verificationId } = await auth.sendEmailCode('user@example.com');\n * await auth.verifyEmailCode(verificationId, code);\n * }\n *\n * // Call backend methods (uses authenticated session)\n * const dashboard = await api.getDashboard();\n *\n * // Upload a file (client-direct to the app's file store)\n * const token = await api.getUploadSlot({ contentType: file.type });\n * const { url } = await platform.upload(token, file);\n *\n * // Current user\n * const user = auth.getCurrentUser();\n * ```\n */\n\nexport { createClient, type InvokeOptions } from './client.js';\nexport {\n createAgentChatClient,\n type AgentChatClient,\n type AgentChatEvent,\n type SendMessageCallbacks,\n type SendMessageOptions,\n type SendMessageResult,\n type AbortablePromise,\n type Thread,\n type ThreadSummary,\n type ThreadListPage,\n type Message,\n} from './agent-chat.js';\nexport {\n platform,\n type UploadFileOptions,\n type UploadToken,\n} from './platform.js';\nexport {\n auth,\n type Auth,\n type AuthStatus,\n type SignInWithRemyOptions,\n} from './auth.js';\nexport {\n analytics,\n type AnalyticsClient,\n type CustomEventProps,\n} from './telemetry-analytics.js';\nexport {\n telemetry,\n type Telemetry,\n type CaptureOptions,\n type ErrorMechanism,\n type ReactErrorHandlerOptions,\n type ReactErrorInfo,\n} from './telemetry-errors.js';\nexport {\n events,\n type AppEvent,\n type EventsConnectOptions,\n type EventsSubscription,\n} from './events.js';\nexport { MindStudioInterfaceError } from './errors.js';\nexport type { BootstrapConfig, AppUser } from './types.js';\nexport type { Country } from './auth-phone.js';\nexport type { Breadcrumb } from './telemetry-breadcrumbs.js';\n\n// ---------------------------------------------------------------------------\n// Eager telemetry init\n//\n// When `window.__MINDSTUDIO__` is present at import time, trigger `getConfig()`\n// so telemetry surfaces (errors, analytics, presence) install at page load\n// rather than on first SDK use. Without this, an app that creates an `api`\n// proxy at the module top but only invokes it from user interactions would\n// have no telemetry until the user clicks something.\n//\n// SYNCHRONOUS, deliberately. This ran in `setTimeout(…, 0)`, which lost the\n// race it did not look like it was in: `createRoot().render()` schedules React's\n// first render through the Scheduler's MessageChannel, and that task runs before\n// a queued timer. Every first-paint crash — the most common kind there is —\n// happened before the error listeners existed. Analytics install stays deferred\n// (in config.ts), where a tick costs nothing.\n//\n// In environments without the bootstrap (SSR/Node, tests, dev without platform\n// context), the guard skips this and the SDK falls back to lazy init — first\n// SDK method call triggers `getConfig()` exactly as before.\n// ---------------------------------------------------------------------------\n\nimport { getConfig } from './config.js';\nimport { maybeRelayRemyPopupCallback } from './auth.js';\n\nif (typeof window !== 'undefined') {\n // If this window is the throwaway \"Sign in with Remy\" popup callback, relay\n // the code to the opener and close — synchronously, *before* installing\n // telemetry, so the popup never fires a phantom pageview / presence\n // connection. When it's not a popup callback this is a cheap no-op.\n const relayed = maybeRelayRemyPopupCallback();\n\n if (!relayed && (globalThis as Record<string, unknown>).__MINDSTUDIO__) {\n try {\n getConfig();\n } catch {\n // bootstrap reads can still happen lazily on first SDK call\n }\n }\n}\n"],"mappings":"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","names":["createClient","_","methodName","input","options","config","getConfig","methodId","MindStudioInterfaceError","url","withBase","wantsStream","res","errorFromResponse","reader","decoder","buffer","finalOutput","streamError","done","value","lines","line","json","event","AGENT_BASE","CLIENT_TOOL_RESULT_MAX_CHARS","request","path","method","body","headers","getConfig","res","withBase","errorFromResponse","postToolResult","threadId","config","dispatchEvent","event","callbacks","runClientTool","handler","result","serialized","err","createAgentChatClient","threadTokens","clientToolHandlers","rememberToken","token","thread","cursor","query","title","options","previousToken","MindStudioInterfaceError","name","content","controller","cb","abortable","url","reader","decoder","buffer","done","value","lines","line","json","input","MULTIPART_THRESHOLD","PART_CONCURRENCY","PART_ATTEMPTS","PART_URL_BATCH","COMPLETE_ATTEMPTS","COMPLETE_RETRY_MS","RESUME_PREFIX","sleep","ms","signal","resolve","reject","timer","call","op","body","res","withBase","errorFromResponse","resume","token","file","key","session","isRestart","err","MindStudioInterfaceError","putPart","url","bytes","onLoaded","xhr","e","uploadInParts","options","onProgress","resumeKey","restarts","partSize","arrived","status","partNumber","size","started","sendParts","complete","partCount","sizeOf","n","todo","doneBytes","loading","report","loaded","batches","urlFor","fresh","batch","urls","first","last","_","i","map","sendOne","start","attempt","workers","platform","getBasePath","file","options","config","getConfig","onProgress","signal","presignUrl","withBase","presignRes","MindStudioInterfaceError","uploadUrl","uploadFields","publicUrl","formData","key","value","xhrUpload","uploadRes","token","MULTIPART_THRESHOLD","uploadInParts","res","url","resolve","reject","xhr","e","countries","byCode","c","timezoneToCountry","detectCountry","tz","format","e164","digits","area","prefix","line","len","candidate","match","rest","toE164","national","countryCode","country","dialDigits","isValid","phone","isValid","email","isEmbedded","authFetch","path","method","body","headers","getConfig","payload","res","withBase","errorFromResponse","REMY_STATE_KEY","REMY_POPUP_MARKER","REMY_POPUP_MESSAGE","remyPopupForwarded","redirectPending","detectColdLoadRedirect","params","generateState","c","bytes","b","stripRemyRedirectParams","url","authListeners","applySession","bundle","updateConfig","cb","requireUser","MindStudioInterfaceError","updateUserAndNotify","update","config","safeCurrentUser","notifyAuthListeners","user","beginAuthenticating","endAuthenticating","REMY_POPUP_WIDTH","REMY_POPUP_HEIGHT","centeredPopupFeatures","width","height","baseLeft","baseTop","viewW","viewH","w","h","left","top","signInWithRemyPopup","redirectUri","state","callbackUrl","startUrl","popup","resolve","reject","appOrigin","settled","accepted","finish","fn","onMessage","pollTimer","timeoutId","event","data","err","maybeRelayRemyPopupCallback","code","opener","auth","email","verificationId","phone","options","mode","usePopup","returnedState","storedState","newEmail","newPhone","result","callback","countries","detectCountry","format","toE164","isValid","EVENTS_ENDPOINT","PUBLISH_FLUSH_MS","PUBLISH_MAX_BATCH","connect","options","token","loop","createSseLoop","err","withBase","status","line","parsed","pending","flushTimer","pausedUntil","closed","publishRefusalSurfaced","flush","events","res","retryAfterSec","channels","data","maybeRelayRemyPopupCallback","getConfig"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/interface",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "Frontend SDK for MindStudio v2 app interfaces",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",