Add Looms to your app
Keep your application router, authentication, database, and model provider. Choose which long-running operation becomes a Looms run, and store its runId beside the application record that owns it.
Choose what belongs in a run#
Use one run for an operation such as a support investigation or procurement request. Its child agents, workflows, and custom threads share one history. Keep independent customers and operations in separate runs. Actor isolation does not replace tenant authorization.
Install only the capabilities you need#
npm install @swirls/loomsImport only the subpaths your application needs. Register every named child definition on its owning module. A reference used as a tool does not automatically register the child. Keep server-only provider configuration and secrets out of client imports; export event catalogs and projections from separate shared files.
Mount behind your application gateway#
After authenticating and authorizing a request, forward it to an actor host or call looms.fetch(request). The embed handler returns null for unrelated paths. Keep the documented /runs paths intact when forwarding, including streaming responses. Do not expose event ingestion directly to untrusted browsers.
// Fragment inside an already-authorized server handler:const response = await looms.fetch(request)return response ?? new Response('Not found', { status: 404 })Connect a client#
import { createLoomsClient } from '@swirls/looms/client'const client = createLoomsClient({ baseUrl: '/execution' })const { runId } = await client.startRun({ kind: 'workflow', definitionName: 'release-v1', input: { topic: 'Product launch' },})const unsubscribe = client.subscribeEvents(runId, (event) => { console.log(event.seq, event.type)})// On disposal:unsubscribe()This assumes your same-origin gateway strips /execution before forwarding and checks both run ownership and allowed actions. Use a custom fetch implementation for service-to-service authentication. Preserve streaming responses and disable proxy buffering for SSE.
Connect React#
import { LoomsProvider, useProjection, useRunStore } from '@swirls/looms/react'import { pendingApprovals } from '@swirls/looms/approval'function Reviews({ runId }: { runId: string }) { const store = useRunStore(runId) const reviews = useProjection(store, pendingApprovals) return <p>{reviews.items.filter((r) => r.status === 'pending').length} pending reviews</p>}export function App({ runId }: { runId: string }) { return <LoomsProvider endpoint="/execution"><Reviews runId={runId} /></LoomsProvider>}Handle decisions through an application endpoint that authorizes the reviewer and constructs the allowed event. See authentication and tenancy before connecting real users.
Keep your agent logic#
Use defineAgent({ runTurn }) to adapt one turn of an existing implementation. Return the assistant message, tool requests, and completion state. The turn can be retried after an interruption; do not hide irreversible operations inside it. Put those operations in typed tools or domain effects so their outcomes can be recorded.