import type { AgentUiComponent } from '../stream-events.js'; import type { Actor, AgentRunInput, StoredMessage } from '../types.js'; import type { OpenCodeClient, OpenCodePromptFile, OpenCodeSessionCreate } from './client.js'; import type { OpenCodeUsage, PendingAsk, TurnOutcome } from './turn.js'; /** * Where a turn runs and how its OpenCode session is set up — the part only the host knows. The * engine owns everything between the library and OpenCode (sessions per thread, the event stream, * the turn's frames, approvals and questions, cancel); the host answers these questions. * * Every method is called as a method of the host (`host.onSettled(…)`), so a class host can read * its own services off `this`. */ export interface OpenCodeHost { /** * The OpenCode server this actor's turns run on — one per tenant in a sandbox, one for the whole * deployment, anything. Asked once per turn, so a server that restarted is picked up. */ server(actor: Actor): Promise; /** * How to create a session for a thread: location, model, OpenCode agent, permission rules. * Asked only when the thread has no live session on this server (its first turn, or the server * restarted since). `input.model` is the model the caller picked for this turn, when it picked one. */ session(context: OpenCodeTurnContext): Promise; /** * Instructions entries refreshed on every turn (key → text), on top of the agent's own prompt * (its `systemPrompt` and persona, under `agora.system`). Profile, the project a chat is about… * An entry that disappears is not cleared: put an empty value to drop it. */ instructions?(context: OpenCodeTurnContext & { sessionId: string; }): Promise>; /** * What the session is prompted with, when it is more than the user message: documents read into * the text, images as files the model can see. Omit → the user message, and `files`. */ promptFor?(context: OpenCodeTurnContext & { sessionId: string; }): Promise<{ text: string; files?: OpenCodePromptFile[]; }>; /** * The user message's attachments (`input.attachments`) in the form `session.prompt` takes them. * Omit → attachments are not sent to OpenCode. Not asked when `promptFor` answers. */ files?(context: OpenCodeTurnContext): Promise; /** * Anything else to do on a NEW session before its first prompt — register MCP servers of your own * (`client.mcp.add`), write files… Runs right after `session` and before `instructions`. */ prepare?(context: OpenCodeTurnContext & { sessionId: string; client: OpenCodeClient; }): Promise; /** * `openCodeDurable()` only: the turn's workflow start options (tags, search attributes, a * concurrency quota) — like `durable.start` in the engine's options, from a host with services. */ startOptions?(input: AgentRunInput, runId: string): Promise>; /** `openCodeDurable()` only: what a refused start becomes (e.g. a concurrency limit → a 429). */ startError?(error: unknown, input: AgentRunInput): unknown; /** * May this turn keep the thread's session? Asked when the session is still on its server; `false` * opens a new one (told the conversation so far) — e.g. the person the session's tools act for * changed. Omit → always reuse. */ reuse?(context: OpenCodeTurnContext & { session: OpenCodeSessionRef; }): Promise; /** * Every turn, once the session is known (`created`: opened for this turn) and before the prompt: * bring it up to date — the turn's model, permission rules that changed, tools. */ beforePrompt?(context: OpenCodeTurnContext & { sessionId: string; client: OpenCodeClient; created: boolean; }): Promise; /** * A person was asked something (an approval or a question form): post it where else they are — * a Slack thread, a Teams chat — or wake whoever waits on the run. May run again for the same ask * after a restart: make it idempotent. Errors are logged, never the run's. */ onAsk?(context: OpenCodeTurnContext & { sessionId: string; ask: PendingAsk; }): Promise; /** A tool pushed a component into the run (`ctx.emitUi` over the engine's MCP endpoint). */ onUi?(context: OpenCodeTurnContext & { component: AgentUiComponent; }): Promise; /** * The run is over and about to settle: the last word on the answer — components appended to it * (a guardrail notice), and for a failed run the error the person reads instead of OpenCode's. */ beforeSettle?(result: OpenCodeRunResult): Promise; /** * The run settled (the stream ended, the thread moved on): deliver the answer elsewhere, record * spend and telemetry. Errors are logged, never the run's. */ onSettled?(result: OpenCodeRunResult): Promise; } /** What a run produced, as the host's settle hooks see it. */ export interface OpenCodeRunResult { runId: string; input: AgentRunInput; outcome: TurnOutcome; /** The run's answer: its assistant messages' text, in order. */ text: string; /** The run's assistant messages, as stored. */ messages: StoredMessage[]; /** * What the run spent, over every model call OpenCode made for it (its steps, the title, a * compaction) and every process that ran a part of it. `inputTokens` counts cached input too; * `cacheReadTokens`/`cacheWriteTokens` are subsets of it. */ usage: OpenCodeUsage; durationMs: number; } /** See {@link OpenCodeHost.beforeSettle}. */ export interface OpenCodeAmendment { ui?: AgentUiComponent[]; /** For a failed run: what the person reads. */ error?: string; } export interface OpenCodeServer { client: OpenCodeClient; /** Identifies the server across turns; sessions are only reused on the server that holds them. */ key: string; /** * Changes whenever the server restarts and loses its sessions (a sandbox boot id). Omit for a * server whose sessions outlive restarts. */ bootId?: string; } export interface OpenCodeTurnContext { input: AgentRunInput; runId: string; } /** The session a thread talks to, and where it lives. */ export interface OpenCodeSessionRef { sessionId: string; serverKey: string; bootId?: string; /** The session's directory (`location.directory`), when it has one. */ directory?: string; /** * The agent (and persona id) the thread's latest turn ran as — what the tools endpoint checks a * call against when it lands on a process that is not following the turn. */ agentName?: string; persona?: string; } /** * Which OpenCode session each thread uses. The default keeps it in memory, which is enough for one * process; a deployment with several replicas (or that wants sessions to survive a restart of its * own) persists it — on the thread row, or in Redis with {@link keyValueOpenCodeSessionStore}. */ export interface OpenCodeSessionStore { get(threadId: string): Promise; set(threadId: string, ref: OpenCodeSessionRef): Promise; } export declare class InMemoryOpenCodeSessionStore implements OpenCodeSessionStore { private readonly refs; get(threadId: string): Promise; set(threadId: string, ref: OpenCodeSessionRef): Promise; } /** The two calls of a key-value store a session store needs — an ioredis client (`@adonisjs/redis`) fits. */ export interface OpenCodeKeyValue { get(key: string): Promise; set(key: string, value: string): Promise; } /** * Sessions kept in a shared key-value store (Redis…), so every process of a deployment finds the * session a thread already has — what running more than one process needs, alongside a * cross-process sink (`tokenSinks.redis()` / `tokenSinks.lucid()`). Keys are ``. */ export declare function keyValueOpenCodeSessionStore(kv: OpenCodeKeyValue, prefix?: string): OpenCodeSessionStore; //# sourceMappingURL=host.d.ts.map