UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

81 lines • 3.17 kB
import { useCallback, useEffect, useRef, useState } from 'react'; import { useAgentBackend } from '../provider.js'; import { notifyThreads, onThreadsEvent } from './threads-events.js'; /** * The actor's conversations, kept current without the host wiring refetches: a chat on the same * backend reports thread creation and settled runs (refetch) and streamed titles (patched in * place). Headless — it renders nothing. */ export function useThreads(options = {}) { const { enabled = true } = options; const backend = useAgentBackend(options.backend); const [threads, setThreads] = useState([]); const [isLoading, setIsLoading] = useState(enabled); const [error, setError] = useState(null); const threadsRef = useRef(threads); threadsRef.current = threads; // Only the newest request may write: a slow response must not overwrite a newer one. const generation = useRef(0); const refresh = useCallback(async () => { const mine = ++generation.current; setIsLoading(true); try { const list = await backend.listThreads(); if (mine !== generation.current) return; setThreads(list); setError(null); } catch (caught) { if (mine !== generation.current) return; setError(caught instanceof Error ? caught : new Error(String(caught))); } finally { if (mine === generation.current) setIsLoading(false); } }, [backend]); useEffect(() => { if (!enabled) return; void refresh(); return onThreadsEvent(backend, (event) => { if (event.type === 'title') { setThreads((current) => current.map((thread) => thread.id === event.threadId ? { ...thread, title: event.title } : thread)); } else if (event.type === 'removed') { setThreads((current) => current.filter((thread) => thread.id !== event.threadId)); } else { void refresh(); } }); }, [backend, enabled, refresh]); const rename = useCallback(async (id, title) => { const before = threadsRef.current; setThreads((current) => current.map((thread) => (thread.id === id ? { ...thread, title } : thread))); try { await backend.updateThread(id, { title }); notifyThreads(backend, { type: 'title', threadId: id, title }); } catch (caught) { setThreads(before); throw caught; } }, [backend]); const remove = useCallback(async (id) => { const before = threadsRef.current; setThreads((current) => current.filter((thread) => thread.id !== id)); try { await backend.deleteThread(id); notifyThreads(backend, { type: 'removed', threadId: id }); } catch (caught) { setThreads(before); throw caught; } }, [backend]); return { threads, isLoading, error, refresh, rename, remove }; } //# sourceMappingURL=use-threads.js.map