@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
83 lines • 4.52 kB
TypeScript
import type { AgentDepsFactory } from './agent-deps-factory.js';
import type { ChatQueueService } from './chat-queue-service.js';
import type { PriceCatalogOptions } from './pricing/boot-pricing.js';
import type { AgentRunner } from './spi/agent-runner.js';
import type { AgentStore } from './spi/agent-store.js';
import type { ModelProvider } from './spi/model-provider.js';
import type { AgentPricingStore } from './spi/pricing-store.js';
import type { ProtocolAdapter } from './spi/protocol-adapter.js';
import type { TokenStreamSink } from './spi/token-stream-sink.js';
import type { ToolRegistry } from './tool-registry.js';
/**
* What the provider hands an engine when it builds the runner: the same graph the library's own
* runners are built from. Everything around a turn stays the library's — the routes and the stream
* protocol, threads and the store, the sink, approvals and answers routed through `AgentService`, the
* queue — and the engine reads the agent's prompt, approval policy, skills and memory off `factory`.
*/
export interface AgentEngineContext {
factory: AgentDepsFactory;
store: AgentStore;
sink: TokenStreamSink;
registry: ToolRegistry;
/** The thread message queue: a settling turn hands its thread to the next queued message. */
queue: ChatQueueService;
/**
* Resolve a container binding (e.g. `@adonis-agora/durable`'s `WorkflowEngine`). Absent when the
* engine is wired outside an AdonisJS app (a test building the graph by hand).
*/
make?: <T = unknown>(binding: unknown) => Promise<T>;
/** The app's `APP_KEY`, when there is one — a stable secret for whatever the engine signs. */
appKey?: string;
/** The configured pricing store, so an engine prices the calls it reports like the loop does. */
pricingStore?: AgentPricingStore;
/** `priceCatalog` from `config/agent.ts`. */
priceCatalog?: PriceCatalogOptions | false;
}
/**
* Something other than this library's loop that runs a turn — what `engine` in `config/agent.ts`
* takes. An engine owns the turn end to end (the model calls, the tools, the context it keeps) and
* builds the {@link AgentRunner} the service runs turns on; everything around the turn stays the
* library's.
*
* `openCode()` (`@adonis-agora/agent/opencode`) is the one that ships. An engine replaces the loop,
* so the options only the loop reads (`model`, processors, `maxSteps`, `historyWindow`, `retriever`,
* delegation) do nothing under one, and `model` becomes optional.
*/
export interface AgentEngine {
/** For logs and diagnostics. */
readonly name: string;
/**
* The engine checkpoints its own turns (`openCodeDurable()`). `durable: true` in the config names the
* LOOP's durable runner, which an engine replaces — so it is refused next to an engine that is not
* durable itself, rather than silently ignored.
*/
readonly durable?: boolean;
/** Build the runner the service starts, signals and cancels turns on. Called once, at boot. */
createRunner(context: AgentEngineContext): AgentRunner | Promise<AgentRunner>;
/**
* Routes the engine serves next to the agent's own (e.g. the MCP endpoint OpenCode reaches the
* app's tools through), mounted like `adapters` once {@link createRunner} has run.
*/
adapters?(): readonly ProtocolAdapter[];
/** Release what the engine holds (event streams, timers) when the app shuts down. */
shutdown?(): void | Promise<void>;
}
/** `engine` in `config/agent.ts`: an engine, or a lazy factory so its module loads with the config. */
export type AgentEngineFactory = () => AgentEngine | Promise<AgentEngine>;
/**
* Refuse a wiring that cannot run a turn, at boot rather than on the first message: no `model` and
* no `engine` leaves the loop nothing to call, and `durable: true` names the loop's own durable
* runner, which a non-durable engine replaces (a durable engine brings its own durability).
*/
export declare function assertRunnable(options: {
model?: unknown;
engine?: AgentEngine | undefined;
durable?: boolean | undefined;
}): void;
/**
* The model the loop is handed when an engine runs the turns and no `model` is configured: nothing
* of the library's calls it on that path, and a caller that does (a background summarizer reading
* `deps.model`) is told why instead of crashing on `undefined`.
*/
export declare function engineOnlyModel(engine: AgentEngine): ModelProvider;
//# sourceMappingURL=engine.d.ts.map