/** * Execution-plane coordinator for goal continuation, research, managed lanes, worker delegation, * and model fitness. * * Coordination state is owned by focused controllers. This coordinator retains one AgentSession * composition seam and a shared lane read model. Everything it needs * — the session manager, settings, model registry, live model, capability envelope, the goal * continuation LOOP, the isolated-completion primitive, spawned-usage accounting, and the telemetry * sink — is reached through narrow deps accessors rather than the whole AgentSession. * * Drive-loop boundary (deliberate): the idle triggers ({@link scheduleGoalAutoContinueFromIdle}, * {@link scheduleResearchLaneFromIdle}) are invoked from the session's prompt tail as one-line * delegations; goal auto-continue itself only ever asks the session to `continueGoalLoop`, so this * controller never touches `prompt()`, the last-assistant-message, retry, or streaming state. */ import type { Api, Model } from "@caupulican/pi-ai"; import type { GoalContinuationLoopOptions, GoalContinuationLoopResult, PromptOptions, ResearchLaneRunOutcome, WorkerDelegationRunOutcome } from "./agent-session-contracts.ts"; import { type LaneRecord } from "./autonomy/lane-tracker.ts"; import type { SessionRootReply, SessionRootReplyQuery, SessionRootReplyWaitOptions, SessionRootReplyWaitResult } from "./delegation/session-root-mailbox.ts"; import type { SessionRootWorkerAgentMessageOptions, WorkerAgentActivity, WorkerAgentBroadcastOptions, WorkerAgentBroadcastResult, WorkerAgentControlPort, WorkerAgentControlScope, WorkerAgentMessageOptions, WorkerAgentReplyResult, WorkerAgentRetireResult, WorkerAgentTaskStartOptions, WorkerAgentTranscriptOptions, WorkerAgentWaitMode, WorkerAgentWaitResult } from "./delegation/worker-agent-control.ts"; import { type WorkerDelegationControllerDeps } from "./delegation/worker-delegation-controller.ts"; import type { WorkerDelegationRequest } from "./delegation/worker-delegation-request.ts"; import type { ManagedLaneEvent } from "./extensions/types.ts"; import type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from "./goals/goal-runtime-snapshot.ts"; import type { GoalState } from "./goals/goal-state.ts"; import type { ModelCapabilityProfile } from "./model-capability.ts"; import type { StoredFitnessReport } from "./models/fitness-store.ts"; import type { WorkerResultContract } from "./orchestration/contracts.ts"; import type { TaskProfileCreateInput, TaskProfileCreateResult, TaskProfileInspection } from "./orchestration/task-profile-writer.ts"; import type { TaskRuntimeProjection } from "./orchestration/task-runtime.ts"; import { type LaneModelResolverDeps } from "./research/lane-model-resolver.ts"; import type { ModelFitnessReport } from "./research/model-fitness.ts"; import { type ModelFitnessControllerDeps } from "./research/model-fitness-controller.ts"; import { type ResearchLaneControllerDeps } from "./research/research-lane-controller.ts"; export { isLocalExecutionModel } from "./delegation/worker-delegation-controller.ts"; export { clampLaneMaxUsd } from "./research/lane-model-resolver.ts"; export interface BackgroundLaneControllerDeps extends WorkerDelegationControllerDeps, ResearchLaneControllerDeps, ModelFitnessControllerDeps, LaneModelResolverDeps { /** True iff the active surface can terminalize a goal through `goal` or `update_goal`. * Explicit tool/profile exclusion and the worker-role ceiling still disable continuation. */ isGoalToolActive(): boolean; /** Capability profile of the SESSION model (gates background lanes, scales continuation budgets). */ getModelCapabilityProfile(): ModelCapabilityProfile; /** Continuation gate + goal state for the idle autosteer scheduler. */ getGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot; /** Drive-loop boundary: the session's bounded goal-continuation loop (owns `prompt()`, not us). */ continueGoalLoop(options: GoalContinuationLoopOptions): Promise; /** True while the session owns a foreground prompt/retry/follow-up run. */ isForegroundBusy(): boolean; /** Resolve only after the complete foreground retry/follow-up run releases the session. */ waitForForegroundIdle(): Promise; /** Persist an explicit stopped state when the selected surface cannot drive the active goal. */ markGoalToolUnavailable(): void; } export declare class BackgroundLaneController implements WorkerAgentControlPort { /** Live lane registry — the real source for AutonomyStatusSnapshot.activeLaneCount. */ private readonly _laneTracker; private readonly _laneModels; private readonly _goalAutoContinue; private readonly _research; private readonly _fitness; /** Lazily materialized only when managed-lane state is queried or reported. */ private _managedLanes; /** Lazily materialized so a UAC surface without `delegate` allocates no worker runtime state. */ private _workers; /** One durable lifecycle shared by every worker execution adapter. */ private _workerLifecycle; /** Shared terminal outbox for managed and in-process workers; lazy under UAC omission. */ private _workerNotifications; /** Active event waits consume matching terminal edges before a redundant parent wake is admitted. */ private readonly _workerWaitConsumers; private readonly deps; /** Emit a warning without ever throwing — used from disposal-adjacent persistence where a * listener failure (or a bare test double missing `emit`) must never block or crash cleanup. */ private _safeWarn; private _recordWorkerTerminal; constructor(deps: BackgroundLaneControllerDeps); private _getWorkerController; private _getWorkerLifecycle; private _getWorkerNotificationCoordinator; private _workerAgentIdForRecord; private _isWorkerTerminalAwaited; private _retainWorkerWaitConsumers; /** Observe only logical-agent terminals explicitly exposed by a bounded model result. */ observeWorkerAgentTerminals(agentIds: readonly string[]): void; observeWorkerTerminalRecords(records: readonly LaneRecord[]): void; /** * Backfills a durable notification for every record about to be flushed, through the SAME * durable notification store `_getWorkerLifecycle()` already replays from on construction * (`getPendingTerminalNotifications()` / `markNotificationsDelivered()`) -- not a new * persistence subsystem. `WorkerLifecycle.getTerminalNotification()` is idempotent (it reuses * an existing durable entry for the same laneId/attemptId or creates exactly one), so calling * it for a record that already has a durable notification is a safe no-op. Without this, a * "transient::" record (recorded without a durableNotificationId — e.g. * because no attempt was resolvable at record time) has no trace in the durable ledger at all: * if it's still stuck behind an unresolved notify() when the process restarts, the replay on * the next construction can only find what was durably enqueued, so it would be lost instead of * replayed. */ private _ensureDurableNotifications; private _getManagedLaneController; private _hydrateManagedLanes; /** Live lane records tracked by this process (running and terminal). */ getLaneRecords(): LaneRecord[]; /** Does not materialize the worker controller when UAC omitted delegation. */ getTaskRuntimeSnapshot(): TaskRuntimeProjection | undefined; getWorkerResult(laneId: string): WorkerResultContract | undefined; /** Reconcile only when delegation has already been materialized; UAC omission stays zero-load. */ synchronizeGoalState(goal: GoalState): void; /** * Resolve a tracked managed-lane dispatch. The caller's stable id is also the canonical durable * lane id, so this is an existence check rather than an id translation. */ resolveManagedLaneId(callerLaneId: string): string | undefined; /** Live count of active lanes — the real source for AutonomyStatusSnapshot.activeLaneCount. */ getActiveLaneCount(): number; /** Belt-and-braces guard: whether ANY queued/running lane is tagged with this goalId. */ private _hasInFlightLaneForGoal; /** Delegate the out-of-process dispatch/terminal claim to its single lifecycle owner. */ recordManagedLane(event: ManagedLaneEvent): LaneRecord | undefined; /** Why the last idle research-lane evaluation skipped, for /autonomy diagnostics. */ getLastResearchLaneSkipReason(): string | undefined; /** * Abort in-flight research and delegate worker disposal to its single owning controller. * * This synchronous body is the LAST provably-safe write window for canceled/in-flight work. * `dispose()` (agent-session.ts) has already set the session's own disposed flag but has not yet * returned — no successor session (e.g. a `/reload` adoption) can exist yet, so an append here * cannot interleave with one; a post-await continuation resuming AFTER this method returns must * not append (see the disposed branch in `runWorkerDelegationOnce`). Persist FIRST, then * complete-in-memory, so a throw from one lane's persist cannot skip another's; each persist gets * its own try/catch — dispose must never throw. */ abortInFlightLanes(): void; clearGoalAutoContinueTimer(): void; scheduleGoalAutoContinueFromIdle(options?: PromptOptions): void; /** * Single-flight entry point for EVERY goal-continuation loop invocation — idle autosteer * ({@link _runScheduledGoalAutoContinue}) AND the manual `/goal start` / `/goal-continue` * commands (reached through `AgentSession.continueGoalLoop`). Both paths ultimately submit * continuation prompts through the session's single `prompt()` path, so two loops racing throws * "Agent is already processing" from whichever submits second. `_isGoalAutoContinuing` is the * ONE owner of that mutex; `deps.continueGoalLoop` (the raw {@link GoalLoopController} loop) * must never be called directly outside this method, or the guard is bypassed. */ continueGoalLoopExclusive(options: GoalContinuationLoopOptions): Promise; clearResearchLaneTimer(): void; scheduleResearchLaneFromIdle(): void; resolveLaneModel(configuredPattern: string | undefined): Model | undefined; getOrchestrationProfileCatalog(): Array<{ profileId: string; role: string; description: string; }>; inspectTaskProfileOptions(): TaskProfileInspection; createTaskProfile(input: TaskProfileCreateInput): TaskProfileCreateResult; /** * Run one bounded, read-only research pass and persist its results: evidence bundle snapshot, * terminal lane record, and spawned-usage cost report (single-hop invariant, idempotent on the * lane's reportId). Explicit calls (e.g. `/autonomy research`) express user intent and bypass the * enabled/mode/dedupe gates the idle scheduler enforces; budget and capability gates always apply. */ runResearchLaneOnce(request?: { query?: string; context?: string; goalId?: string; }): Promise; /** Start a durable leaf worker with inherited, preset, or model-selected authority. */ startWorkerDelegation(request: WorkerDelegationRequest): { started: false; skipReason: string; } | { started: true; record: LaneRecord; }; /** Durable logical-worker controls. Each checks UAC before materializing worker state. */ listWorkerAgents(scope?: WorkerAgentControlScope): ReturnType; getWorkerTaskSessionView(): ReturnType; getWorkerAgentActivity(agentId: string, scope?: WorkerAgentControlScope): WorkerAgentActivity; readWorkerAgentTranscript(agentId: string, options?: WorkerAgentTranscriptOptions): ReturnType; sendWorkerAgentMessage(agentId: string, message: string, options?: WorkerAgentMessageOptions): { messageId: string; queued: true; }; followUpWorkerAgent(agentId: string, message: string, options?: WorkerAgentMessageOptions): { started: boolean; steering: boolean; messageId: string; record?: LaneRecord; skipReason?: string; }; sendSessionRootWorkerAgentMessage(agentId: string, message: string, options?: SessionRootWorkerAgentMessageOptions): { messageId: string; queued: true; }; followUpSessionRootWorkerAgent(agentId: string, message: string, options?: SessionRootWorkerAgentMessageOptions): { started: boolean; steering: boolean; messageId: string; record?: LaneRecord; skipReason?: string; }; replyToWorkerAgentMessage(sourceAgentId: string, message: string, replyToMessageId: string): WorkerAgentReplyResult; listSessionRootReplies(query?: SessionRootReplyQuery): SessionRootReply[]; waitForSessionRootReplies(options?: SessionRootReplyWaitOptions): Promise; acknowledgeSessionRootReply(messageId: string, ackToken: string): boolean; reconcileSessionRootReplies(): void; startWorkerAgentTask(agentId: string, message: string, options?: WorkerAgentTaskStartOptions): ReturnType; interruptWorkerAgent(agentId: string, scope?: WorkerAgentControlScope): { interrupted: boolean; reason?: string; }; resumeWorkerAgent(agentId: string, scope?: WorkerAgentControlScope): { started: boolean; record?: LaneRecord; skipReason?: string; }; cancelWorkerAgent(agentId: string, reasonCode?: string, scope?: WorkerAgentControlScope): LaneRecord | undefined; retireWorkerAgent(agentId: string, scope?: WorkerAgentControlScope): WorkerAgentRetireResult; waitForWorkerAgent(agentId: string, timeoutMs?: number, scope?: WorkerAgentControlScope): ReturnType; waitForWorkerAgents(agentIds: readonly string[], mode: WorkerAgentWaitMode, timeoutMs?: number, scope?: WorkerAgentControlScope): Promise; broadcastWorkerAgentMessage(agentIds: readonly string[], message: string, options: WorkerAgentBroadcastOptions): WorkerAgentBroadcastResult; /** Run one worker immediately; used by focused integrations and tests. */ runWorkerDelegationOnce(request: WorkerDelegationRequest, onStarted?: (record: LaneRecord) => void, existingRecord?: LaneRecord): Promise; /** * Probe a candidate model against the subagent contracts (research/worker/judge/search/ * tool-call surfaces) via {@link runModelFitnessProbe}. The model must resolve and * authenticate; every probe call runs as an isolated completion on that model, and probe * spend is reported through spawned-usage accounting. */ runModelFitness(args: { model: string; trials?: number; /** LLM tool-call id, present only via the model_fitness tool path — see model-fitness.ts. */ toolCallId?: string; }): Promise<{ started: true; model: string; report: ModelFitnessReport; } | { started: false; skipReason: string; }>; /** Start every capacity-eligible queued worker at the owner session's foreground-idle boundary. */ drainQueuedWorkerDelegations(): void; /** Fitness reports persisted for THIS host (measured evidence for architect/profile decisions). */ getStoredFitnessReports(): StoredFitnessReport[]; } //# sourceMappingURL=background-lane-controller.d.ts.map