@rubytech/create-maxy 1.0.478 → 1.0.479
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/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +11 -6
- package/payload/platform/plugins/admin/PLUGIN.md +4 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +221 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +13 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +3 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +3 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +14 -11
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/scripts/seed-neo4j.sh +9 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +10 -0
- package/payload/server/public/assets/ChatInput-D_Am-NZI.css +1 -0
- package/payload/server/public/assets/{admin-DLtY5oYr.js → admin-ZfkvBfx_.js} +60 -60
- package/payload/server/public/assets/{public-B27jukY6.js → public-C0LOoku3.js} +1 -1
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +92 -55
- package/payload/server/public/assets/ChatInput-DNdZ2WD7.css +0 -1
- /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-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/ChatInput-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/ChatInput-
|
|
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-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/ChatInput-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/ChatInput-
|
|
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>
|
package/payload/server/server.js
CHANGED
|
@@ -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(`[
|
|
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
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
|
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,6 +5268,9 @@ 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",
|
|
@@ -5686,7 +5704,7 @@ Respond with ONLY a JSON array. Each element:
|
|
|
5686
5704
|
|
|
5687
5705
|
mergeSourceIds is only for mode "merge" \u2014 list the preferenceIds of the sources to combine.
|
|
5688
5706
|
If no preferences are found, respond with an empty array: []`;
|
|
5689
|
-
async function reflectOnSessionProfile(accountId, sessionKey, profileSummary) {
|
|
5707
|
+
async function reflectOnSessionProfile(accountId, userId, sessionKey, profileSummary) {
|
|
5690
5708
|
const history = getMessageHistory(sessionKey);
|
|
5691
5709
|
if (history.length === 0) return 0;
|
|
5692
5710
|
let apiKey = process.env.ANTHROPIC_API_KEY;
|
|
@@ -5768,7 +5786,7 @@ Extract preference updates as JSON array.`
|
|
|
5768
5786
|
}
|
|
5769
5787
|
console.error(`[profile-reflection] Extracted ${sanitized.length} preference updates via Haiku (${updates.length - sanitized.length} filtered)`);
|
|
5770
5788
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
5771
|
-
return await writeReflectionPreferences(accountId, convId, sanitized);
|
|
5789
|
+
return await writeReflectionPreferences(accountId, userId, convId, sanitized);
|
|
5772
5790
|
} catch (err) {
|
|
5773
5791
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5774
5792
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -5887,7 +5905,7 @@ var COMPACTION_PROMPT = `You are about to reach your context limit. Call session
|
|
|
5887
5905
|
Then respond with only: [COMPACTED]`;
|
|
5888
5906
|
var COMPACTION_TIMEOUT_MS = 45e3;
|
|
5889
5907
|
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, enabledPlugins) {
|
|
5890
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, enabledPlugins) });
|
|
5908
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, void 0, enabledPlugins) });
|
|
5891
5909
|
const specialistsDir = resolve4(accountDir, "specialists");
|
|
5892
5910
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
5893
5911
|
`);
|
|
@@ -6526,7 +6544,8 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
6526
6544
|
`);
|
|
6527
6545
|
cdpLog.end();
|
|
6528
6546
|
}
|
|
6529
|
-
const
|
|
6547
|
+
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
6548
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, ccUserId, enabledPlugins) });
|
|
6530
6549
|
const specialistsDir = resolve4(accountDir, "specialists");
|
|
6531
6550
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
6532
6551
|
`);
|
|
@@ -6610,10 +6629,13 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
6610
6629
|
if (step.value.summary) {
|
|
6611
6630
|
storePendingCompactionSummary(sessionKey, step.value.summary);
|
|
6612
6631
|
}
|
|
6613
|
-
const
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6632
|
+
const reflectionUserId = getUserIdForSession(sessionKey);
|
|
6633
|
+
if (reflectionUserId) {
|
|
6634
|
+
const profileForReflection = await loadUserProfile(accountId, reflectionUserId);
|
|
6635
|
+
reflectOnSessionProfile(accountId, reflectionUserId, sessionKey, profileForReflection).catch((err) => {
|
|
6636
|
+
console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6617
6639
|
clearAgentSessionId(sessionKey);
|
|
6618
6640
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
6619
6641
|
if (convId) {
|
|
@@ -6778,7 +6800,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
6778
6800
|
const cdpOk = await ensureCdp();
|
|
6779
6801
|
if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
6780
6802
|
`);
|
|
6781
|
-
const
|
|
6803
|
+
const managedUserId = getUserIdForSession(sessionKey);
|
|
6804
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedUserId, enabledPlugins) });
|
|
6782
6805
|
const specialistsDir = resolve4(accountDir, "specialists");
|
|
6783
6806
|
if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
6784
6807
|
`);
|
|
@@ -7245,10 +7268,11 @@ ${EXPLANATORY_STYLE_INSTRUCTIONS}` : baseSystemPrompt;
|
|
|
7245
7268
|
if (step.value.summary) {
|
|
7246
7269
|
storePendingCompactionSummary(sessionKey, step.value.summary);
|
|
7247
7270
|
}
|
|
7248
|
-
const
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7271
|
+
const compactAgentType = sessionStore.get(sessionKey)?.agentType;
|
|
7272
|
+
const compactUserId = getUserIdForSession(sessionKey);
|
|
7273
|
+
if (compactAgentType === "admin" && compactUserId) {
|
|
7274
|
+
const currentProfile = await loadUserProfile(account.accountId, compactUserId);
|
|
7275
|
+
reflectOnSessionProfile(account.accountId, compactUserId, sessionKey, currentProfile).catch((err) => {
|
|
7252
7276
|
console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
|
|
7253
7277
|
});
|
|
7254
7278
|
}
|
|
@@ -7265,11 +7289,13 @@ async function* invokeAgent(config2, message, sessionKey, attachments = [], user
|
|
|
7265
7289
|
return;
|
|
7266
7290
|
}
|
|
7267
7291
|
const accountId = sessionAccountId ?? account.accountId;
|
|
7292
|
+
const sessionUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
7293
|
+
const sessionUserName = sessionKey ? getUserNameForSession(sessionKey) : void 0;
|
|
7268
7294
|
const resolvedAgentName = agentName ?? agentType;
|
|
7269
7295
|
const identity = readIdentity(account.accountDir, resolvedAgentName);
|
|
7270
7296
|
const rawSoul = readAgentFile(account.accountDir, resolvedAgentName, "SOUL.md");
|
|
7271
7297
|
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"}`);
|
|
7298
|
+
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
7299
|
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
7300
|
const identityPrompt = identity ?? defaultSystemPrompt;
|
|
7275
7301
|
let baseSystemPrompt = soul ? `${identityPrompt}
|
|
@@ -7277,7 +7303,7 @@ async function* invokeAgent(config2, message, sessionKey, attachments = [], user
|
|
|
7277
7303
|
${soul}` : identityPrompt;
|
|
7278
7304
|
const now = /* @__PURE__ */ new Date();
|
|
7279
7305
|
const isoTimestamp = now.toISOString();
|
|
7280
|
-
const userTimezone = await getUserTimezone(accountId);
|
|
7306
|
+
const userTimezone = await getUserTimezone(accountId, sessionUserId);
|
|
7281
7307
|
const effectiveTimezone = userTimezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
7282
7308
|
if (!userTimezone) {
|
|
7283
7309
|
console.error(`[datetime] getUserTimezone returned null for ${accountId.slice(0, 8)}\u2026, falling back to server timezone: ${effectiveTimezone}`);
|
|
@@ -7301,16 +7327,23 @@ ${isoTimestamp}
|
|
|
7301
7327
|
${humanReadable} (${effectiveTimezone})
|
|
7302
7328
|
</datetime>`;
|
|
7303
7329
|
if (agentType === "admin") {
|
|
7304
|
-
|
|
7305
|
-
loadUserProfile(accountId),
|
|
7306
|
-
loadSessionContext(accountId),
|
|
7307
|
-
loadOnboardingStep(accountId)
|
|
7308
|
-
]);
|
|
7309
|
-
if (profileSummary) {
|
|
7330
|
+
if (sessionUserId) {
|
|
7310
7331
|
baseSystemPrompt += `
|
|
7311
7332
|
|
|
7333
|
+
<admin-identity>
|
|
7334
|
+
You are talking to ${sessionUserName ?? "an admin"}. Their userId is ${sessionUserId}.
|
|
7335
|
+
</admin-identity>`;
|
|
7336
|
+
const profileSummary = await loadUserProfile(accountId, sessionUserId);
|
|
7337
|
+
if (profileSummary) {
|
|
7338
|
+
baseSystemPrompt += `
|
|
7339
|
+
|
|
7312
7340
|
${profileSummary}`;
|
|
7341
|
+
}
|
|
7313
7342
|
}
|
|
7343
|
+
const [sessionContext, onboardingStep] = await Promise.all([
|
|
7344
|
+
loadSessionContext(accountId),
|
|
7345
|
+
loadOnboardingStep(accountId)
|
|
7346
|
+
]);
|
|
7314
7347
|
if (sessionContext) {
|
|
7315
7348
|
baseSystemPrompt += `
|
|
7316
7349
|
|
|
@@ -7405,7 +7438,7 @@ ${stallContext}`;
|
|
|
7405
7438
|
Current session key: ${sessionKey}` : systemPromptBase;
|
|
7406
7439
|
if (sessionKey) {
|
|
7407
7440
|
try {
|
|
7408
|
-
await ensureConversation(accountId, agentType, sessionKey);
|
|
7441
|
+
await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
|
|
7409
7442
|
} catch (err) {
|
|
7410
7443
|
console.error(`[persist] ensureConversation failed in invokeAgent: ${err instanceof Error ? err.message : String(err)}`);
|
|
7411
7444
|
}
|
|
@@ -26680,8 +26713,12 @@ async function GET10(req) {
|
|
|
26680
26713
|
if (!accountId) {
|
|
26681
26714
|
return Response.json({ error: "Account not found for session" }, { status: 401 });
|
|
26682
26715
|
}
|
|
26716
|
+
const userId = getUserIdForSession(sessionKey);
|
|
26717
|
+
if (!userId) {
|
|
26718
|
+
return Response.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, { status: 401 });
|
|
26719
|
+
}
|
|
26683
26720
|
try {
|
|
26684
|
-
const sessions = await listAdminSessions(accountId, 20);
|
|
26721
|
+
const sessions = await listAdminSessions(accountId, userId, 20);
|
|
26685
26722
|
return Response.json({ sessions });
|
|
26686
26723
|
} catch (err) {
|
|
26687
26724
|
console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
|