import type { Agent, AgentCapabilities, AgentInput, AgentMetadata, AgentType, BaseAgentConfig, BaseAgentResult } from '../types/agent/index.js'; import type { AgentManagerContract } from '../types/agent/manager.js'; import type { SessionId, TurnId } from '../types/ids/index.js'; import { type CancelCause } from '../types/session/cancel-cause.js'; import type { SessionEvent, SessionEventListener } from '../types/session/events.js'; import { type Logger } from '../utils/logger.js'; export declare abstract class AbstractAgent implements Agent { abstract readonly type: AgentType; readonly metadata: AgentMetadata; protected log: Logger; /** * The logger bound at construction, before any turn exists. `this.log` * is rebound to a CHILD of this on every `bindTurn` call — never the other * way — so `forTurn()` (which builds a fresh shell from `this.metadata`) * can hand the new instance the same base identity without also handing * it a stale turn id from whichever turn happened to be live last. */ private readonly baseLog; protected abortController: AbortController; private readonly invocationLock; /** * Invocations still running, by the key their caller supplied. * * IN-FLIGHT ONLY, and deliberately: a settled entry kept around would * turn deduplication into caching, and caching an agent's answer is a * decision about staleness that only the host can make. A retry that * arrives after the first finished turns again, which is the honest * behaviour — the world may have moved. */ private readonly inflightByKey; protected agentManager?: AgentManagerContract; /** The session this instance is running a turn of, once `bindTurn` recorded it. */ protected currentSessionId?: SessionId; protected currentTurnId?: TurnId; constructor(metadata: AgentMetadata, log?: Logger); abstract run(input: AgentInput, config: TConfig, listener?: SessionEventListener): Promise; /** * A fresh shell of this agent, for a turn that must not share one. * * See {@link Agent.forTurn}. An agent is a shell around metadata — every * per-turn decision arrives in `config` and `input` — so a second instance * costs one object and gives the turn its own abort controller and turn id, * which is precisely what the invocation lock is protecting. * * Rebuilt from `this.constructor` and `this.metadata`, which covers every * agent in this package: they all take metadata and nothing else. A * subclass with a different constructor signature will throw here, and the * answer to that is `this` — the caller then shares the shell and gets the * existing refusal on a concurrent turn, which is the behaviour before this * existed. Losing parallelism is a worse outcome than not having it; losing * the turn is not on the table. * * A host whose agent needs real construction arguments supplies * `AgentDefinition.createAgent` instead, which wins over this. */ forTurn(): this; /** * Acquire the invocation lock to prevent concurrent execution. * Returns a Disposable that must be disposed to release the lock. * * Usage: * ```typescript * const lock = this.acquireInvocationLock() * try { * // do work * } finally { * lock[Symbol.dispose]() * } * ``` * * @throws {ConcurrentInvocationError} if the agent is already executing */ protected acquireInvocationLock(): import("./lock.js").Disposable; /** * Run `body` under this instance's invocation lock. * * The lock existed, was exported, and had no caller — so concurrent * invocations of one agent instance were not prevented at all, and the * error type that announces the refusal could never be thrown. * * They genuinely are unsafe. `abortController` and `currentSessionId` are * INSTANCE state: two overlapping turns share one abort controller, so * cancelling either kills both, and the second clobbers the first's * session, so `cancel()` afterwards cancels the wrong children. Neither failure * announces itself — the first turn simply stops, or the wrong one does. * * A host that wants parallelism constructs a second instance, which is * cheap; sharing one was never the supported shape, it merely was not * refused. */ protected underInvocationLock(body: () => Promise): Promise; /** * Join an invocation already running under the same key, instead of * starting a second one. * * The failure this exists for: a caller sends a request, the connection * drops, the caller retries. Without a key the retry is a second full * turn — a second set of model calls, and a second set of whatever the * tools did. The invocation lock alone does not help, because refusing * the retry with an error is not what the caller wanted either; they * wanted the answer. * * So a duplicate AWAITS the original and receives its result, error * included. An error is shared for the same reason a result is: both * callers asked the same question once, and telling one of them * something different would make the key a lie. * * Instance-scoped, like the lock. Deduplicating across processes needs * somewhere durable to record the key, which is a store the host owns. */ protected underIdempotencyKey(key: string | undefined, body: () => Promise): Promise; /** * `cause` is optional and has no default, deliberately. An operator * calling this IS the `'user'` case, but a library calling it on the * operator's behalf is not — and a default would attribute every * unlabelled cancellation to a person who did not press anything. * * Children get `'parent'` regardless of what stopped this turn: from a * child's side, the fact is that its parent went away. */ cancel(cause?: CancelCause): Promise; getCapabilities(): AgentCapabilities; /** A fresh id for the turn this invocation runs. */ protected createTurnId(): TurnId; /** * The session this invocation's turn belongs to: the config's, or a new * one when the host named none (a one-shot agent is a one-turn session). */ protected resolveSessionId(configured: SessionId | undefined): SessionId; /** * Rebind this instance's logger to a specific turn, so every record * `this.log` writes for the DURATION of that turn carries `namzu.turn.id` * and the session id — and every record after the NEXT call carries that * turn's ids, not this one's. * * Constructor-time binding was the bug this exists to fix: an agent * constructed once and invoked twice (`forTurn` aside — a host is free to * reuse one instance across sequential turns, and every concrete `run()` * takes fresh `input`/`config` precisely to allow it) held ONE logger for * its whole lifetime, so a warning from turn two carried turn one's id, or * none. Every concrete `run()` implementation calls this before touching * `this.log`, right after resolving the turn's id — see `RouterAgent`, * `PipelineAgent`, `SupervisorAgent` and `ReactiveAgent`. * * `log` lets a per-turn override (`BaseAgentConfig.logger`, a host setting * on ONE call to `.run()`) win over the agent's construction-time base, * without reconstructing the agent to get it. * * Also the one place `currentSessionId` is assigned, which is what * `cancel()` reads to cancel this session's delegated children. */ protected bindTurn(sessionId: SessionId, turnId: TurnId, log?: Logger): void; protected createEmptyResult(sessionId: SessionId, turnId: TurnId, startTime: number): BaseAgentResult; protected emitEvent(event: SessionEvent, listener?: SessionEventListener): Promise; } //# sourceMappingURL=AbstractAgent.d.ts.map