UNPKG

@mastra/core

Version:
329 lines 15 kB
import { Agent } from '../agent/index.js'; import type { MastraBrowser } from '../browser/browser.js'; import { Mastra } from '../mastra/index.js'; import { RequestContext } from '../request-context/index.js'; import type { ObservationalMemoryRecord } from '../storage/types.js'; import { Workspace } from '../workspace/workspace.js'; import { Session } from './session.js'; import type { AvailableModel, IntervalHandler, AgentControllerConfig, AgentControllerMode, ModelAuthStatus, ToolCategory } from './types.js'; /** * Returns Anthropic `providerOptions` that enable a server-side fallback to * {@link FABLE_FALLBACK_MODEL} when the active model is `claude-fable-5`, and * `undefined` otherwise. * * fable-5 can have a turn blocked server-side by its safety classifiers. With * a fallback configured, Anthropic transparently retries the blocked turn on * the fallback model and returns that model's answer instead of refusing. If * the whole chain refuses, the run still ends on a `content-filter` finish * reason, which is handled as a terminal error. * * The match is suffix-based so it covers `anthropic/claude-fable-5`, a bare * `claude-fable-5`, and any pack/provider-prefixed form. */ export declare function buildFableFallbackProviderOptions(modelId: string): { anthropic: { fallbacks: { model: string; }[]; }; } | undefined; /** * Build a user-facing notice when a turn was served by an Anthropic * server-side fallback model instead of the primary model. * * When the primary model's safety classifiers decline a turn and a fallback * chain is configured (see {@link buildFableFallbackProviderOptions}), the API * transparently retries on the fallback model and reports this via * `fallback_message` entries in `providerMetadata.anthropic.iterations`. * Without a notice the user has no way to tell that the response did not come * from the model they selected. */ /** * The AgentController orchestrates multiple agent modes, shared state, memory, and storage. * It's the core abstraction that a TUI (or other UI) controls. * * @example * ```ts * const controller = new AgentController({ * id: "my-coding-agent", * storage: new LibSQLStore({ url: "file:./data.db" }), * stateSchema: z.object({ * currentModelId: z.string().optional(), * }), * modes: [ * { id: "plan", name: "Plan", default: true, agent: planAgent }, * { id: "build", name: "Build", agent: buildAgent }, * ], * }) * * controller.subscribe((event) => { * if (event.type === "message_update") renderMessage(event.message) * }) * * await controller.init() * await controller.sendMessage({ content: "Hello!" }) * ``` */ export declare class AgentController<TState = {}> { #private; readonly id: string; private config; private workspaceInitialized; private initPromise; private browser; private workspace; private intervalTimers; private availableModelsCache; private availableModelsCacheTime; constructor(config: AgentControllerConfig<TState>); /** * Create a new, fully-wired {@link Session} and bring it online: it starts in * the default mode with the seeded model, is connected to the AgentController's shared * machinery (agent, storage/lock, config catalog), and has a current thread * (the most recent thread for `resourceId`, or a freshly created one). * * The AgentController owns no session of its own — every consumer creates its own * session and drives all work through it (`session.sendMessage`, * `session.mode.switch`, `session.thread.*`, `session.subscribe`, ...). In a * server / multiplayer setting, each request / thread / user gets its own * session, isolated from every other: independent event bus, mode, model, * state, and current thread. * * Call {@link init} once before creating sessions so shared storage and * workspace are ready. * * @param id - Stable session identifier (mirrors `SessionRecord.id`). Defaults to the controller `id`. * @param ownerId - Stable session owner (mirrors `SessionRecord.ownerId`). Defaults to the controller `id`. * @param resourceId - Memory resource to bind this session to. Defaults to the controller `resourceId` or `id`. */ createSession({ resourceId, ownerId, id, tags, workspace, browser, requestContext, }?: { resourceId?: string; id?: string; ownerId?: string; /** * Arbitrary string tags that scope this session. Each tag is seeded into the * session's state and used to filter initial thread selection: a thread is a * resume candidate only when its metadata matches every provided tag. This * lets worktrees sharing a resourceId each resume their own thread (via a * `projectPath` tag) and leaves room for future scoping dimensions without * changing the API. Falls back to `initialState` when omitted. */ tags?: Record<string, string>; workspace?: Workspace; browser?: MastraBrowser; requestContext?: RequestContext; }): Promise<Session<TState>>; /** * Resolve a live session by resourceId, if one was created for it via * {@link createSession}. Returns `undefined` when no session owns the * resource. Used by notification delivery to run woken signals as the session * that owns the target thread, rather than an arbitrary session. */ getSessionByResource(resourceId: string): Promise<Session<TState> | undefined>; /** * Access the Mastra instance backing this AgentController. * * Returns the parent Mastra when this AgentController is registered on one (see * {@link __registerMastra}); otherwise the internal Mastra created during * `init()` when storage is configured. * * Useful for scorer registration, observability access, and eval tooling. */ getMastra(): Mastra | undefined; /** * Whether a workspace is configured on this AgentController (static instance, dynamic * factory, or config object). Sessions without an explicit workspace override * fall back to this. */ hasWorkspace(): boolean; /** * Whether the AgentController-level static workspace has been initialized. Dynamic * factory workspaces are resolved and initialized per-session during * `createSession`, so this returns `false` for factory configs until a * session is created. */ isWorkspaceReady(): boolean; /** * The AgentController-level workspace, if it is a static instance. Dynamic factory * workspaces are not resolved here — use {@link resolveWorkspace} to resolve * a factory against a session's request context. */ getWorkspace(): Workspace | undefined; /** * Eagerly resolve the workspace. For dynamic workspaces (factory function), * this triggers resolution against the given session's request context and * caches the result so {@link getWorkspace} returns it. Useful for code paths * outside the request flow (e.g. slash commands). */ resolveWorkspace({ session, requestContext, }: { session: Session<TState>; requestContext?: RequestContext; }): Promise<Workspace | undefined>; /** * Register this AgentController on a parent Mastra. Called by Mastra during * construction when a harness is passed in its config. Once registered, the * AgentController uses the parent Mastra (its storage, agents, gateways, and * observability) instead of building its own internal one during `init()`. * * @internal */ __registerMastra(mastra: Mastra): void; private resolveConfiguredMemory; /** * Sets or updates the harness-level browser and propagates it to mode agents. */ setBrowser(browser: MastraBrowser | undefined): void; /** * Initialize the harness — loads storage and workspace. * Must be called before using the harness. Idempotent: repeated calls * return the same in-flight/completed initialization instead of rebuilding * the internal Mastra instance (which would orphan registered agents). */ init(): Promise<void>; private runInit; private getMemoryStorage; /** * The shared-host storage gateway the Session's thread domain reads/writes * through. The Session owns the thread-domain logic; this adapter just maps * raw storage rows to AgentController types — it does not call back into Session. */ private createThreadDataStore; /** Persist a thread row to memory storage (gateway primitive for the Session thread domain). */ private persistThreadRow; /** Delete a thread row from memory storage (gateway primitive for the Session thread domain). */ private deleteThreadRow; /** Clone a thread (and messages) via the host's memory (gateway primitive for the Session thread domain). */ private cloneThreadRow; private readThreadMetadataValue; private writeThreadMetadataValue; private removeThreadMetadataValue; private queryThreadById; private queryThreads; private queryThreadMessages; private queryFirstUserMessages; listModes(): AgentControllerMode[]; private propagateRuntimeServicesToAgent; private getAgentForMode; /** * Resolve the combined instructions for the current mode: harness-level * instructions + mode-specific instructions. Passed at call time via * `buildAgentMessageStreamOptions` so the agent's own instructions are * never mutated. */ private resolveCurrentModeInstructions; /** * Convert AgentInstructions (string | string[] | system message objects) to * a plain string for combining with mode instructions. */ private instructionsToString; /** * Get the agent for the current mode. */ /** * Resolve the Agent backing the current mode, with runtime services (storage, * pubsub, telemetry) propagated. Public so consumers like MastraCode's * GoalManager can drive the agent's native objective methods * (`setObjective`/`getObjective`/`clearObjective`/`updateObjectiveOptions`), * which read/write the durable `threadState` `'goal'` slot. */ getCurrentAgent(session: Session<TState>): Agent; /** * Check if the current model's provider has authentication configured. * Delegates to the {@link GatewayManager} auth chain (the same resolution * the model router uses at run time). Returns `hasAuth: true` only when no * model is selected; gateway-chain failures return `hasAuth: false` so the * auth-status endpoint stays stable instead of erroring. */ getCurrentModelAuthStatus(session: Session<TState>): Promise<ModelAuthStatus>; /** * Get available models from the app-provided catalog hook with use counts applied. */ listAvailableModels(): Promise<AvailableModel[]>; invalidateAvailableModelsCache(): void; /** * Point the session at a different memory resourceId. The resourceId itself * lives on the session (`session.identity`); the AgentController orchestrates the * surrounding teardown — dropping the current thread subscription and clearing * the active thread — since those are AgentController-owned. */ setResourceId(session: Session<TState>, { resourceId }: { resourceId: string; }): Promise<void>; getKnownResourceIds(session: Session<TState>): Promise<string[]>; /** * Load observational memory progress for the current thread. * Reads the OM record and recent messages to reconstruct status, * then emits an `om_status` event for the UI. */ loadOMProgress(session: Session<TState>): Promise<void>; getObservationalMemoryRecord(session: Session<TState>): Promise<ObservationalMemoryRecord | null>; getToolCategory({ toolName }: { toolName: string; }): ToolCategory | null; /** * Resolve the `activeTools` allowlist for the current mode's run. * * Returns `undefined` when the mode has no `availableTools` configured * (no restriction — all tools visible). When the mode declares * `availableTools`, returns that list filtered to remove tools whose * permission category is denied. * * Per-tool `deny` is already handled by `buildToolsets` (denied tools are * deleted from the toolsets), so those tools won't exist at execution time * regardless of whether they appear in the allowlist. * * The returned list uses the same exposed tool names the execution pipeline * checks against (e.g. `view`, `write_file`, `ask_user`), which matches the * names workspace tools are renamed to via `TOOL_NAME_OVERRIDES`. */ private resolveModeActiveTools; private buildAgentMessageStreamOptions; private formatToolProgressOutput; /** * Options that every harness-driven agent run must carry — the initial stream * AND every `resumeStream`. Centralized so the two paths can't drift: a * missing `maxSteps` on resume silently caps the resumed run at the agent's * small default and ends it mid-task (see {@link HARNESS_MAX_STEPS}). */ private buildSharedRunOptions; /** * Persist a system-reminder message for a thread (host-owned storage). Throws * when no storage is configured — the Session guards the no-thread case before * calling. Returns the saved message converted to {@link AgentControllerMessage}. */ private saveSystemReminder; /** * Resolve the mode the session transitions to when a plan is approved: the * current mode's `transitionsTo`, else the configured default mode. The mode * catalog is AgentController config, so this is host-owned. Returns `undefined` when * no default mode is configured. */ private resolveTransitionModeId; private convertToControllerMessage; private getSubagentDisplayName; /** * Build the toolsets object that includes built-in harness tools (ask_user, submit_plan, * and optionally subagent) plus any user-configured tools. * Used by sendMessage, handleToolApprove, and handleToolDecline. */ private buildToolsets; /** * Build request context for agent execution. * Tools can access controller state via requestContext.get('controller'). */ private buildRequestContext; /** * Resolve memory from config — handles both static instances and dynamic factory functions. */ private resolveMemory; private persistTokenUsage; private startIntervals; registerInterval(handler: IntervalHandler): void; removeInterval({ id }: { id: string; }): Promise<void>; stopIntervals(): Promise<void>; destroy(): Promise<void>; private generateId; } //# sourceMappingURL=agent-controller.d.ts.map