@holaboss/client 0.2.0-beta.1 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
2
- var F=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,t)=>{for(var n in t)F(e,n,{get:t[n],enumerable:!0})},ee=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Q(t))!X.call(e,a)&&a!==n&&F(e,a,{get:()=>t[a],enumerable:!(o=Y(t,a))||o.enumerable});return e};var te=e=>ee(F({},"__esModule",{value:!0}),e);var de={};Z(de,{Chat:()=>B,HolaProvider:()=>J,createApiTransport:()=>j,createHolaClient:()=>G,createPortalData:()=>K,createWidgetTransport:()=>O,mergeMessage:()=>D,textDeltaOf:()=>P,thinkingDeltaOf:()=>I,toolEventOf:()=>R,useArtifactUrl:()=>k,useChat:()=>A,useHolaClient:()=>M,useThread:()=>V});module.exports=te(de);var L=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},P=e=>{let t=L(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},I=e=>{let t=L(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},R=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var re=/^ /,C=e=>{if(e&&typeof e=="object"){let t=e,n=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:n,retryable:t.retryable}:{error:n}}return{error:"request_failed"}},oe=e=>{let t="message",n=[];for(let o of e.split(`
3
- `))o.startsWith(":")||(o.startsWith("event:")?t=o.slice(6).trim():o.startsWith("data:")&&n.push(o.slice(5).replace(re,"")));return n.length>0?{event:t,data:n.join(`
4
- `)}:null},se=(e,t,n)=>{let o;try{o=JSON.parse(e.data)}catch{return}if(e.event==="session"){let a=o.sessionId;a&&(n(a),t.onSession?.(a));return}if(e.event==="turn_event"){t.onEvent?.(o);let a=P(o);a&&t.onText?.(a);let p=I(o);p&&t.onThinking?.(p);let r=R(o);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let a=o;n(a.sessionId),t.onComplete?.(a);return}e.event==="chat_error"&&t.onError?.(C(o))},H=async(e,t)=>{let n=await fetch(e,{headers:t});if(!n.ok)throw new Error(`hola: request failed (${n.status})`);return await n.json()},W=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},N=async(e,t,n,o)=>{let a=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:o});if(!(a.ok&&a.body)){t.onError?.({error:`stream_failed_${a.status}`});return}let p=a.body.getReader(),r=new TextDecoder,s="",d=()=>{let l=s.indexOf(`
2
+ var F=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(e,t)=>{for(var a in t)F(e,a,{get:t[a],enumerable:!0})},te=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of X(t))!Z.call(e,n)&&n!==a&&F(e,n,{get:()=>t[n],enumerable:!(s=Q(t,n))||s.enumerable});return e};var re=e=>te(F({},"__esModule",{value:!0}),e);var pe={};ee(pe,{Chat:()=>G,HolaProvider:()=>q,PortalConflict:()=>v,createApiTransport:()=>z,createHolaClient:()=>K,createPortalData:()=>V,createWidgetTransport:()=>O,mergeMessage:()=>L,textDeltaOf:()=>I,thinkingDeltaOf:()=>R,toolEventOf:()=>E,useArtifactUrl:()=>k,useChat:()=>U,useHolaClient:()=>A,useThread:()=>Y});module.exports=re(pe);var W=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=W(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},R=e=>{let t=W(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var oe=/^ /,C=e=>{if(e&&typeof e=="object"){let t=e,a=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:a,retryable:t.retryable}:{error:a}}return{error:"request_failed"}},se=e=>{let t="message",a=[];for(let s of e.split(`
3
+ `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&a.push(s.slice(5).replace(oe,"")));return a.length>0?{event:t,data:a.join(`
4
+ `)}:null},ne=(e,t,a)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(a(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=I(s);n&&t.onText?.(n);let d=R(s);d&&t.onThinking?.(d);let r=E(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;a(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(C(s))},h=async(e,t)=>{let a=await fetch(e,{headers:t});if(!a.ok)throw new Error(`hola: request failed (${a.status})`);return await a.json()},j=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},N=async(e,t,a,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let d=n.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
5
5
 
6
- `);for(;l!==-1;){let f=s.slice(0,l);s=s.slice(l+2);let u=oe(f);u&&se(u,t,n),l=s.indexOf(`
6
+ `);for(;c!==-1;){let b=o.slice(0,c);o=o.slice(c+2);let u=se(b);u&&ne(u,t,a),c=o.indexOf(`
7
7
 
8
- `)}},i=async()=>{let l=await p.read();if(!l.done)return s+=r.decode(l.value,{stream:!0}),d(),i()};await i()};var ne=/\/+$/,E="/api/v1/end-user",j=e=>{let t=e.baseUrl.replace(ne,""),n=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await n()}`}),a=async p=>{let r=await fetch(`${t}${E}/chat`,{method:"POST",headers:await o(),body:JSON.stringify({message:p})});if(!r.ok)throw new Error(`hola: message failed (${r.status})`);return{reply:(await r.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>H(`${t}${E}/chat/info`,await o()),sendMessage:a,streamMessage:async(p,r)=>{try{let s=await a(p);r.onText?.(s.reply),r.onComplete?.(s)}catch(s){r.onError?.(C(s))}},subscribe:(p,r)=>{let s=r?.since??"0",d=!1,i,l=r?.intervalMs??3e3,f=async()=>{try{let c=await H(`${t}${E}/chat/thread?since=${encodeURIComponent(s)}`,await o());for(let y of c.messages)p.onMessage(y);s=c.cursor}catch(c){p.onError?.(C(c))}d||(i=setTimeout(f,l))},u=()=>{d=!0,i&&clearTimeout(i)};return r?.signal?.addEventListener("abort",u),i=setTimeout(f,0),u},requestHuman:async()=>{await fetch(`${t}${E}/chat/request-human`,{method:"POST",headers:await o(),body:"{}"})}}};var S=require("react"),k=(e,t)=>{let[n,o]=(0,S.useState)("");return(0,S.useEffect)(()=>{if(!e){o("");return}let a=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),p=URL.createObjectURL(new Blob([a],{type:t}));return o(p),()=>URL.revokeObjectURL(p)},[e,t]),n};var U=require("react");var $=require("react"),q=require("react/jsx-runtime"),z=(0,$.createContext)(null),J=({client:e,children:t})=>(0,q.jsx)(z.Provider,{value:e,children:t}),M=()=>{let e=(0,$.useContext)(z);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var b=require("react"),A=e=>{let[t,n]=(0,b.useState)([]),[o,a]=(0,b.useState)(""),[p,r]=(0,b.useState)(!1),[s,d]=(0,b.useState)(null),i=(0,b.useRef)(0),l=(0,b.useCallback)(()=>{let u=i.current;return i.current+=1,u},[]);(0,b.useEffect)(()=>{let u=!0;return e.getInfo().then(c=>{u&&c.greeting&&n([{id:l(),role:"assistant",text:c.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,l]);let f=(0,b.useCallback)(()=>{let u=o.trim();if(!u||p)return;a(""),d(null),r(!0);let c=l(),y=l();n(m=>[...m,{id:c,role:"user",text:u},{id:y,role:"assistant",text:"",streaming:!0}]);let g=m=>{n(x=>x.map(_=>_.id===y?m(_):_))};e.streamMessage(u,{onText:m=>g(x=>({...x,text:x.text+m})),onComplete:m=>{g(x=>({...x,text:m.reply||x.text,artifacts:m.artifacts,streaming:!1})),r(!1)},onError:m=>{g(x=>({...x,streaming:!1})),d(m.error),r(!1)}}).catch(m=>{g(x=>({...x,streaming:!1})),d(m instanceof Error?m.message:"stream_failed"),r(!1)})},[o,p,e,l]);return{messages:t,input:o,setInput:a,send:f,sending:p,error:s}};var h=require("react/jsx-runtime"),T={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},ae=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),n=e.url??t;return n?e.mimeType.startsWith("image/")?(0,h.jsx)("a",{href:n,rel:"noopener noreferrer",target:"_blank",children:(0,h.jsx)("img",{alt:e.filename,src:n,style:T.image})}):(0,h.jsx)("a",{download:e.filename,href:n,style:T.link,children:e.filename}):null},B=()=>{let e=M(),{messages:t,input:n,setInput:o,send:a,sending:p,error:r}=A(e),s=(0,U.useRef)(null);(0,U.useEffect)(()=>{let i=s.current;i&&(i.scrollTop=i.scrollHeight)},[t]);let d=i=>{i.preventDefault(),a()};return(0,h.jsxs)("div",{style:T.root,children:[(0,h.jsx)("div",{ref:s,style:T.log,children:t.map(i=>(0,h.jsxs)("div",{style:i.role==="user"?T.user:T.assistant,children:[i.text||(i.streaming?"\u2026":""),i.artifacts&&i.artifacts.length>0?(0,h.jsx)("div",{style:T.artifacts,children:i.artifacts.map(l=>(0,h.jsx)(ae,{artifact:l},l.filename))}):null]},i.id))}),r?(0,h.jsx)("div",{style:T.error,children:r}):null,(0,h.jsxs)("form",{onSubmit:d,style:T.form,children:[(0,h.jsx)("input",{"aria-label":"Message",onChange:i=>o(i.target.value),placeholder:"Type a message\u2026",style:T.input,value:n}),(0,h.jsx)("button",{disabled:p||n.trim().length===0,style:T.button,type:"submit",children:"Send"})]})]})};var ie=/\/+$/,O=e=>{let t=e.sessionId,n=e.baseUrl.replace(ie,""),o=r=>`${n}/endpoint/${encodeURIComponent(e.token)}${r}`,a={"content-type":"application/json"},p=(r,s)=>({url:o(r),headers:a,body:JSON.stringify({message:s,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>H(o("/config"),{}),sendMessage:async r=>{let s=await W(p("/message",r));return t=s.sessionId,s},streamMessage:(r,s,d)=>N(p("/message/stream",r),s,i=>{t=i},d),subscribe:(r,s)=>{let d=s?.since??"0",i=!1,l,f=s?.intervalMs??3e3,u=async()=>{let y=t;if(y)try{let g=await H(o(`/messages?session=${encodeURIComponent(y)}&since=${encodeURIComponent(d)}`),{});for(let m of g.messages)r.onMessage(m);d=g.cursor}catch(g){r.onError?.(C(g))}i||(l=setTimeout(u,f))},c=()=>{i=!0,l&&clearTimeout(l)};return s?.signal?.addEventListener("abort",c),l=setTimeout(u,0),c},requestHuman:async()=>{t&&await fetch(o("/request-human"),{method:"POST",headers:a,body:JSON.stringify({sessionId:t})})}}};var G=e=>"transport"in e?e.transport:O(e);var le=/\/+$/,v="/api/v1/end-user";function pe(e){let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)}var K=e=>{let t=e.baseUrl.replace(le,""),n=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await n()}`}),a=async(r,s)=>{try{let d=await fetch(`${t}${v}${r}`,{method:s.method,headers:await o(),...s.body===void 0?{}:{body:JSON.stringify(s.body)}});if(!d.ok){let i=await d.text().catch(()=>""),l=`${s.method} ${r} \u2192 ${String(d.status)}`;try{let f=JSON.parse(i);f.message&&(l=f.message)}catch{l=i?`${l}: ${i}`:l}throw C(new Error(l))}return await d.json()}catch(d){throw C(d)}},p=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await H(`${t}${v}/tables`,await o())).tables,listRows:async(r,s)=>await H(`${t}${v}${p(r)}/rows?limit=${String(s??100)}`,await o()),insertRow:async(r,s)=>{let{rowIds:d}=await a(`${p(r)}/rows`,{method:"POST",body:{rows:[s]}}),i=d[0];if(!i)throw C(new Error("The row was not created."));return i},patchRow:async(r,s,d)=>(await a(`${p(r)}/rows/${encodeURIComponent(s)}`,{method:"PATCH",body:d})).updated,deleteRow:async(r,s)=>(await a(`/${encodeURIComponent(r)}/${encodeURIComponent(s)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await H(`${t}${v}/tables/files/rows?limit=200`,await o())).rows.map(s=>({id:s.rowId,filename:String(s.data.filename??""),source:String(s.data.source??"client"),campaign:String(s.data.campaign??""),size:typeof s.data.size=="number"?s.data.size:0,createdAt:s.createdAt})),uploadFile:async(r,s)=>{let d=new Uint8Array(await r.arrayBuffer());return await a("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:pe(d),...s===void 0?{}:{message:s}}})},fileLink:async r=>await H(`${t}${v}/files/${encodeURIComponent(r)}/url`,await o()),deleteFile:async r=>(await a(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted}};var w=require("react");function D(e,t){return e.some(o=>o.id===t.id)?e:[...t.author==="client"?e.filter(o=>!(o.pending&&o.author==="client"&&o.text===t.text)):e,t]}var V=(e,t)=>{let[n,o]=(0,w.useState)([]),[a,p]=(0,w.useState)(""),[r,s]=(0,w.useState)(!1),[d,i]=(0,w.useState)(null),l=(0,w.useRef)(0),f=t?.pollMs??2e3;(0,w.useEffect)(()=>{let c=!0;return e.getInfo().then(y=>{c&&y.greeting&&o(g=>g.length===0?[{id:"greeting",cursor:"",author:"agent",text:y.greeting}]:g)}).catch(()=>{}),()=>{c=!1}},[e]),(0,w.useEffect)(()=>e.subscribe?.({onMessage:c=>o(y=>D(y,c)),onError:c=>i(c.error)},{intervalMs:f}),[e,f]);let u=(0,w.useCallback)(()=>{let c=a.trim();if(!c||r)return;p(""),i(null),s(!0),l.current+=1;let y={id:`pending-${l.current}`,cursor:"",author:"client",text:c,pending:!0};o(g=>[...g,y]),e.sendMessage(c).then(()=>s(!1)).catch(g=>{i(g instanceof Error?g.message:"send_failed"),s(!1)})},[a,r,e]);return{messages:n,input:a,setInput:p,send:u,sending:r,error:d}};0&&(module.exports={Chat,HolaProvider,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,mergeMessage,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
8
+ `)}},i=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),i()};await i()};var ae=/\/+$/,S="/api/v1/end-user",z=e=>{let t=e.baseUrl.replace(ae,""),a=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),d=async r=>{let o=await fetch(`${t}${S}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!o.ok)throw new Error(`hola: message failed (${o.status})`);return{reply:(await o.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${S}/chat/info${s("?")}`,await n()),sendMessage:d,streamMessage:async(r,o)=>{try{let l=await d(r);o.onText?.(l.reply),o.onComplete?.(l)}catch(l){o.onError?.(C(l))}},subscribe:(r,o)=>{let l=o?.since??"0",i=!1,c,b=o?.intervalMs??3e3,u=async()=>{try{let g=await h(`${t}${S}/chat/thread?since=${encodeURIComponent(l)}${s("&")}`,await n());for(let m of g.messages)r.onMessage(m);l=g.cursor}catch(g){r.onError?.(C(g))}i||(c=setTimeout(u,b))},p=()=>{i=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{await fetch(`${t}${S}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};var $=require("react"),k=(e,t)=>{let[a,s]=(0,$.useState)("");return(0,$.useEffect)(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([n],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),a};var D=require("react");var M=require("react"),B=require("react/jsx-runtime"),J=(0,M.createContext)(null),q=({client:e,children:t})=>(0,B.jsx)(J.Provider,{value:e,children:t}),A=()=>{let e=(0,M.useContext)(J);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var w=require("react"),U=e=>{let[t,a]=(0,w.useState)([]),[s,n]=(0,w.useState)(""),[d,r]=(0,w.useState)(!1),[o,l]=(0,w.useState)(null),i=(0,w.useRef)(0),c=(0,w.useCallback)(()=>{let u=i.current;return i.current+=1,u},[]);(0,w.useEffect)(()=>{let u=!0;return e.getInfo().then(p=>{u&&p.greeting&&a([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let b=(0,w.useCallback)(()=>{let u=s.trim();if(!u||d)return;n(""),l(null),r(!0);let p=c(),g=c();a(f=>[...f,{id:p,role:"user",text:u},{id:g,role:"assistant",text:"",streaming:!0}]);let m=f=>{a(H=>H.map(_=>_.id===g?f(_):_))};e.streamMessage(u,{onText:f=>m(H=>({...H,text:H.text+f})),onComplete:f=>{m(H=>({...H,text:f.reply||H.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{m(H=>({...H,streaming:!1})),l(f.error),r(!1)}}).catch(f=>{m(H=>({...H,streaming:!1})),l(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:n,send:b,sending:d,error:o}};var y=require("react/jsx-runtime"),T={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},ie=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),a=e.url??t;return a?e.mimeType.startsWith("image/")?(0,y.jsx)("a",{href:a,rel:"noopener noreferrer",target:"_blank",children:(0,y.jsx)("img",{alt:e.filename,src:a,style:T.image})}):(0,y.jsx)("a",{download:e.filename,href:a,style:T.link,children:e.filename}):null},G=()=>{let e=A(),{messages:t,input:a,setInput:s,send:n,sending:d,error:r}=U(e),o=(0,D.useRef)(null);(0,D.useEffect)(()=>{let i=o.current;i&&(i.scrollTop=i.scrollHeight)},[t]);let l=i=>{i.preventDefault(),n()};return(0,y.jsxs)("div",{style:T.root,children:[(0,y.jsx)("div",{ref:o,style:T.log,children:t.map(i=>(0,y.jsxs)("div",{style:i.role==="user"?T.user:T.assistant,children:[i.text||(i.streaming?"\u2026":""),i.artifacts&&i.artifacts.length>0?(0,y.jsx)("div",{style:T.artifacts,children:i.artifacts.map(c=>(0,y.jsx)(ie,{artifact:c},c.filename))}):null]},i.id))}),r?(0,y.jsx)("div",{style:T.error,children:r}):null,(0,y.jsxs)("form",{onSubmit:l,style:T.form,children:[(0,y.jsx)("input",{"aria-label":"Message",onChange:i=>s(i.target.value),placeholder:"Type a message\u2026",style:T.input,value:a}),(0,y.jsx)("button",{disabled:d||a.trim().length===0,style:T.button,type:"submit",children:"Send"})]})]})};var le=/\/+$/,O=e=>{let t=e.sessionId,a=e.baseUrl.replace(le,""),s=r=>`${a}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:n,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(s("/config"),{}),sendMessage:async r=>{let o=await j(d("/message",r));return t=o.sessionId,o},streamMessage:(r,o,l)=>N(d("/message/stream",r),o,i=>{t=i},l),subscribe:(r,o)=>{let l=o?.since??"0",i=!1,c,b=o?.intervalMs??3e3,u=async()=>{let g=t;if(g)try{let m=await h(s(`/messages?session=${encodeURIComponent(g)}&since=${encodeURIComponent(l)}`),{});for(let f of m.messages)r.onMessage(f);l=m.cursor}catch(m){r.onError?.(C(m))}i||(c=setTimeout(u,b))},p=()=>{i=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var K=e=>"transport"in e?e.transport:O(e);var ce=/\/+$/,P="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function de(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var V=e=>{let t=e.baseUrl.replace(ce,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),n=async(r,o)=>{try{let l=await fetch(`${t}${P}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(l.status===409){let i=await l.json().catch(()=>({}));throw new v(i.message??"Someone else saved this first.")}if(!l.ok){let i=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let b=JSON.parse(i);b.message&&(c=b.message)}catch{c=i?`${c}: ${i}`:c}throw C(new Error(c))}return await l.json()}catch(l){throw C(l)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await h(`${t}${P}/tables`,await s())).tables,listRows:async(r,o)=>await h(`${t}${P}${d(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:l}=await n(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),i=l[0];if(!i)throw C(new Error("The row was not created."));return i},patchRow:async(r,o,l)=>(await n(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${P}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let l=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:de(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await h(`${t}${P}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${P}/documents`,await s())).documents,readDocument:async r=>(await h(`${t}${P}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,o)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};var x=require("react");function L(e,t){return e.some(s=>s.id===t.id)?e:[...t.author==="client"?e.filter(s=>!(s.pending&&s.author==="client"&&s.text===t.text)):e,t]}var Y=(e,t)=>{let[a,s]=(0,x.useState)([]),[n,d]=(0,x.useState)(""),[r,o]=(0,x.useState)(!1),[l,i]=(0,x.useState)(null),c=(0,x.useRef)(0),b=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(g=>{p&&g.greeting&&s(m=>m.length===0?[{id:"greeting",cursor:"",author:"agent",text:g.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.useEffect)(()=>e.subscribe?.({onMessage:p=>s(g=>L(g,p)),onError:p=>i(p.error)},{intervalMs:b}),[e,b]);let u=(0,x.useCallback)(()=>{let p=n.trim();if(!p||r)return;d(""),i(null),o(!0),c.current+=1;let g={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,g]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{i(m instanceof Error?m.message:"send_failed"),o(!1)})},[n,r,e]);return{messages:a,input:n,setInput:d,send:u,sending:r,error:l}};0&&(module.exports={Chat,HolaProvider,PortalConflict,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,mergeMessage,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
package/dist/index.d.cts CHANGED
@@ -108,10 +108,19 @@ type ApiTransportConfig = {
108
108
  /** End-user access token for the Authorization header. A string, or a getter so a
109
109
  * refreshed token is used per request. */
110
110
  token: string | (() => string | Promise<string>);
111
+ /**
112
+ * Which of the customer's projects this conversation is about.
113
+ *
114
+ * Omit for the general thread — the one running conversation with the company,
115
+ * and where everything that predates per-project threads still lives. The
116
+ * server checks the project belongs to this customer before it trusts it, so
117
+ * naming someone else's is a 404 rather than a way into their thread.
118
+ */
119
+ project?: string;
111
120
  };
112
121
  /** The Bearer-authed transport for the signed-in end-user PORTAL (`/api/v1/end-user/*`). Same
113
122
  * client surface as the anonymous widget transport — the token (not a path param) resolves the
114
- * customer and their one lifelong thread. The portal is buffered (no SSE) and one-thread-per-user,
123
+ * customer; an optional `project` picks which of their threads. The portal is buffered (no SSE) and one-thread-per-user,
115
124
  * so `sendMessage` returns just the reply and everything out-of-turn — the AI's turns, a human's
116
125
  * reply, an async artifact (video) — arrives via `subscribe`. */
117
126
  declare const createApiTransport: (config: ApiTransportConfig) => HolaTransport;
@@ -197,6 +206,29 @@ type PortalFile = {
197
206
  size: number;
198
207
  createdAt: string;
199
208
  };
209
+ /** A document shared with this customer. The body arrives only on a read. */
210
+ type PortalDocumentSummary = {
211
+ documentId: string;
212
+ title: string;
213
+ version: number;
214
+ /** Whether the company opened this one for editing, not merely for reading. */
215
+ editable: boolean;
216
+ updatedAt: string;
217
+ };
218
+ type PortalDocument = PortalDocumentSummary & {
219
+ body: string;
220
+ };
221
+ /**
222
+ * A save that lost a race.
223
+ *
224
+ * Thrown rather than returned so it cannot be mistaken for success, and separate
225
+ * from a plain error because the caller should do something different: re-read,
226
+ * reapply, save again — never retry the same bytes, which would overwrite whoever
227
+ * got there first.
228
+ */
229
+ declare class PortalConflict extends Error {
230
+ constructor(message: string);
231
+ }
200
232
  type PortalData = {
201
233
  /** The tables this customer can see. A table the company has not shared is
202
234
  * absent, not empty — it answers as if it did not exist. */
@@ -236,6 +268,23 @@ type PortalData = {
236
268
  }>;
237
269
  /** Only files this customer sent, and only before they are filed. */
238
270
  deleteFile(rowId: string): Promise<number>;
271
+ /** Documents the company shared with this customer. */
272
+ listDocuments(): Promise<PortalDocumentSummary[]>;
273
+ readDocument(documentId: string): Promise<PortalDocument>;
274
+ /**
275
+ * Save new text.
276
+ *
277
+ * `baseVersion` is the version that was open. Rejects with a PortalConflict if
278
+ * it has moved — do not retry with the same text: read again, put the change
279
+ * back on top of theirs, and save that.
280
+ */
281
+ saveDocument(documentId: string, edit: {
282
+ body: string;
283
+ baseVersion: number;
284
+ note?: string;
285
+ }): Promise<{
286
+ version: number;
287
+ }>;
239
288
  };
240
289
  declare const createPortalData: (config: ApiTransportConfig) => PortalData;
241
290
 
@@ -291,4 +340,4 @@ declare const useThread: (client: HolaClient, options?: {
291
340
  pollMs?: number;
292
341
  }) => UseThread;
293
342
 
294
- export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type PortalData, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
343
+ export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
package/dist/index.d.ts CHANGED
@@ -108,10 +108,19 @@ type ApiTransportConfig = {
108
108
  /** End-user access token for the Authorization header. A string, or a getter so a
109
109
  * refreshed token is used per request. */
110
110
  token: string | (() => string | Promise<string>);
111
+ /**
112
+ * Which of the customer's projects this conversation is about.
113
+ *
114
+ * Omit for the general thread — the one running conversation with the company,
115
+ * and where everything that predates per-project threads still lives. The
116
+ * server checks the project belongs to this customer before it trusts it, so
117
+ * naming someone else's is a 404 rather than a way into their thread.
118
+ */
119
+ project?: string;
111
120
  };
112
121
  /** The Bearer-authed transport for the signed-in end-user PORTAL (`/api/v1/end-user/*`). Same
113
122
  * client surface as the anonymous widget transport — the token (not a path param) resolves the
114
- * customer and their one lifelong thread. The portal is buffered (no SSE) and one-thread-per-user,
123
+ * customer; an optional `project` picks which of their threads. The portal is buffered (no SSE) and one-thread-per-user,
115
124
  * so `sendMessage` returns just the reply and everything out-of-turn — the AI's turns, a human's
116
125
  * reply, an async artifact (video) — arrives via `subscribe`. */
117
126
  declare const createApiTransport: (config: ApiTransportConfig) => HolaTransport;
@@ -197,6 +206,29 @@ type PortalFile = {
197
206
  size: number;
198
207
  createdAt: string;
199
208
  };
209
+ /** A document shared with this customer. The body arrives only on a read. */
210
+ type PortalDocumentSummary = {
211
+ documentId: string;
212
+ title: string;
213
+ version: number;
214
+ /** Whether the company opened this one for editing, not merely for reading. */
215
+ editable: boolean;
216
+ updatedAt: string;
217
+ };
218
+ type PortalDocument = PortalDocumentSummary & {
219
+ body: string;
220
+ };
221
+ /**
222
+ * A save that lost a race.
223
+ *
224
+ * Thrown rather than returned so it cannot be mistaken for success, and separate
225
+ * from a plain error because the caller should do something different: re-read,
226
+ * reapply, save again — never retry the same bytes, which would overwrite whoever
227
+ * got there first.
228
+ */
229
+ declare class PortalConflict extends Error {
230
+ constructor(message: string);
231
+ }
200
232
  type PortalData = {
201
233
  /** The tables this customer can see. A table the company has not shared is
202
234
  * absent, not empty — it answers as if it did not exist. */
@@ -236,6 +268,23 @@ type PortalData = {
236
268
  }>;
237
269
  /** Only files this customer sent, and only before they are filed. */
238
270
  deleteFile(rowId: string): Promise<number>;
271
+ /** Documents the company shared with this customer. */
272
+ listDocuments(): Promise<PortalDocumentSummary[]>;
273
+ readDocument(documentId: string): Promise<PortalDocument>;
274
+ /**
275
+ * Save new text.
276
+ *
277
+ * `baseVersion` is the version that was open. Rejects with a PortalConflict if
278
+ * it has moved — do not retry with the same text: read again, put the change
279
+ * back on top of theirs, and save that.
280
+ */
281
+ saveDocument(documentId: string, edit: {
282
+ body: string;
283
+ baseVersion: number;
284
+ note?: string;
285
+ }): Promise<{
286
+ version: number;
287
+ }>;
239
288
  };
240
289
  declare const createPortalData: (config: ApiTransportConfig) => PortalData;
241
290
 
@@ -291,4 +340,4 @@ declare const useThread: (client: HolaClient, options?: {
291
340
  pollMs?: number;
292
341
  }) => UseThread;
293
342
 
294
- export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type PortalData, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
343
+ export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
2
- var O=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},R=e=>{let t=O(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{let t=O(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},S=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var j=/^ /,x=e=>{if(e&&typeof e=="object"){let t=e,a=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:a,retryable:t.retryable}:{error:a}}return{error:"request_failed"}},z=e=>{let t="message",a=[];for(let s of e.split(`
3
- `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&a.push(s.slice(5).replace(j,"")));return a.length>0?{event:t,data:a.join(`
4
- `)}:null},J=(e,t,a)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let i=s.sessionId;i&&(a(i),t.onSession?.(i));return}if(e.event==="turn_event"){t.onEvent?.(s);let i=R(s);i&&t.onText?.(i);let p=E(s);p&&t.onThinking?.(p);let r=S(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let i=s;a(i.sessionId),t.onComplete?.(i);return}e.event==="chat_error"&&t.onError?.(x(s))},b=async(e,t)=>{let a=await fetch(e,{headers:t});if(!a.ok)throw new Error(`hola: request failed (${a.status})`);return await a.json()},_=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},F=async(e,t,a,s)=>{let i=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(i.ok&&i.body)){t.onError?.({error:`stream_failed_${i.status}`});return}let p=i.body.getReader(),r=new TextDecoder,o="",d=()=>{let l=o.indexOf(`
2
+ var O=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},E=e=>{let t=O(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},S=e=>{let t=O(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},$=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var z=/^ /,x=e=>{if(e&&typeof e=="object"){let t=e,i=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:i,retryable:t.retryable}:{error:i}}return{error:"request_failed"}},J=e=>{let t="message",i=[];for(let s of e.split(`
3
+ `))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&i.push(s.slice(5).replace(z,"")));return i.length>0?{event:t,data:i.join(`
4
+ `)}:null},q=(e,t,i)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let a=s.sessionId;a&&(i(a),t.onSession?.(a));return}if(e.event==="turn_event"){t.onEvent?.(s);let a=E(s);a&&t.onText?.(a);let d=S(s);d&&t.onThinking?.(d);let r=$(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let a=s;i(a.sessionId),t.onComplete?.(a);return}e.event==="chat_error"&&t.onError?.(x(s))},b=async(e,t)=>{let i=await fetch(e,{headers:t});if(!i.ok)throw new Error(`hola: request failed (${i.status})`);return await i.json()},_=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},F=async(e,t,i,s)=>{let a=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(a.ok&&a.body)){t.onError?.({error:`stream_failed_${a.status}`});return}let d=a.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
5
5
 
6
- `);for(;l!==-1;){let f=o.slice(0,l);o=o.slice(l+2);let u=z(f);u&&J(u,t,a),l=o.indexOf(`
6
+ `);for(;c!==-1;){let y=o.slice(0,c);o=o.slice(c+2);let u=J(y);u&&q(u,t,i),c=o.indexOf(`
7
7
 
8
- `)}},n=async()=>{let l=await p.read();if(!l.done)return o+=r.decode(l.value,{stream:!0}),d(),n()};await n()};var q=/\/+$/,C="/api/v1/end-user",B=e=>{let t=e.baseUrl.replace(q,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),i=async p=>{let r=await fetch(`${t}${C}/chat`,{method:"POST",headers:await s(),body:JSON.stringify({message:p})});if(!r.ok)throw new Error(`hola: message failed (${r.status})`);return{reply:(await r.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${C}/chat/info`,await s()),sendMessage:i,streamMessage:async(p,r)=>{try{let o=await i(p);r.onText?.(o.reply),r.onComplete?.(o)}catch(o){r.onError?.(x(o))}},subscribe:(p,r)=>{let o=r?.since??"0",d=!1,n,l=r?.intervalMs??3e3,f=async()=>{try{let c=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(o)}`,await s());for(let y of c.messages)p.onMessage(y);o=c.cursor}catch(c){p.onError?.(x(c))}d||(n=setTimeout(f,l))},u=()=>{d=!0,n&&clearTimeout(n)};return r?.signal?.addEventListener("abort",u),n=setTimeout(f,0),u},requestHuman:async()=>{await fetch(`${t}${C}/chat/request-human`,{method:"POST",headers:await s(),body:"{}"})}}};import{useEffect as G,useState as K}from"react";var k=(e,t)=>{let[a,s]=K("");return G(()=>{if(!e){s("");return}let i=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),p=URL.createObjectURL(new Blob([i],{type:t}));return s(p),()=>URL.revokeObjectURL(p)},[e,t]),a};import{useEffect as te,useRef as re}from"react";import{createContext as V,useContext as Y}from"react";import{jsx as X}from"react/jsx-runtime";var D=V(null),Q=({client:e,children:t})=>X(D.Provider,{value:e,children:t}),$=()=>{let e=Y(D);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as L,useEffect as Z,useRef as ee,useState as v}from"react";var M=e=>{let[t,a]=v([]),[s,i]=v(""),[p,r]=v(!1),[o,d]=v(null),n=ee(0),l=L(()=>{let u=n.current;return n.current+=1,u},[]);Z(()=>{let u=!0;return e.getInfo().then(c=>{u&&c.greeting&&a([{id:l(),role:"assistant",text:c.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,l]);let f=L(()=>{let u=s.trim();if(!u||p)return;i(""),d(null),r(!0);let c=l(),y=l();a(m=>[...m,{id:c,role:"user",text:u},{id:y,role:"assistant",text:"",streaming:!0}]);let g=m=>{a(h=>h.map(I=>I.id===y?m(I):I))};e.streamMessage(u,{onText:m=>g(h=>({...h,text:h.text+m})),onComplete:m=>{g(h=>({...h,text:m.reply||h.text,artifacts:m.artifacts,streaming:!1})),r(!1)},onError:m=>{g(h=>({...h,streaming:!1})),d(m.error),r(!1)}}).catch(m=>{g(h=>({...h,streaming:!1})),d(m instanceof Error?m.message:"stream_failed"),r(!1)})},[s,p,e,l]);return{messages:t,input:s,setInput:i,send:f,sending:p,error:o}};import{jsx as H,jsxs as A}from"react/jsx-runtime";var w={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},oe=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),a=e.url??t;return a?e.mimeType.startsWith("image/")?H("a",{href:a,rel:"noopener noreferrer",target:"_blank",children:H("img",{alt:e.filename,src:a,style:w.image})}):H("a",{download:e.filename,href:a,style:w.link,children:e.filename}):null},se=()=>{let e=$(),{messages:t,input:a,setInput:s,send:i,sending:p,error:r}=M(e),o=re(null);te(()=>{let n=o.current;n&&(n.scrollTop=n.scrollHeight)},[t]);let d=n=>{n.preventDefault(),i()};return A("div",{style:w.root,children:[H("div",{ref:o,style:w.log,children:t.map(n=>A("div",{style:n.role==="user"?w.user:w.assistant,children:[n.text||(n.streaming?"\u2026":""),n.artifacts&&n.artifacts.length>0?H("div",{style:w.artifacts,children:n.artifacts.map(l=>H(oe,{artifact:l},l.filename))}):null]},n.id))}),r?H("div",{style:w.error,children:r}):null,A("form",{onSubmit:d,style:w.form,children:[H("input",{"aria-label":"Message",onChange:n=>s(n.target.value),placeholder:"Type a message\u2026",style:w.input,value:a}),H("button",{disabled:p||a.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ne=/\/+$/,U=e=>{let t=e.sessionId,a=e.baseUrl.replace(ne,""),s=r=>`${a}/endpoint/${encodeURIComponent(e.token)}${r}`,i={"content-type":"application/json"},p=(r,o)=>({url:s(r),headers:i,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(s("/config"),{}),sendMessage:async r=>{let o=await _(p("/message",r));return t=o.sessionId,o},streamMessage:(r,o,d)=>F(p("/message/stream",r),o,n=>{t=n},d),subscribe:(r,o)=>{let d=o?.since??"0",n=!1,l,f=o?.intervalMs??3e3,u=async()=>{let y=t;if(y)try{let g=await b(s(`/messages?session=${encodeURIComponent(y)}&since=${encodeURIComponent(d)}`),{});for(let m of g.messages)r.onMessage(m);d=g.cursor}catch(g){r.onError?.(x(g))}n||(l=setTimeout(u,f))},c=()=>{n=!0,l&&clearTimeout(l)};return o?.signal?.addEventListener("abort",c),l=setTimeout(u,0),c},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:i,body:JSON.stringify({sessionId:t})})}}};var ae=e=>"transport"in e?e.transport:U(e);var ie=/\/+$/,T="/api/v1/end-user";function le(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var pe=e=>{let t=e.baseUrl.replace(ie,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),i=async(r,o)=>{try{let d=await fetch(`${t}${T}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(!d.ok){let n=await d.text().catch(()=>""),l=`${o.method} ${r} \u2192 ${String(d.status)}`;try{let f=JSON.parse(n);f.message&&(l=f.message)}catch{l=n?`${l}: ${n}`:l}throw x(new Error(l))}return await d.json()}catch(d){throw x(d)}},p=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${T}/tables`,await s())).tables,listRows:async(r,o)=>await b(`${t}${T}${p(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:d}=await i(`${p(r)}/rows`,{method:"POST",body:{rows:[o]}}),n=d[0];if(!n)throw x(new Error("The row was not created."));return n},patchRow:async(r,o,d)=>(await i(`${p(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:d})).updated,deleteRow:async(r,o)=>(await i(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${T}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let d=new Uint8Array(await r.arrayBuffer());return await i("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:le(d),...o===void 0?{}:{message:o}}})},fileLink:async r=>await b(`${t}${T}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await i(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted}};import{useCallback as de,useEffect as W,useRef as ce,useState as P}from"react";function N(e,t){return e.some(s=>s.id===t.id)?e:[...t.author==="client"?e.filter(s=>!(s.pending&&s.author==="client"&&s.text===t.text)):e,t]}var ue=(e,t)=>{let[a,s]=P([]),[i,p]=P(""),[r,o]=P(!1),[d,n]=P(null),l=ce(0),f=t?.pollMs??2e3;W(()=>{let c=!0;return e.getInfo().then(y=>{c&&y.greeting&&s(g=>g.length===0?[{id:"greeting",cursor:"",author:"agent",text:y.greeting}]:g)}).catch(()=>{}),()=>{c=!1}},[e]),W(()=>e.subscribe?.({onMessage:c=>s(y=>N(y,c)),onError:c=>n(c.error)},{intervalMs:f}),[e,f]);let u=de(()=>{let c=i.trim();if(!c||r)return;p(""),n(null),o(!0),l.current+=1;let y={id:`pending-${l.current}`,cursor:"",author:"client",text:c,pending:!0};s(g=>[...g,y]),e.sendMessage(c).then(()=>o(!1)).catch(g=>{n(g instanceof Error?g.message:"send_failed"),o(!1)})},[i,r,e]);return{messages:a,input:i,setInput:p,send:u,sending:r,error:d}};export{se as Chat,Q as HolaProvider,B as createApiTransport,ae as createHolaClient,pe as createPortalData,U as createWidgetTransport,N as mergeMessage,R as textDeltaOf,E as thinkingDeltaOf,S as toolEventOf,k as useArtifactUrl,M as useChat,$ as useHolaClient,ue as useThread};
8
+ `)}},n=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),n()};await n()};var B=/\/+$/,C="/api/v1/end-user",G=e=>{let t=e.baseUrl.replace(B,""),i=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",a=async()=>({"content-type":"application/json",authorization:`Bearer ${await i()}`}),d=async r=>{let o=await fetch(`${t}${C}/chat${s("?")}`,{method:"POST",headers:await a(),body:JSON.stringify({message:r})});if(!o.ok)throw new Error(`hola: message failed (${o.status})`);return{reply:(await o.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${C}/chat/info${s("?")}`,await a()),sendMessage:d,streamMessage:async(r,o)=>{try{let l=await d(r);o.onText?.(l.reply),o.onComplete?.(l)}catch(l){o.onError?.(x(l))}},subscribe:(r,o)=>{let l=o?.since??"0",n=!1,c,y=o?.intervalMs??3e3,u=async()=>{try{let g=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(l)}${s("&")}`,await a());for(let m of g.messages)r.onMessage(m);l=g.cursor}catch(g){r.onError?.(x(g))}n||(c=setTimeout(u,y))},p=()=>{n=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{await fetch(`${t}${C}/chat/request-human${s("?")}`,{method:"POST",headers:await a(),body:"{}"})}}};import{useEffect as K,useState as V}from"react";var k=(e,t)=>{let[i,s]=V("");return K(()=>{if(!e){s("");return}let a=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([a],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),i};import{useEffect as re,useRef as oe}from"react";import{createContext as Y,useContext as Q}from"react";import{jsx as Z}from"react/jsx-runtime";var L=Y(null),X=({client:e,children:t})=>Z(L.Provider,{value:e,children:t}),M=()=>{let e=Q(L);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as W,useEffect as ee,useRef as te,useState as P}from"react";var A=e=>{let[t,i]=P([]),[s,a]=P(""),[d,r]=P(!1),[o,l]=P(null),n=te(0),c=W(()=>{let u=n.current;return n.current+=1,u},[]);ee(()=>{let u=!0;return e.getInfo().then(p=>{u&&p.greeting&&i([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let y=W(()=>{let u=s.trim();if(!u||d)return;a(""),l(null),r(!0);let p=c(),g=c();i(f=>[...f,{id:p,role:"user",text:u},{id:g,role:"assistant",text:"",streaming:!0}]);let m=f=>{i(h=>h.map(R=>R.id===g?f(R):R))};e.streamMessage(u,{onText:f=>m(h=>({...h,text:h.text+f})),onComplete:f=>{m(h=>({...h,text:f.reply||h.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{m(h=>({...h,streaming:!1})),l(f.error),r(!1)}}).catch(f=>{m(h=>({...h,streaming:!1})),l(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:a,send:y,sending:d,error:o}};import{jsx as H,jsxs as U}from"react/jsx-runtime";var w={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},se=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),i=e.url??t;return i?e.mimeType.startsWith("image/")?H("a",{href:i,rel:"noopener noreferrer",target:"_blank",children:H("img",{alt:e.filename,src:i,style:w.image})}):H("a",{download:e.filename,href:i,style:w.link,children:e.filename}):null},ne=()=>{let e=M(),{messages:t,input:i,setInput:s,send:a,sending:d,error:r}=A(e),o=oe(null);re(()=>{let n=o.current;n&&(n.scrollTop=n.scrollHeight)},[t]);let l=n=>{n.preventDefault(),a()};return U("div",{style:w.root,children:[H("div",{ref:o,style:w.log,children:t.map(n=>U("div",{style:n.role==="user"?w.user:w.assistant,children:[n.text||(n.streaming?"\u2026":""),n.artifacts&&n.artifacts.length>0?H("div",{style:w.artifacts,children:n.artifacts.map(c=>H(se,{artifact:c},c.filename))}):null]},n.id))}),r?H("div",{style:w.error,children:r}):null,U("form",{onSubmit:l,style:w.form,children:[H("input",{"aria-label":"Message",onChange:n=>s(n.target.value),placeholder:"Type a message\u2026",style:w.input,value:i}),H("button",{disabled:d||i.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ae=/\/+$/,D=e=>{let t=e.sessionId,i=e.baseUrl.replace(ae,""),s=r=>`${i}/endpoint/${encodeURIComponent(e.token)}${r}`,a={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:a,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(s("/config"),{}),sendMessage:async r=>{let o=await _(d("/message",r));return t=o.sessionId,o},streamMessage:(r,o,l)=>F(d("/message/stream",r),o,n=>{t=n},l),subscribe:(r,o)=>{let l=o?.since??"0",n=!1,c,y=o?.intervalMs??3e3,u=async()=>{let g=t;if(g)try{let m=await b(s(`/messages?session=${encodeURIComponent(g)}&since=${encodeURIComponent(l)}`),{});for(let f of m.messages)r.onMessage(f);l=m.cursor}catch(m){r.onError?.(x(m))}n||(c=setTimeout(u,y))},p=()=>{n=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:a,body:JSON.stringify({sessionId:t})})}}};var ie=e=>"transport"in e?e.transport:D(e);var le=/\/+$/,T="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function ce(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var de=e=>{let t=e.baseUrl.replace(le,""),i=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await i()}`}),a=async(r,o)=>{try{let l=await fetch(`${t}${T}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(l.status===409){let n=await l.json().catch(()=>({}));throw new v(n.message??"Someone else saved this first.")}if(!l.ok){let n=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let y=JSON.parse(n);y.message&&(c=y.message)}catch{c=n?`${c}: ${n}`:c}throw x(new Error(c))}return await l.json()}catch(l){throw x(l)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${T}/tables`,await s())).tables,listRows:async(r,o)=>await b(`${t}${T}${d(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:l}=await a(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),n=l[0];if(!n)throw x(new Error("The row was not created."));return n},patchRow:async(r,o,l)=>(await a(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await a(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${T}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let l=new Uint8Array(await r.arrayBuffer());return await a("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:ce(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await b(`${t}${T}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await a(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${T}/documents`,await s())).documents,readDocument:async r=>(await b(`${t}${T}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,o)=>await a(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};import{useCallback as pe,useEffect as j,useRef as ue,useState as I}from"react";function N(e,t){return e.some(s=>s.id===t.id)?e:[...t.author==="client"?e.filter(s=>!(s.pending&&s.author==="client"&&s.text===t.text)):e,t]}var me=(e,t)=>{let[i,s]=I([]),[a,d]=I(""),[r,o]=I(!1),[l,n]=I(null),c=ue(0),y=t?.pollMs??2e3;j(()=>{let p=!0;return e.getInfo().then(g=>{p&&g.greeting&&s(m=>m.length===0?[{id:"greeting",cursor:"",author:"agent",text:g.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),j(()=>e.subscribe?.({onMessage:p=>s(g=>N(g,p)),onError:p=>n(p.error)},{intervalMs:y}),[e,y]);let u=pe(()=>{let p=a.trim();if(!p||r)return;d(""),n(null),o(!0),c.current+=1;let g={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,g]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{n(m instanceof Error?m.message:"send_failed"),o(!1)})},[a,r,e]);return{messages:i,input:a,setInput:d,send:u,sending:r,error:l}};export{ne as Chat,X as HolaProvider,v as PortalConflict,G as createApiTransport,ie as createHolaClient,de as createPortalData,D as createWidgetTransport,N as mergeMessage,E as textDeltaOf,S as thinkingDeltaOf,$ as toolEventOf,k as useArtifactUrl,A as useChat,M as useHolaClient,me as useThread};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holaboss/client",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.0-beta.3",
4
4
  "description": "Injectable client SDK for embedding a Holaboss AI employee (chat today; dashboard + artifacts next) into your own web app.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",