import type { DbAdapter } from '../db/types'; import type { LLMProvider } from '../llm/providers/types'; import { ApprovalManager } from '../ai/tool-approval'; /** * The default token sink for `start()` when no `onToken` is supplied. * * Exported so a test can call it rather than re-implementing the guard. A * webview has no `stdout`: unguarded, this threw on the FIRST streamed token, * so streaming died immediately even though the constructor had succeeded. */ export declare function writeTokenToStdout(token: string): void; /** * Agent Configuration * * The Agent class is the primary entry point for PraisonAI. * It supports both simple instruction-based agents and advanced configurations. * * @example Simple usage (3 lines) * ```typescript * import { Agent } from 'praisonai'; * const agent = new Agent({ instructions: "You are helpful" }); * await agent.chat("Hello!"); * ``` * * @example With tools (5 lines) * ```typescript * const getWeather = (city: string) => `Weather in ${city}: 20°C`; * const agent = new Agent({ * instructions: "You provide weather info", * tools: [getWeather] * }); * await agent.chat("Weather in Paris?"); * ``` * * @example With persistence (4 lines) * ```typescript * import { Agent, db } from 'praisonai'; * const agent = new Agent({ * instructions: "You are helpful", * db: db("sqlite:./data.db"), * sessionId: "my-session" * }); * await agent.chat("Hello!"); * ``` */ /** * Terminal reason for the most recent agent run, mirroring Python's * `Agent.last_stop_reason`. Lets callers distinguish a completed run from a * truncated one instead of both looking identical. */ export type StopReason = 'completed' | 'max_steps' | 'cancelled' | 'error'; /** * A single conversation message, as stored on the Agent. * * This is the shape returned by {@link Agent.getHistory} and accepted by * {@link Agent.setHistory}. It carries the tool context (`tool_calls`, * `tool_call_id`) so a saved conversation round-trips without silently * dropping tool calls or their results — persisting `getHistory()` output is * the intended way to save a chat for later restoration. */ export interface AgentMessage { /** The provider role. A restored message with any other value is rejected. */ readonly role: 'system' | 'user' | 'assistant' | 'tool'; /** Text content. `null` on an assistant turn that only called tools. */ readonly content: string | null; /** Present on an assistant turn that called tools. */ readonly tool_calls?: ReadonlyArray<{ id: string; type: string; function: { name: string; arguments: string; }; }>; /** Present on a tool turn; pairs it to the `tool_calls` entry above. */ readonly tool_call_id?: string; /** * The tool's name, present on a `tool` turn. Required so the AI SDK adapter * (`toAISDKPrompt`) can set a non-empty `toolName` on the tool-result part — * without it, a restored tool history sent to a non-OpenAI provider is * rejected. The live tool loop already records this (`processToolCalls`); it * must survive the getHistory/setHistory round-trip too. */ readonly name?: string; } export interface SimpleAgentConfig { /** Agent instructions/system prompt (required) */ instructions: string; /** Agent name (auto-generated if not provided) */ name?: string; /** Enable verbose logging (default: true) */ verbose?: boolean; /** Enable pretty output formatting */ pretty?: boolean; /** * LLM model to use. Accepts: * - Model name: "gpt-4o-mini", "claude-3-sonnet" * - Provider/model: "openai/gpt-4o", "anthropic/claude-3" * Default: "gpt-4o-mini" */ llm?: string; /** * API key for the LLM provider. Mirrors Python's per-agent `api_key` * (agent/agent.py). Falls back to OPENAI_API_KEY when omitted. */ apiKey?: string; /** * Base URL for the LLM provider. Mirrors Python's per-agent `base_url` * (agent/agent.py). Useful for OpenAI-compatible endpoints. */ baseURL?: string; /** * Custom fetch implementation. Lets a host route provider egress through * native code (e.g. a Tauri command), keeping the API key out of the JS * heap and avoiding CORS for embedded webviews. Implies browser support. */ fetch?: typeof fetch; /** Enable markdown formatting in responses */ markdown?: boolean; /** Enable streaming responses (default: true) */ stream?: boolean; /** * Tools available to the agent. * Can be plain functions (auto-schema) or OpenAI tool definitions. */ tools?: any[] | Function[]; /** * JSON Schema for structured output (mirrors Python's output_json / * output_pydantic). When set, responses are constrained via OpenAI's * response_format json_schema and the raw JSON string is returned. */ outputSchema?: Record; /** Name for the outputSchema (default: "response") */ outputSchemaName?: string; /** Map of tool function implementations */ toolFunctions?: Record; /** * Human-in-the-loop approval gate for tool calls (mirrors Python's * `approval`). When `true`, every tool is gated behind an interactive CLI * prompt (approve/deny per call); pass an `ApprovalManager` instance to use * custom handlers / auto-approve-deny rules (e.g. a UI responder). Denied * calls are reported back to the model as the tool result rather than * throwing, so it can course-correct. */ approval?: boolean | ApprovalManager; /** * Maximum number of tool-call round-trips before the loop is aborted * (default: 20, matching Python's `ExecutionConfig.max_iter`). When the cap * is reached the run sets `lastStopReason = 'max_steps'` and throws instead * of silently returning an empty string, so exhaustion is observable. */ maxIterations?: number; /** * Maximum number of tool calls executed within a single round-trip * (default: 10, matching Python's `ExecutionConfig.max_tool_calls_per_turn`). * Extra tool calls beyond this cap in one turn are ignored. */ maxToolCallsPerTurn?: number; /** Database adapter for persistence */ db?: DbAdapter; /** Session ID for conversation persistence (auto-generated if not provided) */ sessionId?: string; /** Run ID for tracing (auto-generated if not provided) */ runId?: string; /** Max messages to restore from history (default: 100) */ historyLimit?: number; /** Auto-restore conversation history from db (default: true) */ autoRestore?: boolean; /** Auto-persist messages to db (default: true) */ autoPersist?: boolean; /** Enable caching of responses */ cache?: boolean; /** Cache TTL in seconds (default: 3600) */ cacheTTL?: number; /** Enable telemetry tracking (default: false, opt-in) */ telemetry?: boolean; /** * Default abort signal for cancellation. Aborting it stops the underlying * provider request (a working Stop button). An explicit signal passed to * chat()/start() takes precedence over this. Mirrors Python's * interrupt_controller / cancel_token. */ signal?: AbortSignal; /** Agent role (advanced mode) */ role?: string; /** Agent goal (advanced mode) */ goal?: string; /** Agent backstory (advanced mode) */ backstory?: string; } /** * Discriminated union emitted by {@link Agent.streamEvents}. * * Mirrors Python's `StreamEvent` channel (praisonaiagents streaming/events.py): * structured information travels here while {@link Agent.stream} yields plain * text tokens. `text` carries an incremental delta; `finish` carries the full * response; `error` carries a thrown error. */ export type AgentEvent = { type: 'text'; delta: string; } /** * A tool is about to run. `callId` is the provider's id for this invocation * and is the ONLY key that may pair a result to its call -- matching by * position holds only while exactly one call is ever in flight, and silently * attributes the wrong output the moment that stops being true. */ | { type: 'tool_call'; callId: string; name: string; args: Record; } /** * A tool finished. `ok` is the only signal of success: never infer it from a * non-empty `output`, because a tool that failed with a message looks * identical to one that succeeded with one. */ | { type: 'tool_result'; callId: string; name: string; ok: boolean; output: string; } | { type: 'finish'; text: string; } | { type: 'error'; error: Error; }; /** Options for {@link Agent.stream} / {@link Agent.streamEvents}. */ export interface AgentStreamOptions { /** Previous result to substitute for the `{{previous}}` placeholder. */ previousResult?: string; /** * Turn-scoped abort signal. Aborting it stops the underlying provider * request so no further tokens are generated or billed. Independent of the * agent-level `SimpleAgentConfig.signal`: cancel one turn while keeping the * agent. Breaking out of the `for await` loop also aborts automatically — * the iterator's `return()` is the language's own cancellation signal. */ signal?: AbortSignal; } export declare class Agent { private instructions; name: string; private verbose; private pretty; private llm; private markdown; private streamEnabled; private llmService; private tools?; private outputSchema?; private outputSchemaName; private toolFunctions; private approvalManager?; private maxIterations; private maxToolCallsPerTurn; /** * Terminal reason for the most recent run (mirrors Python's * `Agent.last_stop_reason`). `null` until the agent has run at least once. */ lastStopReason: StopReason | null; private dbAdapter?; private sessionId; private runId; private messages; private dbInitialized; private historyLimit; private autoRestore; private autoPersist; private cache; private cacheTTL; private responseCache; private telemetryEnabled; private signal?; private _backend; private _backendPromise; private _backendSource; private _useAISDKBackend; private _apiKey?; private _baseURL?; constructor(config: SimpleAgentConfig); /** * Extract declared parameter names from a plain function. * Returns [] when parameters can't be parsed reliably (e.g. destructuring), * in which case callers fall back to passing the raw args object. */ private extractParamNames; /** * Register a plain (positional-argument) function as a tool. * The model returns named args ({city: "Paris"}); plain functions like * (city) => ... need them mapped back to positional order — previously the * whole object landed in the first parameter ("Weather in [object Object]"). */ private registerPlainFunction; /** Build the OpenAI response_format payload when outputSchema is set. */ private getResponseFormat; /** * Flatten OpenAI-shape tool definitions ({ type: 'function', function: {...} }) * into the provider-agnostic ToolDefinition shape the AI SDK backend expects. * Tools already in the flat shape pass through unchanged. This mirrors what * litellm does for the Python SDK: format once, translate at the transport — * so tools are never dropped by provider. */ private getFlatToolDefinitions; /** * Generate a unique session ID based on current hour, agent name, and random suffix */ private generateSessionId; /** * Initialize DB session - restore history on first chat (lazy) */ private initDbSession; /** * Get cached response if available and not expired */ private getCachedResponse; /** * Cache a response */ private cacheResponse; private createSystemPrompt; /** * Register a tool function that can be called by the model * @param name Function name * @param fn Function implementation */ registerToolFunction(name: string, fn: Function): void; /** * Check if a tool definition exists for the given function name * @param name Function name * @returns True if a tool definition exists */ private hasToolDefinition; /** * Auto-generate a tool definition based on the function * @param name Function name * @param func Function implementation */ private addAutoGeneratedToolDefinition; /** * Process tool calls from the model * @param toolCalls Tool calls from the model * @param signal Optional AbortSignal; if already aborted, no tool is invoked * @returns Array of tool results */ private processToolCalls; start(prompt: string, previousResult?: string, onToken?: (token: string) => void, signal?: AbortSignal, /** * Structured events, for callers that need to SEE tool activity rather * than infer it from prose. Optional and additive: every existing caller * passes four arguments and gets exactly the behaviour it had before. */ onEvent?: (event: AgentEvent) => void): Promise; /** * Stream the agent's response token-by-token as an async iterable of plain * strings — mirrors Python's `Agent.iter_stream()`, which yields bare `str`. * This lets any host (browser, webview, React Native, server) render an * answer as it arrives, instead of only receiving the final string. * * Backpressure is free (the loop pulls) and cancellation is a single path: * breaking out of the `for await` runs the iterator's `return()`, which * detaches the token sink so no further tokens are queued. * * When the underlying execution path does not stream (e.g. `stream: false`, * tools, or a structured `outputSchema`), no text deltas are produced; in * that case the full response from the terminal `finish` event is yielded as * a single token so callers always receive the answer. * * @param prompt - The user prompt to send to the agent. * @param opts - Optional {@link AgentStreamOptions} (e.g. `previousResult`). * @returns An async iterable of plain text tokens. * @example * ```typescript * for await (const token of agent.stream("Tell me a story")) { * process.stdout.write(token); * } * ``` */ stream(prompt: string, opts?: AgentStreamOptions): AsyncIterable; /** * Stream structured {@link AgentEvent}s (text deltas, finish, error) — the * TypeScript analogue of Python's `stream_emitter` channel. Prefer * {@link Agent.stream} when you only need the text tokens. * * @param prompt - The user prompt to send to the agent. * @param opts - Optional {@link AgentStreamOptions} (e.g. `previousResult`). * @returns An async iterable of {@link AgentEvent}s: zero or more `text` * deltas followed by a single terminal `finish` (or `error`) event. * @example * ```typescript * for await (const event of agent.streamEvents("Hi")) { * if (event.type === 'text') process.stdout.write(event.delta); * else if (event.type === 'finish') console.log('\n', event.text); * } * ``` */ streamEvents(prompt: string, opts?: AgentStreamOptions): AsyncIterable; chat(prompt: string, previousResult?: string, signal?: AbortSignal): Promise; execute(previousResult?: string): Promise; /** * Persist a message to the database */ private persistMessage; /** * Get the session ID for this agent */ getSessionId(): string; /** * Get the run ID for this agent */ getRunId(): string; getResult(): string | null; getInstructions(): string; /** * Get the full conversation history, including tool context. * * The returned messages carry `tool_calls` and `tool_call_id`, so persisting * this output is a lossless way to save a chat — a tool-calling conversation * survives the round-trip through {@link Agent.setHistory}. The array and its * messages are copied on read: mutating the result does not change the * agent's state. * * @returns A copy of the conversation history. */ getHistory(): AgentMessage[]; /** * Restore a previously saved conversation so the model regains its memory of * it. Without this, reopening a saved chat renders the transcript on screen * while the model behaves as though the conversation never happened. * * Input is validated because it comes from disk, and disk contents outlive * the code that wrote them. A malformed history accepted here would fail on * the *next* model call, far from the code that loaded it, so it is refused * loudly and immediately: * * - a non-array input, or a message that is not an object, is rejected * - an unknown `role` is rejected * - a `tool` message whose `tool_call_id` matches no preceding `tool_calls` * entry is rejected (an orphaned tool result is a 400 from every provider) * * `system` messages: a leading `system` message is accepted and stripped. * `start()` prepends the agent's own instructions as the system prompt on * every run, so keeping a restored one would double it. Any non-leading * `system` message is rejected as malformed. * * The input is copied on write, mirroring {@link Agent.getHistory}'s copy on * read: mutating the array you pass in does not change the agent's state. * * @param messages The conversation to restore, e.g. from `getHistory()`. * @throws {Error} If the history is malformed (see rules above). */ setHistory(messages: readonly AgentMessage[]): void; /** * Deep-copy a single message so neither getHistory() nor setHistory() shares * the caller's mutable references (tool_calls is an array of objects). */ private copyMessage; /** * Clear conversation history (in memory and optionally in DB) */ clearHistory(clearDb?: boolean): Promise; /** * Clear response cache */ clearCache(): void; /** * Get the resolved backend (AI SDK preferred, native fallback) * Lazy initialization - backend is only resolved on first use */ getBackend(): Promise; /** * Get the backend source (ai-sdk, native, custom, or legacy) */ getBackendSource(): 'ai-sdk' | 'native' | 'custom' | 'legacy'; /** * Embed text using AI SDK (preferred) or native provider * * @param text - Text to embed (string or array of strings) * @param options - Embedding options * @returns Embedding vector(s) * * @example Single text * ```typescript * const embedding = await agent.embed("Hello world"); * ``` * * @example Multiple texts * ```typescript * const embeddings = await agent.embed(["Hello", "World"]); * ``` */ embed(text: string | string[], options?: { model?: string; }): Promise; /** * Get the model string for this agent */ getModel(): string; } /** * Configuration for multi-agent orchestration */ export interface AgentTeamConfig { agents: Agent[]; tasks?: string[]; verbose?: boolean; pretty?: boolean; process?: 'sequential' | 'parallel'; } /** * @deprecated Use AgentTeamConfig instead. This is a silent alias for backward compatibility. */ export type PraisonAIAgentsConfig = AgentTeamConfig; /** * @deprecated Use AgentTeamConfig instead. This is a silent alias for backward compatibility. */ export interface AgentsConfig { agents: Agent[]; tasks?: string[]; verbose?: boolean; pretty?: boolean; process?: 'sequential' | 'parallel'; } /** * Multi-agent orchestration class * * @example Simple array syntax * ```typescript * import { Agent, Agents } from 'praisonai'; * * const researcher = new Agent({ instructions: "Research the topic" }); * const writer = new Agent({ instructions: "Write based on research" }); * * const agents = new Agents([researcher, writer]); * await agents.start(); * ``` * * @example Config object syntax * ```typescript * const agents = new Agents({ * agents: [researcher, writer], * process: 'parallel' * }); * ``` */ export declare class AgentTeam { private agents; private tasks; private verbose; private pretty; private process; /** * Create a multi-agent orchestration * @param configOrAgents - Either an array of agents or a config object */ constructor(configOrAgents: AgentTeamConfig | Agent[]); private generateTasks; private executeSequential; start(): Promise; chat(): Promise; } /** * PraisonAIAgents - Silent alias for AgentTeam (backward compatibility) * @deprecated Use AgentTeam instead */ export declare const PraisonAIAgents: typeof AgentTeam; /** * Agents - Silent alias for AgentTeam (backward compatibility) * @deprecated Use AgentTeam instead * * @example * ```typescript * import { Agent, AgentTeam } from 'praisonai'; * * const team = new AgentTeam([ * new Agent({ instructions: "Research the topic" }), * new Agent({ instructions: "Write based on research" }) * ]); * await team.start(); * ``` */ export declare const Agents: typeof AgentTeam;