UNPKG

@adonis-agora/agent

Version:

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

106 lines • 5.76 kB
import type { AgentDepsFactory } from '../agent-deps-factory.js'; import { type ChatQueueService } from '../chat-queue-service.js'; import { type HumanReply } from '../elicitation.js'; import type { AgentRunner, AgentRunStartOptions } from '../spi/agent-runner.js'; import type { AgentStore } from '../spi/agent-store.js'; import type { AgentRunInput } from '../types.js'; /** * Runs the agent turn in-process — the default runner (`durable: false`). A HITL wait resolves a * pending promise keyed run-namespaced by `${runId}:${toolCallId}`, so one run's reply can never * satisfy another's pending action. * * Sub-agent delegation runs a nested loop on a transient sub-thread, and that nested run parks on a * human exactly as a top-level one does — under its OWN runId, which is what makes it answerable: * the pending row it writes carries that runId, and its frames are forwarded into the top-level * ancestor's stream (the only stream anyone subscribed to) carrying it too. The mirror of the * durable runner's `sinkRunId`. * * Single-replica only — durable is the scaled path (deferred). */ export declare class InlineAgentRunner implements AgentRunner { private readonly factory; private readonly store; /** The thread message queue this runner drains when a thread's own turn settles. */ private readonly queue?; private readonly pending; /** * The input of each top-level run this process is running — what {@link isRunActive} answers * from, and what a cancel settles the thread (and its queue) of. */ private readonly live; /** Runs someone asked to stop; the loop observes it at its next safe point. */ private readonly cancelled; /** * Aborted by {@link cancel}: what cuts short the model call or tool a stopped run is in (the * loop's `abortSignal` hook). Keyed like {@link cancelled}, and dropped with it. */ private readonly aborts; /** The detached delegates this process is running, by runId — what a Stop on one settles. */ private readonly detached; constructor(factory: AgentDepsFactory, store: AgentStore, /** The thread message queue this runner drains when a thread's own turn settles. */ queue?: ChatQueueService | undefined); /** * In-process, so exact: a run this process is not running is not running anywhere — the process * that was running it is gone (a restart), and a thread it still holds is a stale claim. */ isRunActive(runId: string): Promise<boolean>; start(input: AgentRunInput, options?: AgentRunStartOptions): Promise<{ runId: string; }>; /** * Move the thread past a settling TOP-LEVEL run — to the next queued message, which starts here, * or to a paused/empty queue — and write the resulting `queue` frame into this run's stream. A * no-op without a queue-capable store, and for a run that is not a thread's own turn. * * Never throws: the run is settling either way, and a queue that could not be advanced is picked * up by the next send or resume on the thread. */ private settleQueue; /** * Deliver a human's reply to whichever wait this run is parked on. * * An approval wait takes a {@link Decision} only. Answers addressed there are refused rather than * delivered: `approved` is absent on them, and the tool-call row would record a rejection the * person never made. The refusal leaves the wait intact, so the approval is still there to make. * * The other direction IS delivered — a question set parks as a `pending_approval` action, so an * operator pressing Approve on it is expected, and the loop reads that as "confirmed the * pre-picked answers". */ signal(runId: string, toolCallId: string, reply: HumanReply): Promise<void>; /** The signal {@link cancel} aborts for `runId` — one per run, shared by its awaited delegates. */ private abortSignalFor; /** Drop a settled run's Stop: its flag and its abort controller. */ private forgetCancel; cancel(runId: string): Promise<void>; private topLevelHooks; /** * The two waits a run parks on, keyed by the run that is parked. Identical for a top-level run and * for a delegated one: `signal(runId, …)` finds either, because the pending map belongs to the * runner rather than to a run. */ private humanHooks; /** Hold a run at `key` until a human's reply arrives through {@link InlineAgentRunner.signal}. */ /** * {@link park} for an approval, bounded by the policy's time to live when it has one: the timer * settles the wait as `expired` — the same Decision the durable runner builds from its signal * timeout — and a decision that arrives first disarms it. */ private parkApproval; private park; /** * Delegate WITHOUT waiting: a nested loop nobody awaits, on its own scratch thread and its OWN * stream, which posts its answer back into the delegating thread when it lands (`deliverTo`, * delivered by the loop). The calling turn gets the run id straight back and finishes. * * No `sinkRunId`, unlike {@link runNested}: forwarding into the stream the human is watching would * write into a turn that has already ended. It parks on a human under its own runId, which is what * `tool-call/approve` resolves the pending row to; and it is stoppable on that id (the receipt * carries it), which settles the delegation as `cancelled` in the thread that asked. */ private startDetached; /** Delegate to another agent as a nested in-process run (a transient sub-thread). */ private runNested; } //# sourceMappingURL=inline-agent-runner.d.ts.map