@arcteninc/core 0.0.177 → 0.0.179

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,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react"),Qe=require("@ai-sdk/react"),De=require("ai"),R=Symbol.for("@arcteninc/core:originalName");function Xe(u,i){return u[R]=i,u}function Ze(u,i,y,w){if(typeof u!="function")return null;const b=u[R];if(b){const E=i[b];if(E&&!y.has(E.name))return{fn:u,meta:E}}const f=u.name;let c;if(f&&(c=i[f],c&&!y.has(c.name)))return u[R]=c.name,{fn:u,meta:c};if(w&&w.length>0){for(const E of w)if(!y.has(E)&&(c=i[E],c))return u[R]=c.name,{fn:u,meta:c}}else if(c=Object.values(i).find(j=>!y.has(j.name)),c)return u[R]=c.name,{fn:u,meta:c};return null}function Ue(u,i){if(!i)return[];let y,w;if("functions"in i&&typeof i.functions=="object"){const f=i;y=f.functions,w=f.toolOrder}else y=i,w=void 0;const b=new Set;return u.map(f=>{const c=Ze(f,y,b,w);if(!c){const E=f.name,N=f[R]||E||"unnamed";return typeof process<"u"&&(process.env.NODE_ENV==="development"||process.env.NODE_ENV!=="production")&&typeof console<"u"&&console.warn&&console.warn(`⚠️ [@arcteninc/core] No metadata found for tool "${N}". This tool will be excluded.
2
- To fix: Run "arcten-extract-types ." and ensure "${N}" is used in ArctenAgent/useAgent.`),null}return b.add(c.meta.name),{name:c.meta.name,description:c.meta.description,jsonSchema:c.meta.parameters}}).filter(f=>f!==null)}const C=process.env.NODE_ENV==="development";function oe(u){if(typeof u=="string")try{const i=JSON.parse(u);return oe(i)}catch{return u}if(Array.isArray(u))return u.map(oe);if(u&&typeof u=="object"){const i={};for(const[y,w]of Object.entries(u))i[y]=oe(w);return i}return u}function et(u){if(typeof u=="string"){try{const i=JSON.parse(u);if(i&&typeof i=="object"&&!Array.isArray(i))return oe(i)}catch{}return{}}return u&&typeof u=="object"&&!Array.isArray(u)?oe(u):{}}function tt(u,i){if(!u||u.length===0)return i;const y=new Map;i.forEach(f=>{const c=f[R]||f.name;c&&y.set(c,f)});const w=new Map(u.map(f=>[f.name,f])),b=[];return u.forEach(f=>{const c=y.get(f.name);if(!c){C&&console.log(`[useAgent] Skipping server-only tool: ${f.name}`);return}if(!f.isEnabled&&!f.isOverridable){C&&console.log(`[useAgent] Blocking non-overridable disabled tool: ${f.name}`);return}if(!f.isEnabled&&f.isOverridable&&c){C&&console.log(`[useAgent] Allowing overridable disabled tool with local implementation: ${f.name}`),b.push(c);return}f.isEnabled&&c&&b.push(c)}),i.forEach(f=>{const c=f[R]||f.name;c&&!w.has(c)&&(C&&console.log(`[useAgent] Allowing client-only tool (forward compatible): ${c}`),b.push(f))}),b}function ot({apiBaseUrl:u="https://api.arcten.com",tokenEndpoint:i="/api/arcten/token",clientToken:y,skipTokenFetch:w=!1,user:b,tools:f=[],safeTools:c=[],safeToolNames:E,toolMetadata:j,systemPrompt:N="",agentId:B,agentName:K,projectId:x,enableRemoteConfig:$,remoteConfigUrl:S,initialMessages:_=[],conversationId:Q,state:J,onToolCall:D,onFinish:P,sources:M,ragConfig:re,ragFilters:z}={}){const q=a.useRef(J),v=u.replace(/\/+$/,""),[m,fe]=a.useState(null),[nt,Ee]=a.useState(!1),[st,Te]=a.useState(null),ne=a.useMemo(()=>{const t=M||[],e=m?.config?.sources||[];return[...new Set([...t,...e])]},[M,m]),T=a.useMemo(()=>re!==void 0?re:z?{enabled:!0,filters:z}:ne&&ne.length>0?{enabled:!0,organizationId:ne[0],autoExecute:!0,filters:void 0}:{enabled:!1},[ne,re,z]),Ae=a.useMemo(()=>$===!1?!1:!!x,[x,$]);a.useEffect(()=>{if(!Ae||!A.current)return;(async()=>{Ee(!0),Te(null);try{const o=`${S||v.replace("/chat","").replace(/\/$/,"")}/convex/api/query`,r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${A.current}`},body:JSON.stringify({path:"agents:getConfigForClient",args:{projectId:x,agentName:K,agentId:B},format:"json"})});if(!r.ok){if(r.status===404){C&&console.warn("[useAgent] Remote config endpoint not found (404), using local tools only"),fe(null);return}const s=await r.text().catch(()=>"Unknown error");throw new Error(`Failed to fetch remote config: ${r.status} ${s}`)}const n=await r.json();fe(n?.value||null),C&&console.log("[useAgent] Remote config loaded successfully:",n?.value)}catch(e){console.error("[useAgent] Failed to fetch remote config:",e);const o=e instanceof Error?e:new Error(String(e));Te(o),fe(null)}finally{Ee(!1)}})()},[Ae,x,K,B,v,S]);const[W,de]=a.useState(y||null),A=a.useRef(y||null);a.useEffect(()=>{q.current=J},[J]);const[Ie,ee]=a.useState(null),[X,he]=a.useState(null),F=a.useRef(null),[Ce,ve]=a.useState([]),[pe,Me]=a.useState(Q||(typeof window<"u"?crypto.randomUUID():null)),[Be,se]=a.useState(!1),[ge,me]=a.useState(new Map),ae=a.useRef(new Set),ye=a.useRef(new Set),ce=a.useRef(new Set);a.useEffect(()=>{q.current=J},[J]);const L=a.useMemo(()=>T?.enabled?async function(e,o=20,r){const n={...T.filters,...r};if(M&&M.length>1){const d=M.map(p=>fetch(`${v}/search`,{method:"POST",headers:{"Content-Type":"application/json",...A.current?{Authorization:`Bearer ${A.current}`}:{}},body:JSON.stringify({q:e,k:Math.min(o,100),organizationId:p,...Object.keys(n).length>0?{filters:n}:{}})}).then(g=>g.ok?g.json():null)),h=await Promise.all(d),l=[];for(const p of h)p?.candidates&&l.push(...p.candidates);l.sort((p,g)=>{const O=p.scores?.rerank||p.scores?.dense||0;return(g.scores?.rerank||g.scores?.dense||0)-O});const k=l.slice(0,o).map((p,g)=>{const O=p.citation||p.url;return{rank:g+1,doc_id:p.doc_id,block_id:p.block_id,url:p.url,citation:O,title:p.title||"Untitled",snippet:p.snippet,score:p.scores?.rerank||p.scores?.dense||0,metadata:{site:p.metadata?.site,lang:p.metadata?.lang,contentType:p.metadata?.content_type}}});return{success:!0,query:e,total:l.length,results:k,summary:`Found ${l.length} relevant results across ${M.length} source(s) for "${e}". Top result: ${k[0]?.title||"N/A"}`}}const s=T.organizationId&&T.organizationId!=="default"?T.organizationId:M&&M.length>0?M[0]:void 0;try{const d={q:e,k:Math.min(o,100)};s&&(d.organizationId=s),Object.keys(n).length>0&&(d.filters=n);const h={"Content-Type":"application/json"};A.current&&(h.Authorization=`Bearer ${A.current}`);const l=await fetch(`${v}/search`,{method:"POST",headers:h,body:JSON.stringify(d)});if(!l.ok){const g=await l.text();return{success:!1,error:`Search failed: ${l.status} ${g}`}}const k=await l.json(),p=k.candidates.map((g,O)=>{const ue=g.citation||g.url;return{rank:O+1,doc_id:g.doc_id,block_id:g.block_id,url:g.url,citation:ue,title:g.title||"Untitled",snippet:g.snippet,score:g.scores?.rerank||g.scores?.dense||0,metadata:{site:g.metadata?.site,lang:g.metadata?.lang,contentType:g.metadata?.content_type}}});return{success:!0,query:k.query,total:k.total,results:p,summary:`Found ${k.total} relevant results for "${e}". Top result: ${p[0]?.title||"N/A"}`}}catch(d){return{success:!1,error:d instanceof Error?d.message:"Unknown error occurred"}}}:null,[T,v,A]),G=a.useMemo(()=>T?.enabled?async function(e,o){const r=T.organizationId&&T.organizationId!=="default"?T.organizationId:M&&M.length>0?M[0]:void 0;try{const n={"Content-Type":"application/json"};A.current&&(n.Authorization=`Bearer ${A.current}`);const s=await fetch(`${v}/fetch`,{method:"POST",headers:n,body:JSON.stringify({doc_id:e,block_ids:o,...r?{organizationId:r}:{}})});if(!s.ok){const h=await s.text();return{success:!1,error:`Fetch failed: ${s.status} ${h}`}}const d=await s.json();return{success:!0,blocks:d.blocks||[],fullContent:d.fullContent||"",pageUrl:d.pageUrl||"",pageTitle:d.pageTitle||"",summary:`Fetched ${d.blocks?.length||0} blocks from ${d.pageTitle||e}`}}catch(n){return{success:!1,error:n instanceof Error?n.message:"Unknown error occurred"}}}:null,[T,v,A]),ie=a.useMemo(()=>{if(m&&m.config?.tools){const t=tt(m.config.tools,f);return C&&console.log("[useAgent] Merged remote config:",{remoteToolCount:m.config.tools.length,localToolCount:f.length,mergedToolCount:t.length}),t}return f},[m,f]),U=m?.enableRagTools??T?.enabled??!1,Oe=a.useMemo(()=>{const t=[...ie,...c];return U&&(L&&t.push(L),G&&t.push(G)),t},[ie,c,L,G,U]),je=a.useMemo(()=>[...ie,...c],[ie,c]),H=a.useMemo(()=>Ue(je,j),[je,j]),te=a.useMemo(()=>{const t=new Map,e=new Map(H.map(o=>[o.name,o]));return T?.enabled&&(L&&t.set("searchDocs",L),G&&t.set("fetchDocContent",G)),Oe.forEach(o=>{if(o===L||o===G)return;const r=o[R];if(r){const s=e.get(r);if(s){t.set(s.name,o);return}}const n=H.find(s=>s.name===o.name);n&&t.set(n.name,o)}),t},[Oe,H,L,G,T]),Ne=a.useMemo(()=>new Set(H.map(t=>t.name)),[H]),Re=a.useMemo(()=>{const t=[];U&&t.push({name:"searchDocs",description:"Search documentation using semantic search. Returns relevant results with snippets and scores. Use this first to find relevant documentation. Default returns 20 results - use higher k values (30-50) when you need to find many sources.",jsonSchema:{type:"object",properties:{query:{type:"string",description:"The search query (user's question or keywords)"},k:{type:"number",description:"Number of results to return (default: 20, max: 100). Use 20-30 for comprehensive searches, or higher (50-100) when you need to find all relevant sources.",default:20},filters:{type:"object",description:"Optional filters to scope the search",properties:{site:{type:"string"},lang:{type:"string"},tags:{type:"array",items:{type:"string"}},docIds:{type:"array",items:{type:"string"}},pageIds:{type:"array",items:{type:"string"}}}}},required:["query"]}},{name:"fetchDocContent",description:"Fetch full content of documentation blocks after searching. Use this AFTER searchDocs when you find relevant results (scores > 0.3) to get complete text. Automatically fetches nearby blocks (5 chunks before/after each requested block) for context. You can pass multiple block_ids to fetch comprehensive content from multiple blocks/pages.",jsonSchema:{type:"object",properties:{doc_id:{type:"string",description:"The doc_id from searchDocs results"},block_ids:{type:"array",items:{type:"string"},description:"Array of block_ids from searchDocs results. You can pass multiple block_ids to fetch comprehensive content."}},required:["doc_id","block_ids"]}});const e=[];return m?.config?.workflows&&m.config.workflows.forEach(o=>{const r={},n=[];o.parameters&&o.parameters.forEach(s=>{r[s.name]={type:s.type||"string",description:s.description},s.required&&n.push(s.name)}),e.push({name:`workflow_${o.name.replace(/\s+/g,"_").toLowerCase()}`,description:o.description||`Execute the "${o.name}" workflow. ${o.contentPlaintext?.slice(0,200)||""}`,jsonSchema:{type:"object",properties:r,required:n},_workflow:o})}),[...H,...t,...e]},[H,U,m]),Z=a.useMemo(()=>Re.filter(t=>t.name==="searchDocs"||t.name==="fetchDocContent"?U:t.name.startsWith("workflow_")?!0:Ne.has(t.name)),[Re,Ne,U]),le=a.useMemo(()=>{const t=new Set;return U&&T?.autoExecute!==!1&&(t.add("searchDocs"),t.add("fetchDocContent")),m?.config?.tools?m.config.tools.forEach(e=>{e.isEnabled&&!e.requiresApproval&&t.add(e.name)}):(c.forEach(e=>{for(const[o,r]of te.entries())if(r===e){t.add(o);break}}),E&&Array.isArray(E)&&E.forEach(e=>{typeof e=="string"&&t.add(e)})),m?.config?.workflows&&m.config.workflows.forEach(e=>{const o=`workflow_${e.name.replace(/\s+/g,"_").toLowerCase()}`;e.requiresApproval||t.add(o)}),t},[m,c,E,te,U,T]);a.useEffect(()=>{if(w||y)return;let t=!1,e=null;const o=async(r=1,n=3)=>{if(!t)try{const s=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:b})});if(!s.ok)throw new Error("Failed to fetch token");const d=await s.json();t||(de(d.clientToken),A.current=d.clientToken,he(d.expiresAt),ee(null))}catch(s){if(console.error(`[useAgent] Token fetch failed (attempt ${r}/${n}):`,s),r<n&&!t){const d=Math.pow(2,r-1)*1e3;e=setTimeout(()=>o(r+1,n),d)}else t||ee(s instanceof Error?s.message:"Failed to fetch token")}};return o(),()=>{t=!0,e&&clearTimeout(e),F.current&&clearTimeout(F.current)}},[i,JSON.stringify(b),w,y]),a.useEffect(()=>{if(!X||w||y)return;F.current&&clearTimeout(F.current);const t=async(s=1,d=3)=>{try{const h=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:b})});if(!h.ok)throw new Error("Failed to refresh token");const l=await h.json();de(l.clientToken),A.current=l.clientToken,he(l.expiresAt),ee(null)}catch(h){if(console.error(`[useAgent] Token refresh failed (attempt ${s}/${d}):`,h),s<d){const l=Math.pow(2,s-1)*1e3;setTimeout(()=>t(s+1,d),l)}else ee(h instanceof Error?h.message:"Failed to refresh token")}},e=Math.floor(Date.now()/1e3),o=X-e,n=Math.max(0,o-5);return F.current=setTimeout(()=>t(),n*1e3),()=>{F.current&&clearTimeout(F.current)}},[X,i,JSON.stringify(b),w,y]),a.useEffect(()=>{if(w||y)return;const t=async()=>{if(document.visibilityState!=="visible")return;const e=Math.floor(Date.now()/1e3);if(!X||X<=e+5){C&&console.log("[useAgent] Tab visible, token expired - refreshing");try{const r=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:b})});if(r.ok){const n=await r.json();de(n.clientToken),A.current=n.clientToken,he(n.expiresAt),ee(null)}}catch(r){console.error("[useAgent] Token refresh on visibility change failed:",r)}}};return document.addEventListener("visibilitychange",t),()=>document.removeEventListener("visibilitychange",t)},[i,JSON.stringify(b),X,w,y]);const Je=async(t,e={})=>{const o=e.headers?new Headers(e.headers):new Headers;return o.delete("user-agent"),o.delete("User-Agent"),fetch(t,{...e,headers:o})},we=a.useRef(null),Pe=`${v}/chat`,{messages:I,sendMessage:ze,status:be,error:ke,stop:qe,addToolResult:Se,setMessages:$e}=Qe.useChat({id:pe||void 0,messages:_,transport:new De.DefaultChatTransport({api:Pe,fetch:Je,headers:()=>{const t={};return A.current&&(t.Authorization=`Bearer ${A.current}`),t},body:()=>{const t=Z.map(o=>({name:o.name,description:o.description,inputSchema:o.jsonSchema})),e=q.current;return{tools:t,...B&&{agentId:B},...K&&{agentName:K},...z&&{ragFilters:z},...e&&{state:e}}}}),sendAutomaticallyWhen:t=>{const e=t.messages;if(e[e.length-1]?.role!=="assistant")return!1;const n=[...e].reverse().find(d=>d.role==="user")?.id;if(n&&we.current===n)return!1;const s=De.lastAssistantMessageIsCompleteWithToolCalls(t);return s&&n&&(we.current=n),s}}),xe=ze,We=a.useCallback(t=>(we.current=null,ce.current=new Set,xe(t)),[xe]),V=a.useCallback((t,e,o)=>{Se({toolCallId:e,tool:t,output:o})},[Se]);a.useEffect(()=>{I.forEach(t=>{t.role==="assistant"&&t.parts.forEach(e=>{if(!e.type?.startsWith("tool-")||e.state!=="input-available")return;const o=e.type.replace("tool-",""),r=e.toolCallId;if(ae.current.has(r)||ye.current.has(r)){C&&console.log(`[useAgent] Skipping ${o} (${r}) - already processing or completed`);return}C&&console.log(`[useAgent] Starting ${o} (${r})`),ae.current.add(r);const n=h=>{C&&console.log(`[useAgent] Completed ${o} (${h})`),ye.current.add(h),ae.current.delete(h)},s=et(e.input),d=`${o}:${JSON.stringify(s)}`;if(ce.current.has(d)){C&&console.log(`[useAgent] Skipping duplicate tool call: ${d}`),V(o,r,"This tool was already called successfully with the same arguments in this response. Using previous result."),n(r);return}if(o.startsWith("workflow_")&&le.has(o)){(async()=>{try{const l=Z.find(p=>p.name===o)?._workflow;if(!l)throw new Error(`Workflow metadata not found for ${o}`);let k;if(l.webhook?.url){let p=s;if(l.webhook.bodyTemplate){let O=l.webhook.bodyTemplate;Object.entries(s).forEach(([ue,Ke])=>{O=O.replace(new RegExp(`\\{\\{${ue}\\}\\}`,"g"),String(Ke))}),p=JSON.parse(O)}const g=await fetch(l.webhook.url,{method:l.webhook.method||"POST",headers:{"Content-Type":"application/json",...l.webhook.headers||{}},body:JSON.stringify(p)});if(g.ok)k={success:!0,data:await g.json().catch(()=>({success:!0}))};else{const O=await g.text();k={success:!1,error:`Webhook failed: ${g.status} ${O}`}}}else k={success:!0,message:`Workflow "${l.name}" acknowledged. Follow the workflow instructions to proceed.`,instructions:l.contentPlaintext};n(r),V(o,r,k),ce.current.add(d)}catch(h){console.error(`[useAgent] Error executing workflow ${o}:`,h),n(r),V(o,r,`Error: ${h}`)}})();return}if(le.has(o)){const h=te.get(o);h?(async()=>{try{const l=Z.find(p=>p.name===o);let k;if(l&&l.jsonSchema?.properties){const g=Object.keys(l.jsonSchema.properties).map(O=>s[O]);k=await h(...g)}else k=await h(...Object.values(s));n(r),V(o,r,k),ce.current.add(d)}catch(l){console.error(`[useAgent] Error executing tool ${o}:`,l),n(r),V(o,r,`Error: ${l}`)}})():(C&&console.warn(`[useAgent] Tool ${o} not found in toolsMap`),V(o,r,`Error: Tool "${o}" not found. The tool may not be registered or available.`),n(r))}else if(o.startsWith("workflow_")&&D)D({toolCall:{toolCallId:r,toolName:o,args:s}});else if(D)D({toolCall:{toolCallId:r,toolName:o,args:s}});else{const h={toolCallId:r,toolName:o,args:s};me(l=>{const k=new Map(l);return k.set(r,h),k})}})})},[I,le,te,D,V,Z]),a.useEffect(()=>{if(be==="ready"&&I.length>0&&P){const t=I[I.length-1];t&&t.role==="assistant"&&P({message:t,messages:I,isAbort:!1,isDisconnect:!1,isError:!!ke})}},[be,I,ke,P]);async function Fe(){if(!(!b?.id||!W)){se(!0);try{const e=await(await fetch(`${v}/conversations`,{headers:{Authorization:`Bearer ${W}`}})).json();ve(e.conversations||[])}catch(t){console.error("Failed to fetch conversations:",t)}finally{se(!1)}}}a.useEffect(()=>{b?.id&&W&&Fe()},[JSON.stringify(b),W,v]);async function Le(t){Me(t),se(!0);try{const e=await fetch(`${v}/conversations/${t}/messages`,{headers:{Authorization:`Bearer ${A.current}`}});if(!e.ok)throw new Error(`Failed to fetch messages: ${e.statusText}`);const r=(await e.json()).messages||[];$e(r)}catch(e){console.error("Failed to load conversation messages:",e)}finally{se(!1)}}async function Ge(t){if(W)try{await fetch(`${v}/conversations/${t}`,{method:"DELETE",headers:{Authorization:`Bearer ${W}`}}),ve(Ce.filter(e=>e._id!==t)),t===pe&&_e()}catch(e){console.error("Failed to delete conversation:",e)}}function _e(){const t=crypto.randomUUID();Me(t),$e([]),ae.current=new Set,ye.current=new Set}function Y(t){const e=t.tool||"unknown-tool";Se({toolCallId:t.toolCallId,tool:e,output:t.output})}async function He(t,e,o){if(!e.startsWith("workflow_"))throw new Error(`${e} is not a workflow tool`);try{const n=Z.find(d=>d.name===e)?._workflow;if(!n)throw new Error(`Workflow metadata not found for ${e}`);let s;if(n.webhook?.url){let d=o;if(n.webhook.bodyTemplate){let l=n.webhook.bodyTemplate;Object.entries(o).forEach(([k,p])=>{l=l.replace(new RegExp(`\\{\\{${k}\\}\\}`,"g"),String(p))}),d=JSON.parse(l)}const h=await fetch(n.webhook.url,{method:n.webhook.method||"POST",headers:{"Content-Type":"application/json",...n.webhook.headers||{}},body:JSON.stringify(d)});if(h.ok)s={success:!0,data:await h.json().catch(()=>({success:!0}))};else{const l=await h.text();s={success:!1,error:`Webhook failed: ${h.status} ${l}`}}}else s={success:!0,message:`Workflow "${n.name}" acknowledged. Follow the workflow instructions to proceed.`,instructions:n.contentPlaintext};return Y({toolCallId:t,tool:e,output:s}),s}catch(r){const n={success:!1,error:String(r)};throw Y({toolCallId:t,tool:e,output:n}),r}}async function Ve(t){const e=ge.get(t);if(!e){C&&console.warn(`[useAgent] Tool call ${t} not found in pending approvals`);return}me(r=>{const n=new Map(r);return n.delete(t),n});const o=te.get(e.toolName);if(o)try{const r=Z.find(s=>s.name===e.toolName);let n;if(r&&r.jsonSchema?.properties){const d=Object.keys(r.jsonSchema.properties).map(h=>e.args[h]);n=await o(...d)}else n=await o(...Object.values(e.args));Y({toolCallId:e.toolCallId,tool:e.toolName,output:n})}catch(r){console.error(`[useAgent] Error executing tool ${e.toolName}:`,r),Y({toolCallId:e.toolCallId,tool:e.toolName,output:`Error: ${r}`})}else C&&console.warn(`[useAgent] Tool ${e.toolName} not found in toolsMap`),Y({toolCallId:e.toolCallId,tool:e.toolName,output:`Error: Tool "${e.toolName}" not found`})}function Ye(t){const e=ge.get(t);if(!e){C&&console.warn(`[useAgent] Tool call ${t} not found in pending approvals`);return}me(o=>{const r=new Map(o);return r.delete(t),r}),Y({toolCallId:e.toolCallId,tool:e.toolName,output:{denied:!0,message:"Tool execution was denied by user"}})}return{id:pe,messages:I,status:be,error:ke,sendMessage:We,stop:qe,addToolOutput:Y,executeWorkflowTool:He,setMessages:$e,conversations:Ce,loadConversation:Le,deleteConversation:Ge,startNewConversation:_e,isLoadingConversations:Be,clientToken:W,tokenError:Ie,pendingToolApprovals:Array.from(ge.values()),approveToolCall:Ve,denyToolCall:Ye,safeToolNames:le}}function rt(u={}){const{apiBaseUrl:i="https://api.arcten.com",tokenEndpoint:y="/token",clientToken:w,skipTokenFetch:b=!1}=u,[f,c]=a.useState(!1),[E,j]=a.useState(null),N=a.useRef(w||null);a.useEffect(()=>{if(w){N.current=w;return}if(b)return;(async()=>{try{const $=await fetch(`${i}${y}`,{method:"POST",headers:{"Content-Type":"application/json"}});if($.ok){const S=await $.json();N.current=S.token}}catch($){console.error("[useGenerate] Failed to fetch token:",$)}})()},[i,y,w,b]);const B=a.useCallback(async x=>{c(!0),j(null);try{const $={"Content-Type":"application/json"};N.current&&($.Authorization=`Bearer ${N.current}`);const S=await fetch(`${i}/generate`,{method:"POST",headers:$,body:JSON.stringify(x)});if(!S.ok){const Q=await S.json().catch(()=>({}));throw new Error(Q.error||`HTTP ${S.status}`)}return await S.json()}catch($){const S=$ instanceof Error?$.message:String($);throw j(S),$}finally{c(!1)}},[i]),K=a.useCallback(async(x,$)=>{c(!0),j(null);try{const S={"Content-Type":"application/json"};N.current&&(S.Authorization=`Bearer ${N.current}`);const _=await fetch(`${i}/generate/stream`,{method:"POST",headers:S,body:JSON.stringify(x)});if(!_.ok){const P=await _.json().catch(()=>({}));throw new Error(P.error||`HTTP ${_.status}`)}const Q=_.body?.getReader();if(!Q)throw new Error("No response body");const J=new TextDecoder;let D=null;for(;;){const{done:P,value:M}=await Q.read();if(P)break;const z=J.decode(M,{stream:!0}).split(`
3
- `).filter(q=>q.startsWith("data: "));for(const q of z){const v=q.slice(6).trim();if(v)try{const m=JSON.parse(v);if(m.error)throw new Error(m.error);m.done&&m.object?D={object:m.object,usage:m.usage,finishReason:m.finishReason}:m.partial&&$&&$(m.partial)}catch{}}}if(!D)throw new Error("Stream ended without final result");return D}catch(S){const _=S instanceof Error?S.message:String(S);throw j(_),S}finally{c(!1)}},[i]);return{generate:B,streamGenerate:K,isLoading:f,error:E}}exports.ARCTEN_ORIGINAL_NAME=R;exports.extractToolsMetadata=Ue;exports.preserveToolName=Xe;exports.useAgent=ot;exports.useGenerate=rt;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react"),ot=require("@ai-sdk/react"),Je=require("ai"),_=Symbol.for("@arcteninc/core:originalName");function rt(f,l){return f[_]=l,f}function nt(f,l,g,y){if(typeof f!="function")return null;const w=f[_];if(w){const $=l[w];if($&&!g.has($.name))return{fn:f,meta:$}}const d=f.name;let c;if(d&&(c=l[d],c&&!g.has(c.name)))return f[_]=c.name,{fn:f,meta:c};if(y&&y.length>0){for(const $ of y)if(!g.has($)&&(c=l[$],c))return f[_]=c.name,{fn:f,meta:c}}else if(c=Object.values(l).find(N=>!g.has(N.name)),c)return f[_]=c.name,{fn:f,meta:c};return null}function Pe(f,l){if(!l)return[];let g,y;if("functions"in l&&typeof l.functions=="object"){const d=l;g=d.functions,y=d.toolOrder}else g=l,y=void 0;const w=new Set;return f.map(d=>{const c=nt(d,g,w,y);if(!c){const $=d.name,R=d[_]||$||"unnamed";return typeof process<"u"&&(process.env.NODE_ENV==="development"||process.env.NODE_ENV!=="production")&&typeof console<"u"&&console.warn&&console.warn(`⚠️ [@arcteninc/core] No metadata found for tool "${R}". This tool will be excluded.
2
+ To fix: Run "arcten-extract-types ." and ensure "${R}" is used in ArctenAgent/useAgent.`),null}return w.add(c.meta.name),{name:c.meta.name,description:c.meta.description,jsonSchema:c.meta.parameters}}).filter(d=>d!==null)}const M=process.env.NODE_ENV==="development";function re(f){if(typeof f=="string")try{const l=JSON.parse(f);return re(l)}catch{return f}if(Array.isArray(f))return f.map(re);if(f&&typeof f=="object"){const l={};for(const[g,y]of Object.entries(f))l[g]=re(y);return l}return f}function st(f){if(typeof f=="string"){try{const l=JSON.parse(f);if(l&&typeof l=="object"&&!Array.isArray(l))return re(l)}catch{}return{}}return f&&typeof f=="object"&&!Array.isArray(f)?re(f):{}}function at(f,l){if(!f||f.length===0)return l;const g=new Map;l.forEach(d=>{const c=d[_]||d.name;c&&g.set(c,d)});const y=new Map(f.map(d=>[d.name,d])),w=[];return f.forEach(d=>{const c=g.get(d.name);if(!c){M&&console.log(`[useAgent] Skipping server-only tool: ${d.name}`);return}if(!d.isEnabled&&!d.isOverridable){M&&console.log(`[useAgent] Blocking non-overridable disabled tool: ${d.name}`);return}if(!d.isEnabled&&d.isOverridable&&c){M&&console.log(`[useAgent] Allowing overridable disabled tool with local implementation: ${d.name}`),w.push(c);return}d.isEnabled&&c&&w.push(c)}),l.forEach(d=>{const c=d[_]||d.name;c&&!y.has(c)&&(M&&console.log(`[useAgent] Allowing client-only tool (forward compatible): ${c}`),w.push(d))}),w}function it({apiBaseUrl:f="https://api.arcten.com",tokenEndpoint:l="/api/arcten/token",clientToken:g,skipTokenFetch:y=!1,user:w,tools:d=[],safeTools:c=[],safeToolNames:$,toolMetadata:N,systemPrompt:R="",agentId:J,agentName:K,projectId:x,enableRemoteConfig:T,remoteConfigUrl:S,initialMessages:D=[],conversationId:Q,state:P,onToolCall:U,onFinish:q,sources:j,ragConfig:ne,ragFilters:z,planning:E=!1}={}){const X=s.useRef(P),b=f.replace(/\/+$/,""),[A,pe]=s.useState(null),[lt,ve]=s.useState(!1),[ut,Me]=s.useState(null),se=s.useMemo(()=>{const t=j||[],e=A?.config?.sources||[];return[...new Set([...t,...e])]},[j,A]),C=s.useMemo(()=>ne!==void 0?ne:z?{enabled:!0,filters:z}:se&&se.length>0?{enabled:!0,organizationId:se[0],autoExecute:!0,filters:void 0}:{enabled:!1},[se,ne,z]),je=s.useMemo(()=>T===!1?!1:!!x,[x,T]);s.useEffect(()=>{if(!je||!v.current)return;(async()=>{ve(!0),Me(null);try{const o=`${S||b.replace("/chat","").replace(/\/$/,"")}/convex/api/query`,r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${v.current}`},body:JSON.stringify({path:"agents:getConfigForClient",args:{projectId:x,agentName:K,agentId:J},format:"json"})});if(!r.ok){if(r.status===404){M&&console.warn("[useAgent] Remote config endpoint not found (404), using local tools only"),pe(null);return}const a=await r.text().catch(()=>"Unknown error");throw new Error(`Failed to fetch remote config: ${r.status} ${a}`)}const n=await r.json();pe(n?.value||null),M&&console.log("[useAgent] Remote config loaded successfully:",n?.value)}catch(e){console.error("[useAgent] Failed to fetch remote config:",e);const o=e instanceof Error?e:new Error(String(e));Me(o),pe(null)}finally{ve(!1)}})()},[je,x,K,J,b,S]);const[W,me]=s.useState(g||null),v=s.useRef(g||null);s.useEffect(()=>{X.current=P},[P]);const[qe,te]=s.useState(null),[Z,ge]=s.useState(null),F=s.useRef(null),[Oe,Ne]=s.useState([]),[ye,Re]=s.useState(Q||(typeof window<"u"?crypto.randomUUID():null)),[ze,ae]=s.useState(!1),[we,be]=s.useState(new Map),ie=s.useRef(new Set),ke=s.useRef(new Set),ce=s.useRef(new Set);s.useEffect(()=>{X.current=P},[P]);const[Se,We]=s.useState([]),le=s.useMemo(()=>E?async function(){return{todos:Se}}:null,[E,Se]),ue=s.useMemo(()=>E?async function(e){return We(e),{success:!0,todos:e}}:null,[E]),L=s.useMemo(()=>C?.enabled?async function(e,o=20,r){const n={...C.filters,...r};if(j&&j.length>1){const i=j.map(p=>fetch(`${b}/search`,{method:"POST",headers:{"Content-Type":"application/json",...v.current?{Authorization:`Bearer ${v.current}`}:{}},body:JSON.stringify({q:e,k:Math.min(o,100),organizationId:p,...Object.keys(n).length>0?{filters:n}:{}})}).then(m=>m.ok?m.json():null)),h=await Promise.all(i),u=[];for(const p of h)p?.candidates&&u.push(...p.candidates);u.sort((p,m)=>{const O=p.scores?.rerank||p.scores?.dense||0;return(m.scores?.rerank||m.scores?.dense||0)-O});const k=u.slice(0,o).map((p,m)=>{const O=p.citation||p.url;return{rank:m+1,doc_id:p.doc_id,block_id:p.block_id,url:p.url,citation:O,title:p.title||"Untitled",snippet:p.snippet,score:p.scores?.rerank||p.scores?.dense||0,metadata:{site:p.metadata?.site,lang:p.metadata?.lang,contentType:p.metadata?.content_type}}});return{success:!0,query:e,total:u.length,results:k,summary:`Found ${u.length} relevant results across ${j.length} source(s) for "${e}". Top result: ${k[0]?.title||"N/A"}`}}const a=C.organizationId&&C.organizationId!=="default"?C.organizationId:j&&j.length>0?j[0]:void 0;try{const i={q:e,k:Math.min(o,100)};a&&(i.organizationId=a),Object.keys(n).length>0&&(i.filters=n);const h={"Content-Type":"application/json"};v.current&&(h.Authorization=`Bearer ${v.current}`);const u=await fetch(`${b}/search`,{method:"POST",headers:h,body:JSON.stringify(i)});if(!u.ok){const m=await u.text();return{success:!1,error:`Search failed: ${u.status} ${m}`}}const k=await u.json(),p=k.candidates.map((m,O)=>{const he=m.citation||m.url;return{rank:O+1,doc_id:m.doc_id,block_id:m.block_id,url:m.url,citation:he,title:m.title||"Untitled",snippet:m.snippet,score:m.scores?.rerank||m.scores?.dense||0,metadata:{site:m.metadata?.site,lang:m.metadata?.lang,contentType:m.metadata?.content_type}}});return{success:!0,query:k.query,total:k.total,results:p,summary:`Found ${k.total} relevant results for "${e}". Top result: ${p[0]?.title||"N/A"}`}}catch(i){return{success:!1,error:i instanceof Error?i.message:"Unknown error occurred"}}}:null,[C,b,v]),G=s.useMemo(()=>C?.enabled?async function(e,o){const r=C.organizationId&&C.organizationId!=="default"?C.organizationId:j&&j.length>0?j[0]:void 0;try{const n={"Content-Type":"application/json"};v.current&&(n.Authorization=`Bearer ${v.current}`);const a=await fetch(`${b}/fetch`,{method:"POST",headers:n,body:JSON.stringify({doc_id:e,block_ids:o,...r?{organizationId:r}:{}})});if(!a.ok){const h=await a.text();return{success:!1,error:`Fetch failed: ${a.status} ${h}`}}const i=await a.json();return{success:!0,blocks:i.blocks||[],fullContent:i.fullContent||"",pageUrl:i.pageUrl||"",pageTitle:i.pageTitle||"",summary:`Fetched ${i.blocks?.length||0} blocks from ${i.pageTitle||e}`}}catch(n){return{success:!1,error:n instanceof Error?n.message:"Unknown error occurred"}}}:null,[C,b,v]),fe=s.useMemo(()=>{if(A&&A.config?.tools){const t=at(A.config.tools,d);return M&&console.log("[useAgent] Merged remote config:",{remoteToolCount:A.config.tools.length,localToolCount:d.length,mergedToolCount:t.length}),t}return d},[A,d]),I=A?.enableRagTools??C?.enabled??!1,_e=s.useMemo(()=>{const t=[...fe,...c];return I&&(L&&t.push(L),G&&t.push(G)),t},[fe,c,L,G,I]),xe=s.useMemo(()=>[...fe,...c],[fe,c]),H=s.useMemo(()=>Pe(xe,N),[xe,N]),oe=s.useMemo(()=>{const t=new Map,e=new Map(H.map(o=>[o.name,o]));return C?.enabled&&(L&&t.set("searchDocs",L),G&&t.set("fetchDocContent",G)),E&&(le&&t.set("todo_read",le),ue&&t.set("todo_write",ue)),_e.forEach(o=>{if(o===L||o===G||o===le||o===ue)return;const r=o[_];if(r){const a=e.get(r);if(a){t.set(a.name,o);return}}const n=H.find(a=>a.name===o.name);n&&t.set(n.name,o)}),t},[_e,H,L,G,C,E,le,ue]),De=s.useMemo(()=>new Set(H.map(t=>t.name)),[H]),Ue=s.useMemo(()=>{const t=[];I&&t.push({name:"searchDocs",description:"Search documentation using semantic search. Returns relevant results with snippets and scores. Use this first to find relevant documentation. Default returns 20 results - use higher k values (30-50) when you need to find many sources.",jsonSchema:{type:"object",properties:{query:{type:"string",description:"The search query (user's question or keywords)"},k:{type:"number",description:"Number of results to return (default: 20, max: 100). Use 20-30 for comprehensive searches, or higher (50-100) when you need to find all relevant sources.",default:20},filters:{type:"object",description:"Optional filters to scope the search",properties:{site:{type:"string"},lang:{type:"string"},tags:{type:"array",items:{type:"string"}},docIds:{type:"array",items:{type:"string"}},pageIds:{type:"array",items:{type:"string"}}}}},required:["query"]}},{name:"fetchDocContent",description:"Fetch full content of documentation blocks after searching. Use this AFTER searchDocs when you find relevant results (scores > 0.3) to get complete text. Automatically fetches nearby blocks (5 chunks before/after each requested block) for context. You can pass multiple block_ids to fetch comprehensive content from multiple blocks/pages.",jsonSchema:{type:"object",properties:{doc_id:{type:"string",description:"The doc_id from searchDocs results"},block_ids:{type:"array",items:{type:"string"},description:"Array of block_ids from searchDocs results. You can pass multiple block_ids to fetch comprehensive content."}},required:["doc_id","block_ids"]}});const e=[];A?.config?.workflows&&A.config.workflows.forEach(r=>{const n={},a=[];r.parameters&&r.parameters.forEach(i=>{n[i.name]={type:i.type||"string",description:i.description},i.required&&a.push(i.name)}),e.push({name:`workflow_${r.name.replace(/\s+/g,"_").toLowerCase()}`,description:r.description||`Execute the "${r.name}" workflow. ${r.contentPlaintext?.slice(0,200)||""}`,jsonSchema:{type:"object",properties:n,required:a},_workflow:r})});const o=[];return E&&o.push({name:"todo_read",description:"Read the current todo list. Returns all todo items with their status (pending, in_progress, completed).",jsonSchema:{type:"object",properties:{},required:[]}},{name:"todo_write",description:"Write/update the todo list. Pass the complete list of todos to replace the current list. Each todo must have id, content, and status (pending, in_progress, completed).",jsonSchema:{type:"object",properties:{todoList:{type:"array",description:"The complete list of todo items",items:{type:"object",properties:{id:{type:"string",description:"Unique identifier for the todo item"},content:{type:"string",description:"The content/description of the todo item"},status:{type:"string",enum:["pending","in_progress","completed"],description:"The current status of the todo item"}},required:["id","content","status"]}}},required:["todoList"]}}),[...H,...t,...e,...o]},[H,I,A,E]),ee=s.useMemo(()=>Ue.filter(t=>t.name==="searchDocs"||t.name==="fetchDocContent"?I:t.name.startsWith("workflow_")?!0:t.name==="todo_read"||t.name==="todo_write"?E:De.has(t.name)),[Ue,De,I,E]),de=s.useMemo(()=>{const t=new Set;return I&&C?.autoExecute!==!1&&(t.add("searchDocs"),t.add("fetchDocContent")),E&&(t.add("todo_read"),t.add("todo_write")),A?.config?.tools?A.config.tools.forEach(e=>{e.isEnabled&&!e.requiresApproval&&t.add(e.name)}):(c.forEach(e=>{for(const[o,r]of oe.entries())if(r===e){t.add(o);break}}),$&&Array.isArray($)&&$.forEach(e=>{typeof e=="string"&&t.add(e)})),A?.config?.workflows&&A.config.workflows.forEach(e=>{const o=`workflow_${e.name.replace(/\s+/g,"_").toLowerCase()}`;e.requiresApproval||t.add(o)}),t},[A,c,$,oe,I,C,E]);s.useEffect(()=>{if(y||g)return;let t=!1,e=null;const o=async(r=1,n=3)=>{if(!t)try{const a=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:w})});if(!a.ok)throw new Error("Failed to fetch token");const i=await a.json();t||(me(i.clientToken),v.current=i.clientToken,ge(i.expiresAt),te(null))}catch(a){if(console.error(`[useAgent] Token fetch failed (attempt ${r}/${n}):`,a),r<n&&!t){const i=Math.pow(2,r-1)*1e3;e=setTimeout(()=>o(r+1,n),i)}else t||te(a instanceof Error?a.message:"Failed to fetch token")}};return o(),()=>{t=!0,e&&clearTimeout(e),F.current&&clearTimeout(F.current)}},[l,JSON.stringify(w),y,g]),s.useEffect(()=>{if(!Z||y||g)return;F.current&&clearTimeout(F.current);const t=async(a=1,i=3)=>{try{const h=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:w})});if(!h.ok)throw new Error("Failed to refresh token");const u=await h.json();me(u.clientToken),v.current=u.clientToken,ge(u.expiresAt),te(null)}catch(h){if(console.error(`[useAgent] Token refresh failed (attempt ${a}/${i}):`,h),a<i){const u=Math.pow(2,a-1)*1e3;setTimeout(()=>t(a+1,i),u)}else te(h instanceof Error?h.message:"Failed to refresh token")}},e=Math.floor(Date.now()/1e3),o=Z-e,n=Math.max(0,o-5);return F.current=setTimeout(()=>t(),n*1e3),()=>{F.current&&clearTimeout(F.current)}},[Z,l,JSON.stringify(w),y,g]),s.useEffect(()=>{if(y||g)return;const t=async()=>{if(document.visibilityState!=="visible")return;const e=Math.floor(Date.now()/1e3);if(!Z||Z<=e+5){M&&console.log("[useAgent] Tab visible, token expired - refreshing");try{const r=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:w})});if(r.ok){const n=await r.json();me(n.clientToken),v.current=n.clientToken,ge(n.expiresAt),te(null)}}catch(r){console.error("[useAgent] Token refresh on visibility change failed:",r)}}};return document.addEventListener("visibilitychange",t),()=>document.removeEventListener("visibilitychange",t)},[l,JSON.stringify(w),Z,y,g]);const Fe=async(t,e={})=>{const o=e.headers?new Headers(e.headers):new Headers;return o.delete("user-agent"),o.delete("User-Agent"),fetch(t,{...e,headers:o})},Te=s.useRef(null),Le=`${b}/chat`,{messages:B,sendMessage:Ge,status:Ee,error:$e,stop:He,addToolResult:Ae,setMessages:Ce}=ot.useChat({id:ye||void 0,messages:D,transport:new Je.DefaultChatTransport({api:Le,fetch:Fe,headers:()=>{const t={};return v.current&&(t.Authorization=`Bearer ${v.current}`),t},body:()=>{const t=ee.map(o=>({name:o.name,description:o.description,inputSchema:o.jsonSchema})),e=X.current;return{tools:t,...J&&{agentId:J},...K&&{agentName:K},...z&&{ragFilters:z},...e&&{state:e},...E&&{planning:!0}}}}),sendAutomaticallyWhen:t=>{const e=t.messages;if(e[e.length-1]?.role!=="assistant")return!1;const n=[...e].reverse().find(i=>i.role==="user")?.id;if(n&&Te.current===n)return!1;const a=Je.lastAssistantMessageIsCompleteWithToolCalls(t);return a&&n&&(Te.current=n),a}}),Ie=Ge,Ve=s.useCallback(t=>(Te.current=null,ce.current=new Set,Ie(t)),[Ie]),V=s.useCallback((t,e,o)=>{Ae({toolCallId:e,tool:t,output:o})},[Ae]);s.useEffect(()=>{B.forEach(t=>{t.role==="assistant"&&t.parts.forEach(e=>{if(!e.type?.startsWith("tool-")||e.state!=="input-available")return;const o=e.type.replace("tool-",""),r=e.toolCallId;if(ie.current.has(r)||ke.current.has(r)){M&&console.log(`[useAgent] Skipping ${o} (${r}) - already processing or completed`);return}M&&console.log(`[useAgent] Starting ${o} (${r})`),ie.current.add(r);const n=h=>{M&&console.log(`[useAgent] Completed ${o} (${h})`),ke.current.add(h),ie.current.delete(h)},a=st(e.input),i=`${o}:${JSON.stringify(a)}`;if(ce.current.has(i)){M&&console.log(`[useAgent] Skipping duplicate tool call: ${i}`),V(o,r,"This tool was already called successfully with the same arguments in this response. Using previous result."),n(r);return}if(o.startsWith("workflow_")&&de.has(o)){(async()=>{try{const u=ee.find(p=>p.name===o)?._workflow;if(!u)throw new Error(`Workflow metadata not found for ${o}`);let k;if(u.webhook?.url){let p=a;if(u.webhook.bodyTemplate){let O=u.webhook.bodyTemplate;Object.entries(a).forEach(([he,tt])=>{O=O.replace(new RegExp(`\\{\\{${he}\\}\\}`,"g"),String(tt))}),p=JSON.parse(O)}const m=await fetch(u.webhook.url,{method:u.webhook.method||"POST",headers:{"Content-Type":"application/json",...u.webhook.headers||{}},body:JSON.stringify(p)});if(m.ok)k={success:!0,data:await m.json().catch(()=>({success:!0}))};else{const O=await m.text();k={success:!1,error:`Webhook failed: ${m.status} ${O}`}}}else k={success:!0,message:`Workflow "${u.name}" acknowledged. Follow the workflow instructions to proceed.`,instructions:u.contentPlaintext};n(r),V(o,r,k),ce.current.add(i)}catch(h){console.error(`[useAgent] Error executing workflow ${o}:`,h),n(r),V(o,r,`Error: ${h}`)}})();return}if(de.has(o)){const h=oe.get(o);h?(async()=>{try{const u=ee.find(p=>p.name===o);let k;if(u&&u.jsonSchema?.properties){const m=Object.keys(u.jsonSchema.properties).map(O=>a[O]);k=await h(...m)}else k=await h(...Object.values(a));n(r),V(o,r,k),ce.current.add(i)}catch(u){console.error(`[useAgent] Error executing tool ${o}:`,u),n(r),V(o,r,`Error: ${u}`)}})():(M&&console.warn(`[useAgent] Tool ${o} not found in toolsMap`),V(o,r,`Error: Tool "${o}" not found. The tool may not be registered or available.`),n(r))}else if(o.startsWith("workflow_")&&U)U({toolCall:{toolCallId:r,toolName:o,args:a}});else if(U)U({toolCall:{toolCallId:r,toolName:o,args:a}});else{const h={toolCallId:r,toolName:o,args:a};be(u=>{const k=new Map(u);return k.set(r,h),k})}})})},[B,de,oe,U,V,ee]),s.useEffect(()=>{if(Ee==="ready"&&B.length>0&&q){const t=B[B.length-1];t&&t.role==="assistant"&&q({message:t,messages:B,isAbort:!1,isDisconnect:!1,isError:!!$e})}},[Ee,B,$e,q]);async function Ye(){if(!(!w?.id||!W)){ae(!0);try{const e=await(await fetch(`${b}/conversations`,{headers:{Authorization:`Bearer ${W}`}})).json();Ne(e.conversations||[])}catch(t){console.error("Failed to fetch conversations:",t)}finally{ae(!1)}}}s.useEffect(()=>{w?.id&&W&&Ye()},[JSON.stringify(w),W,b]);async function Ke(t){Re(t),ae(!0);try{const e=await fetch(`${b}/conversations/${t}/messages`,{headers:{Authorization:`Bearer ${v.current}`}});if(!e.ok)throw new Error(`Failed to fetch messages: ${e.statusText}`);const r=(await e.json()).messages||[];Ce(r)}catch(e){console.error("Failed to load conversation messages:",e)}finally{ae(!1)}}async function Qe(t){if(W)try{await fetch(`${b}/conversations/${t}`,{method:"DELETE",headers:{Authorization:`Bearer ${W}`}}),Ne(Oe.filter(e=>e._id!==t)),t===ye&&Be()}catch(e){console.error("Failed to delete conversation:",e)}}function Be(){const t=crypto.randomUUID();Re(t),Ce([]),ie.current=new Set,ke.current=new Set}function Y(t){const e=t.tool||"unknown-tool";Ae({toolCallId:t.toolCallId,tool:e,output:t.output})}async function Xe(t,e,o){if(!e.startsWith("workflow_"))throw new Error(`${e} is not a workflow tool`);try{const n=ee.find(i=>i.name===e)?._workflow;if(!n)throw new Error(`Workflow metadata not found for ${e}`);let a;if(n.webhook?.url){let i=o;if(n.webhook.bodyTemplate){let u=n.webhook.bodyTemplate;Object.entries(o).forEach(([k,p])=>{u=u.replace(new RegExp(`\\{\\{${k}\\}\\}`,"g"),String(p))}),i=JSON.parse(u)}const h=await fetch(n.webhook.url,{method:n.webhook.method||"POST",headers:{"Content-Type":"application/json",...n.webhook.headers||{}},body:JSON.stringify(i)});if(h.ok)a={success:!0,data:await h.json().catch(()=>({success:!0}))};else{const u=await h.text();a={success:!1,error:`Webhook failed: ${h.status} ${u}`}}}else a={success:!0,message:`Workflow "${n.name}" acknowledged. Follow the workflow instructions to proceed.`,instructions:n.contentPlaintext};return Y({toolCallId:t,tool:e,output:a}),a}catch(r){const n={success:!1,error:String(r)};throw Y({toolCallId:t,tool:e,output:n}),r}}async function Ze(t){const e=we.get(t);if(!e){M&&console.warn(`[useAgent] Tool call ${t} not found in pending approvals`);return}be(r=>{const n=new Map(r);return n.delete(t),n});const o=oe.get(e.toolName);if(o)try{const r=ee.find(a=>a.name===e.toolName);let n;if(r&&r.jsonSchema?.properties){const i=Object.keys(r.jsonSchema.properties).map(h=>e.args[h]);n=await o(...i)}else n=await o(...Object.values(e.args));Y({toolCallId:e.toolCallId,tool:e.toolName,output:n})}catch(r){console.error(`[useAgent] Error executing tool ${e.toolName}:`,r),Y({toolCallId:e.toolCallId,tool:e.toolName,output:`Error: ${r}`})}else M&&console.warn(`[useAgent] Tool ${e.toolName} not found in toolsMap`),Y({toolCallId:e.toolCallId,tool:e.toolName,output:`Error: Tool "${e.toolName}" not found`})}function et(t){const e=we.get(t);if(!e){M&&console.warn(`[useAgent] Tool call ${t} not found in pending approvals`);return}be(o=>{const r=new Map(o);return r.delete(t),r}),Y({toolCallId:e.toolCallId,tool:e.toolName,output:{denied:!0,message:"Tool execution was denied by user"}})}return{id:ye,messages:B,status:Ee,error:$e,sendMessage:Ve,stop:He,addToolOutput:Y,executeWorkflowTool:Xe,setMessages:Ce,conversations:Oe,loadConversation:Ke,deleteConversation:Qe,startNewConversation:Be,isLoadingConversations:ze,clientToken:W,tokenError:qe,pendingToolApprovals:Array.from(we.values()),approveToolCall:Ze,denyToolCall:et,safeToolNames:de,todos:Se}}function ct(f={}){const{apiBaseUrl:l="https://api.arcten.com",tokenEndpoint:g="/token",clientToken:y,skipTokenFetch:w=!1}=f,[d,c]=s.useState(!1),[$,N]=s.useState(null),R=s.useRef(y||null);s.useEffect(()=>{if(y){R.current=y;return}if(w)return;(async()=>{try{const T=await fetch(`${l}${g}`,{method:"POST",headers:{"Content-Type":"application/json"}});if(T.ok){const S=await T.json();R.current=S.token}}catch(T){console.error("[useGenerate] Failed to fetch token:",T)}})()},[l,g,y,w]);const J=s.useCallback(async x=>{c(!0),N(null);try{const T={"Content-Type":"application/json"};R.current&&(T.Authorization=`Bearer ${R.current}`);const S=await fetch(`${l}/generate`,{method:"POST",headers:T,body:JSON.stringify(x)});if(!S.ok){const Q=await S.json().catch(()=>({}));throw new Error(Q.error||`HTTP ${S.status}`)}return await S.json()}catch(T){const S=T instanceof Error?T.message:String(T);throw N(S),T}finally{c(!1)}},[l]),K=s.useCallback(async(x,T)=>{c(!0),N(null);try{const S={"Content-Type":"application/json"};R.current&&(S.Authorization=`Bearer ${R.current}`);const D=await fetch(`${l}/generate/stream`,{method:"POST",headers:S,body:JSON.stringify(x)});if(!D.ok){const q=await D.json().catch(()=>({}));throw new Error(q.error||`HTTP ${D.status}`)}const Q=D.body?.getReader();if(!Q)throw new Error("No response body");const P=new TextDecoder;let U=null;for(;;){const{done:q,value:j}=await Q.read();if(q)break;const z=P.decode(j,{stream:!0}).split(`
3
+ `).filter(E=>E.startsWith("data: "));for(const E of z){const X=E.slice(6).trim();if(X)try{const b=JSON.parse(X);if(b.error)throw new Error(b.error);b.done&&b.object?U={object:b.object,usage:b.usage,finishReason:b.finishReason}:b.partial&&T&&T(b.partial)}catch{}}}if(!U)throw new Error("Stream ended without final result");return U}catch(S){const D=S instanceof Error?S.message:String(S);throw N(D),S}finally{c(!1)}},[l]);return{generate:J,streamGenerate:K,isLoading:d,error:$}}exports.ARCTEN_ORIGINAL_NAME=_;exports.extractToolsMetadata=Pe;exports.preserveToolName=rt;exports.useAgent=it;exports.useGenerate=ct;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { useAgent } from './lib/useAgent';
2
- export type { UseAgentOptions, UseAgentReturn, ToolCall, OnToolCallOptions, OnFinishOptions, Conversation, ToolFunction, RagConfig, } from './types/use-agent';
2
+ export type { UseAgentOptions, UseAgentReturn, ToolCall, OnToolCallOptions, OnFinishOptions, Conversation, ToolFunction, RagConfig, TodoItem, TodoStatus, } from './types/use-agent';
3
3
  export { useGenerate } from './lib/useGenerate';
4
4
  export type { GenerateOptions, GenerateObjectParams, StreamObjectParams, GenerateResult, UseGenerateReturn, } from './lib/useGenerate';
5
5
  export { preserveToolName, ARCTEN_ORIGINAL_NAME, extractToolsMetadata } from './utils/extract-tool-metadata';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EACV,eAAe,EACf,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC7G,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EACV,eAAe,EACf,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,UAAU,GACX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC7G,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}