UNPKG

@adonis-agora/agent

Version:

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

85 lines • 3.78 kB
import { frameToEvents } from '../sse.js'; import { withFrameSeq } from './core/index.js'; /** * One frame of this package's sink as the frame the local AG-UI encoder reads. The typed `approval` / `elicitation` frames carry what the * stream vocabulary leaves optional — the PARKED run, the tool and its arguments — so an interrupt * addresses a delegated sub-agent's own run. */ export function toAgUiFrame(frame) { switch (frame.t) { case 'text': return { kind: 'text', text: frame.v }; case 'component': return { kind: 'ui', component: frame.name, props: frame.data, ...(frame.id !== undefined ? { id: frame.id } : {}), ...(frame.version !== undefined ? { version: frame.version } : {}), ...(frame.fallbackText !== undefined ? { fallbackText: frame.fallbackText } : {}), ...(frame.componentVersions !== undefined ? { componentVersions: frame.componentVersions } : {}), ...(frame.toolCallId !== undefined ? { toolCallId: frame.toolCallId } : {}), ...(frame.partial === true ? { partial: true } : {}), }; case 'approval': return { kind: 'approval-requested', id: frame.id, runId: frame.runId, toolName: frame.toolName, input: frame.input ?? null, approver: frame.approver ?? 'requester', ...(frame.target !== undefined ? { target: frame.target } : {}), ...(frame.confirmation !== undefined ? { confirmation: frame.confirmation } : {}), ...(frame.expiresAt !== undefined ? { expiresAt: frame.expiresAt } : {}), }; case 'elicitation': return { kind: 'elicitation', id: frame.id, runId: frame.runId, request: frame.request, }; case 'error': return { kind: 'error', code: frame.code, message: frame.message }; case 'event': // The same vocabulary, copied: shared field for field (see `stream-events.ts`). return frame.event; } } /** * A library run's stream as the local encoder's frames. A hand-rolled iterator rather than a * generator: the AG-UI driver lets go of a parked stream while a read is still pending, and a * generator would queue that `return()` behind the read that never comes — the sink's subscription * has to be released at once. * * Each frame is tagged ({@link withFrameSeq}) with the run's own sequence number through it — the * SSE `id:` the native stream (`chat/:runId/stream`) gives its last event — so the AG-UI route can * number its events the way a native re-attach (`?after=`) counts. */ export function agUiFrames(frames) { return { [Symbol.asyncIterator]() { const inner = frames[Symbol.asyncIterator](); let position = 0; let seq = 0; return { next: async () => { const result = await inner.next(); if (result.done === true) return { done: true, value: undefined }; seq += frameToEvents(result.value, position).length; position += 1; return { done: false, value: withFrameSeq(toAgUiFrame(result.value), seq) }; }, return: async () => { await inner.return?.(); return { done: true, value: undefined }; }, }; }, }; } //# sourceMappingURL=frames.js.map