// @generated — DO NOT EDIT. Source: packages/ai/base-session.ts /** * Shared session base class — extracts the common lifecycle, abort, and * ID-resolution logic that every AIProvider session needs. * * Concrete providers extend this and implement query(). */ import type { AIMessage, AISession } from "./types.ts"; export abstract class BaseSession implements AISession { readonly parentSessionId: string | null; onIdResolved?: (oldId: string, newId: string) => void; protected _placeholderId: string; protected _resolvedId: string | null = null; protected _isActive = false; protected _currentAbort: AbortController | null = null; protected _queryGen = 0; protected _firstQuerySent = false; constructor(opts: { parentSessionId: string | null; initialId?: string }) { this.parentSessionId = opts.parentSessionId; this._placeholderId = opts.initialId ?? crypto.randomUUID(); } get id(): string { return this._resolvedId ?? this._placeholderId; } get isActive(): boolean { return this._isActive; } // --------------------------------------------------------------------------- // Query lifecycle helpers — call from concrete query() implementations // --------------------------------------------------------------------------- /** Error message returned when a query is already active. */ static readonly BUSY_ERROR: AIMessage = { type: "error", error: "A query is already in progress. Abort the current query before sending a new one.", code: "session_busy", }; /** * Call at the start of query(). Returns the generation number and abort * signal, or null if the session is busy. */ protected startQuery(): { gen: number; signal: AbortSignal } | null { if (this._isActive) return null; const gen = ++this._queryGen; this._isActive = true; this._currentAbort = new AbortController(); return { gen, signal: this._currentAbort.signal }; } /** * Call in the finally block of query(). Only clears state if the * generation matches (prevents a stale finally from clobbering a newer query). */ protected endQuery(gen: number): void { if (this._queryGen === gen) { this._isActive = false; this._currentAbort = null; } } /** * Call when the provider resolves the real session ID from the backend. * Fires the onIdResolved callback so the SessionManager can remap its key. */ protected resolveId(newId: string): void { if (this._resolvedId) return; // Already resolved const oldId = this._placeholderId; this._resolvedId = newId; this.onIdResolved?.(oldId, newId); } /** * Abort the current in-flight query. Subclasses should call super.abort() * after any provider-specific cleanup. */ abort(): void { if (this._currentAbort) { this._currentAbort.abort(); this._isActive = false; this._currentAbort = null; } } abstract query(prompt: string): AsyncIterable; }