/** * ContextBuilder - Contexts and Steps workflow system. * * Contexts contain ordered Steps, each with prompt text/POM sections, * completion criteria, function restrictions, and navigation rules. */ /** * Reserved tool names auto-injected by the runtime when contexts/steps are * present. User-defined SWAIG tools must not collide with these names. * * - `next_step` / `change_context` are injected when valid_steps or * valid_contexts is set so the model can navigate the flow. * - `gather_submit` is injected while a step's gather_info is collecting * answers. * * ContextBuilder.validate() rejects any agent that registers a user tool * sharing one of these names — the runtime would never call the user tool * because the native one wins. */ export declare const RESERVED_NATIVE_TOOL_NAMES: ReadonlySet; /** Represents a single question within a gather operation. */ export declare class GatherQuestion { /** Unique key used to store the answer. */ key: string; /** The question text presented to the user. */ question: string; /** Expected answer type (e.g., "string", "number"). */ type: string; /** Whether the answer requires user confirmation. */ confirm: boolean; /** Optional additional prompt context for this question. */ prompt?: string; /** Optional list of SWAIG function names available during this question. */ functions?: string[]; /** * Creates a new GatherQuestion. * @param opts - Question configuration including key, question text, and optional type/confirm/prompt/functions. */ constructor(opts: { key: string; question: string; type?: string; confirm?: boolean; prompt?: string; functions?: string[]; }); /** * Serializes this question to a plain object for SWML output. * @returns A dictionary representation of this question. */ toDict(): Record; } /** Collects structured information from the user through a series of questions. */ export declare class GatherInfo { private questions; private outputKey?; private completionAction?; private prompt?; /** * Creates a new GatherInfo. * @param opts - Optional output key, completion action, and prompt configuration. */ constructor(opts?: { outputKey?: string; completionAction?: string; prompt?: string; }); /** * Adds a question to this gather operation. * @param opts - Question configuration including key, question text, and optional type/confirm/prompt/functions. * @returns This GatherInfo for chaining. */ addQuestion(opts: { key: string; question: string; type?: string; confirm?: boolean; prompt?: string; functions?: string[]; }): this; /** * Returns all questions in this gather operation. * @returns The array of GatherQuestion instances. */ getQuestions(): GatherQuestion[]; /** * Returns the completion action for this gather info. * @internal */ getCompletionAction(): string | undefined; /** * Serializes this gather operation to a plain object for SWML output. * @returns A dictionary representation of the gather info and its questions. */ toDict(): Record; } /** A single step within a context, with prompt content, criteria, function restrictions, and navigation rules. */ export declare class Step { /** The unique name identifying this step within its context. */ name: string; private text; private stepCriteria; private functions; private validSteps; private validContexts; private sections; private gatherInfo; private _end; private skipUserTurn; private _skipToNextStep; private resetSystemPrompt; private resetUserPrompt; private resetConsolidate; private resetFullReset; /** * Creates a new Step. * @param name - The unique step name. */ constructor(name: string); /** * Sets raw text content for this step, mutually exclusive with POM sections. * @param text - The raw prompt text. * @returns This step for chaining. */ setText(text: string): this; /** * Adds a POM section with a body, mutually exclusive with raw text. * @param title - The section heading. * @param body - The section body text. * @returns This step for chaining. */ addSection(title: string, body: string): this; /** * Adds a POM section with bullet points, mutually exclusive with raw text. * @param title - The section heading. * @param bullets - The list of bullet point strings. * @returns This step for chaining. */ addBullets(title: string, bullets: string[]): this; /** * Sets the criteria that must be met before this step is considered complete. * @param criteria - A description of the completion criteria. * @returns This step for chaining. */ setStepCriteria(criteria: string): this; /** * Set which non-internal functions are callable while this step is active. * * IMPORTANT — inheritance behavior: * If you do NOT call this method, the step inherits whichever function * set was active on the previous step (or the previous context's last * step). The server-side runtime only resets the active set when a * step explicitly declares its `functions` field. This is by design, * but it is the most common source of bugs in multi-step agents: * forgetting setFunctions() on a later step lets the previous step's * tools leak through. * * Best practice: call setFunctions() explicitly on every step that * should have a different toolset than the previous one. * * Keep the per-step active set small: LLM tool selection accuracy * degrades noticeably past ~7-8 simultaneously-active tools per call. * Use per-step whitelisting to partition large tool collections. * * Internal functions (e.g. `startup_hook`, `hangup_hook`, `gather_submit`) * are ALWAYS protected and cannot be deactivated by this whitelist. The * native navigation tools `next_step` and `change_context` are injected * automatically when validSteps/validContexts is set; they are not * affected by this list and do not need to appear in it. * * @param functions - One of: * - `string[]` — whitelist of function names allowed in this step. * Functions not in the list become inactive. * - `[]` — explicit disable-all (no user functions callable). * - `"none"` — synonym for `[]`, same effect. * @returns This step for chaining. * * @example * step.setFunctions(['lookup_account', 'check_balance']); // whitelist * step.setFunctions([]); // disable all * step.setFunctions('none'); // disable all (alt) */ setFunctions(functions: string | string[]): this; /** * Sets which steps the AI may navigate to from this step. * @param steps - List of allowed step names. * @returns This step for chaining. */ setValidSteps(steps: string[]): this; /** * Sets which contexts the AI may navigate to from this step. * @param contexts - List of allowed context names. * @returns This step for chaining. */ setValidContexts(contexts: string[]): this; /** * Mark this step as terminal for the step flow. * * IMPORTANT: `end=true` does NOT end the conversation or hang up the * call. It exits step mode entirely after this step executes — clearing * the steps list, current step index, validSteps, and validContexts. * The agent keeps running, but operates only under the base system * prompt and the context-level prompt; no more step instructions are * injected and no more `next_step` tool is offered. * * To actually end the call, call a hangup tool or define a hangup hook. * * @param end - True to exit step mode after this step. * @returns This step for chaining. */ setEnd(end: boolean): this; /** * Sets whether to skip waiting for user input when entering this step. * @param skip - Whether to skip the user turn. * @returns This step for chaining. */ setSkipUserTurn(skip: boolean): this; /** * Sets whether to automatically advance to the next step after this one completes. * @param skip - Whether to skip to the next step. * @returns This step for chaining. */ setSkipToNextStep(skip: boolean): this; /** * Initializes a gather info operation on this step for collecting structured data. * @param opts - Optional output key, completion action, and prompt configuration. * @returns This step for chaining. */ setGatherInfo(opts?: { outputKey?: string; completionAction?: string; prompt?: string; }): this; /** * Add a question to this step's gather_info configuration. * setGatherInfo() must be called before this method. * * IMPORTANT — gather mode locks function access: * While the model is asking gather questions, the runtime forcibly * deactivates ALL of the step's other functions. The only callable * tools during a gather question are: * * - `gather_submit` (the native answer-submission tool) * - Whatever names you list in this question's `functions` option * * `next_step` and `change_context` are also filtered out — the model * cannot navigate away until the gather completes. This is by design: * it forces a tight ask → submit → next-question loop. * * If a question needs to call out to a tool (e.g. validate an email, * geocode a ZIP), list that tool name in this question's `functions`. * Functions listed here are active ONLY for this question. * * @param opts - Question configuration including key, question text, and optional type/confirm/prompt/functions. * @returns This step for chaining. */ addGatherQuestion(opts: { key: string; question: string; type?: string; confirm?: boolean; prompt?: string; functions?: string[]; }): this; /** * Returns the gather info for this step, if any. * @internal */ getGatherInfo(): GatherInfo | null; /** * Returns the valid steps list for this step, if any. * @internal */ getValidSteps(): string[] | null; /** * Returns the valid contexts list for this step, if any. * @internal */ getStepValidContexts(): string[] | null; /** * Removes all POM sections and raw text from this step. * @returns This step for chaining. */ clearSections(): this; /** * Sets the system prompt to use when resetting context at this step. * @param systemPrompt - The replacement system prompt text. * @returns This step for chaining. */ setResetSystemPrompt(systemPrompt: string): this; /** * Sets the user prompt to use when resetting context at this step. * @param userPrompt - The replacement user prompt text. * @returns This step for chaining. */ setResetUserPrompt(userPrompt: string): this; /** * Sets whether to consolidate conversation history when resetting at this step. * @param consolidate - Whether to consolidate. * @returns This step for chaining. */ setResetConsolidate(consolidate: boolean): this; /** * Sets whether to perform a full conversation reset at this step. * @param fullReset - Whether to fully reset. * @returns This step for chaining. */ setResetFullReset(fullReset: boolean): this; private renderText; /** * Serializes this step to a plain object for SWML output. * @returns A dictionary representation of this step. */ toDict(): Record; } /** A named context containing ordered steps, prompt configuration, and navigation rules. */ export declare class Context { /** The unique name identifying this context. */ name: string; private steps; private stepOrder; private _validContexts; private _validSteps; private _postPrompt; private _systemPrompt; private systemPromptSections; private _consolidate; private _fullReset; private _userPrompt; private _isolated; private promptText; private promptSections; private _initialStep; private _enterFillers; private _exitFillers; /** * Creates a new Context. * @param name - The unique context name. */ constructor(name: string); /** * Adds a new step to this context. * @param name - The unique step name within this context. * @param opts - Optional shorthand for task text, bullets, criteria, functions, and valid steps. * @returns The newly created Step for further configuration. */ addStep(name: string, opts?: { task?: string; bullets?: string[]; criteria?: string; functions?: string | string[]; validSteps?: string[]; }): Step; /** * Returns a step by name. * @param name - The step name to retrieve. * @returns The matching Step, or undefined if not found. */ getStep(name: string): Step | undefined; /** * Removes a step from this context by name. * @param name - The step name to remove. * @returns This context for chaining. */ removeStep(name: string): this; /** * Moves a step to a new position in the step order. * @param name - The step name to move. * @param position - The zero-based index to insert at. * @returns This context for chaining. */ moveStep(name: string, position: number): this; /** * Sets which contexts the AI may navigate to from this context. * @param contexts - List of allowed context names. * @returns This context for chaining. */ setValidContexts(contexts: string[]): this; /** * Sets which steps the AI may navigate to from this context. * @param steps - List of allowed step names. * @returns This context for chaining. */ setValidSteps(steps: string[]): this; /** * Sets the post-prompt text for this context. * @param postPrompt - The post-prompt string. * @returns This context for chaining. */ setPostPrompt(postPrompt: string): this; /** * Sets raw system prompt text, mutually exclusive with system POM sections. * @param systemPrompt - The system prompt string. * @returns This context for chaining. */ setSystemPrompt(systemPrompt: string): this; /** * Sets whether to consolidate conversation history when entering this context. * @param consolidate - Whether to consolidate. * @returns This context for chaining. */ setConsolidate(consolidate: boolean): this; /** * Sets whether to fully reset conversation history when entering this context. * @param fullReset - Whether to fully reset. * @returns This context for chaining. */ setFullReset(fullReset: boolean): this; /** * Sets the user prompt text for this context. * @param userPrompt - The user prompt string. * @returns This context for chaining. */ setUserPrompt(userPrompt: string): this; /** * Set which step the context starts on when entered. * * By default, a context starts on its first step (index 0). Use this to * skip a preamble step on re-entry via `change_context`. * * @param stepName - Name of the step to start on. Must exist in this context (validated by ContextBuilder.validate()). * @returns This context for chaining. */ setInitialStep(stepName: string): this; /** * Mark this context as isolated — entering it wipes conversation history. * * When `isolated=true` and the context is entered via change_context, the * runtime wipes the conversation array. The model starts fresh with only * the new context's systemPrompt + step instructions, with no memory of * prior turns. * * EXCEPTION — `reset` overrides the wipe: * If the context also has a `reset` configuration (via consolidate or * full_reset), the wipe is skipped in favor of the reset behavior. Use * reset with consolidate=true to summarize prior history into a single * message instead of dropping it entirely. * * Use cases: * - Switching to a sensitive billing flow that should not see prior * small-talk * - Handing off to a different agent persona * - Resetting after a long off-topic detour * * @param isolated - True to wipe conversation history on context entry * (subject to the reset exception above). * @returns This context for chaining. */ setIsolated(isolated: boolean): this; /** * Adds a POM section to the system prompt, mutually exclusive with raw system prompt text. * @param title - The section heading. * @param body - The section body text. * @returns This context for chaining. */ addSystemSection(title: string, body: string): this; /** * Adds a POM section with bullets to the system prompt, mutually exclusive with raw system prompt text. * @param title - The section heading. * @param bullets - The list of bullet point strings. * @returns This context for chaining. */ addSystemBullets(title: string, bullets: string[]): this; /** * Sets raw prompt text for this context, mutually exclusive with POM prompt sections. * @param prompt - The prompt string. * @returns This context for chaining. */ setPrompt(prompt: string): this; /** * Adds a POM section with a body to the context prompt, mutually exclusive with raw prompt text. * @param title - The section heading. * @param body - The section body text. * @returns This context for chaining. */ addSection(title: string, body: string): this; /** * Adds a POM section with bullets to the context prompt, mutually exclusive with raw prompt text. * @param title - The section heading. * @param bullets - The list of bullet point strings. * @returns This context for chaining. */ addBullets(title: string, bullets: string[]): this; /** * Sets filler phrases spoken when entering this context, keyed by language code. * @param fillers - Map of language codes to arrays of filler phrases. * @returns This context for chaining. */ setEnterFillers(fillers: Record): this; /** * Sets filler phrases spoken when exiting this context, keyed by language code. * @param fillers - Map of language codes to arrays of filler phrases. * @returns This context for chaining. */ setExitFillers(fillers: Record): this; /** * Adds enter filler phrases for a specific language. * @param languageCode - The language code (e.g., "en-US"). * @param fillers - Array of filler phrases. * @returns This context for chaining. */ addEnterFiller(languageCode: string, fillers: string[]): this; /** * Adds exit filler phrases for a specific language. * @param languageCode - The language code (e.g., "en-US"). * @param fillers - Array of filler phrases. * @returns This context for chaining. */ addExitFiller(languageCode: string, fillers: string[]): this; /** @internal */ getSteps(): Map; /** @internal */ getStepOrder(): readonly string[]; /** @internal */ getInitialStep(): string | null; /** @internal */ getValidContexts(): string[] | null; private renderPrompt; private renderSystemPrompt; private renderSections; /** * Serializes this context and all its steps to a plain object for SWML output. * @returns A dictionary representation of this context. */ toDict(): Record; } /** * Builder for multi-step, multi-context AI agent workflows. * * A ContextBuilder owns one or more Contexts; each Context owns an ordered * list of Steps. Only one context and one step is active at a time. Per * chat turn, the runtime injects the current step's instructions as a * system message, then asks the LLM for a response. * * ## Native tools auto-injected by the runtime * * When a step (or its enclosing context) declares `validSteps` or * `validContexts`, the runtime auto-injects two native tools so the model * can navigate the flow: * * - `next_step(step: enum)` — present when validSteps is set * - `change_context(context: enum)` — present when validContexts is set * * Their `enum` schemas are rewritten on every turn to match whatever * validSteps / validContexts apply to the current step. You do NOT need * to define these tools yourself; they appear automatically. * * A third native tool — `gather_submit` — is injected during gather_info * questioning (see Step.setGatherInfo / addGatherQuestion). * * These three names — `next_step`, `change_context`, `gather_submit` — * are reserved. ContextBuilder.validate() will reject any agent that * defines a SWAIG tool with one of these names. * * ## Function whitelisting (Step.setFunctions) * * Each step may declare a `functions` whitelist. The whitelist is applied * in-memory at the start of each LLM turn. CRITICALLY: if a step does NOT * declare a `functions` field, it INHERITS the previous step's active set. * See Step.setFunctions() for details and examples. * * ## Validation * * Call validate() (or toDict(), which calls it) to check that: * * - At least one context is defined * - A single context must be named "default" * - Every context has at least one step * - validSteps references resolve to real step names (or "next") * - validContexts references resolve to real context names * - gather_info questions are non-empty and have unique keys * - gather_info completion_action targets a reachable step * - No user-defined SWAIG tool collides with a reserved native name * * @example Two-step support bot * ```ts * import { AgentBase, ContextBuilder } from '@signalwire/sdk'; * * const agent = new AgentBase({ name: 'support', route: '/' }); * * const contexts = new ContextBuilder(); * const flow = contexts.addContext('default'); * * flow.addStep('greeting') * .setText("Greet the caller and ask if they're an existing customer.") * .setValidSteps(['existing', 'new']); * * flow.addStep('existing') * .setText('Ask for the account number and read it back to confirm.'); * * flow.addStep('new') * .setText('Collect name, email, and reason for calling.'); * * agent.defineContexts(contexts); * ``` * * @see {@link Context} * @see {@link Step} * @see {@link GatherInfo} * @see {@link AgentBase.defineContexts} */ export declare class ContextBuilder { private contexts; private contextOrder; private agent; /** * Attach an agent reference so validate() can check user tool names * against reserved native tool names. Called internally by * AgentBase.defineContexts(). * @internal */ attachAgent(agent: unknown): this; /** * Removes all contexts, returning the builder to its initial state. * Use this in a dynamic config callback when you need to rebuild * contexts from scratch for a specific request. * @returns This builder for chaining. */ reset(): this; /** * Adds a new named context to the builder. * * @param name - The unique context name. A single-context flow must name * its only context `"default"`. * @returns The newly created {@link Context} for further configuration. * @throws {Error} When the max-context limit is exceeded or a context with * the same name already exists. */ addContext(name: string): Context; /** * Returns a context by name. * @param name - The context name to retrieve. * @returns The matching Context, or undefined if not found. */ getContext(name: string): Context | undefined; /** * Validates that all contexts have steps, naming constraints are met, and cross-context references are valid. * @throws Error if validation fails. */ validate(): void; /** * Validates and serializes all contexts to a plain object for SWML output. * @returns A dictionary mapping context names to their serialized representations. */ toDict(): Record; } /** * Creates a standalone Context instance without a ContextBuilder. * @param name - The context name (defaults to "default"). * @returns A new Context instance. */ export declare function createSimpleContext(name?: string): Context;