import type { AgentHarnessStreamOptions } from '@earendil-works/pi-agent-core'; import type { Model } from '@earendil-works/pi-ai'; import type { AgentAdapter, AgentAdapterKind, AgentSendOpts, ChatMessage } from '../../../core/types.js'; import type { ApprovalGate } from '../../../core/llm/agent-loop.js'; import type { ToolDef } from '../../../core/llm/provider-base.js'; import type { ToolDispatcher } from '../../../core/llm/tool-dispatcher.js'; /** Resolved LLM binding for one turn: a pi-ai Model + how to authenticate it. */ export interface NativeModelBinding { model: Model; /** Whether model.cost came from a known catalog entry. */ costKnown?: boolean; getApiKeyAndHeaders: () => Promise<{ apiKey: string; headers: Record; }>; } export interface NativeModelChoice { name: string; providerType?: string; model?: string; } export interface NativeAgentDeps { /** Adapter name to register under. Defaults to 'agim'. */ name?: string; /** Adapter aliases. Defaults to the public Agim Agent aliases. */ aliases?: string[]; /** Directory under which per-session JSONL files live. */ sessionsRoot: string; /** agim BackendConfig → pi-ai Model + auth. Returns null when no backend is * configured (adapter then reports unavailable / fails the turn cleanly). */ resolveModel: (opts?: AgentSendOpts) => Promise; /** Optional model roster for direct adapter slash-command fallback. The * normal web/IM path handles `/models` in the router; this keeps REST/job * style direct calls from sending the command to the LLM as plain text. */ listModels?: () => NativeModelChoice[]; /** The agim tool surface (schemas + dispatcher) to expose this turn. Receives * the session id so per-thread tool state (todo tracker, plan-exit) keys * correctly. */ resolveTools?: (opts: AgentSendOpts | undefined, sessionId: string) => { tools: ToolDef[]; dispatch: ToolDispatcher; parallelSafe?: (toolName: string) => boolean; } | null; /** Tools allowed under Plan Mode. Unknown tools fail closed. */ planModeAllowedTools?: ReadonlySet; /** Read-only verdict for parallel tool execution. */ parallelSafe?: (toolName: string) => boolean; /** Per-turn agim policy approval gate (read-only / allow-list / deny-all + * IM approval-card escalation). Consulted from the beforeToolCall hook. */ resolveApprovalGate?: (opts: AgentSendOpts | undefined, sessionId: string) => ApprovalGate | null; /** Per-turn system prompt (runtime identity: real backend + model + role, * operator role, skill routing, tool guides). Without it the model has no * idea what it is and hallucinates its identity. */ resolveSystemPrompt?: (opts: AgentSendOpts | undefined, sessionId: string) => string | undefined; /** Max tool-call rounds per turn before the loop is force-stopped. pi's loop * has no built-in cap, so we enforce one (mirrors agim's * AGIM_NATIVE_AGENT_MAX_ITER). Defaults to 50; clamped to [1, 100]. */ maxIterations?: number; /** Hard cap on tool calls declared by assistant messages in one run. A batch * that would exceed it is aborted before dispatch. Defaults to maxIterations. */ maxToolCalls?: number; /** Proactive context compaction before each turn. pi never auto-compacts, so * when a durable session grows past the model window (minus reserve) the * adapter summarizes old history — without this, long-lived IM sessions grow * the JSONL unbounded until the context overflows. Defaults to enabled; set * false (factory reads AGIM_CTX_COMPACT=off) to disable. */ compactionEnabled?: boolean; /** Provider request resilience + caching, snapshotted per turn by pi: * maxRetries / maxRetryDelayMs / timeoutMs (ride out transient 429/5xx from * the LLM provider) + cacheRetention (provider prompt cache → cuts the repeat * token cost of the system prompt + history). Factory builds it from * AGIM_NATIVE_* envs; omit to use pi defaults. */ streamOptions?: AgentHarnessStreamOptions; /** Active AgentHarness loop watchdog in milliseconds, resolved per turn so * goal-aware callers can extend it. Starts after optional compaction because * pi 0.79.x does not expose a cancellable compaction signal. 0 disables. */ resolveRunTimeoutMs?: (opts: AgentSendOpts | undefined) => number; } /** * Build the user-facing reply for one harness turn. * * Models often emit the full report in a mid-turn assistant message that also * carries tool calls (e.g. todo_write), then end with a short "盘点完成". * `harness.prompt()`'s result only contains that last hop — so without this * merge the conversation stream loses the body. See composeDeliveredAssistantText * tests / CHANGELOG. */ export declare function composeDeliveredAssistantText(midTurnTexts: readonly string[], finalText: string): string; export declare class NativeAgentAdapter implements AgentAdapter { private deps; readonly name: string; readonly aliases: string[]; readonly kind: AgentAdapterKind; readonly handlesAgimSkillsPrompt = true; readonly handlesEphemeralContext = true; private readonly sessionMeta; private readonly sessionModelOverrides; private readonly sessionLocks; constructor(deps: NativeAgentDeps); /** True while a turn for this session id is queued or running. */ isSessionBusy(sessionId: string): boolean; /** Drop cached JSONL metadata / model override after quarantine or external delete. */ invalidateSessionMeta(sessionId: string): void; isAvailable(): Promise; describe(): string | undefined; sendPrompt(sessionId: string, prompt: string, history?: ChatMessage[], opts?: AgentSendOpts): AsyncGenerator; private runTurn; private tryHandleModelSlash; } //# sourceMappingURL=index.d.ts.map