@rubytech/create-maxy 1.0.478 → 1.0.480

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +11 -6
  3. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  4. package/payload/platform/plugins/admin/mcp/dist/index.js +221 -1
  5. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  6. package/payload/platform/plugins/cloudflare/PLUGIN.md +3 -2
  7. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +68 -4
  8. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
  9. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +8 -0
  10. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
  11. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +48 -6
  12. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
  13. package/payload/platform/plugins/cloudflare/references/dashboard-api.md +125 -0
  14. package/payload/platform/plugins/cloudflare/references/setup-guide.md +11 -10
  15. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +81 -42
  16. package/payload/platform/plugins/memory/mcp/dist/index.js +13 -2
  17. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  18. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +1 -0
  19. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +3 -3
  21. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +1 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  24. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +3 -2
  25. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  26. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +1 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +14 -11
  29. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  30. package/payload/platform/scripts/seed-neo4j.sh +9 -0
  31. package/payload/platform/templates/agents/admin/IDENTITY.md +10 -0
  32. package/payload/platform/templates/specialists/agents/admin-specialist.md +1 -1
  33. package/payload/server/public/assets/ChatInput-D_Am-NZI.css +1 -0
  34. package/payload/server/public/assets/{admin-DLtY5oYr.js → admin-ZfkvBfx_.js} +60 -60
  35. package/payload/server/public/assets/{public-B27jukY6.js → public-C0LOoku3.js} +1 -1
  36. package/payload/server/public/index.html +3 -3
  37. package/payload/server/public/public.html +3 -3
  38. package/payload/server/server.js +96 -55
  39. package/payload/server/public/assets/ChatInput-DNdZ2WD7.css +0 -1
  40. /package/payload/server/public/assets/{ChatInput-BWyEqP0k.js → ChatInput-B2MUVSm4.js} +0 -0
@@ -1,4 +1,4 @@
1
- import{C as e,D as t,S as n,T as r,_ as i,a,b as o,d as s,f as c,h as l,i as u,j as d,k as f,m as p,n as m,p as h,r as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./ChatInput-BWyEqP0k.js";var w=t(),T=d(f(),1),E=`image/jpeg,image/png,image/gif,image/webp,application/pdf,text/plain,text/markdown,text/csv,text/html`,D=new Set(E.split(`,`)),O=20*1024*1024,k=typeof window<`u`&&/^(localhost|127\.0\.0\.1|[a-z0-9-]+\.local)(:|$)/.test(window.location.hostname);function A(){let e=crypto.getRandomValues(new Uint8Array(16));e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t=[...e].map(e=>e.toString(16).padStart(2,`0`)).join(``);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}function j(){let e=window.location.pathname.match(/^\/([a-z][a-z0-9-]{2,49})$/);return e?e[1]:void 0}function M(e){return[{key:`length`,label:`At least 8 characters`,met:e.length>=8},{key:`number`,label:`Contains a number`,met:/\d/.test(e)},{key:`special`,label:`Contains a special character`,met:/[^A-Za-z0-9]/.test(e)},{key:`whitespace`,label:`No leading or trailing spaces`,met:e.length>0&&e===e.trim()}]}function N(e,t){if(t===`phone`)return e.length>4?e.slice(0,e.length-4).replace(/\d/g,`•`)+` `+e.slice(-4):e;let n=e.indexOf(`@`);return n<=2?e:e.slice(0,2)+`•••`+e.slice(n)}function P(e){let[t,n]=(0,T.useState)(null),[r,i]=(0,T.useState)(null),[a,o]=(0,T.useState)(`loading`),[s,c]=(0,T.useState)(``),[l,u]=(0,T.useState)(null),[d,f]=(0,T.useState)(null),[p,m]=(0,T.useState)(!1),[h,g]=(0,T.useState)(null),_=(0,T.useMemo)(()=>j(),[]),v=(0,T.useRef)(_||``),y=(0,T.useRef)(null),b=(0,T.useRef)(null),x=(0,T.useRef)(!1),S=(0,T.useRef)(null),[C,w]=(0,T.useState)(`sign-in`),[E,D]=(0,T.useState)(null),O=(0,T.useRef)(null),M=(0,T.useRef)(null);(0,T.useEffect)(()=>{try{let e=sessionStorage.getItem(`maxy_session`);e&&(M.current=e)}catch{}},[]);let N=(0,T.useCallback)(t=>{y.current=t,n(t);try{sessionStorage.setItem(`maxy_session`,t)}catch{}o(`chat`),e(t)},[e]),P=(0,T.useCallback)(()=>{y.current=null;try{sessionStorage.removeItem(`maxy_session`)}catch{}n(null),o(`auth-required`),w(`sign-in`)},[]),F=(0,T.useCallback)(async e=>{try{let t=v.current,n=await fetch(`/api/access/verify-token`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:e,agentSlug:t})}),r=await n.json();n.ok?(O.current=r.session_key,D(r.grant),w(`set-password`),window.history.replaceState({},``,window.location.pathname)):w(`link-expired`)}catch{w(`sign-in`)}},[]);return{sessionId:t,sessionKeyRef:y,sessionError:r,pageState:a,setPageState:o,agentDisplayName:s,agentImage:l,agentImageShape:d,showAgentName:p,agentSlug:_,branding:h,resolvedSlugRef:v,ensureSession:(0,T.useCallback)(async()=>{if(y.current)return y.current;if(b.current)return b.current;let e=(async()=>{try{let e=M.current,t=await fetch(`/api/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({session_id:A(),..._?{agent:_}:{},...e?{session_key:e}:{}})});if(!t.ok){let e=await t.json().catch(()=>({error:``}));return k&&console.error(`[session] POST /api/session failed: ${t.status} ${t.statusText}`,e),t.status===404?i(e.error||`Agent not found`):t.status===503&&i(e.error||`Service unavailable`),null}let r=await t.json();if(r.auth_required){r.agent_id&&(v.current=r.agent_id),c(r.displayName||``),r.agentImage&&u(r.agentImage),r.agentImageShape&&f(r.agentImageShape),r.showAgentName&&m(r.showAgentName),r.branding&&g(r.branding),o(`auth-required`);let e=new URLSearchParams(window.location.search).get(`token`);return e?F(e):w(`sign-in`),null}y.current=r.session_key,n(r.session_key),r.displayName&&c(r.displayName),r.agentImage&&u(r.agentImage),r.agentImageShape&&f(r.agentImageShape),r.showAgentName&&m(r.showAgentName),r.branding&&g(r.branding),o(`chat`),r.resumed&&r.messages&&(x.current=!0,S.current=r.messages);try{sessionStorage.setItem(`maxy_session`,r.session_key)}catch{}return r.session_key}catch(e){return k&&console.error(`[session] fetch /api/session threw:`,e),i(`Unable to connect. Please check your connection and try again.`),null}finally{b.current=null}})();return b.current=e,e},[_,F]),enterChat:N,enterGate:P,resumedRef:x,resumeMessagesRef:S,gateState:C,setGateState:w,grantInfo:E,setGrantInfo:D,gateSessionKeyRef:O}}function F({sessionKeyRef:e,ensureSession:t,sessionError:n,pageState:r,inputRef:a}){let[o,s]=(0,T.useState)([]),[c,l]=(0,T.useState)(!1),u=(0,T.useRef)(!1),d=e=>{u.current=e,l(e)},f=(0,T.useCallback)(async o=>{if(!u.current){d(!0),s([{role:`maxy`,content:``,timestamp:Date.now()}]);try{let n=await fetch(`/api/chat`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({greeting:!0,session_key:o})});if(n.status===401){e.current=null;try{sessionStorage.removeItem(`maxy_session`)}catch{}k&&console.warn(`[greeting] stale session, retrying with fresh session`);let r=await t();if(!r)return;n=await fetch(`/api/chat`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({greeting:!0,session_key:r})})}if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error||`Something went wrong`)}let r=``,a=``,c=()=>{a&&(r+=a,a=``,s(e=>{let t=[...e];return t[0]={...t[0],content:r},t}))},l=setInterval(c,60);try{for await(let e of i(n)){if(e.error)throw Object.assign(Error(e.error),{fromSSE:!0});e.text&&(a+=e.text),e.type===`component`&&(c(),s(t=>{let n=[...t],r={...n[0]},i=[...r.components||[]];return i.push({name:e.name,data:e.data,submitted:!1}),n[0]={...r,components:i},n}))}}finally{clearInterval(l),c()}}catch(e){if(k&&console.error(`[chat] greeting failed:`,e),r===`auth-required`)return;let t=e instanceof Error?e.message:String(e);s([{role:`maxy`,content:e instanceof Error&&e.fromSSE?t:n??`I'm having trouble connecting right now. Try refreshing the page.`,timestamp:Date.now()}])}finally{d(!1),r===`chat`&&a.current?.focus()}}},[e,t,n,r,a]),p=(0,T.useCallback)(async(o,c)=>{let l=c?.hidden??!1,f=c?.files??[];if(!o&&f.length===0||u.current)return;d(!0);let p=f.map(e=>({filename:e.name,mimeType:e.type})),m={role:`visitor`,content:o,attachments:p.length>0?p:void 0,timestamp:Date.now(),hidden:l},h;s(e=>{let t=[...e,m,{role:`maxy`,content:``,timestamp:Date.now()}];return h=t.length-1,t});try{let n=await t();if(!n)throw Error(`session`);let r=e=>{if(f.length>0){let t=new FormData;t.append(`message`,o),t.append(`session_key`,e);for(let e of f)t.append(`attachments`,e);return{body:t,headers:{}}}return{body:JSON.stringify({message:o,session_key:e}),headers:{"Content-Type":`application/json`}}},{body:a,headers:c}=r(n),l=await fetch(`/api/chat`,{method:`POST`,headers:c,body:a});if(l.status===401){e.current=null;try{sessionStorage.removeItem(`maxy_session`)}catch{}k&&console.warn(`[session-expired] stale key cleared, retrying with fresh session`);let n=await t();if(!n)throw Error(`session`);({body:a,headers:c}=r(n)),l=await fetch(`/api/chat`,{method:`POST`,headers:c,body:a})}if(!l.ok){let e=await l.json().catch(()=>({}));throw Error(e.error||`Something went wrong`)}let u=``,d=``,p=()=>{d&&(u+=d,d=``,s(e=>{let t=[...e];return t[h]={...t[h],content:u},t}))},m=setInterval(p,60);try{for await(let e of i(l)){if(e.error)throw Object.assign(Error(e.error),{fromSSE:!0});e.text&&(d+=e.text),e.type===`component`&&(p(),s(t=>{let n=[...t],r={...n[h]},i=[...r.components||[]];return i.push({name:e.name,data:e.data,submitted:!1}),n[h]={...r,components:i},n}))}}finally{clearInterval(m),p()}}catch(e){if(k&&console.error(`[chat] sendMessage failed:`,e),r===`auth-required`)return;let t=e instanceof Error?e.message:String(e),i=e instanceof Error&&e.fromSSE;s(e=>{let r=[...e];return r[h]={...r[h],content:i?t:t===`session`?n??`I'm having trouble connecting right now. Try refreshing the page.`:`Sorry, I hit a snag. Try again in a moment.`},r})}finally{d(!1),r===`chat`&&a.current?.focus()}},[e,t,n,r,a]);return{messages:o,setMessages:s,isStreaming:c,sendMessage:p,sendGreeting:f,handleComponentSubmit:(0,T.useCallback)((e,t,n)=>{s(n=>{let r=[...n],i={...r[e]},a=[...i.components||[]];return a[t]={...a[t],submitted:!0},r[e]={...i,components:a},r}),p(n,{hidden:!0})},[p])}}var I=c();function L({value:e,onChange:t,onComplete:n,disabled:r}){let i=(0,T.useRef)([]);function a(n,r){r.key===`Backspace`?(r.preventDefault(),e[n]?t(e.slice(0,n)+e.slice(n+1)):n>0&&(t(e.slice(0,n-1)+e.slice(n)),i.current[n-1]?.focus())):r.key===`ArrowLeft`&&n>0?i.current[n-1]?.focus():r.key===`ArrowRight`&&n<5&&i.current[n+1]?.focus()}function o(r,a){let o=a.nativeEvent.data;if(!o||!/^\d$/.test(o))return;let s=e.split(``);for(s[r]=o;s.length<r;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);t(c),c.length===6?n(c):r<5&&i.current[r+1]?.focus()}function s(e){e.preventDefault();let r=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);r&&(t(r),r.length===6?n(r):i.current[r.length]?.focus())}return(0,I.jsx)(`div`,{className:`gate-otp-field`,children:Array.from({length:6}).map((t,n)=>(0,I.jsx)(`input`,{ref:e=>{i.current[n]=e},type:`text`,inputMode:`numeric`,className:`pin-box${e[n]?` pin-box-filled`:``}`,value:e[n]||``,onKeyDown:e=>a(n,e),onInput:e=>o(n,e),onPaste:s,onFocus:e=>e.target.select(),autoFocus:n===0,autoComplete:`off`,maxLength:1,disabled:r,"aria-label":`Code digit ${n+1}`},n))})}function R({gateState:e,setGateState:t,grantInfo:n,setGrantInfo:r,gateSessionKeyRef:i,resolvedSlugRef:a,onAuthenticated:o}){let[c,l]=(0,T.useState)(``),[u,d]=(0,T.useState)(``),[f,p]=(0,T.useState)(``),[m,h]=(0,T.useState)(!1),[g,_]=(0,T.useState)(``),[v,b]=(0,T.useState)(``),[x,S]=(0,T.useState)(!1),[C,w]=(0,T.useState)(null),[E,D]=(0,T.useState)(null),[O,k]=(0,T.useState)(!1);async function A(e){if(e.preventDefault(),!O){w(null),k(!0);try{let e=a.current,n=await fetch(`/api/access/login`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({contact:c,password:u,agentSlug:e})}),i=await n.json();n.ok?o(i.session_key):n.status===401?i.error?.includes(`setup not complete`)||i.error?.includes(`invitation link`)?(t(`private-agent`),w(null)):w(i.error||`Invalid credentials`):n.status===403?(t(`access-expired`),i.expiresAt&&r(e=>e?{...e,expiresAt:i.expiresAt}:{displayName:null,contactValue:c,contactMethod:`email`,expiresAt:i.expiresAt,status:`expired`})):n.status===429?w(i.error||`Too many attempts. Please try again later.`):w(i.error||`Something went wrong`)}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}async function j(e){if(!O){w(null),k(!0);try{let n=a.current,o=await fetch(`/api/access/verify-otp`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({phone:v,code:e,agentSlug:n})}),s=await o.json();o.ok?(i.current=s.session_key,r(s.grant),t(`set-password`)):o.status===429?t(`otp-failed`):(w(s.error||`Invalid code`),_(``))}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}async function P(e){if(e.preventDefault(),!O){if(w(null),u!==f){w(`Passwords do not match`);return}if(!M(u).every(e=>e.met)){w(`Password does not meet requirements`);return}k(!0);try{let e=i.current;if(!e){w(`Session expired. Please use your invitation link again.`);return}let t=await fetch(`/api/access/create-credentials`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({session_key:e,password:u})}),n=await t.json();t.ok?o(n.session_key):t.status===400&&n.requirements?w(n.requirements.filter(e=>!e.met).map(e=>e.label).join(`, `)):w(n.error||`Something went wrong`)}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}async function F(e){if(e.preventDefault(),!(O||!c)){w(null),k(!0);try{let e=a.current,t=await fetch(`/api/access/forgot-password`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({contact:c,agentSlug:e})});t.status===429?w((await t.json()).error||`Too many attempts. Please try again later.`):(D(`If an account exists, a reset link has been sent.`),setTimeout(()=>{S(!1),D(null)},4e3))}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}switch(e){case`set-password`:{let e=M(u),t=e.every(e=>e.met),r=u===f,i=t&&r&&f.length>0;return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[n?.expiresAt&&(0,I.jsxs)(`div`,{className:`gate-expiry-badge`,children:[`Access until `,new Date(n.expiresAt).toLocaleDateString(`en-GB`,{day:`numeric`,month:`short`,year:`numeric`})]}),(0,I.jsxs)(`h2`,{className:`gate-title`,children:[`Welcome, `,n?.displayName||`there`]}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:`Create a password to access this agent in the future.`}),(0,I.jsxs)(`form`,{className:`gate-form`,onSubmit:P,children:[(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-display-name`,children:`Display name`}),(0,I.jsx)(`input`,{id:`gate-display-name`,type:`text`,value:n?.displayName||``,disabled:!0})]}),(0,I.jsxs)(`div`,{className:`gate-field gate-pw-row`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-pw`,children:`Password`}),(0,I.jsx)(`div`,{className:`gate-pw-toggle`,children:(0,I.jsx)(s,{checked:m,onChange:h,label:`Show`})}),(0,I.jsx)(`input`,{id:`gate-pw`,type:m?`text`:`password`,value:u,onChange:e=>d(e.target.value),placeholder:`Your password`,autoFocus:!0,autoComplete:`new-password`}),u.length>0&&(0,I.jsx)(`div`,{className:`gate-strength`,children:e.map(e=>(0,I.jsxs)(`span`,{className:`gate-strength-item${e.met?` met`:``}`,children:[e.met?`✓`:`○`,` `,e.label]},e.key))})]}),(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-pw-confirm`,children:`Confirm password`}),(0,I.jsx)(`input`,{id:`gate-pw-confirm`,type:m?`text`:`password`,value:f,onChange:e=>p(e.target.value),placeholder:`Confirm your password`,autoComplete:`new-password`}),f.length>0&&!r&&(0,I.jsx)(`div`,{className:`gate-error`,children:`Passwords do not match`})]}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),(0,I.jsx)(`div`,{className:`gate-submit`,children:(0,I.jsx)(y,{variant:`primary`,type:`submit`,fullWidth:!0,disabled:!i||O,loading:O,children:`Create account & enter chat`})})]})]})})}case`enter-otp`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`h2`,{className:`gate-title`,children:`Enter your code`}),(0,I.jsxs)(`p`,{className:`gate-subtitle`,children:[`Enter the 6-digit code sent to `,N(v,`phone`)]}),(0,I.jsx)(L,{value:g,onChange:_,onComplete:j,disabled:O}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),O&&(0,I.jsxs)(`div`,{className:`gate-loading`,children:[(0,I.jsx)(`span`,{className:`spinner`}),`Verifying...`]}),(0,I.jsx)(`div`,{className:`gate-resend`,children:(0,I.jsx)(`button`,{type:`button`,className:`gate-link`,onClick:()=>{t(`sign-in`),_(``),w(null)},children:`Use a different number`})})]})});case`sign-in`:case`private-agent`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`h2`,{className:`gate-title`,children:e===`private-agent`?`Private Agent`:`Welcome back`}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:e===`private-agent`?`This agent is invitation-only. If you have an account, sign in below.`:`Sign in to continue your conversation.`}),x?(0,I.jsxs)(`form`,{className:`gate-form`,onSubmit:F,children:[(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-forgot-contact`,children:`Email or phone`}),(0,I.jsx)(`input`,{id:`gate-forgot-contact`,type:`text`,value:c,onChange:e=>l(e.target.value),placeholder:`you@example.com`,autoFocus:!0,autoComplete:`email`})]}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),E&&(0,I.jsx)(`div`,{className:`gate-success`,children:E}),(0,I.jsx)(`div`,{className:`gate-submit`,children:(0,I.jsx)(y,{variant:`primary`,type:`submit`,fullWidth:!0,disabled:!c.trim()||O,loading:O,children:`Send reset link`})}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(`button`,{type:`button`,className:`gate-link`,onClick:()=>{S(!1),w(null),D(null)},children:`Back to sign in`})})]}):(0,I.jsxs)(`form`,{className:`gate-form`,onSubmit:A,children:[(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-contact`,children:`Email or phone`}),(0,I.jsx)(`input`,{id:`gate-contact`,type:`text`,value:c,onChange:e=>l(e.target.value),placeholder:`you@example.com`,autoFocus:!0,autoComplete:`email`})]}),(0,I.jsxs)(`div`,{className:`gate-field gate-pw-row`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-login-pw`,children:`Password`}),(0,I.jsx)(`div`,{className:`gate-pw-toggle`,children:(0,I.jsx)(s,{checked:m,onChange:h,label:`Show`})}),(0,I.jsx)(`input`,{id:`gate-login-pw`,type:m?`text`:`password`,value:u,onChange:e=>d(e.target.value),placeholder:`Your password`,autoComplete:`current-password`})]}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),(0,I.jsx)(`div`,{className:`gate-submit`,children:(0,I.jsx)(y,{variant:`primary`,type:`submit`,fullWidth:!0,disabled:!c.trim()||!u||O,loading:O,children:`Sign in`})}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(`button`,{type:`button`,className:`gate-link`,onClick:()=>{S(!0),w(null)},children:`Forgot password?`})})]}),e===`private-agent`&&(0,I.jsx)(`p`,{className:`gate-hint`,children:`No account? You need an invitation from the agent owner.`})]})});case`access-expired`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`div`,{className:`gate-icon`,children:`⏰`}),(0,I.jsx)(`h2`,{className:`gate-title`,children:`Access expired`}),(0,I.jsxs)(`p`,{className:`gate-subtitle`,children:[n?.expiresAt?`Your access expired on ${new Date(n.expiresAt).toLocaleDateString(`en-GB`,{day:`numeric`,month:`long`,year:`numeric`})}.`:`Your access to this agent has expired.`,` `,`Contact the agent owner to renew.`]}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(y,{variant:`secondary`,onClick:()=>{t(`sign-in`),w(null),d(``)},children:`Sign in with a different account`})})]})});case`link-expired`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`div`,{className:`gate-icon`,children:`⚠️`}),(0,I.jsx)(`h2`,{className:`gate-title`,children:`Link expired`}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:`This invitation link has expired or has already been used. If you already set your password, sign in below. Otherwise, ask the agent owner to send a new invitation.`}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(y,{variant:`primary`,onClick:()=>{t(`sign-in`),w(null)},children:`Go to sign in`})})]})});case`otp-failed`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`div`,{className:`gate-icon`,children:`⛔`}),(0,I.jsx)(`h2`,{className:`gate-title`,children:`Too many attempts`}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:`You've entered the wrong code too many times. Ask the agent owner to send a new code.`}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(y,{variant:`secondary`,onClick:()=>{t(`sign-in`),w(null),_(``)},children:`Back to sign in`})})]})})}}var z={"single-select":a,"multi-select":u};function B({name:e,data:t,onSubmit:n,submitted:r}){let i=z[e];return i?(0,I.jsx)(i,{data:t,onSubmit:n,submitted:r}):(console.warn(`[PublicComponentRenderer] Unknown component: "${e}". Registered: ${Object.keys(z).join(`, `)}`),(0,I.jsx)(`div`,{className:`component-card component-card--error`,children:(0,I.jsxs)(`p`,{style:{fontFamily:`var(--font-body)`,fontSize:12,color:`var(--text-secondary)`},children:[`Component “`,e,`” is not available. This may require a platform update.`]})}))}function V({messages:t,isStreaming:i,sessionError:a,selectionMode:o,selectedItems:c,toggleSelectItem:l,onComponentSubmit:u,remainingSuggestions:d,onSuggestionClick:f,isAtBottom:p,setIsAtBottom:m}){let[h,v]=(0,T.useState)(new Set),[b,x]=(0,T.useState)(new Set),S=(0,T.useRef)(null),C=(0,T.useRef)(null),w=(0,T.useRef)(null),E=(0,T.useRef)(!1),D=(0,T.useRef)(!1),O=(0,T.useRef)(!1),k=(0,T.useRef)(0),A=(0,T.useRef)(!1),j=(0,T.useRef)(new Map);A.current=i,(0,T.useEffect)(()=>{i&&!E.current&&p&&(D.current=!0)},[i,p]),(0,T.useEffect)(()=>{if(!D.current&&!p){E.current=i,O.current=!1;return}if(E.current&&!i&&w.current&&C.current){let e=w.current;if(e.offsetHeight>C.current.clientHeight){D.current=!1,O.current=!0,e.scrollIntoView({behavior:`smooth`,block:`start`}),E.current=i;return}}(p||D.current)&&!O.current&&S.current?.scrollIntoView({behavior:`smooth`}),E.current=i},[t,p,i]),(0,T.useEffect)(()=>{let e=C.current;if(!e)return;let t=()=>{let t=e.scrollTop;A.current&&t<k.current&&(D.current=!1),k.current=t,m(e.scrollHeight-e.scrollTop-e.clientHeight<80)};return e.addEventListener(`scroll`,t,{passive:!0}),t(),()=>e.removeEventListener(`scroll`,t)},[m]),(0,T.useEffect)(()=>{function e(){x(e=>{let t=new Set;return j.current.forEach((n,r)=>{n.isConnected&&(h.has(r)?e.has(r)&&t.add(r):n.scrollHeight>n.clientHeight+2&&t.add(r))}),e.size===t.size&&[...e].every(e=>t.has(e))?e:t})}e();let t=new ResizeObserver(e);return j.current.forEach(e=>t.observe(e)),()=>t.disconnect()},[t.length,h]);let M=t.reduce((e,t,n)=>t.role===`maxy`&&!t.hidden?n:e,-1);return(0,I.jsxs)(`div`,{className:`chat-messages-wrap`,children:[o&&(0,I.jsx)(`div`,{className:`selection-overlay-band`}),(0,I.jsxs)(`div`,{className:`chat-messages`,ref:C,children:[a&&t.length===0&&(0,I.jsx)(`div`,{className:`session-error`,children:(0,I.jsx)(`p`,{children:a})}),t.map((a,d)=>{if(a.hidden)return null;let f=i&&d===t.length-1,p=`${a.timestamp}_${a.role}`,m=c.has(p);return(0,I.jsxs)(`div`,{ref:d===M?w:void 0,className:`message ${a.role}${m?` selected`:``}`,children:[o&&!f&&(0,I.jsx)(`div`,{className:`message-select-check`,children:(0,I.jsx)(s,{checked:m,onChange:()=>l(p)})}),(0,I.jsxs)(`div`,{className:`bubble`,children:[a.role===`visitor`&&a.content?(()=>{let e=h.has(d),t=b.has(d);return(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(`div`,{ref:e=>{e?j.current.set(d,e):j.current.delete(d)},className:e?`bubble-content`:`bubble-content clamped${t?` overflowing`:``}`,children:a.content}),t&&(0,I.jsx)(`div`,{className:`bubble-expand`,children:(0,I.jsx)(y,{variant:`icon`,className:e?`expanded`:``,onClick:()=>v(e=>{let t=new Set(e);return t.has(d)?t.delete(d):t.add(d),t}),"aria-label":e?`Collapse message`:`Expand message`,children:(0,I.jsx)(r,{size:14})})})]})})():a.content?(0,I.jsx)(_,{content:a.content,timestamp:g(a.timestamp)}):(0,I.jsxs)(`span`,{className:`typing-indicator`,children:[(0,I.jsx)(`span`,{className:`typing-dot`}),(0,I.jsx)(`span`,{className:`typing-dot`}),(0,I.jsx)(`span`,{className:`typing-dot`})]}),a.attachments&&a.attachments.length>0&&(0,I.jsx)(`div`,{className:`message-attachments`,children:a.attachments.map((t,r)=>(0,I.jsxs)(`span`,{className:`message-attachment-chip no-action`,children:[t.mimeType===`application/pdf`?(0,I.jsx)(n,{size:12}):t.mimeType.startsWith(`text/`)?(0,I.jsx)(e,{size:12}):null,(0,I.jsx)(`span`,{children:t.filename})]},r))}),a.role===`visitor`&&(0,I.jsx)(`span`,{className:`message-timestamp`,children:g(a.timestamp)})]}),a.role===`maxy`&&a.components&&a.components.length>0&&(0,I.jsx)(`div`,{className:`public-components`,children:a.components.map((e,t)=>(0,I.jsx)(`div`,{className:`public-component-enter`,children:(0,I.jsx)(B,{name:e.name,data:e.data,onSubmit:e=>u(d,t,e),submitted:e.submitted})},t))})]},d)}),!i&&d.length>0&&(0,I.jsx)(`div`,{className:`chat-suggestions`,children:d.map(e=>(0,I.jsx)(y,{variant:`suggestion`,onClick:()=>f(e),children:e},e))}),(0,I.jsx)(`div`,{ref:S})]}),!p&&(0,I.jsx)(`button`,{className:`scroll-to-bottom`,onClick:()=>S.current?.scrollIntoView({behavior:`smooth`}),"aria-label":`Scroll to bottom`,children:(0,I.jsx)(r,{size:18})})]})}function H({selectedItems:e,messages:t,copySelected:n,exitSelection:r,showCopyToast:i}){let[a,o]=(0,T.useState)(!1),s=(0,T.useRef)(null),c=(0,T.useRef)(!1);function l(e){let n=e.lastIndexOf(`_`),r=e.slice(0,n),i=e.slice(n+1),a=parseInt(r,10);return t.find(e=>e.timestamp===a&&e.role===i)?.content??``}function u(e,t){return parseInt(e.slice(0,e.lastIndexOf(`_`)),10)-parseInt(t.slice(0,t.lastIndexOf(`_`)),10)}function d(){let e=[...t].sort((e,t)=>e.timestamp-t.timestamp),n=[];for(let t of e){if(t.hidden||!t.content)continue;let e=t.role===`visitor`?`Visitor`:`Maxy`;n.push(`${e}:\n${t.content}`)}return n.join(`
1
+ import{C as e,D as t,S as n,T as r,_ as i,a,b as o,d as s,f as c,h as l,i as u,j as d,k as f,m as p,n as m,p as h,r as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./ChatInput-B2MUVSm4.js";var w=t(),T=d(f(),1),E=`image/jpeg,image/png,image/gif,image/webp,application/pdf,text/plain,text/markdown,text/csv,text/html`,D=new Set(E.split(`,`)),O=20*1024*1024,k=typeof window<`u`&&/^(localhost|127\.0\.0\.1|[a-z0-9-]+\.local)(:|$)/.test(window.location.hostname);function A(){let e=crypto.getRandomValues(new Uint8Array(16));e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t=[...e].map(e=>e.toString(16).padStart(2,`0`)).join(``);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}function j(){let e=window.location.pathname.match(/^\/([a-z][a-z0-9-]{2,49})$/);return e?e[1]:void 0}function M(e){return[{key:`length`,label:`At least 8 characters`,met:e.length>=8},{key:`number`,label:`Contains a number`,met:/\d/.test(e)},{key:`special`,label:`Contains a special character`,met:/[^A-Za-z0-9]/.test(e)},{key:`whitespace`,label:`No leading or trailing spaces`,met:e.length>0&&e===e.trim()}]}function N(e,t){if(t===`phone`)return e.length>4?e.slice(0,e.length-4).replace(/\d/g,`•`)+` `+e.slice(-4):e;let n=e.indexOf(`@`);return n<=2?e:e.slice(0,2)+`•••`+e.slice(n)}function P(e){let[t,n]=(0,T.useState)(null),[r,i]=(0,T.useState)(null),[a,o]=(0,T.useState)(`loading`),[s,c]=(0,T.useState)(``),[l,u]=(0,T.useState)(null),[d,f]=(0,T.useState)(null),[p,m]=(0,T.useState)(!1),[h,g]=(0,T.useState)(null),_=(0,T.useMemo)(()=>j(),[]),v=(0,T.useRef)(_||``),y=(0,T.useRef)(null),b=(0,T.useRef)(null),x=(0,T.useRef)(!1),S=(0,T.useRef)(null),[C,w]=(0,T.useState)(`sign-in`),[E,D]=(0,T.useState)(null),O=(0,T.useRef)(null),M=(0,T.useRef)(null);(0,T.useEffect)(()=>{try{let e=sessionStorage.getItem(`maxy_session`);e&&(M.current=e)}catch{}},[]);let N=(0,T.useCallback)(t=>{y.current=t,n(t);try{sessionStorage.setItem(`maxy_session`,t)}catch{}o(`chat`),e(t)},[e]),P=(0,T.useCallback)(()=>{y.current=null;try{sessionStorage.removeItem(`maxy_session`)}catch{}n(null),o(`auth-required`),w(`sign-in`)},[]),F=(0,T.useCallback)(async e=>{try{let t=v.current,n=await fetch(`/api/access/verify-token`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:e,agentSlug:t})}),r=await n.json();n.ok?(O.current=r.session_key,D(r.grant),w(`set-password`),window.history.replaceState({},``,window.location.pathname)):w(`link-expired`)}catch{w(`sign-in`)}},[]);return{sessionId:t,sessionKeyRef:y,sessionError:r,pageState:a,setPageState:o,agentDisplayName:s,agentImage:l,agentImageShape:d,showAgentName:p,agentSlug:_,branding:h,resolvedSlugRef:v,ensureSession:(0,T.useCallback)(async()=>{if(y.current)return y.current;if(b.current)return b.current;let e=(async()=>{try{let e=M.current,t=await fetch(`/api/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({session_id:A(),..._?{agent:_}:{},...e?{session_key:e}:{}})});if(!t.ok){let e=await t.json().catch(()=>({error:``}));return k&&console.error(`[session] POST /api/session failed: ${t.status} ${t.statusText}`,e),t.status===404?i(e.error||`Agent not found`):t.status===503&&i(e.error||`Service unavailable`),null}let r=await t.json();if(r.auth_required){r.agent_id&&(v.current=r.agent_id),c(r.displayName||``),r.agentImage&&u(r.agentImage),r.agentImageShape&&f(r.agentImageShape),r.showAgentName&&m(r.showAgentName),r.branding&&g(r.branding),o(`auth-required`);let e=new URLSearchParams(window.location.search).get(`token`);return e?F(e):w(`sign-in`),null}y.current=r.session_key,n(r.session_key),r.displayName&&c(r.displayName),r.agentImage&&u(r.agentImage),r.agentImageShape&&f(r.agentImageShape),r.showAgentName&&m(r.showAgentName),r.branding&&g(r.branding),o(`chat`),r.resumed&&r.messages&&(x.current=!0,S.current=r.messages);try{sessionStorage.setItem(`maxy_session`,r.session_key)}catch{}return r.session_key}catch(e){return k&&console.error(`[session] fetch /api/session threw:`,e),i(`Unable to connect. Please check your connection and try again.`),null}finally{b.current=null}})();return b.current=e,e},[_,F]),enterChat:N,enterGate:P,resumedRef:x,resumeMessagesRef:S,gateState:C,setGateState:w,grantInfo:E,setGrantInfo:D,gateSessionKeyRef:O}}function F({sessionKeyRef:e,ensureSession:t,sessionError:n,pageState:r,inputRef:a}){let[o,s]=(0,T.useState)([]),[c,l]=(0,T.useState)(!1),u=(0,T.useRef)(!1),d=e=>{u.current=e,l(e)},f=(0,T.useCallback)(async o=>{if(!u.current){d(!0),s([{role:`maxy`,content:``,timestamp:Date.now()}]);try{let n=await fetch(`/api/chat`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({greeting:!0,session_key:o})});if(n.status===401){e.current=null;try{sessionStorage.removeItem(`maxy_session`)}catch{}k&&console.warn(`[greeting] stale session, retrying with fresh session`);let r=await t();if(!r)return;n=await fetch(`/api/chat`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({greeting:!0,session_key:r})})}if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error||`Something went wrong`)}let r=``,a=``,c=()=>{a&&(r+=a,a=``,s(e=>{let t=[...e];return t[0]={...t[0],content:r},t}))},l=setInterval(c,60);try{for await(let e of i(n)){if(e.error)throw Object.assign(Error(e.error),{fromSSE:!0});e.text&&(a+=e.text),e.type===`component`&&(c(),s(t=>{let n=[...t],r={...n[0]},i=[...r.components||[]];return i.push({name:e.name,data:e.data,submitted:!1}),n[0]={...r,components:i},n}))}}finally{clearInterval(l),c()}}catch(e){if(k&&console.error(`[chat] greeting failed:`,e),r===`auth-required`)return;let t=e instanceof Error?e.message:String(e);s([{role:`maxy`,content:e instanceof Error&&e.fromSSE?t:n??`I'm having trouble connecting right now. Try refreshing the page.`,timestamp:Date.now()}])}finally{d(!1),r===`chat`&&a.current?.focus()}}},[e,t,n,r,a]),p=(0,T.useCallback)(async(o,c)=>{let l=c?.hidden??!1,f=c?.files??[];if(!o&&f.length===0||u.current)return;d(!0);let p=f.map(e=>({filename:e.name,mimeType:e.type})),m={role:`visitor`,content:o,attachments:p.length>0?p:void 0,timestamp:Date.now(),hidden:l},h;s(e=>{let t=[...e,m,{role:`maxy`,content:``,timestamp:Date.now()}];return h=t.length-1,t});try{let n=await t();if(!n)throw Error(`session`);let r=e=>{if(f.length>0){let t=new FormData;t.append(`message`,o),t.append(`session_key`,e);for(let e of f)t.append(`attachments`,e);return{body:t,headers:{}}}return{body:JSON.stringify({message:o,session_key:e}),headers:{"Content-Type":`application/json`}}},{body:a,headers:c}=r(n),l=await fetch(`/api/chat`,{method:`POST`,headers:c,body:a});if(l.status===401){e.current=null;try{sessionStorage.removeItem(`maxy_session`)}catch{}k&&console.warn(`[session-expired] stale key cleared, retrying with fresh session`);let n=await t();if(!n)throw Error(`session`);({body:a,headers:c}=r(n)),l=await fetch(`/api/chat`,{method:`POST`,headers:c,body:a})}if(!l.ok){let e=await l.json().catch(()=>({}));throw Error(e.error||`Something went wrong`)}let u=``,d=``,p=()=>{d&&(u+=d,d=``,s(e=>{let t=[...e];return t[h]={...t[h],content:u},t}))},m=setInterval(p,60);try{for await(let e of i(l)){if(e.error)throw Object.assign(Error(e.error),{fromSSE:!0});e.text&&(d+=e.text),e.type===`component`&&(p(),s(t=>{let n=[...t],r={...n[h]},i=[...r.components||[]];return i.push({name:e.name,data:e.data,submitted:!1}),n[h]={...r,components:i},n}))}}finally{clearInterval(m),p()}}catch(e){if(k&&console.error(`[chat] sendMessage failed:`,e),r===`auth-required`)return;let t=e instanceof Error?e.message:String(e),i=e instanceof Error&&e.fromSSE;s(e=>{let r=[...e];return r[h]={...r[h],content:i?t:t===`session`?n??`I'm having trouble connecting right now. Try refreshing the page.`:`Sorry, I hit a snag. Try again in a moment.`},r})}finally{d(!1),r===`chat`&&a.current?.focus()}},[e,t,n,r,a]);return{messages:o,setMessages:s,isStreaming:c,sendMessage:p,sendGreeting:f,handleComponentSubmit:(0,T.useCallback)((e,t,n)=>{s(n=>{let r=[...n],i={...r[e]},a=[...i.components||[]];return a[t]={...a[t],submitted:!0},r[e]={...i,components:a},r}),p(n,{hidden:!0})},[p])}}var I=c();function L({value:e,onChange:t,onComplete:n,disabled:r}){let i=(0,T.useRef)([]);function a(n,r){r.key===`Backspace`?(r.preventDefault(),e[n]?t(e.slice(0,n)+e.slice(n+1)):n>0&&(t(e.slice(0,n-1)+e.slice(n)),i.current[n-1]?.focus())):r.key===`ArrowLeft`&&n>0?i.current[n-1]?.focus():r.key===`ArrowRight`&&n<5&&i.current[n+1]?.focus()}function o(r,a){let o=a.nativeEvent.data;if(!o||!/^\d$/.test(o))return;let s=e.split(``);for(s[r]=o;s.length<r;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);t(c),c.length===6?n(c):r<5&&i.current[r+1]?.focus()}function s(e){e.preventDefault();let r=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);r&&(t(r),r.length===6?n(r):i.current[r.length]?.focus())}return(0,I.jsx)(`div`,{className:`gate-otp-field`,children:Array.from({length:6}).map((t,n)=>(0,I.jsx)(`input`,{ref:e=>{i.current[n]=e},type:`text`,inputMode:`numeric`,className:`pin-box${e[n]?` pin-box-filled`:``}`,value:e[n]||``,onKeyDown:e=>a(n,e),onInput:e=>o(n,e),onPaste:s,onFocus:e=>e.target.select(),autoFocus:n===0,autoComplete:`off`,maxLength:1,disabled:r,"aria-label":`Code digit ${n+1}`},n))})}function R({gateState:e,setGateState:t,grantInfo:n,setGrantInfo:r,gateSessionKeyRef:i,resolvedSlugRef:a,onAuthenticated:o}){let[c,l]=(0,T.useState)(``),[u,d]=(0,T.useState)(``),[f,p]=(0,T.useState)(``),[m,h]=(0,T.useState)(!1),[g,_]=(0,T.useState)(``),[v,b]=(0,T.useState)(``),[x,S]=(0,T.useState)(!1),[C,w]=(0,T.useState)(null),[E,D]=(0,T.useState)(null),[O,k]=(0,T.useState)(!1);async function A(e){if(e.preventDefault(),!O){w(null),k(!0);try{let e=a.current,n=await fetch(`/api/access/login`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({contact:c,password:u,agentSlug:e})}),i=await n.json();n.ok?o(i.session_key):n.status===401?i.error?.includes(`setup not complete`)||i.error?.includes(`invitation link`)?(t(`private-agent`),w(null)):w(i.error||`Invalid credentials`):n.status===403?(t(`access-expired`),i.expiresAt&&r(e=>e?{...e,expiresAt:i.expiresAt}:{displayName:null,contactValue:c,contactMethod:`email`,expiresAt:i.expiresAt,status:`expired`})):n.status===429?w(i.error||`Too many attempts. Please try again later.`):w(i.error||`Something went wrong`)}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}async function j(e){if(!O){w(null),k(!0);try{let n=a.current,o=await fetch(`/api/access/verify-otp`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({phone:v,code:e,agentSlug:n})}),s=await o.json();o.ok?(i.current=s.session_key,r(s.grant),t(`set-password`)):o.status===429?t(`otp-failed`):(w(s.error||`Invalid code`),_(``))}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}async function P(e){if(e.preventDefault(),!O){if(w(null),u!==f){w(`Passwords do not match`);return}if(!M(u).every(e=>e.met)){w(`Password does not meet requirements`);return}k(!0);try{let e=i.current;if(!e){w(`Session expired. Please use your invitation link again.`);return}let t=await fetch(`/api/access/create-credentials`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({session_key:e,password:u})}),n=await t.json();t.ok?o(n.session_key):t.status===400&&n.requirements?w(n.requirements.filter(e=>!e.met).map(e=>e.label).join(`, `)):w(n.error||`Something went wrong`)}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}async function F(e){if(e.preventDefault(),!(O||!c)){w(null),k(!0);try{let e=a.current,t=await fetch(`/api/access/forgot-password`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({contact:c,agentSlug:e})});t.status===429?w((await t.json()).error||`Too many attempts. Please try again later.`):(D(`If an account exists, a reset link has been sent.`),setTimeout(()=>{S(!1),D(null)},4e3))}catch{w(`Unable to connect. Please try again.`)}finally{k(!1)}}}switch(e){case`set-password`:{let e=M(u),t=e.every(e=>e.met),r=u===f,i=t&&r&&f.length>0;return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[n?.expiresAt&&(0,I.jsxs)(`div`,{className:`gate-expiry-badge`,children:[`Access until `,new Date(n.expiresAt).toLocaleDateString(`en-GB`,{day:`numeric`,month:`short`,year:`numeric`})]}),(0,I.jsxs)(`h2`,{className:`gate-title`,children:[`Welcome, `,n?.displayName||`there`]}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:`Create a password to access this agent in the future.`}),(0,I.jsxs)(`form`,{className:`gate-form`,onSubmit:P,children:[(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-display-name`,children:`Display name`}),(0,I.jsx)(`input`,{id:`gate-display-name`,type:`text`,value:n?.displayName||``,disabled:!0})]}),(0,I.jsxs)(`div`,{className:`gate-field gate-pw-row`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-pw`,children:`Password`}),(0,I.jsx)(`div`,{className:`gate-pw-toggle`,children:(0,I.jsx)(s,{checked:m,onChange:h,label:`Show`})}),(0,I.jsx)(`input`,{id:`gate-pw`,type:m?`text`:`password`,value:u,onChange:e=>d(e.target.value),placeholder:`Your password`,autoFocus:!0,autoComplete:`new-password`}),u.length>0&&(0,I.jsx)(`div`,{className:`gate-strength`,children:e.map(e=>(0,I.jsxs)(`span`,{className:`gate-strength-item${e.met?` met`:``}`,children:[e.met?`✓`:`○`,` `,e.label]},e.key))})]}),(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-pw-confirm`,children:`Confirm password`}),(0,I.jsx)(`input`,{id:`gate-pw-confirm`,type:m?`text`:`password`,value:f,onChange:e=>p(e.target.value),placeholder:`Confirm your password`,autoComplete:`new-password`}),f.length>0&&!r&&(0,I.jsx)(`div`,{className:`gate-error`,children:`Passwords do not match`})]}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),(0,I.jsx)(`div`,{className:`gate-submit`,children:(0,I.jsx)(y,{variant:`primary`,type:`submit`,fullWidth:!0,disabled:!i||O,loading:O,children:`Create account & enter chat`})})]})]})})}case`enter-otp`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`h2`,{className:`gate-title`,children:`Enter your code`}),(0,I.jsxs)(`p`,{className:`gate-subtitle`,children:[`Enter the 6-digit code sent to `,N(v,`phone`)]}),(0,I.jsx)(L,{value:g,onChange:_,onComplete:j,disabled:O}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),O&&(0,I.jsxs)(`div`,{className:`gate-loading`,children:[(0,I.jsx)(`span`,{className:`spinner`}),`Verifying...`]}),(0,I.jsx)(`div`,{className:`gate-resend`,children:(0,I.jsx)(`button`,{type:`button`,className:`gate-link`,onClick:()=>{t(`sign-in`),_(``),w(null)},children:`Use a different number`})})]})});case`sign-in`:case`private-agent`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`h2`,{className:`gate-title`,children:e===`private-agent`?`Private Agent`:`Welcome back`}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:e===`private-agent`?`This agent is invitation-only. If you have an account, sign in below.`:`Sign in to continue your conversation.`}),x?(0,I.jsxs)(`form`,{className:`gate-form`,onSubmit:F,children:[(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-forgot-contact`,children:`Email or phone`}),(0,I.jsx)(`input`,{id:`gate-forgot-contact`,type:`text`,value:c,onChange:e=>l(e.target.value),placeholder:`you@example.com`,autoFocus:!0,autoComplete:`email`})]}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),E&&(0,I.jsx)(`div`,{className:`gate-success`,children:E}),(0,I.jsx)(`div`,{className:`gate-submit`,children:(0,I.jsx)(y,{variant:`primary`,type:`submit`,fullWidth:!0,disabled:!c.trim()||O,loading:O,children:`Send reset link`})}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(`button`,{type:`button`,className:`gate-link`,onClick:()=>{S(!1),w(null),D(null)},children:`Back to sign in`})})]}):(0,I.jsxs)(`form`,{className:`gate-form`,onSubmit:A,children:[(0,I.jsxs)(`div`,{className:`gate-field`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-contact`,children:`Email or phone`}),(0,I.jsx)(`input`,{id:`gate-contact`,type:`text`,value:c,onChange:e=>l(e.target.value),placeholder:`you@example.com`,autoFocus:!0,autoComplete:`email`})]}),(0,I.jsxs)(`div`,{className:`gate-field gate-pw-row`,children:[(0,I.jsx)(`label`,{htmlFor:`gate-login-pw`,children:`Password`}),(0,I.jsx)(`div`,{className:`gate-pw-toggle`,children:(0,I.jsx)(s,{checked:m,onChange:h,label:`Show`})}),(0,I.jsx)(`input`,{id:`gate-login-pw`,type:m?`text`:`password`,value:u,onChange:e=>d(e.target.value),placeholder:`Your password`,autoComplete:`current-password`})]}),C&&(0,I.jsx)(`div`,{className:`gate-error`,children:C}),(0,I.jsx)(`div`,{className:`gate-submit`,children:(0,I.jsx)(y,{variant:`primary`,type:`submit`,fullWidth:!0,disabled:!c.trim()||!u||O,loading:O,children:`Sign in`})}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(`button`,{type:`button`,className:`gate-link`,onClick:()=>{S(!0),w(null)},children:`Forgot password?`})})]}),e===`private-agent`&&(0,I.jsx)(`p`,{className:`gate-hint`,children:`No account? You need an invitation from the agent owner.`})]})});case`access-expired`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`div`,{className:`gate-icon`,children:`⏰`}),(0,I.jsx)(`h2`,{className:`gate-title`,children:`Access expired`}),(0,I.jsxs)(`p`,{className:`gate-subtitle`,children:[n?.expiresAt?`Your access expired on ${new Date(n.expiresAt).toLocaleDateString(`en-GB`,{day:`numeric`,month:`long`,year:`numeric`})}.`:`Your access to this agent has expired.`,` `,`Contact the agent owner to renew.`]}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(y,{variant:`secondary`,onClick:()=>{t(`sign-in`),w(null),d(``)},children:`Sign in with a different account`})})]})});case`link-expired`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`div`,{className:`gate-icon`,children:`⚠️`}),(0,I.jsx)(`h2`,{className:`gate-title`,children:`Link expired`}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:`This invitation link has expired or has already been used. If you already set your password, sign in below. Otherwise, ask the agent owner to send a new invitation.`}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(y,{variant:`primary`,onClick:()=>{t(`sign-in`),w(null)},children:`Go to sign in`})})]})});case`otp-failed`:return(0,I.jsx)(`div`,{className:`gate-wrap`,children:(0,I.jsxs)(`div`,{className:`gate-card`,children:[(0,I.jsx)(`div`,{className:`gate-icon`,children:`⛔`}),(0,I.jsx)(`h2`,{className:`gate-title`,children:`Too many attempts`}),(0,I.jsx)(`p`,{className:`gate-subtitle`,children:`You've entered the wrong code too many times. Ask the agent owner to send a new code.`}),(0,I.jsx)(`div`,{className:`gate-actions`,children:(0,I.jsx)(y,{variant:`secondary`,onClick:()=>{t(`sign-in`),w(null),_(``)},children:`Back to sign in`})})]})})}}var z={"single-select":a,"multi-select":u};function B({name:e,data:t,onSubmit:n,submitted:r}){let i=z[e];return i?(0,I.jsx)(i,{data:t,onSubmit:n,submitted:r}):(console.warn(`[PublicComponentRenderer] Unknown component: "${e}". Registered: ${Object.keys(z).join(`, `)}`),(0,I.jsx)(`div`,{className:`component-card component-card--error`,children:(0,I.jsxs)(`p`,{style:{fontFamily:`var(--font-body)`,fontSize:12,color:`var(--text-secondary)`},children:[`Component “`,e,`” is not available. This may require a platform update.`]})}))}function V({messages:t,isStreaming:i,sessionError:a,selectionMode:o,selectedItems:c,toggleSelectItem:l,onComponentSubmit:u,remainingSuggestions:d,onSuggestionClick:f,isAtBottom:p,setIsAtBottom:m}){let[h,v]=(0,T.useState)(new Set),[b,x]=(0,T.useState)(new Set),S=(0,T.useRef)(null),C=(0,T.useRef)(null),w=(0,T.useRef)(null),E=(0,T.useRef)(!1),D=(0,T.useRef)(!1),O=(0,T.useRef)(!1),k=(0,T.useRef)(0),A=(0,T.useRef)(!1),j=(0,T.useRef)(new Map);A.current=i,(0,T.useEffect)(()=>{i&&!E.current&&p&&(D.current=!0)},[i,p]),(0,T.useEffect)(()=>{if(!D.current&&!p){E.current=i,O.current=!1;return}if(E.current&&!i&&w.current&&C.current){let e=w.current;if(e.offsetHeight>C.current.clientHeight){D.current=!1,O.current=!0,e.scrollIntoView({behavior:`smooth`,block:`start`}),E.current=i;return}}(p||D.current)&&!O.current&&S.current?.scrollIntoView({behavior:`smooth`}),E.current=i},[t,p,i]),(0,T.useEffect)(()=>{let e=C.current;if(!e)return;let t=()=>{let t=e.scrollTop;A.current&&t<k.current&&(D.current=!1),k.current=t,m(e.scrollHeight-e.scrollTop-e.clientHeight<80)};return e.addEventListener(`scroll`,t,{passive:!0}),t(),()=>e.removeEventListener(`scroll`,t)},[m]),(0,T.useEffect)(()=>{function e(){x(e=>{let t=new Set;return j.current.forEach((n,r)=>{n.isConnected&&(h.has(r)?e.has(r)&&t.add(r):n.scrollHeight>n.clientHeight+2&&t.add(r))}),e.size===t.size&&[...e].every(e=>t.has(e))?e:t})}e();let t=new ResizeObserver(e);return j.current.forEach(e=>t.observe(e)),()=>t.disconnect()},[t.length,h]);let M=t.reduce((e,t,n)=>t.role===`maxy`&&!t.hidden?n:e,-1);return(0,I.jsxs)(`div`,{className:`chat-messages-wrap`,children:[o&&(0,I.jsx)(`div`,{className:`selection-overlay-band`}),(0,I.jsxs)(`div`,{className:`chat-messages`,ref:C,children:[a&&t.length===0&&(0,I.jsx)(`div`,{className:`session-error`,children:(0,I.jsx)(`p`,{children:a})}),t.map((a,d)=>{if(a.hidden)return null;let f=i&&d===t.length-1,p=`${a.timestamp}_${a.role}`,m=c.has(p);return(0,I.jsxs)(`div`,{ref:d===M?w:void 0,className:`message ${a.role}${m?` selected`:``}`,children:[o&&!f&&(0,I.jsx)(`div`,{className:`message-select-check`,children:(0,I.jsx)(s,{checked:m,onChange:()=>l(p)})}),(0,I.jsxs)(`div`,{className:`bubble`,children:[a.role===`visitor`&&a.content?(()=>{let e=h.has(d),t=b.has(d);return(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(`div`,{ref:e=>{e?j.current.set(d,e):j.current.delete(d)},className:e?`bubble-content`:`bubble-content clamped${t?` overflowing`:``}`,children:a.content}),t&&(0,I.jsx)(`div`,{className:`bubble-expand`,children:(0,I.jsx)(y,{variant:`icon`,className:e?`expanded`:``,onClick:()=>v(e=>{let t=new Set(e);return t.has(d)?t.delete(d):t.add(d),t}),"aria-label":e?`Collapse message`:`Expand message`,children:(0,I.jsx)(r,{size:14})})})]})})():a.content?(0,I.jsx)(_,{content:a.content,timestamp:g(a.timestamp)}):(0,I.jsxs)(`span`,{className:`typing-indicator`,children:[(0,I.jsx)(`span`,{className:`typing-dot`}),(0,I.jsx)(`span`,{className:`typing-dot`}),(0,I.jsx)(`span`,{className:`typing-dot`})]}),a.attachments&&a.attachments.length>0&&(0,I.jsx)(`div`,{className:`message-attachments`,children:a.attachments.map((t,r)=>(0,I.jsxs)(`span`,{className:`message-attachment-chip no-action`,children:[t.mimeType===`application/pdf`?(0,I.jsx)(n,{size:12}):t.mimeType.startsWith(`text/`)?(0,I.jsx)(e,{size:12}):null,(0,I.jsx)(`span`,{children:t.filename})]},r))}),a.role===`visitor`&&(0,I.jsx)(`span`,{className:`message-timestamp`,children:g(a.timestamp)})]}),a.role===`maxy`&&a.components&&a.components.length>0&&(0,I.jsx)(`div`,{className:`public-components`,children:a.components.map((e,t)=>(0,I.jsx)(`div`,{className:`public-component-enter`,children:(0,I.jsx)(B,{name:e.name,data:e.data,onSubmit:e=>u(d,t,e),submitted:e.submitted})},t))})]},d)}),!i&&d.length>0&&(0,I.jsx)(`div`,{className:`chat-suggestions`,children:d.map(e=>(0,I.jsx)(y,{variant:`suggestion`,onClick:()=>f(e),children:e},e))}),(0,I.jsx)(`div`,{ref:S})]}),!p&&(0,I.jsx)(`button`,{className:`scroll-to-bottom`,onClick:()=>S.current?.scrollIntoView({behavior:`smooth`}),"aria-label":`Scroll to bottom`,children:(0,I.jsx)(r,{size:18})})]})}function H({selectedItems:e,messages:t,copySelected:n,exitSelection:r,showCopyToast:i}){let[a,o]=(0,T.useState)(!1),s=(0,T.useRef)(null),c=(0,T.useRef)(!1);function l(e){let n=e.lastIndexOf(`_`),r=e.slice(0,n),i=e.slice(n+1),a=parseInt(r,10);return t.find(e=>e.timestamp===a&&e.role===i)?.content??``}function u(e,t){return parseInt(e.slice(0,e.lastIndexOf(`_`)),10)-parseInt(t.slice(0,t.lastIndexOf(`_`)),10)}function d(){let e=[...t].sort((e,t)=>e.timestamp-t.timestamp),n=[];for(let t of e){if(t.hidden||!t.content)continue;let e=t.role===`visitor`?`Visitor`:`Maxy`;n.push(`${e}:\n${t.content}`)}return n.join(`
2
2
 
3
3
  ---
4
4
 
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/admin-DLtY5oYr.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/ChatInput-BWyEqP0k.js">
10
- <link rel="stylesheet" crossorigin href="/assets/ChatInput-DNdZ2WD7.css">
8
+ <script type="module" crossorigin src="/assets/admin-ZfkvBfx_.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/ChatInput-B2MUVSm4.js">
10
+ <link rel="stylesheet" crossorigin href="/assets/ChatInput-D_Am-NZI.css">
11
11
  <link rel="stylesheet" href="/brand-defaults.css">
12
12
  </head>
13
13
  <body>
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/public-B27jukY6.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/ChatInput-BWyEqP0k.js">
10
- <link rel="stylesheet" crossorigin href="/assets/ChatInput-DNdZ2WD7.css">
8
+ <script type="module" crossorigin src="/assets/public-C0LOoku3.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/ChatInput-B2MUVSm4.js">
10
+ <link rel="stylesheet" crossorigin href="/assets/ChatInput-D_Am-NZI.css">
11
11
  <link rel="stylesheet" href="/brand-defaults.css">
12
12
  </head>
13
13
  <body>
@@ -3342,7 +3342,7 @@ function cacheConversationId(sessionKey, conversationId) {
3342
3342
  }
3343
3343
  }
3344
3344
  var GREETING_DIRECTIVE = "[New session. Greet the visitor.]";
3345
- async function ensureConversation(accountId, agentType, sessionKey, visitorId, agentSlug) {
3345
+ async function ensureConversation(accountId, agentType, sessionKey, visitorId, agentSlug, userId) {
3346
3346
  const cached2 = getCachedConversationId(sessionKey);
3347
3347
  if (cached2) return cached2;
3348
3348
  const conversationId = randomUUID();
@@ -3356,6 +3356,7 @@ async function ensureConversation(accountId, agentType, sessionKey, visitorId, a
3356
3356
  c.agentType = $agentType,
3357
3357
  ${visitorId ? "c.visitorId = $visitorId," : ""}
3358
3358
  ${agentSlug ? "c.agentSlug = $agentSlug," : ""}
3359
+ ${userId ? "c.userId = $userId," : ""}
3359
3360
  c.createdAt = datetime(),
3360
3361
  c.updatedAt = datetime()
3361
3362
  ON MATCH SET
@@ -3372,13 +3373,14 @@ async function ensureConversation(accountId, agentType, sessionKey, visitorId, a
3372
3373
  accountId,
3373
3374
  agentType,
3374
3375
  ...visitorId ? { visitorId } : {},
3375
- ...agentSlug ? { agentSlug } : {}
3376
+ ...agentSlug ? { agentSlug } : {},
3377
+ ...userId ? { userId } : {}
3376
3378
  }
3377
3379
  );
3378
3380
  const id = result.records[0]?.get("conversationId");
3379
3381
  if (id) {
3380
3382
  cacheConversationId(sessionKey, id);
3381
- console.error(`[persist] Conversation ${id.slice(0, 8)}\u2026 ensured for ${agentType}/${accountId.slice(0, 8)}\u2026`);
3383
+ console.error(`[session] conversation attributed: conversationId=${id.slice(0, 8)}\u2026 userId=${userId ?? "none"} ${agentType}/${accountId.slice(0, 8)}\u2026`);
3382
3384
  }
3383
3385
  return id ?? null;
3384
3386
  } catch (err) {
@@ -3584,17 +3586,18 @@ async function searchMessages(accountId, queryEmbedding, limit = 10) {
3584
3586
  await session.close();
3585
3587
  }
3586
3588
  }
3587
- async function listAdminSessions(accountId, limit = 20) {
3589
+ async function listAdminSessions(accountId, userId, limit = 20) {
3588
3590
  const session = getSession();
3589
3591
  try {
3590
3592
  const result = await session.run(
3591
3593
  `MATCH (c:Conversation {accountId: $accountId, agentType: 'admin'})
3594
+ WHERE c.userId = $userId OR c.userId IS NULL
3592
3595
  RETURN c.conversationId AS conversationId,
3593
3596
  c.name AS name,
3594
3597
  c.updatedAt AS updatedAt
3595
3598
  ORDER BY c.updatedAt DESC
3596
3599
  LIMIT $limit`,
3597
- { accountId, limit: neo4j.int(limit) }
3600
+ { accountId, userId, limit: neo4j.int(limit) }
3598
3601
  );
3599
3602
  return result.records.map((r) => ({
3600
3603
  conversationId: r.get("conversationId"),
@@ -3802,14 +3805,13 @@ var VALID_CATEGORIES = /* @__PURE__ */ new Set([
3802
3805
  "content",
3803
3806
  "interaction"
3804
3807
  ]);
3805
- async function getUserTimezone(accountId) {
3808
+ async function getUserTimezone(accountId, userId) {
3806
3809
  const session = getSession();
3807
3810
  try {
3808
- const result = await session.run(
3809
- `MATCH (up:UserProfile {accountId: $accountId})
3810
- RETURN up.timezone AS timezone`,
3811
- { accountId }
3812
- );
3811
+ const query = userId ? `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
3812
+ RETURN up.timezone AS timezone` : `MATCH (up:UserProfile {accountId: $accountId})
3813
+ RETURN up.timezone AS timezone LIMIT 1`;
3814
+ const result = await session.run(query, { accountId, userId: userId ?? "" });
3813
3815
  if (result.records.length === 0) return null;
3814
3816
  const tz = result.records[0].get("timezone");
3815
3817
  return tz && tz.trim().length > 0 ? tz : null;
@@ -3820,11 +3822,11 @@ async function getUserTimezone(accountId) {
3820
3822
  await session.close();
3821
3823
  }
3822
3824
  }
3823
- async function loadUserProfile(accountId) {
3825
+ async function loadUserProfile(accountId, userId) {
3824
3826
  const session = getSession();
3825
3827
  try {
3826
- await session.run(
3827
- `MERGE (up:UserProfile {accountId: $accountId})
3828
+ const mergeResult = await session.run(
3829
+ `MERGE (up:UserProfile {accountId: $accountId, userId: $userId})
3828
3830
  ON CREATE SET
3829
3831
  up.createdAt = $now,
3830
3832
  up.updatedAt = $now,
@@ -3834,18 +3836,23 @@ async function loadUserProfile(accountId) {
3834
3836
  OPTIONAL MATCH (b:LocalBusiness {accountId: $accountId})
3835
3837
  FOREACH (_ IN CASE WHEN b IS NOT NULL THEN [1] ELSE [] END |
3836
3838
  MERGE (up)-[:BELONGS_TO]->(b)
3837
- )`,
3838
- { accountId, now: (/* @__PURE__ */ new Date()).toISOString() }
3839
+ )
3840
+ RETURN up.createdAt = $now AS isNew`,
3841
+ { accountId, userId, now: (/* @__PURE__ */ new Date()).toISOString() }
3839
3842
  );
3843
+ const isNew = mergeResult.records[0]?.get("isNew");
3844
+ if (isNew) {
3845
+ console.error(`[profile] created new profile: userId=${userId} accountId=${accountId.slice(0, 8)}\u2026`);
3846
+ }
3840
3847
  const nowMs = Date.now();
3841
3848
  const thresholdMs = DECAY_THRESHOLD_DAYS * 24 * 60 * 60 * 1e3;
3842
3849
  const staleResult = await session.run(
3843
- `MATCH (up:UserProfile {accountId: $accountId})-[:HAS_PREFERENCE]->(pref:Preference)
3850
+ `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})-[:HAS_PREFERENCE]->(pref:Preference)
3844
3851
  WHERE pref.observedAt IS NOT NULL
3845
3852
  RETURN pref.preferenceId AS preferenceId,
3846
3853
  pref.observedAt AS observedAt,
3847
3854
  pref.confidence AS confidence`,
3848
- { accountId }
3855
+ { accountId, userId }
3849
3856
  );
3850
3857
  let decayCount = 0;
3851
3858
  for (const record2 of staleResult.records) {
@@ -3871,7 +3878,7 @@ async function loadUserProfile(accountId) {
3871
3878
  }
3872
3879
  }
3873
3880
  const result = await session.run(
3874
- `MATCH (up:UserProfile {accountId: $accountId})
3881
+ `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
3875
3882
  OPTIONAL MATCH (up)-[:HAS_PREFERENCE]->(pref:Preference)
3876
3883
  WHERE pref.confidence >= $threshold
3877
3884
  WITH up, pref
@@ -3879,7 +3886,7 @@ async function loadUserProfile(accountId) {
3879
3886
  WITH up, collect(pref) AS allPrefs
3880
3887
  WITH up, allPrefs[0..$limit] AS prefs
3881
3888
  RETURN up, prefs`,
3882
- { accountId, threshold: INJECTION_THRESHOLD, limit: MAX_SUMMARY_PREFERENCES }
3889
+ { accountId, userId, threshold: INJECTION_THRESHOLD, limit: MAX_SUMMARY_PREFERENCES }
3883
3890
  );
3884
3891
  if (result.records.length === 0 || !result.records[0].get("up")) {
3885
3892
  return null;
@@ -3898,7 +3905,7 @@ async function loadUserProfile(accountId) {
3898
3905
  }));
3899
3906
  const summary = formatProfileSummary(profileProps, preferences);
3900
3907
  console.error(
3901
- `[profile] Loaded ${preferences.length} preferences for ${accountId.slice(0, 8)}\u2026 (decay: ${decayCount} updated)`
3908
+ `[profile] loaded for userId=${userId} accountId=${accountId.slice(0, 8)}\u2026 preferences=${preferences.length} (decay: ${decayCount} updated)`
3902
3909
  );
3903
3910
  return summary;
3904
3911
  } catch (err) {
@@ -4085,7 +4092,7 @@ async function loadOnboardingStep(accountId) {
4085
4092
  await session.close();
4086
4093
  }
4087
4094
  }
4088
- async function writeReflectionPreferences(accountId, conversationId, updates) {
4095
+ async function writeReflectionPreferences(accountId, userId, conversationId, updates) {
4089
4096
  if (updates.length === 0) return 0;
4090
4097
  const session = getSession();
4091
4098
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -4137,6 +4144,7 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
4137
4144
  const props = {
4138
4145
  preferenceId,
4139
4146
  accountId,
4147
+ userId,
4140
4148
  category: update.category,
4141
4149
  key: update.key,
4142
4150
  value: update.value,
@@ -4149,10 +4157,10 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
4149
4157
  };
4150
4158
  if (embedding2) props.embedding = embedding2;
4151
4159
  await txc.run(
4152
- `MATCH (up:UserProfile {accountId: $accountId})
4160
+ `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
4153
4161
  CREATE (pref:Preference $props)
4154
4162
  CREATE (up)-[:HAS_PREFERENCE]->(pref)`,
4155
- { accountId, props }
4163
+ { accountId, userId, props }
4156
4164
  );
4157
4165
  for (const convId of allConvIds) {
4158
4166
  await txc.run(
@@ -4185,6 +4193,7 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
4185
4193
  const mode = update.mode || "reinforce";
4186
4194
  const mergeParams = {
4187
4195
  accountId,
4196
+ userId,
4188
4197
  category: update.category,
4189
4198
  key: update.key,
4190
4199
  newPrefId,
@@ -4198,8 +4207,8 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
4198
4207
  };
4199
4208
  if (embedding) mergeParams.embedding = embedding;
4200
4209
  const mergeResult = await session.run(
4201
- `MATCH (up:UserProfile {accountId: $accountId})
4202
- MERGE (up)-[:HAS_PREFERENCE]->(pref:Preference {accountId: $accountId, category: $category, key: $key})
4210
+ `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
4211
+ MERGE (up)-[:HAS_PREFERENCE]->(pref:Preference {accountId: $accountId, userId: $userId, category: $category, key: $key})
4203
4212
  ON CREATE SET
4204
4213
  pref.preferenceId = $newPrefId,
4205
4214
  pref.value = $value,
@@ -4239,13 +4248,13 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
4239
4248
  }
4240
4249
  if (written > 0) {
4241
4250
  await session.run(
4242
- `MATCH (up:UserProfile {accountId: $accountId})
4251
+ `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
4243
4252
  SET up.profileVersion = coalesce(up.profileVersion, 0) + 1,
4244
4253
  up.updatedAt = $now`,
4245
- { accountId, now }
4254
+ { accountId, userId, now }
4246
4255
  );
4247
4256
  }
4248
- console.error(`[profile-reflection] Wrote ${written}/${updates.length} preference updates for ${accountId.slice(0, 8)}\u2026`);
4257
+ console.error(`[profile-reflection] Wrote ${written}/${updates.length} preference updates for userId=${userId} accountId=${accountId.slice(0, 8)}\u2026`);
4249
4258
  return written;
4250
4259
  } catch (err) {
4251
4260
  console.error(`[profile-reflection] writeReflectionPreferences failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -5025,6 +5034,12 @@ function consumePendingCompactionSummary(sessionKey) {
5025
5034
  function getAccountIdForSession(sessionKey) {
5026
5035
  return sessionStore.get(sessionKey)?.accountId;
5027
5036
  }
5037
+ function getUserIdForSession(sessionKey) {
5038
+ return sessionStore.get(sessionKey)?.userId;
5039
+ }
5040
+ function getUserNameForSession(sessionKey) {
5041
+ return sessionStore.get(sessionKey)?.userName;
5042
+ }
5028
5043
  function registerGrantSession(sessionKey, accountId, agentName, opts) {
5029
5044
  sessionStore.set(sessionKey, {
5030
5045
  createdAt: Date.now(),
@@ -5101,12 +5116,12 @@ function consumeStalledSubagents(sessionKey) {
5101
5116
  delete session.stalledSubagents;
5102
5117
  return stalls && stalls.length > 0 ? stalls : void 0;
5103
5118
  }
5104
- function getMcpServers(accountId, enabledPlugins) {
5119
+ function getMcpServers(accountId, userId, enabledPlugins) {
5105
5120
  const servers = {
5106
5121
  "memory": {
5107
5122
  command: "node",
5108
5123
  args: [resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
5109
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5124
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, ...userId ? { USER_ID: userId } : {} }
5110
5125
  },
5111
5126
  "contacts": {
5112
5127
  command: "node",
@@ -5121,7 +5136,7 @@ function getMcpServers(accountId, enabledPlugins) {
5121
5136
  "admin": {
5122
5137
  command: "node",
5123
5138
  args: [resolve4(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
5124
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, PLATFORM_PORT: process.env.PORT ?? "19200" }
5139
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
5125
5140
  },
5126
5141
  "scheduling": {
5127
5142
  command: "node",
@@ -5253,15 +5268,22 @@ var ADMIN_CORE_TOOLS = [
5253
5268
  "mcp__admin__render-component",
5254
5269
  "mcp__admin__session-reset",
5255
5270
  "mcp__admin__session-resume",
5271
+ "mcp__admin__admin-add",
5272
+ "mcp__admin__admin-remove",
5273
+ "mcp__admin__admin-list",
5256
5274
  "mcp__admin__api-key-store",
5257
5275
  "mcp__admin__api-key-verify",
5258
5276
  "mcp__admin__file-attach",
5277
+ "mcp__cloudflare__cf-set-token",
5278
+ "mcp__cloudflare__cf-add-zone",
5279
+ "mcp__cloudflare__cf-zone-status",
5259
5280
  "mcp__cloudflare__tunnel-status",
5260
5281
  "mcp__cloudflare__tunnel-install",
5261
5282
  "mcp__cloudflare__tunnel-login",
5262
5283
  "mcp__cloudflare__tunnel-create",
5263
5284
  "mcp__cloudflare__tunnel-enable",
5264
5285
  "mcp__cloudflare__tunnel-disable",
5286
+ "mcp__cloudflare__tunnel-add-hostname",
5265
5287
  "mcp__cloudflare__dns-lookup",
5266
5288
  "mcp__tasks__task-create",
5267
5289
  "mcp__tasks__task-update",
@@ -5686,7 +5708,7 @@ Respond with ONLY a JSON array. Each element:
5686
5708
 
5687
5709
  mergeSourceIds is only for mode "merge" \u2014 list the preferenceIds of the sources to combine.
5688
5710
  If no preferences are found, respond with an empty array: []`;
5689
- async function reflectOnSessionProfile(accountId, sessionKey, profileSummary) {
5711
+ async function reflectOnSessionProfile(accountId, userId, sessionKey, profileSummary) {
5690
5712
  const history = getMessageHistory(sessionKey);
5691
5713
  if (history.length === 0) return 0;
5692
5714
  let apiKey = process.env.ANTHROPIC_API_KEY;
@@ -5768,7 +5790,7 @@ Extract preference updates as JSON array.`
5768
5790
  }
5769
5791
  console.error(`[profile-reflection] Extracted ${sanitized.length} preference updates via Haiku (${updates.length - sanitized.length} filtered)`);
5770
5792
  const convId = sessionStore.get(sessionKey)?.conversationId;
5771
- return await writeReflectionPreferences(accountId, convId, sanitized);
5793
+ return await writeReflectionPreferences(accountId, userId, convId, sanitized);
5772
5794
  } catch (err) {
5773
5795
  const reason = err instanceof Error ? err.message : String(err);
5774
5796
  if (err instanceof Error && err.name === "AbortError") {
@@ -5887,7 +5909,7 @@ var COMPACTION_PROMPT = `You are about to reach your context limit. Call session
5887
5909
  Then respond with only: [COMPACTED]`;
5888
5910
  var COMPACTION_TIMEOUT_MS = 45e3;
5889
5911
  async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, enabledPlugins) {
5890
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, enabledPlugins) });
5912
+ const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, void 0, enabledPlugins) });
5891
5913
  const specialistsDir = resolve4(accountDir, "specialists");
5892
5914
  if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
5893
5915
  `);
@@ -6526,7 +6548,8 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
6526
6548
  `);
6527
6549
  cdpLog.end();
6528
6550
  }
6529
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, enabledPlugins) });
6551
+ const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
6552
+ const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, ccUserId, enabledPlugins) });
6530
6553
  const specialistsDir = resolve4(accountDir, "specialists");
6531
6554
  if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
6532
6555
  `);
@@ -6610,10 +6633,13 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
6610
6633
  if (step.value.summary) {
6611
6634
  storePendingCompactionSummary(sessionKey, step.value.summary);
6612
6635
  }
6613
- const profileForReflection = await loadUserProfile(accountId);
6614
- reflectOnSessionProfile(accountId, sessionKey, profileForReflection).catch((err) => {
6615
- console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
6616
- });
6636
+ const reflectionUserId = getUserIdForSession(sessionKey);
6637
+ if (reflectionUserId) {
6638
+ const profileForReflection = await loadUserProfile(accountId, reflectionUserId);
6639
+ reflectOnSessionProfile(accountId, reflectionUserId, sessionKey, profileForReflection).catch((err) => {
6640
+ console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
6641
+ });
6642
+ }
6617
6643
  clearAgentSessionId(sessionKey);
6618
6644
  const convId = sessionStore.get(sessionKey)?.conversationId;
6619
6645
  if (convId) {
@@ -6778,7 +6804,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
6778
6804
  const cdpOk = await ensureCdp();
6779
6805
  if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
6780
6806
  `);
6781
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, enabledPlugins) });
6807
+ const managedUserId = getUserIdForSession(sessionKey);
6808
+ const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedUserId, enabledPlugins) });
6782
6809
  const specialistsDir = resolve4(accountDir, "specialists");
6783
6810
  if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
6784
6811
  `);
@@ -7245,10 +7272,11 @@ ${EXPLANATORY_STYLE_INSTRUCTIONS}` : baseSystemPrompt;
7245
7272
  if (step.value.summary) {
7246
7273
  storePendingCompactionSummary(sessionKey, step.value.summary);
7247
7274
  }
7248
- const agentType = sessionStore.get(sessionKey)?.agentType;
7249
- if (agentType === "admin") {
7250
- const currentProfile = await loadUserProfile(account.accountId);
7251
- reflectOnSessionProfile(account.accountId, sessionKey, currentProfile).catch((err) => {
7275
+ const compactAgentType = sessionStore.get(sessionKey)?.agentType;
7276
+ const compactUserId = getUserIdForSession(sessionKey);
7277
+ if (compactAgentType === "admin" && compactUserId) {
7278
+ const currentProfile = await loadUserProfile(account.accountId, compactUserId);
7279
+ reflectOnSessionProfile(account.accountId, compactUserId, sessionKey, currentProfile).catch((err) => {
7252
7280
  console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
7253
7281
  });
7254
7282
  }
@@ -7265,11 +7293,13 @@ async function* invokeAgent(config2, message, sessionKey, attachments = [], user
7265
7293
  return;
7266
7294
  }
7267
7295
  const accountId = sessionAccountId ?? account.accountId;
7296
+ const sessionUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
7297
+ const sessionUserName = sessionKey ? getUserNameForSession(sessionKey) : void 0;
7268
7298
  const resolvedAgentName = agentName ?? agentType;
7269
7299
  const identity = readIdentity(account.accountDir, resolvedAgentName);
7270
7300
  const rawSoul = readAgentFile(account.accountDir, resolvedAgentName, "SOUL.md");
7271
7301
  const soul = rawSoul && hasSoulContent(rawSoul) ? rawSoul : null;
7272
- console.log(`[invoke-agent] agent=${resolvedAgentName} type=${agentType} session=${sessionKey?.slice(0, 8) ?? "none"} identity=${identity ? `${identity.length}b` : "MISSING"} soul=${rawSoul ? soul ? `${soul.length}b` : "EMPTY(header-only)" : "MISSING"}`);
7302
+ console.log(`[invoke-agent] agent=${resolvedAgentName} type=${agentType} session=${sessionKey?.slice(0, 8) ?? "none"} userId=${sessionUserId ?? "none"} identity=${identity ? `${identity.length}b` : "MISSING"} soul=${rawSoul ? soul ? `${soul.length}b` : "EMPTY(header-only)" : "MISSING"}`);
7273
7303
  const defaultSystemPrompt = agentType === "public" ? "You are a public assistant. Answer only from the memory context provided. Never use training data. British English. Be concise." : "You are an admin agent. Full access. Professional, concise. British English. Be proactive \u2014 tell the user what needs doing and do it.";
7274
7304
  const identityPrompt = identity ?? defaultSystemPrompt;
7275
7305
  let baseSystemPrompt = soul ? `${identityPrompt}
@@ -7277,7 +7307,7 @@ async function* invokeAgent(config2, message, sessionKey, attachments = [], user
7277
7307
  ${soul}` : identityPrompt;
7278
7308
  const now = /* @__PURE__ */ new Date();
7279
7309
  const isoTimestamp = now.toISOString();
7280
- const userTimezone = await getUserTimezone(accountId);
7310
+ const userTimezone = await getUserTimezone(accountId, sessionUserId);
7281
7311
  const effectiveTimezone = userTimezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
7282
7312
  if (!userTimezone) {
7283
7313
  console.error(`[datetime] getUserTimezone returned null for ${accountId.slice(0, 8)}\u2026, falling back to server timezone: ${effectiveTimezone}`);
@@ -7301,16 +7331,23 @@ ${isoTimestamp}
7301
7331
  ${humanReadable} (${effectiveTimezone})
7302
7332
  </datetime>`;
7303
7333
  if (agentType === "admin") {
7304
- const [profileSummary, sessionContext, onboardingStep] = await Promise.all([
7305
- loadUserProfile(accountId),
7306
- loadSessionContext(accountId),
7307
- loadOnboardingStep(accountId)
7308
- ]);
7309
- if (profileSummary) {
7334
+ if (sessionUserId) {
7310
7335
  baseSystemPrompt += `
7311
7336
 
7337
+ <admin-identity>
7338
+ You are talking to ${sessionUserName ?? "an admin"}. Their userId is ${sessionUserId}.
7339
+ </admin-identity>`;
7340
+ const profileSummary = await loadUserProfile(accountId, sessionUserId);
7341
+ if (profileSummary) {
7342
+ baseSystemPrompt += `
7343
+
7312
7344
  ${profileSummary}`;
7345
+ }
7313
7346
  }
7347
+ const [sessionContext, onboardingStep] = await Promise.all([
7348
+ loadSessionContext(accountId),
7349
+ loadOnboardingStep(accountId)
7350
+ ]);
7314
7351
  if (sessionContext) {
7315
7352
  baseSystemPrompt += `
7316
7353
 
@@ -7405,7 +7442,7 @@ ${stallContext}`;
7405
7442
  Current session key: ${sessionKey}` : systemPromptBase;
7406
7443
  if (sessionKey) {
7407
7444
  try {
7408
- await ensureConversation(accountId, agentType, sessionKey);
7445
+ await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
7409
7446
  } catch (err) {
7410
7447
  console.error(`[persist] ensureConversation failed in invokeAgent: ${err instanceof Error ? err.message : String(err)}`);
7411
7448
  }
@@ -26680,8 +26717,12 @@ async function GET10(req) {
26680
26717
  if (!accountId) {
26681
26718
  return Response.json({ error: "Account not found for session" }, { status: 401 });
26682
26719
  }
26720
+ const userId = getUserIdForSession(sessionKey);
26721
+ if (!userId) {
26722
+ return Response.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, { status: 401 });
26723
+ }
26683
26724
  try {
26684
- const sessions = await listAdminSessions(accountId, 20);
26725
+ const sessions = await listAdminSessions(accountId, userId, 20);
26685
26726
  return Response.json({ sessions });
26686
26727
  } catch (err) {
26687
26728
  console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);