@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
JavaScript
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