import type { MsgActionRequest, MsgGameOver } from "../protocol/types"; import { type ReconnectingWSClient, type ReconnectingWSClientOptions, type ReconnectStateHandler, type ReconnectStateSnapshot } from "../wsclient/reconnect"; import type { WSClientMessage } from "../wsclient/client"; import type { ServerMessageEnvelope } from "../wsclient/frame-handler"; import { type AgentDecisionWireDecision, type AgentDecisionWireUsage, type AgentFSMEffect, type AgentFSMState } from "./state-machine"; export type { AgentDecisionWireDecision, AgentDecisionWireUsage } from "./state-machine"; export interface AgentDecisionContext { readonly actionRequest: MsgActionRequest; readonly matchId: string; readonly game?: string; readonly state: AgentFSMState; /** * Aborts when this decision is superseded by a newer action_request for the * same match (or the agent stops). Providers that make a paid network call * SHOULD forward this to the request so a superseded decision cancels its * in-flight HTTP call instead of running to completion (R13-F02). Optional so * existing/mock providers keep working unchanged. */ readonly signal?: AbortSignal; } /** * Structured decision result: the chosen action plus optional model usage * metadata (protocol v1.1) and optional decision-provenance telemetry * (protocol v1.2, F09) to attach to the outgoing action message. * Providers may also return the bare action (legacy shape) — the agent * unwraps both. The wrapper is recognized by its exact key set * ({action} plus any of usage/decision); real game actions always carry a * `type` key instead, so the two shapes cannot collide. */ export interface AgentDecisionOutput { readonly action: unknown; readonly usage?: AgentDecisionWireUsage; readonly decision?: AgentDecisionWireDecision; } export interface AgentDecisionProvider { decide(ctx: AgentDecisionContext): Promise; } export interface AgentInstanceNotify { readonly level: "info" | "warning" | "error"; readonly code: string; readonly message: string; readonly cause?: unknown; } export interface AgentInstanceSnapshot { readonly name: string; readonly state: AgentFSMState | null; readonly transport: ReconnectingWSClient["state"] | "idle"; readonly started: boolean; readonly stopped: boolean; } export interface AgentInstanceOptions { readonly name: string; readonly ws: ReconnectingWSClientOptions; readonly autoConfirmMatches?: boolean; readonly decisionProvider: AgentDecisionProvider; readonly connect?: (opts: ReconnectingWSClientOptions) => Promise; readonly now?: () => number; readonly onNotify?: (event: AgentInstanceNotify) => void; readonly onServerMessage?: (message: ServerMessageEnvelope) => void; readonly onClientMessage?: (message: WSClientMessage) => void; readonly onReadinessCheck?: (data: unknown) => Promise | unknown; readonly onResult?: (gameOver: MsgGameOver, context: { readonly game?: string; }) => void; readonly onFallbackRequired?: (effect: Extract) => void; } export type AgentInstanceErrorKind = "agent_start" | "agent_not_started" | "agent_stopped" | "agent_effect"; export declare abstract class AgentInstanceError extends Error { abstract readonly kind: AgentInstanceErrorKind; readonly cause: unknown; protected constructor(message: string, cause?: unknown); } export declare class AgentInstanceStartError extends AgentInstanceError { readonly name = "AgentInstanceStartError"; readonly kind: "agent_start"; constructor(message: string, cause?: unknown); } export declare class AgentInstanceNotStartedError extends AgentInstanceError { readonly name = "AgentInstanceNotStartedError"; readonly kind: "agent_not_started"; constructor(message: string, cause?: unknown); } export declare class AgentInstanceStoppedError extends AgentInstanceError { readonly name = "AgentInstanceStoppedError"; readonly kind: "agent_stopped"; constructor(message: string, cause?: unknown); } export declare class AgentInstanceEffectError extends AgentInstanceError { readonly name = "AgentInstanceEffectError"; readonly kind: "agent_effect"; constructor(message: string, cause?: unknown); } type StateHandler = (snapshot: AgentInstanceSnapshot) => void; export declare class AgentInstance { #private; constructor(opts: AgentInstanceOptions); start(): Promise; stop(reason?: string): Promise; joinQueue(game: string, mode?: string, opts?: { readonly oneShot?: boolean; }): void; leaveQueue(): void; confirmMatch(confirmId?: string): void; snapshot(): AgentInstanceSnapshot; /** Wake the reconnect loop now (P2): backoff → dial, parked → probe, * suspended → resume. Safe no-op before start / after stop. */ poke(): void; /** Non-terminal sleep parking (P5): hand the seat back gracefully and stop * scheduling retries until poke(). Safe no-op before start / after stop. */ suspendConnection(): void; /** Live connection-state projection (P4). The handler also fires once * immediately with the standing snapshot. Returns an unsubscribe. */ onConnectionStateChange(handler: ReconnectStateHandler): () => void; /** Pull counterpart of onConnectionStateChange, null before start. */ connectionSnapshot(): ReconnectStateSnapshot | null; /** Number of matches with a decision currently in flight — the local "busy" * signal for the Phase 1B readiness handshake (no LLM call involved). */ get activeMatchCount(): number; onState(handler: StateHandler): () => void; }