import type { Model, Context } from '@earendil-works/pi-ai/compat'; import type { Api, SimpleStreamOptions } from '@earendil-works/pi-ai'; import type { StoreEventEmitter } from '../store/event-emitter.js'; import type { PDRuntimeAdapter, RuntimeKind, RuntimeCapabilities, RuntimeHealth, RunHandle, RunStatus, StartRunInput, StructuredRunOutput, RuntimeArtifactRef } from '../runtime-protocol.js'; import { type PdL2ArtifactReader, type PdL2PrincipleReader } from '../tools/agent-tool-contract.js'; /** Configuration for L2AgentLoopAdapter. */ export interface L2AgentLoopAdapterConfig { /** Provider id (e.g. 'openai', 'anthropic'). */ provider: string; /** Model id. */ model: string; /** Env var name holding the API key. */ apiKeyEnv: string; /** Optional custom base URL (OpenAI-compatible endpoints). */ baseUrl?: string; /** Optional workspace path (for diagnostics only). */ workspace?: string; /** Optional event emitter; defaults to the shared singleton. */ eventEmitter?: StoreEventEmitter; /** Max agent-loop turns before forced stop (default 5). */ maxTurns?: number; /** Total wall-clock budget for the whole loop in ms (default 300_000). */ totalBudgetMs?: number; /** * PRI-420: max auto-retries when the agent loop returns an empty response (no submit_output * capture and no parseable text). The model API occasionally returns content=[] on long prompts; * retrying with fresh state recovers ~100%. Default 2. Set to 0 to disable. */ maxEmptyRetries?: number; /** * PRI-420: when true (default), if all L2 attempts fail, fall back to a one-shot completeSimple * call (L1 equivalent) so the dreamer still produces output. Emits dreamer_l2_fallback_to_l1. * Set to false to fail loud without fallback. */ l2FallbackToL1?: boolean; /** * PRI-633: optional profile-level system prompt (append layer, DPB-07). * Appended AFTER the run's base-layer systemPrompt and the L2 tool protocol * in agentContext.systemPrompt. Omitted when unset. */ systemPrompt?: string; } /** * Resolve a pi-ai Model from provider/model/baseUrl config (L2 variant of * PiAiRuntimeAdapter's internal resolveModel — kept separate because L2 runs * on a streaming agent loop rather than one-shot completeSimple; both now live * on the single @earendil-works scope, so the historical "no cross-import" * reason for full duplication is gone and a dedupe is planned as PR3 follow-up). * * PRI-795 r2 — catalog-first for custom endpoints, mirroring * PiAiRuntimeAdapter.resolveModel: a custom baseUrl relaying a catalog-known * model keeps the catalog's authoritative metadata (reasoning, contextWindow, * maxTokens, thinkingLevelMap, compat incl. thinkingFormat/supportsReasoningEffort) * with only the transport (provider name + baseUrl) overridden. The previous * hand-built literal hardcoded `reasoning:false`, `contextWindow:128000`, * `maxTokens:32000`, and a compat that could not send `reasoning_effort` — * EP002-R3 live evidence: for glm-5.3-flash (catalog: 1M context / 131072 * output / zai thinking / effort-capable) the profile's `reasoning:low` never * reached the wire, so the model thought at default strength and burned the * entire 16000-token response budget before emitting a tool call. The literal * remains only as the fallback for model ids absent from every catalog. * (The original "deliberately NOT catalog-first" caution is superseded by * EP002-R3: the PiAi path has run catalog-borrowed entries in production * since PRI-758, and the L2 loop ran 25 live model calls against this exact * model without shape issues.) */ export declare function resolveL2Model(provider: string, modelId: string, baseUrl?: string, opts?: { reasoning?: boolean; maxTokens?: number; }): Model; /** Read-only readers injected by the factory (bound to the dreamer's task + stores). */ export interface L2AgentLoopAdapterDeps { artifactReader: PdL2ArtifactReader; principleReader: PdL2PrincipleReader; } /** * PRI-683: streamSimple bound to a transport whose undici idle caps are * disabled, so agent-loop LLM calls are not silently aborted at Node fetch's * implicit 300s boundary before the configured budget fires. * PR #1524 review follow-up: the fetch is resolved per model API — the * google-generative-ai / google-vertex adapters reject any non-globalThis * fetch at entry, so those APIs keep Node's global fetch instead of failing * every L2 call. Exported for ArtificerL2Adapter, which runs the same loop. */ export declare function pdStreamSimple(model: Model, context: Context, options?: SimpleStreamOptions): import("@earendil-works/pi-ai").AssistantMessageEventStream; export declare class L2AgentLoopAdapter implements PDRuntimeAdapter { private readonly config; private readonly deps; private readonly eventEmitter; private readonly runs; private readonly abortControllers; constructor(config: L2AgentLoopAdapterConfig, deps: L2AgentLoopAdapterDeps); kind(): RuntimeKind; getCapabilities(): Promise; refreshCapabilities(): Promise; healthCheck(): Promise; startRun(input: StartRunInput): Promise; /** * PRI-420: L1 one-shot fallback. Runs a single completeSimple call with the same prompt * (without tool instructions) and extracts JSON from the response. This is the safety net * when all L2 attempts fail — it ensures the dreamer still produces output. */ private runL1Fallback; pollRun(runId: string): Promise; cancelRun(runId: string): Promise; fetchOutput(runId: string): Promise; fetchArtifacts(_runId: string): Promise; /** * Bound the runs Map to MAX_RETAINED_RUNS to prevent unbounded memory growth in * long-running services (the auto-consumer wakes every 120s). Evicts the oldest * entries by insertion order (Map preserves it). Called at the start of each run. */ private evictOldRuns; private emitComplete; } //# sourceMappingURL=l2-agent-loop-adapter.d.ts.map