basion-ai-sdk 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2158,6 +2158,7 @@ declare class Agent {
2158
2158
  private readonly heartbeatInterval;
2159
2159
  private readonly heartbeatFailureThreshold;
2160
2160
  private readonly onHeartbeatFailure?;
2161
+ private readonly enableHeartbeat;
2161
2162
  /** Set by BasionAgentApp.registerMe() to enable agent.call() */
2162
2163
  _app?: BasionAgentApp;
2163
2164
  private messageHandlers;
@@ -2193,6 +2194,7 @@ declare class Agent {
2193
2194
  attachmentClient?: AttachmentClient;
2194
2195
  renderClient?: RenderClient;
2195
2196
  errorMessageTemplate?: string;
2197
+ enableHeartbeat?: boolean;
2196
2198
  });
2197
2199
  /**
2198
2200
  * Create an AbortSignal for a conversation's current operation.
@@ -2607,6 +2609,67 @@ declare class PatientProfileClient {
2607
2609
  private json;
2608
2610
  }
2609
2611
 
2612
+ /**
2613
+ * Judge types for the Basion Agent SDK
2614
+ */
2615
+ interface JudgeMetricDef {
2616
+ key: string;
2617
+ label: string;
2618
+ cluster: string;
2619
+ description?: string;
2620
+ scoreMin?: number;
2621
+ scoreMax?: number;
2622
+ }
2623
+ interface RegisterJudgeOptions {
2624
+ name: string;
2625
+ representationName?: string;
2626
+ about: string;
2627
+ document: string;
2628
+ metrics: JudgeMetricDef[];
2629
+ defaultScope?: {
2630
+ autoAgentName?: string;
2631
+ autoAgentVersion?: string;
2632
+ autoWindowN?: number;
2633
+ autoSamplePct?: number;
2634
+ autoUserIds?: string[];
2635
+ };
2636
+ }
2637
+ interface JudgeRequestPayload {
2638
+ targetConversationId: string;
2639
+ baseVfsCommit: string | null;
2640
+ vfsFilesystemId: string | null;
2641
+ runId?: string;
2642
+ }
2643
+ interface JudgeVerdictMetric {
2644
+ key: string;
2645
+ score: number | null;
2646
+ reason: string;
2647
+ }
2648
+ interface JudgeVerdict {
2649
+ status: 'completed' | 'skipped_ineligible' | 'failed';
2650
+ metrics: JudgeVerdictMetric[];
2651
+ summary?: string;
2652
+ usage?: {
2653
+ promptTokens?: number;
2654
+ completionTokens?: number;
2655
+ costUsd?: number;
2656
+ };
2657
+ }
2658
+ interface JudgeContext {
2659
+ app: BasionAgentApp;
2660
+ targetConversationId: string;
2661
+ userId: string;
2662
+ runId?: string;
2663
+ baseVfsCommit: string | null;
2664
+ vfsFilesystemId: string | null;
2665
+ conversation: {
2666
+ userView: unknown[];
2667
+ agentTrace: unknown[];
2668
+ };
2669
+ metrics: JudgeMetricDef[];
2670
+ }
2671
+ type JudgeHandler = (ctx: JudgeContext) => Promise<JudgeVerdict>;
2672
+
2610
2673
  /**
2611
2674
  * Loki log handler for remote log collection.
2612
2675
  *
@@ -2792,8 +2855,31 @@ declare class BasionAgentApp {
2792
2855
  private reinitializeAgents;
2793
2856
  /**
2794
2857
  * Register an agent with the backend.
2858
+ *
2859
+ * @param options - Agent registration options
2860
+ * @param internal - Internal options for special agents (judges, orchestrators).
2861
+ * When `skipInventory` is true, the agent is NOT registered in the AI Inventory
2862
+ * catalog; an AgentRegistrationData is synthesized from the options instead.
2863
+ * When `enableHeartbeat` is false, the heartbeat manager will not be started.
2864
+ */
2865
+ registerMe(options: RegisterOptions, internal?: {
2866
+ skipInventory?: boolean;
2867
+ enableHeartbeat?: boolean;
2868
+ }): Promise<Agent>;
2869
+ /**
2870
+ * Register a judge agent.
2871
+ *
2872
+ * Judges are NOT registered in the AI Inventory agent catalog — the gateway
2873
+ * routes on the API key alone. This method:
2874
+ * 1. Connects to the gateway WITHOUT an inventory row (`skipInventory:true`).
2875
+ * 2. Does NOT start a heartbeat (`enableHeartbeat:false`) — judges are
2876
+ * invoked on-demand, not long-running listeners.
2877
+ * 3. Self-registers into the JUDGES table via POST /s/judge/judges/sync.
2878
+ *
2879
+ * @param opts - Judge registration options (name, about, document, metrics, defaultScope)
2880
+ * @returns The connected Agent instance
2795
2881
  */
2796
- registerMe(options: RegisterOptions): Promise<Agent>;
2882
+ registerJudge(opts: RegisterJudgeOptions): Promise<Agent>;
2797
2883
  /**
2798
2884
  * Get the Loki log handler for remote logging.
2799
2885
  * Returns undefined if remote logging is not enabled.
@@ -2861,4 +2947,4 @@ declare class BasionAgentApp {
2861
2947
  private doShutdown;
2862
2948
  }
2863
2949
 
2864
- export { type DraftDecision as $, type AttachmentInfo as A, BasionAgentApp as B, type CheckboxConfig as C, type DatePickerConfig as D, type BinaryResult as E, type Field as F, GatewayClient as G, type HiddenConfig as H, type InferFields as I, type BraintrustConfig as J, type BundleRenderOptions as K, type Capability as L, type MultiSelectConfig as M, type NumberConfig as N, type OptionDef as O, type CapabilityItem as P, type ChunkResult as Q, type ConnectionState as R, StructuralStreamer as S, type TextConfig as T, type ConnectionStateListener as U, type ValidationResult as V, Conversation as W, ConversationClient as X, type ConversationData as Y, ConversationMessage as Z, DEFAULT_RECONNECTION_OPTIONS as _, type CheckboxGroupConfig as a, type ValidateCodeResult as a$, type DraftResult as a0, type Edge as a1, type Entity as a2, type ExpandValueSetResult as a3, type GenuiComponent as a4, type GenuiComponentDict as a5, type GetMessagesOptions as a6, type GroupedMatch as a7, type GroupedMatchEntry as a8, type HeartbeatFailureCallback as a9, type NeedWithMatches as aA, type PathStep as aB, PatientProfileClient as aC, type PdfOptions as aD, type ProduceAck as aE, type Prompt as aF, type QueryOptions as aG, type ReconnectionOptions as aH, type RegisterOptions as aI, type RelatedPage as aJ, type RenderBufferResult as aK, RenderClient as aL, type RenderOptions as aM, type RenderResult as aN, type SearchMatch as aO, type SearchOptions as aP, type SearchResult as aQ, SenderFilter as aR, type SenderFilterOptions as aS, type SentryConfig as aT, type SentryLoggerProxy as aU, type SimilarDisease as aV, type StreamOptions as aW, Streamer as aX, type StreamerOptions as aY, Tools as aZ, type UserSummary as a_, HeartbeatManager as aa, type HeartbeatOptions as ab, type IngestResponse as ac, type KafkaHeaders as ad, type KafkaMessage as ae, KnowledgeGraphTool as af, type LogEntry as ag, type LogLevel as ah, type LokiHandlerOptions as ai, LokiLogHandler as aj, type LookupCodeResult as ak, MatchingClient as al, MatchingTool as am, Memory as an, MemoryClient as ao, type MemoryIngestOptions as ap, type MemoryMessage as aq, type MemorySearchOptions as ar, type MemorySearchResult as as, MemoryV2 as at, MemoryV2Client as au, type MemoryV2IngestPayload as av, type MemoryV2SearchPayload as aw, type MessageHandler as ax, type Need as ay, type NeedItem as az, type FileFieldConfig as b, createAttachmentInfo as b0, getSentryLogger as b1, isBraintrustEnabled as b2, isSentryEnabled as b3, reportPath as b4, type SelectConfig as c, type SliderConfig as d, type SwitchConfig as e, type ConfirmationConfig as f, Message as g, type ConfirmationResult as h, type StepDef as i, type ToDict as j, type AccordionItemDef as k, type AccordionConfig as l, type AccordionComponentDict as m, type CardVariant as n, type CardSectionDef as o, type CardButtonDef as p, type CardConfig as q, type CardComponentDict as r, Agent as s, type AgentInfo as t, AgentInventoryTool as u, type AgentRegistrationData as v, type AgentSqlResult as w, type AppOptions as x, type ApproveDraftResult as y, AttachmentClient as z };
2950
+ export { ConversationMessage as $, type AttachmentInfo as A, BasionAgentApp as B, type CheckboxConfig as C, type DatePickerConfig as D, AttachmentClient as E, type Field as F, GatewayClient as G, type HiddenConfig as H, type InferFields as I, type JudgeHandler as J, type BinaryResult as K, type BraintrustConfig as L, type MultiSelectConfig as M, type NumberConfig as N, type OptionDef as O, type BundleRenderOptions as P, type Capability as Q, type CapabilityItem as R, StructuralStreamer as S, type TextConfig as T, type ChunkResult as U, type ValidationResult as V, type ConnectionState as W, type ConnectionStateListener as X, Conversation as Y, ConversationClient as Z, type ConversationData as _, type CheckboxGroupConfig as a, type SentryLoggerProxy as a$, DEFAULT_RECONNECTION_OPTIONS as a0, type DraftDecision as a1, type DraftResult as a2, type Edge as a3, type Entity as a4, type ExpandValueSetResult as a5, type GenuiComponent as a6, type GenuiComponentDict as a7, type GetMessagesOptions as a8, type GroupedMatch as a9, MemoryV2 as aA, MemoryV2Client as aB, type MemoryV2IngestPayload as aC, type MemoryV2SearchPayload as aD, type Need as aE, type NeedItem as aF, type NeedWithMatches as aG, type PathStep as aH, PatientProfileClient as aI, type PdfOptions as aJ, type ProduceAck as aK, type Prompt as aL, type QueryOptions as aM, type ReconnectionOptions as aN, type RegisterJudgeOptions as aO, type RegisterOptions as aP, type RelatedPage as aQ, type RenderBufferResult as aR, RenderClient as aS, type RenderOptions as aT, type RenderResult as aU, type SearchMatch as aV, type SearchOptions as aW, type SearchResult as aX, SenderFilter as aY, type SenderFilterOptions as aZ, type SentryConfig as a_, type GroupedMatchEntry as aa, type HeartbeatFailureCallback as ab, HeartbeatManager as ac, type HeartbeatOptions as ad, type IngestResponse as ae, type JudgeContext as af, type JudgeMetricDef as ag, type JudgeRequestPayload as ah, type JudgeVerdict as ai, type JudgeVerdictMetric as aj, type KafkaHeaders as ak, type KafkaMessage as al, KnowledgeGraphTool as am, type LogEntry as an, type LogLevel as ao, type LokiHandlerOptions as ap, LokiLogHandler as aq, type LookupCodeResult as ar, MatchingClient as as, MatchingTool as at, Memory as au, MemoryClient as av, type MemoryIngestOptions as aw, type MemoryMessage as ax, type MemorySearchOptions as ay, type MemorySearchResult as az, type FileFieldConfig as b, type SimilarDisease as b0, type StreamOptions as b1, Streamer as b2, type StreamerOptions as b3, Tools as b4, type UserSummary as b5, type ValidateCodeResult as b6, createAttachmentInfo as b7, getSentryLogger as b8, isBraintrustEnabled as b9, isSentryEnabled as ba, reportPath as bb, type SelectConfig as c, type SliderConfig as d, type SwitchConfig as e, type ConfirmationConfig as f, Message as g, type ConfirmationResult as h, type StepDef as i, type ToDict as j, type AccordionItemDef as k, type AccordionConfig as l, type AccordionComponentDict as m, type CardVariant as n, type CardSectionDef as o, type CardButtonDef as p, type CardConfig as q, type CardComponentDict as r, Agent as s, type MessageHandler as t, type AgentInfo as u, AgentInventoryTool as v, type AgentRegistrationData as w, type AgentSqlResult as x, type AppOptions as y, type ApproveDraftResult as z };
@@ -1,4 +1,4 @@
1
- import { B as BasionAgentApp } from '../app-mRFPU6oe.js';
1
+ import { B as BasionAgentApp } from '../app-ChZ3-kr7.js';
2
2
  import 'zod';
3
3
  import '../vfs-client-CsHAFj1R.js';
4
4
 
@@ -1,4 +1,4 @@
1
- import { B as BasionAgentApp } from '../app-mRFPU6oe.js';
1
+ import { B as BasionAgentApp } from '../app-ChZ3-kr7.js';
2
2
  import 'zod';
3
3
  import '../vfs-client-CsHAFj1R.js';
4
4
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exceptions.ts","../../src/types.ts","../../src/agent.ts","../../src/agent-state-client.ts","../../src/extensions/vercel-ai.ts"],"names":[],"mappings":"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","file":"vercel-ai.js","sourcesContent":["/**\n * Basion Agent SDK - Custom Errors\n * Port of Python basion_agent/exceptions.py\n */\n\n/**\n * Base error class for Basion Agent SDK\n */\nexport class BasionAgentError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'BasionAgentError'\n Error.captureStackTrace?.(this, this.constructor)\n }\n}\n\n/**\n * Error during agent registration with AI Inventory\n */\nexport class RegistrationError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'RegistrationError'\n }\n}\n\n/**\n * Error during Kafka operations (produce/consume)\n */\nexport class KafkaError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'KafkaError'\n }\n}\n\n/**\n * Error in SDK configuration\n */\nexport class ConfigurationError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Error from API calls to backend services\n */\nexport class APIException extends BasionAgentError {\n statusCode?: number\n\n constructor(message: string, statusCode?: number) {\n super(message)\n this.name = 'APIException'\n this.statusCode = statusCode\n }\n}\n\n/**\n * Error during heartbeat operations\n */\nexport class HeartbeatError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'HeartbeatError'\n }\n}\n\n/**\n * Error during gRPC connection/communication\n */\nexport class GatewayError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'GatewayError'\n }\n}\n\n/**\n * Error during reconnection attempts\n */\nexport class ReconnectionError extends BasionAgentError {\n /** Number of attempts made before giving up */\n attempts: number\n /** The last error that occurred during reconnection */\n lastError?: Error\n\n constructor(message: string, attempts: number, lastError?: Error) {\n super(message)\n this.name = 'ReconnectionError'\n this.attempts = attempts\n this.lastError = lastError\n }\n}\n","/**\n * Basion Agent SDK - Shared Types\n * Port of Python basion_agent types\n */\n\nimport { z } from 'zod'\n\n// ============================================================================\n// Kafka Message Types\n// ============================================================================\n\nexport interface KafkaMessage {\n topic: string\n partition: number\n offset: number\n key: string\n headers: Record<string, string>\n body: Record<string, unknown>\n timestamp: number\n}\n\nexport interface KafkaHeaders {\n conversationId?: string\n userId?: string\n from_?: string\n to_?: string\n messageMetadata?: string\n messageSchema?: string\n /** @deprecated Use attachments (plural) instead. Kept for backward compatibility. */\n attachment?: string\n /** JSON-stringified array of AttachmentInfo objects */\n attachments?: string\n /** UUID of the user message in conversation-store (for cross-system mapping) */\n userMessageId?: string\n /** Pre-generated UUID for the assistant response in conversation-store (for cross-system mapping) */\n assistantMessageId?: string\n [key: string]: string | undefined\n}\n\n// ============================================================================\n// Agent Registration Types\n// ============================================================================\n\nexport interface AgentRegistrationData {\n id: string\n name: string\n about: string\n document: string\n representationName?: string\n baseUrl?: string\n metadata?: Record<string, unknown>\n relatedPages?: RelatedPage[]\n}\n\nexport interface RelatedPage {\n name: string\n endpoint: string\n}\n\nexport interface Prompt {\n label: string\n prompt: string\n}\n\nexport interface RegisterOptions {\n name: string\n about: string\n document: string\n representationName?: string\n baseUrl?: string\n metadata?: Record<string, unknown>\n relatedPages?: RelatedPage[]\n categoryNames?: string[]\n prompts?: Prompt[]\n detailedDescription?: string\n version?: string\n lifecycleStage?: 'dev' | 'test' | 'private-preview' | 'public-preview' | 'public'\n welcomeMessage?: string\n waitlist?: boolean\n forceUpdate?: boolean\n}\n\n// ============================================================================\n// Message Types\n// ============================================================================\n\n// Forward reference - actual Message class is imported where needed\n// Using 'any' here to avoid circular dependency, but consumers should use Message type\nexport interface MessageHandler {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (message: any, sender: string): void | Promise<void>\n}\n\nexport interface SenderFilterOptions {\n senders?: string[]\n}\n\n// ============================================================================\n// Streamer Types\n// ============================================================================\n\nexport interface StreamOptions {\n persist?: boolean\n eventType?: string\n}\n\nexport interface StreamerOptions {\n awaiting?: boolean\n}\n\n// ============================================================================\n// Attachment Types\n// ============================================================================\n\nexport const AttachmentInfoSchema = z.object({\n url: z.string(),\n filename: z.string(),\n contentType: z.string(),\n size: z.number().optional(),\n fileType: z.string().optional(),\n objectKey: z.string().optional(),\n})\n\nexport type AttachmentInfo = z.infer<typeof AttachmentInfoSchema>\n\nexport function isImageAttachment(attachment: AttachmentInfo): boolean {\n return attachment.contentType?.startsWith('image/') || false\n}\n\nexport function isPdfAttachment(attachment: AttachmentInfo): boolean {\n return attachment.contentType === 'application/pdf'\n}\n\n// ============================================================================\n// Conversation Message Types\n// ============================================================================\n\nexport interface ConversationMessageData {\n id: string\n conversationId: string\n role: 'user' | 'assistant' | 'system'\n content: string\n from?: string\n to?: string\n createdAt: string\n metadata?: Record<string, unknown>\n}\n\n// ============================================================================\n// Gateway Client Types\n// ============================================================================\n\nexport interface ProduceAck {\n topic: string\n partition: number\n offset: number\n correlationId?: string\n}\n\n// ============================================================================\n// Reconnection Types\n// ============================================================================\n\n/**\n * Configuration options for automatic reconnection behavior.\n */\nexport interface ReconnectionOptions {\n /** Maximum number of reconnection attempts before giving up. Default: 10 */\n maxRetries?: number\n /** Initial delay in milliseconds before first retry. Default: 1000 */\n initialDelayMs?: number\n /** Maximum delay in milliseconds between retries. Default: 30000 */\n maxDelayMs?: number\n /** Multiplier for exponential backoff. Default: 2 */\n backoffMultiplier?: number\n /** Whether to automatically reconnect on disconnect. Default: true */\n autoReconnect?: boolean\n}\n\n/**\n * Connection state for the gateway client.\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'\n\n/**\n * Listener callback for connection state changes.\n */\nexport interface ConnectionStateListener {\n (state: ConnectionState, error?: Error): void\n}\n\n/**\n * Default reconnection options.\n */\nexport const DEFAULT_RECONNECTION_OPTIONS: Required<ReconnectionOptions> = {\n maxRetries: 10,\n initialDelayMs: 1000,\n maxDelayMs: 30000,\n backoffMultiplier: 2,\n autoReconnect: true,\n}\n\n// ============================================================================\n// App Configuration Types\n// ============================================================================\n\nexport interface AppOptions {\n gatewayUrl: string\n apiKey: string\n heartbeatInterval?: number\n maxConcurrentTasks?: number\n errorMessageTemplate?: string\n secure?: boolean\n /** Reconnection options for the gateway connection */\n reconnection?: ReconnectionOptions\n /** Enable remote logging to Loki via the gateway (default: false) */\n enableRemoteLogging?: boolean\n /** Minimum log level for remote logging (default: 'info') */\n remoteLogLevel?: 'debug' | 'info' | 'warn' | 'error'\n /** Number of logs to batch before sending (default: 100) */\n remoteLogBatchSize?: number\n /** Maximum seconds between log flushes (default: 5.0) */\n remoteLogFlushInterval?: number\n /** Sentry configuration (optional — omit or leave dsn empty to disable) */\n sentry?: import('./sentry.js').SentryConfig\n /** Braintrust configuration (optional — omit or leave apiKey empty to disable) */\n braintrust?: import('./braintrust.js').BraintrustConfig\n}\n\n// ============================================================================\n// HTTP Response Types\n// ============================================================================\n\nexport interface ConversationData {\n id: string\n userId: string\n agentId?: string\n awaitingRoute?: string\n pendingResponseSchema?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n metadata?: Record<string, unknown>\n}\n\nexport interface GetMessagesOptions {\n role?: string\n from?: string\n to?: string\n limit?: number\n offset?: number\n}\n\n// ============================================================================\n// Memory Types\n// ============================================================================\n\n/**\n * A message stored in ai-memory.\n * Matches the ai-memory API MessageStored schema.\n */\nexport interface MemoryMessage {\n id?: string\n role: 'user' | 'assistant' | 'system'\n content: string\n tenantId?: string\n userId?: string\n conversationId?: string\n metadata?: Record<string, unknown>\n timestamp?: string\n sequenceNumber?: number\n}\n\n/**\n * Options for memory search operations.\n */\nexport interface MemorySearchOptions {\n /** Maximum number of results (1-100, default: 10) */\n limit?: number\n /** Minimum similarity threshold (0-100, default: 70) */\n minSimilarity?: number\n /** Number of surrounding context messages to include (0-20, default: 0) */\n contextMessages?: number\n /** Tenant ID for multi-tenant filtering */\n tenantId?: string\n /** Tenant matching mode: 'exact' or 'hierarchy' */\n tenantMatch?: 'exact' | 'hierarchy'\n}\n\n/**\n * A memory search result with similarity score and optional context.\n */\nexport interface MemorySearchResult {\n message: MemoryMessage\n score: number\n context: MemoryMessage[]\n}\n\n/**\n * User conversation summary from ai-memory.\n */\nexport interface UserSummary {\n text: string\n lastUpdated: string\n messageCount: number\n version: number\n}\n","/**\n * Basion Agent SDK - Agent\n * Port of Python basion_agent/agent.py\n */\n\nimport { randomUUID } from 'crypto'\nimport { AsyncLocalStorage } from 'node:async_hooks'\n\nimport type { BasionAgentApp } from './app.js'\nimport { DedupLRU } from './dedup-lru.js'\nimport type { AttachmentClient } from './attachment-client.js'\nimport { logBraintrustError,withBraintrustSpan } from './braintrust.js'\nimport type { ConversationClient } from './conversation-client.js'\nimport { sendDlqEnvelope } from './dlq.js'\nimport { ConfigurationError } from './exceptions.js'\nimport type { GatewayClient } from './gateway-client.js'\nimport { type HeartbeatFailureCallback,HeartbeatManager } from './heartbeat.js'\nimport type { MemoryClient } from './memory-client.js'\nimport type { MemoryV2Client } from './memory-v2-client.js'\nimport { Message } from './message.js'\nimport type { RenderClient } from './render-client.js'\nimport { captureAgentError, setConversationId,withAgentSpan } from './sentry.js'\nimport { Streamer } from './streamer.js'\nimport { Tools } from './tools/container.js'\nimport type {\n AgentRegistrationData,\n KafkaMessage,\n MessageHandler,\n SenderFilterOptions,\n StreamerOptions,\n} from './types.js'\n\n/**\n * AsyncLocalStorage context set automatically by the agent message handler.\n * VercelAIMessageStore reads from this to embed cross-system IDs without\n * requiring agent developers to pass them explicitly.\n *\n * Stored on globalThis so that the same instance is shared across all bundle\n * chunks (tsup can include this module in multiple output files; globalThis\n * ensures they all reference the same AsyncLocalStorage object).\n *\n * @internal — consumed by VercelAIMessageStore; not part of the public API.\n */\nconst _msgCtxKey = Symbol.for('basion-ai-sdk.messageContext')\nif (!(globalThis as Record<symbol, unknown>)[_msgCtxKey]) {\n (globalThis as Record<symbol, unknown>)[_msgCtxKey] = new AsyncLocalStorage<{\n userMessageId?: string\n responseMessageId?: string\n }>()\n}\nexport const _messageContext = (globalThis as Record<symbol, unknown>)[_msgCtxKey] as AsyncLocalStorage<{\n userMessageId?: string\n responseMessageId?: string\n}>\n\n/**\n * Filter for matching message senders.\n */\nexport class SenderFilter {\n private readonly _include: Set<string> = new Set()\n private readonly _exclude: Set<string> = new Set()\n readonly matchAll: boolean\n\n constructor(senders?: string[]) {\n if (!senders || senders.length === 0) {\n this.matchAll = true\n } else {\n this.matchAll = false\n for (const sender of senders) {\n if (sender.startsWith('~')) {\n this._exclude.add(sender.slice(1))\n } else {\n this._include.add(sender)\n }\n }\n }\n }\n\n /**\n * Get included senders as an array (for compatibility with Python tests).\n */\n get include(): string[] {\n return Array.from(this._include)\n }\n\n /**\n * Get excluded senders as an array (for compatibility with Python tests).\n */\n get exclude(): string[] {\n return Array.from(this._exclude)\n }\n\n /**\n * Check if sender matches this filter.\n */\n matches(sender: string): boolean {\n // If excluded, reject\n if (this._exclude.has(sender)) {\n return false\n }\n // If match all or explicitly included\n if (this.matchAll || this._include.size === 0) {\n return true\n }\n return this._include.has(sender)\n }\n}\n\n// Message handlers stored as tuples: [SenderFilter, MessageHandler]\ntype RegisteredHandler = [SenderFilter, MessageHandler]\n\n/**\n * Core agent class that handles messaging via the Agent Gateway.\n */\nexport class Agent {\n readonly name: string\n readonly agentData: AgentRegistrationData\n sendErrorResponses: boolean = true\n errorMessageTemplate: string\n\n private readonly gatewayClient: GatewayClient\n private readonly conversationClient?: ConversationClient\n private readonly memoryClient?: MemoryClient\n private readonly memoryV2Client?: MemoryV2Client\n private readonly attachmentClient?: AttachmentClient\n private readonly renderClient?: RenderClient\n private readonly heartbeatInterval: number\n private readonly heartbeatFailureThreshold: number\n private readonly onHeartbeatFailure?: HeartbeatFailureCallback\n\n /** Set by BasionAgentApp.registerMe() to enable agent.call() */\n _app?: BasionAgentApp\n\n private messageHandlers: RegisteredHandler[] = []\n private heartbeatManager?: HeartbeatManager\n private initialized: boolean = false\n private running: boolean = false\n private _tools?: Tools\n private readonly dedup: DedupLRU = new DedupLRU(\n Number(process.env.BASION_SDK_DEDUP_LRU_SIZE) || 100_000,\n )\n\n /**\n * Live AbortControllers for in-flight turns, keyed by conversationId.\n * `assistantMessageId` scopes the controller to a specific turn so a cancel\n * for a previous turn cannot abort the current one.\n */\n private _cancelControllers = new Map<\n string,\n { controller: AbortController; assistantMessageId?: string }\n >()\n /**\n * Pre-cancel intents that arrived before the handler registered its controller.\n * Keyed by `assistantMessageId` (never by conversationId alone) so a stray\n * cancel from an earlier turn cannot poison the next turn. Entries self-expire\n * after PENDING_CANCEL_TTL_MS as defense-in-depth.\n */\n private _pendingCancels = new Map<string, { expiresAt: number }>()\n private static readonly PENDING_CANCEL_TTL_MS = 5000\n\n constructor(options: {\n name: string\n gatewayClient: GatewayClient\n agentData: AgentRegistrationData\n heartbeatInterval?: number\n heartbeatFailureThreshold?: number\n onHeartbeatFailure?: HeartbeatFailureCallback\n conversationClient?: ConversationClient\n memoryClient?: MemoryClient\n memoryV2Client?: MemoryV2Client\n attachmentClient?: AttachmentClient\n renderClient?: RenderClient\n errorMessageTemplate?: string\n }) {\n this.name = options.name\n this.gatewayClient = options.gatewayClient\n this.agentData = options.agentData\n this.heartbeatInterval = options.heartbeatInterval ?? 60\n this.heartbeatFailureThreshold = options.heartbeatFailureThreshold ?? 3\n this.onHeartbeatFailure = options.onHeartbeatFailure\n this.conversationClient = options.conversationClient\n this.memoryClient = options.memoryClient\n this.memoryV2Client = options.memoryV2Client\n this.attachmentClient = options.attachmentClient\n this.renderClient = options.renderClient\n this.errorMessageTemplate = options.errorMessageTemplate ??\n 'I encountered an error while processing your message. Please try again or contact support if the issue persists.'\n }\n\n /**\n * Create an AbortSignal for a conversation's current operation.\n * Call this before starting work (e.g. streamText) and pass the\n * returned signal so the operation can be cancelled externally.\n *\n * Pass `assistantMessageId` (typically `message.responseMessageId`) so\n * a cancel message targeting THIS turn can be matched precisely; a\n * cancel for any other turn will be ignored.\n *\n * If a cancel request arrived before this call AND carried a matching\n * `assistantMessageId`, the returned signal will already be aborted.\n */\n createAbortSignal(conversationId: string, assistantMessageId?: string): AbortSignal {\n // Replace any existing controller (defensive cleanup)\n const existing = this._cancelControllers.get(conversationId)\n if (existing) {\n existing.controller.abort()\n }\n\n const controller = new AbortController()\n this._cancelControllers.set(conversationId, { controller, assistantMessageId })\n\n this.purgeExpiredPendingCancels()\n\n // A pre-cancel only pre-aborts the new signal if it names the same turn.\n // Without this scoping, a stale cancel from a prior turn leaks across turns.\n if (assistantMessageId && this._pendingCancels.has(assistantMessageId)) {\n this._pendingCancels.delete(assistantMessageId)\n controller.abort()\n }\n\n return controller.signal\n }\n\n /**\n * Cancel the running operation for a conversation.\n * Called by the message interceptor when a cancel message arrives.\n *\n * If `assistantMessageId` is provided and does not match the live\n * controller's, the cancel is dropped as stale (prevents a late cancel\n * from a previous turn from aborting the current one).\n */\n cancelConversation(conversationId: string, assistantMessageId?: string): void {\n const entry = this._cancelControllers.get(conversationId)\n\n if (entry) {\n // Both IDs present and mismatched → stale cancel for a prior turn. Drop it.\n if (\n assistantMessageId &&\n entry.assistantMessageId &&\n assistantMessageId !== entry.assistantMessageId\n ) {\n return\n }\n entry.controller.abort()\n this._cancelControllers.delete(conversationId)\n return\n }\n\n // No live controller. Only remember cancels that name a specific turn;\n // blind conversation-scoped intent is what caused bug #1 and is removed.\n if (assistantMessageId) {\n this._pendingCancels.set(assistantMessageId, {\n expiresAt: Date.now() + Agent.PENDING_CANCEL_TTL_MS,\n })\n this.purgeExpiredPendingCancels()\n }\n }\n\n /**\n * Clean up the abort controller after the handler completes.\n * Also purges any matching pre-cancel intent so a stale entry cannot\n * outlive the turn it was meant for.\n */\n clearAbortController(conversationId: string, assistantMessageId?: string): void {\n this._cancelControllers.delete(conversationId)\n if (assistantMessageId) {\n this._pendingCancels.delete(assistantMessageId)\n }\n }\n\n private purgeExpiredPendingCancels(): void {\n const now = Date.now()\n for (const [id, { expiresAt }] of this._pendingCancels) {\n if (expiresAt <= now) {\n this._pendingCancels.delete(id)\n }\n }\n }\n\n /**\n * Access to tools (knowledge graph, etc.).\n *\n * Example:\n * const kg = agent.tools.knowledgeGraph\n * const diseases = await kg.searchDiseases({ name: \"Huntington\" })\n */\n get tools(): Tools {\n if (!this._tools) {\n this._tools = new Tools(this.gatewayClient)\n }\n return this._tools\n }\n\n /**\n * Register a message handler.\n * \n * Usage:\n * agent.onMessage(async (message, sender) => { ... })\n * agent.onMessage(handler, { senders: ['user'] })\n */\n onMessage(handler: MessageHandler): void\n onMessage(handler: MessageHandler, options: SenderFilterOptions): void\n onMessage(\n handler: MessageHandler,\n options?: SenderFilterOptions\n ): void {\n const filter = new SenderFilter(options?.senders)\n this.messageHandlers.push([filter, handler])\n }\n\n /**\n * Initialize agent after gateway connection is established.\n * Can be called multiple times (e.g., after reconnection) to re-register handlers.\n */\n initializeWithGateway(): void {\n // Register message handler with gateway\n // This is idempotent - calling it again just updates the handler\n const topic = `${this.name}.inbox`\n this.gatewayClient.registerHandler(topic, (msg) => {\n this.handleKafkaMessage(msg)\n })\n\n // Start heartbeat if not already created\n if (!this.heartbeatManager) {\n this.heartbeatManager = new HeartbeatManager(\n this.gatewayClient,\n this.name,\n this.heartbeatInterval,\n {\n failureThreshold: this.heartbeatFailureThreshold,\n onFailureThresholdExceeded: this.handleHeartbeatFailure.bind(this),\n }\n )\n }\n\n // Start or resume heartbeat (in case it was stopped during disconnect)\n if (!this.heartbeatManager.isRunning()) {\n this.heartbeatManager.start()\n } else if (this.heartbeatManager.isPaused()) {\n this.heartbeatManager.resume()\n }\n\n this.initialized = true\n }\n\n /**\n * Handle heartbeat failure threshold exceeded.\n * This can indicate a stale connection that needs reconnection.\n */\n private handleHeartbeatFailure(consecutiveFailures: number, lastError?: Error): void {\n console.warn(\n `Agent '${this.name}' heartbeat failure threshold exceeded (${consecutiveFailures} failures)`\n )\n\n // Call custom callback if provided\n if (this.onHeartbeatFailure) {\n this.onHeartbeatFailure(consecutiveFailures, lastError)\n }\n\n // If gateway is not connected, try to trigger reconnection\n if (!this.gatewayClient.isConnected() && !this.gatewayClient.isReconnectingNow()) {\n console.warn('Gateway not connected, attempting force reconnect...')\n this.gatewayClient.forceReconnect().catch((err) => {\n console.error('Force reconnect from heartbeat failure failed:', err)\n })\n }\n }\n\n /**\n * Pause heartbeat sending (e.g., during reconnection).\n */\n pauseHeartbeat(): void {\n this.heartbeatManager?.pause()\n }\n\n /**\n * Resume heartbeat sending.\n */\n resumeHeartbeat(): void {\n this.heartbeatManager?.resume()\n }\n\n /**\n * Get sender from message headers.\n */\n private getSender(headers: Record<string, string>): string {\n return headers.from_ ?? headers.from ?? 'user'\n }\n\n /**\n * Find first handler that matches the sender.\n */\n private findMatchingHandler(sender: string): MessageHandler | undefined {\n for (const [filter, handler] of this.messageHandlers) {\n if (filter.matches(sender)) {\n return handler\n }\n }\n return undefined\n }\n\n /**\n * Handle incoming Kafka message.\n *\n * Consults the dedup LRU before dispatching to suppress duplicate deliveries.\n * The dedup key is `userMessageId` when present; otherwise falls back to\n * `${topic}:${partition}:${offset}` so at-most-once semantics hold even for\n * messages that pre-date the userMessageId header.\n *\n * Note: ConsumeAck is sent by GatewayClient BEFORE this method is called,\n * so dedup suppression here only prevents double handler dispatch — it does\n * not affect ack delivery.\n */\n private handleKafkaMessage(kafkaMsg: KafkaMessage): void {\n const dedupKey = kafkaMsg.headers?.userMessageId\n ?? `${kafkaMsg.topic}:${kafkaMsg.partition}:${kafkaMsg.offset}`\n if (this.dedup.checkAndAdd(dedupKey)) {\n console.debug(`[basion-sdk] dedup hit: ${dedupKey} — suppressing handler dispatch`)\n return\n }\n this.handleMessageAsync(kafkaMsg).catch((error) => {\n console.error('Error in message handler:', error)\n })\n }\n\n /**\n * Async message handling with error recovery.\n */\n private async handleMessageAsync(kafkaMsg: KafkaMessage): Promise<void> {\n let message: Message | undefined\n\n try {\n // Create message from Kafka data\n message = Message.fromKafkaMessage(\n kafkaMsg.body,\n kafkaMsg.headers,\n {\n conversationClient: this.conversationClient,\n memoryClient: this.memoryClient,\n memoryV2Client: this.memoryV2Client,\n attachmentClient: this.attachmentClient,\n agentName: this.name,\n gatewayClient: this.gatewayClient,\n }\n )\n\n // Find matching handler\n const sender = this.getSender(kafkaMsg.headers)\n const handler = this.findMatchingHandler(sender)\n\n if (!handler) {\n console.warn(`No handler matched for sender: ${sender}`)\n return\n }\n\n // Extract trace ID from Kafka headers for cross-agent Braintrust correlation\n const traceId = kafkaMsg.headers.traceId\n\n // Wrap handler execution in Sentry invoke_agent span\n // setConversationId must be called INSIDE the span so the conversation ID\n // is applied within the span's scope (see Sentry AI Agents docs)\n await withAgentSpan(\n `invoke_agent ${this.name}`,\n this.name,\n 'gen_ai.invoke_agent',\n async () => {\n setConversationId(message!.conversationId)\n\n // Also wrap in Braintrust span (nested under conversation trace)\n return withBraintrustSpan(\n `invoke_agent ${this.name}`,\n () => _messageContext.run(\n {\n userMessageId: message!.userMessageId,\n responseMessageId: message!.responseMessageId,\n },\n () => handler(message!, sender),\n ),\n {\n conversationId: message!.conversationId,\n agentName: this.name,\n traceId,\n type: 'llm',\n input: [{ role: 'user', content: message!.content }],\n metadata: {\n conversationId: message!.conversationId,\n agentName: this.name,\n sender,\n },\n },\n )\n },\n {\n 'gen_ai.operation.name': 'invoke_agent',\n 'gen_ai.conversation.id': message.conversationId,\n }\n )\n\n } catch (error) {\n console.error('Error handling message:', error)\n\n // Capture error in Sentry with agent context\n captureAgentError(error, this.name, {\n conversationId: message?.conversationId ?? 'unknown',\n })\n\n // Log error in Braintrust\n logBraintrustError(error, {\n conversationId: message?.conversationId ?? 'unknown',\n agentName: this.name,\n })\n\n // DLQ-capture: without this, a thrown handler is silent —\n // the gateway has already auto-committed the offset, so the\n // user message gets no reply and there's no record on the\n // platform side. The DLQ envelope flows through gatewayClient\n // to the dlq.events Kafka topic via the agent's existing\n // gRPC stream.\n try {\n sendDlqEnvelope(this.gatewayClient, {\n service: `agent-${this.name}`,\n source: { kind: 'kafka', topic: kafkaMsg.topic },\n stage: 'agent.handler_error',\n error,\n payloadHeaders: kafkaMsg.headers,\n payload: kafkaMsg.body,\n correlation: {\n conversationId: message?.conversationId,\n userMessageId: message?.userMessageId,\n assistantMessageId: kafkaMsg.headers?.assistantMessageId,\n },\n })\n } catch (dlqErr) {\n // sendDlqEnvelope is supposed to be fire-and-forget but\n // belt and suspenders — never let DLQ capture break the\n // already-failing error path.\n console.error('[basion-sdk] DLQ envelope send failed', dlqErr)\n }\n\n // Send error response if we have a message\n if (message && this.sendErrorResponses) {\n await this.sendErrorResponse(message, error as Error)\n }\n }\n }\n\n /**\n * Send error response to user when handler fails.\n */\n private async sendErrorResponse(message: Message, _error: Error): Promise<void> {\n try {\n const streamer = this.streamer(message)\n streamer.stream(this.errorMessageTemplate)\n await streamer.finish()\n } catch (err) {\n console.error('Failed to send error response:', err)\n }\n }\n\n /**\n * Report an error with agent context.\n *\n * Logs to console.error and captures in Sentry (if configured) in one call.\n * Use this for errors that don't involve streaming a response (e.g. background tasks).\n * For errors during streaming, use `streamer.streamError()` instead.\n *\n * @param error - The error to report\n * @param context - Optional additional context tags\n *\n * @example\n * ```typescript\n * try {\n * await riskyOperation()\n * } catch (e) {\n * agent.reportError(e)\n * }\n * ```\n */\n reportError(error: unknown, context?: Record<string, string>): void {\n console.error(`[${this.name}] Error:`, error)\n captureAgentError(error, this.name, context)\n }\n\n /**\n * Create a new Streamer for streaming responses.\n */\n streamer(message: Message, options: StreamerOptions = {}): Streamer {\n return new Streamer({\n agentName: this.name,\n originalMessage: message,\n gatewayClient: this.gatewayClient,\n conversationClient: this.conversationClient,\n attachmentClient: this.attachmentClient,\n renderClient: this.renderClient,\n awaiting: options.awaiting ?? false,\n })\n }\n\n /**\n * Call another agent and wait for its response.\n *\n * Sends a message to the target agent with isCall/callId headers.\n * The response is intercepted by the app's call interception logic\n * and returned as a string.\n *\n * @param agentName - Target agent name to call\n * @param conversationId - The conversation ID for context\n * @param content - Content to send to the target agent\n * @param timeout - Timeout in milliseconds (default: 30000)\n * @returns The target agent's response as a Message (with content, attachments, metadata, etc.)\n *\n * @example\n * ```typescript\n * const response = await agent.call('medical-agent', message.conversationId, 'Is ibuprofen safe?')\n * streamer.stream(`Medical agent says: ${response.content}`)\n * // Access attachments:\n * if (response.hasAttachments()) {\n * const bytes = await response.getAttachmentBytes()\n * }\n * ```\n */\n async call(\n agentName: string,\n conversationId: string,\n content: string,\n timeout: number = 30000,\n ): Promise<Message> {\n if (!this._app) {\n throw new Error(\n 'agent.call() requires a BasionAgentApp. ' +\n 'Ensure the agent was created via app.registerMe().'\n )\n }\n\n return withAgentSpan(\n `handoff from ${this.name} to ${agentName}`,\n this.name,\n 'gen_ai.handoff',\n async () => {\n const callId = randomUUID()\n\n // Create a promise that will be resolved when the response arrives\n let resolvePromise!: (value: Message) => void\n let rejectPromise!: (reason: Error) => void\n const resultPromise = new Promise<Message>((resolve, reject) => {\n resolvePromise = resolve\n rejectPromise = reject\n })\n\n // Register the pending call with the app\n this._app!._pendingCalls.set(callId, {\n resolve: resolvePromise,\n reject: rejectPromise,\n callerName: this.name,\n })\n\n try {\n // Produce the call message\n const headers: Record<string, string> = {\n conversationId,\n from_: this.name,\n to_: agentName,\n nextRoute: agentName,\n isCall: 'true',\n callId,\n }\n\n const body: Record<string, unknown> = {\n content,\n done: true,\n persist: false,\n }\n\n await this.gatewayClient.produce('router.inbox', conversationId, headers, body)\n\n // Wait for response with timeout\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error('TIMEOUT')), timeout)\n })\n\n const result = await Promise.race([resultPromise, timeoutPromise])\n return result\n } catch (error) {\n // Clean up on error/timeout\n this._app!._pendingCalls.delete(callId)\n this._app!._callContent.delete(callId)\n\n if (error instanceof Error && error.message === 'TIMEOUT') {\n throw new Error(`agent.call() to '${agentName}' timed out after ${timeout}ms`)\n }\n throw error\n }\n }\n ) as Promise<Message>\n }\n\n /**\n * Start a new conversation and send the first message as an agent.\n *\n * Creates a conversation in the conversation store and returns a Streamer\n * for sending the initial message. The message flows through the normal\n * Kafka pipeline (router.inbox → provider → Centrifugo).\n *\n * The user's reply will enter the normal handler pipeline via onMessage().\n *\n * @param options - Configuration for the new conversation\n * @returns Tuple of [conversationId, streamer]\n *\n * @example\n * ```typescript\n * const [convId, streamer] = await agent.startConversation({\n * userId: 'user-uuid',\n * title: 'Weekly Check-in',\n * })\n * streamer.stream('Hi! How are you feeling today?')\n * await streamer.finish()\n * ```\n */\n async startConversation(options: {\n userId: string\n title?: string\n awaiting?: boolean\n responseSchema?: Record<string, unknown>\n messageMetadata?: Record<string, unknown>\n metadata?: Record<string, unknown>\n }): Promise<[string, Streamer]> {\n if (!this.conversationClient) {\n throw new ConfigurationError(\n 'ConversationClient is required for startConversation. ' +\n 'Ensure the agent is created via BasionAgentApp.'\n )\n }\n\n // Create conversation with current_route and locked_by set atomically\n const conversation = await this.conversationClient.createConversation({\n userId: options.userId,\n title: options.title ?? 'Agent-initiated conversation',\n agentIdentifier: this.name,\n currentRoute: this.name,\n lockedBy: this.name,\n isNew: true,\n metadata: options.metadata,\n })\n\n const conversationId = String(conversation.id)\n\n // Create streamer with direct conversationId/userId (no originalMessage)\n const streamer = new Streamer({\n agentName: this.name,\n conversationId,\n userId: options.userId,\n gatewayClient: this.gatewayClient,\n conversationClient: this.conversationClient,\n attachmentClient: this.attachmentClient,\n renderClient: this.renderClient,\n awaiting: options.awaiting ?? false,\n })\n\n if (options.responseSchema) {\n streamer.setResponseSchema(options.responseSchema)\n }\n if (options.messageMetadata) {\n streamer.setMessageMetadata(options.messageMetadata)\n }\n\n return [conversationId, streamer]\n }\n\n /**\n * Mark agent as ready to consume messages.\n */\n startConsuming(): void {\n if (this.messageHandlers.length === 0) {\n throw new ConfigurationError('No message handlers registered')\n }\n\n if (!this.initialized) {\n throw new ConfigurationError('Agent not initialized with gateway')\n }\n\n this.running = true\n console.log(`Agent ${this.name} is now consuming messages`)\n }\n\n /**\n * Shutdown agent gracefully.\n */\n shutdown(): void {\n this.running = false\n\n if (this.heartbeatManager) {\n this.heartbeatManager.stop()\n }\n\n console.log(`Agent ${this.name} shutdown`)\n }\n\n /**\n * Check if agent is running.\n */\n isRunning(): boolean {\n return this.running\n }\n}\n","/**\n * Basion Agent SDK - Agent State Client\n * Port of Python basion_agent/agent_state_client.py\n */\n\nimport { APIException } from './exceptions.js'\n\nexport interface AgentStateResponse {\n conversationId: string\n namespace: string\n state: Record<string, unknown>\n createdAt?: string\n updatedAt?: string\n}\n\n/**\n * Async HTTP client for agent-state API.\n * Used for storing framework-specific state (e.g., Pydantic AI messages).\n */\nexport class AgentStateClient {\n private readonly baseUrl: string\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n }\n\n /**\n * Store or update agent state.\n */\n async put(\n conversationId: string,\n namespace: string,\n state: Record<string, unknown>\n ): Promise<{ conversationId: string; namespace: string; created: boolean }> {\n const url = `${this.baseUrl}/agent-state/${conversationId}`\n const payload = {\n namespace,\n state,\n }\n\n try {\n const response = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to store agent state: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversationId: string; namespace: string; created: boolean }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to store agent state: ${error}`)\n }\n }\n\n /**\n * Get agent state.\n */\n async get(\n conversationId: string,\n namespace: string\n ): Promise<AgentStateResponse | null> {\n const url = new URL(`${this.baseUrl}/agent-state/${conversationId}`)\n url.searchParams.set('namespace', namespace)\n\n try {\n const response = await fetch(url.toString())\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to get agent state: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as AgentStateResponse\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to get agent state: ${error}`)\n }\n }\n\n /**\n * Delete agent state.\n */\n async delete(\n conversationId: string,\n namespace?: string\n ): Promise<{ conversationId: string; namespace?: string; deleted: number }> {\n const url = new URL(`${this.baseUrl}/agent-state/${conversationId}`)\n if (namespace) {\n url.searchParams.set('namespace', namespace)\n }\n\n try {\n const response = await fetch(url.toString(), { method: 'DELETE' })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to delete agent state: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversationId: string; namespace?: string; deleted: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to delete agent state: ${error}`)\n }\n }\n\n // ── Vercel AI Messages (row-per-message store) ──────────────────────\n\n /**\n * Append Vercel AI messages to a conversation.\n */\n async appendVercelMessages(\n conversationId: string,\n messages: Record<string, unknown>[],\n ): Promise<{ conversation_id: string; appended: number; total: number }> {\n const url = `${this.baseUrl}/vercel-ai-messages/${conversationId}/append`\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ messages }),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to append vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; appended: number; total: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to append vercel messages: ${error}`)\n }\n }\n\n /**\n * Get Vercel AI messages for a conversation.\n * Returns AgentStateResponse-compatible shape or null if not found.\n */\n async getVercelMessages(\n conversationId: string,\n last?: number,\n ): Promise<AgentStateResponse | null> {\n const url = new URL(`${this.baseUrl}/vercel-ai-messages/${conversationId}`)\n if (last !== undefined) {\n url.searchParams.set('last', String(last))\n }\n\n try {\n const response = await fetch(url.toString())\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to get vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as AgentStateResponse\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to get vercel messages: ${error}`)\n }\n }\n\n /**\n * Prune Vercel AI messages before a given sequence number.\n * Pruned messages are excluded from load but retained for audit.\n */\n async pruneVercelMessages(\n conversationId: string,\n beforeSequence: number,\n ): Promise<{ conversation_id: string; pruned: number }> {\n const url = `${this.baseUrl}/vercel-ai-messages/${conversationId}/prune`\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ before_sequence: beforeSequence }),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to prune vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; pruned: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to prune vercel messages: ${error}`)\n }\n }\n\n /**\n * Delete all Vercel AI messages for a conversation.\n */\n async deleteVercelMessages(\n conversationId: string,\n ): Promise<{ conversation_id: string; deleted: number }> {\n const url = `${this.baseUrl}/vercel-ai-messages/${conversationId}`\n\n try {\n const response = await fetch(url, { method: 'DELETE' })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to delete vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; deleted: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to delete vercel messages: ${error}`)\n }\n }\n\n // ── Filesystem Snapshots (row-per-snapshot store) ─────────────────────\n\n /**\n * Append filesystem snapshots to a conversation.\n */\n async appendFilesystemSnapshots(\n conversationId: string,\n snapshots: Record<string, unknown>[],\n ): Promise<{ conversation_id: string; appended: number; total: number }> {\n const url = `${this.baseUrl}/filesystem-snapshots/${conversationId}/append`\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ snapshots }),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to append filesystem snapshots: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; appended: number; total: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to append filesystem snapshots: ${error}`)\n }\n }\n\n /**\n * Get filesystem snapshots for a conversation.\n * Returns AgentStateResponse-compatible shape or null if not found.\n */\n async getFilesystemSnapshots(\n conversationId: string,\n ): Promise<AgentStateResponse | null> {\n const url = `${this.baseUrl}/filesystem-snapshots/${conversationId}`\n\n try {\n const response = await fetch(url.toString())\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to get filesystem snapshots: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as AgentStateResponse\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to get filesystem snapshots: ${error}`)\n }\n }\n}\n","/**\n * Basion Agent SDK - Vercel AI SDK Extension\n *\n * VercelAIMessageStore for persistent message history with Vercel AI SDK.\n *\n * @packageDocumentation\n */\n\nimport type { BasionAgentApp } from '../app.js'\nimport { _messageContext } from '../agent.js'\nimport { AgentStateClient } from '../agent-state-client.js'\n\n// Type definitions compatible with Vercel AI SDK's CoreMessage\ninterface CoreMessage {\n role: 'system' | 'user' | 'assistant' | 'tool'\n content: string | Array<{ type: string;[key: string]: unknown }>\n [key: string]: unknown\n}\n\n/**\n * Persistent message store for Vercel AI SDK.\n *\n * Stores and retrieves AI SDK message history using the agent-state API.\n * Cross-system message IDs (`_messageId`) are embedded automatically — no\n * manual ID passing required.\n *\n * @example\n * ```typescript\n * import { BasionAgentApp } from 'basion-ai-sdk'\n * import { VercelAIMessageStore } from 'basion-ai-sdk/extensions/vercel-ai'\n * import { streamText } from 'ai'\n * import { anthropic } from '@ai-sdk/anthropic'\n *\n * const app = new BasionAgentApp({ gatewayUrl: 'agent-gateway:8080', apiKey: 'your-key' })\n * const store = new VercelAIMessageStore(app)\n * const agent = await app.registerMe({ ... })\n *\n * agent.onMessage(async (message) => {\n * const history = await store.load(message.conversationId)\n *\n * const messages = [...history, { role: 'user', content: message.content }]\n *\n * const result = await streamText({ model: anthropic('claude-sonnet-4-6'), messages })\n *\n * // _messageId is embedded automatically — no IDs to pass\n * await store.append(message.conversationId, [\n * { role: 'user', content: message.content },\n * ...result.response.messages,\n * ])\n * })\n *\n * app.run()\n * ```\n */\nexport class VercelAIMessageStore {\n private static readonly NAMESPACE = 'vercel_ai'\n private readonly client: AgentStateClient\n\n constructor(app: BasionAgentApp) {\n this.client = new AgentStateClient(app.gatewayClient.conversationStoreUrl)\n }\n\n /**\n * Load message history for a conversation.\n *\n * @param conversationId - The conversation ID to load history for\n * @returns Array of CoreMessage objects (empty array if no history)\n */\n async load(conversationId: string): Promise<CoreMessage[]> {\n const result = await this.client.getVercelMessages(conversationId)\n\n if (!result) {\n return []\n }\n\n const state = result.state ?? {}\n const messages = state.messages as unknown[]\n\n if (!Array.isArray(messages)) {\n return []\n }\n\n return messages.filter(this.isValidMessage) as CoreMessage[]\n }\n\n /**\n * Append new messages to existing history.\n *\n * Cross-system `_messageId` fields are embedded automatically from the\n * current message handler context — no IDs need to be passed manually.\n *\n * @param conversationId - The conversation ID\n * @param newMessages - New messages to append (user turn + response messages)\n */\n async append(conversationId: string, newMessages: CoreMessage[]): Promise<void> {\n const ctx = _messageContext.getStore()\n const tagged = ctx ? this.embedMessageIds(newMessages, ctx) : newMessages\n await this.client.appendVercelMessages(\n conversationId,\n tagged.map(this.serializeMessage),\n )\n }\n\n /**\n * Clear message history for a conversation.\n *\n * @param conversationId - The conversation ID to clear history for\n */\n async clear(conversationId: string): Promise<void> {\n await this.client.deleteVercelMessages(conversationId)\n }\n\n /**\n * Prune messages before a given sequence number.\n *\n * Called after server-side compaction fires. Pruned messages are excluded\n * from `load()` but retained in the database for audit.\n *\n * @param conversationId - The conversation ID\n * @param beforeSequence - Prune all messages with sequence < this value\n * @returns Number of messages pruned\n */\n async pruneBeforeSequence(conversationId: string, beforeSequence: number): Promise<number> {\n const result = await this.client.pruneVercelMessages(conversationId, beforeSequence)\n return result.pruned\n }\n\n /**\n * Get the last N messages from history.\n *\n * @param conversationId - The conversation ID\n * @param count - Number of messages to retrieve\n */\n async getLastMessages(conversationId: string, count: number): Promise<CoreMessage[]> {\n const result = await this.client.getVercelMessages(conversationId, count)\n\n if (!result) {\n return []\n }\n\n const state = result.state ?? {}\n const messages = state.messages as unknown[]\n\n if (!Array.isArray(messages)) {\n return []\n }\n\n return messages.filter(this.isValidMessage) as CoreMessage[]\n }\n\n private embedMessageIds(\n messages: CoreMessage[],\n ids: { userMessageId?: string; responseMessageId?: string },\n ): CoreMessage[] {\n const result = [...messages]\n\n if (ids.userMessageId) {\n const userIdx = result.findIndex((m) => m.role === 'user')\n if (userIdx !== -1) {\n result[userIdx] = { ...result[userIdx], _messageId: ids.userMessageId }\n }\n }\n\n if (ids.responseMessageId) {\n for (let i = result.length - 1; i >= 0; i--) {\n const m = result[i]\n if (\n m.role === 'assistant' &&\n (typeof m.content === 'string'\n ? m.content.length > 0\n : (m.content as Array<{ type: string }>).some((c) => c.type === 'text'))\n ) {\n result[i] = { ...result[i], _messageId: ids.responseMessageId }\n break\n }\n }\n }\n\n return result\n }\n\n private isValidMessage(msg: unknown): msg is CoreMessage {\n if (typeof msg !== 'object' || msg === null) return false\n const m = msg as Record<string, unknown>\n return (\n typeof m.role === 'string' &&\n ['system', 'user', 'assistant', 'tool'].includes(m.role) &&\n (typeof m.content === 'string' || Array.isArray(m.content))\n )\n }\n\n private serializeMessage(msg: CoreMessage): Record<string, unknown> {\n return { ...msg }\n }\n}\n\nexport type { CoreMessage }\n"]}
1
+ {"version":3,"sources":["../../src/exceptions.ts","../../src/types.ts","../../src/agent.ts","../../src/agent-state-client.ts","../../src/extensions/vercel-ai.ts"],"names":[],"mappings":"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","file":"vercel-ai.js","sourcesContent":["/**\n * Basion Agent SDK - Custom Errors\n * Port of Python basion_agent/exceptions.py\n */\n\n/**\n * Base error class for Basion Agent SDK\n */\nexport class BasionAgentError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'BasionAgentError'\n Error.captureStackTrace?.(this, this.constructor)\n }\n}\n\n/**\n * Error during agent registration with AI Inventory\n */\nexport class RegistrationError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'RegistrationError'\n }\n}\n\n/**\n * Error during Kafka operations (produce/consume)\n */\nexport class KafkaError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'KafkaError'\n }\n}\n\n/**\n * Error in SDK configuration\n */\nexport class ConfigurationError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'ConfigurationError'\n }\n}\n\n/**\n * Error from API calls to backend services\n */\nexport class APIException extends BasionAgentError {\n statusCode?: number\n\n constructor(message: string, statusCode?: number) {\n super(message)\n this.name = 'APIException'\n this.statusCode = statusCode\n }\n}\n\n/**\n * Error during heartbeat operations\n */\nexport class HeartbeatError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'HeartbeatError'\n }\n}\n\n/**\n * Error during gRPC connection/communication\n */\nexport class GatewayError extends BasionAgentError {\n constructor(message: string) {\n super(message)\n this.name = 'GatewayError'\n }\n}\n\n/**\n * Error during reconnection attempts\n */\nexport class ReconnectionError extends BasionAgentError {\n /** Number of attempts made before giving up */\n attempts: number\n /** The last error that occurred during reconnection */\n lastError?: Error\n\n constructor(message: string, attempts: number, lastError?: Error) {\n super(message)\n this.name = 'ReconnectionError'\n this.attempts = attempts\n this.lastError = lastError\n }\n}\n","/**\n * Basion Agent SDK - Shared Types\n * Port of Python basion_agent types\n */\n\nimport { z } from 'zod'\n\n// ============================================================================\n// Kafka Message Types\n// ============================================================================\n\nexport interface KafkaMessage {\n topic: string\n partition: number\n offset: number\n key: string\n headers: Record<string, string>\n body: Record<string, unknown>\n timestamp: number\n}\n\nexport interface KafkaHeaders {\n conversationId?: string\n userId?: string\n from_?: string\n to_?: string\n messageMetadata?: string\n messageSchema?: string\n /** @deprecated Use attachments (plural) instead. Kept for backward compatibility. */\n attachment?: string\n /** JSON-stringified array of AttachmentInfo objects */\n attachments?: string\n /** UUID of the user message in conversation-store (for cross-system mapping) */\n userMessageId?: string\n /** Pre-generated UUID for the assistant response in conversation-store (for cross-system mapping) */\n assistantMessageId?: string\n [key: string]: string | undefined\n}\n\n// ============================================================================\n// Agent Registration Types\n// ============================================================================\n\nexport interface AgentRegistrationData {\n id: string\n name: string\n about: string\n document: string\n representationName?: string\n baseUrl?: string\n metadata?: Record<string, unknown>\n relatedPages?: RelatedPage[]\n}\n\nexport interface RelatedPage {\n name: string\n endpoint: string\n}\n\nexport interface Prompt {\n label: string\n prompt: string\n}\n\nexport interface RegisterOptions {\n name: string\n about: string\n document: string\n representationName?: string\n baseUrl?: string\n metadata?: Record<string, unknown>\n relatedPages?: RelatedPage[]\n categoryNames?: string[]\n prompts?: Prompt[]\n detailedDescription?: string\n version?: string\n lifecycleStage?: 'dev' | 'test' | 'private-preview' | 'public-preview' | 'public'\n welcomeMessage?: string\n waitlist?: boolean\n forceUpdate?: boolean\n}\n\n// ============================================================================\n// Message Types\n// ============================================================================\n\n// Forward reference - actual Message class is imported where needed\n// Using 'any' here to avoid circular dependency, but consumers should use Message type\nexport interface MessageHandler {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (message: any, sender: string): void | Promise<void>\n}\n\nexport interface SenderFilterOptions {\n senders?: string[]\n}\n\n// ============================================================================\n// Streamer Types\n// ============================================================================\n\nexport interface StreamOptions {\n persist?: boolean\n eventType?: string\n}\n\nexport interface StreamerOptions {\n awaiting?: boolean\n}\n\n// ============================================================================\n// Attachment Types\n// ============================================================================\n\nexport const AttachmentInfoSchema = z.object({\n url: z.string(),\n filename: z.string(),\n contentType: z.string(),\n size: z.number().optional(),\n fileType: z.string().optional(),\n objectKey: z.string().optional(),\n})\n\nexport type AttachmentInfo = z.infer<typeof AttachmentInfoSchema>\n\nexport function isImageAttachment(attachment: AttachmentInfo): boolean {\n return attachment.contentType?.startsWith('image/') || false\n}\n\nexport function isPdfAttachment(attachment: AttachmentInfo): boolean {\n return attachment.contentType === 'application/pdf'\n}\n\n// ============================================================================\n// Conversation Message Types\n// ============================================================================\n\nexport interface ConversationMessageData {\n id: string\n conversationId: string\n role: 'user' | 'assistant' | 'system'\n content: string\n from?: string\n to?: string\n createdAt: string\n metadata?: Record<string, unknown>\n}\n\n// ============================================================================\n// Gateway Client Types\n// ============================================================================\n\nexport interface ProduceAck {\n topic: string\n partition: number\n offset: number\n correlationId?: string\n}\n\n// ============================================================================\n// Reconnection Types\n// ============================================================================\n\n/**\n * Configuration options for automatic reconnection behavior.\n */\nexport interface ReconnectionOptions {\n /** Maximum number of reconnection attempts before giving up. Default: 10 */\n maxRetries?: number\n /** Initial delay in milliseconds before first retry. Default: 1000 */\n initialDelayMs?: number\n /** Maximum delay in milliseconds between retries. Default: 30000 */\n maxDelayMs?: number\n /** Multiplier for exponential backoff. Default: 2 */\n backoffMultiplier?: number\n /** Whether to automatically reconnect on disconnect. Default: true */\n autoReconnect?: boolean\n}\n\n/**\n * Connection state for the gateway client.\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'\n\n/**\n * Listener callback for connection state changes.\n */\nexport interface ConnectionStateListener {\n (state: ConnectionState, error?: Error): void\n}\n\n/**\n * Default reconnection options.\n */\nexport const DEFAULT_RECONNECTION_OPTIONS: Required<ReconnectionOptions> = {\n maxRetries: 10,\n initialDelayMs: 1000,\n maxDelayMs: 30000,\n backoffMultiplier: 2,\n autoReconnect: true,\n}\n\n// ============================================================================\n// App Configuration Types\n// ============================================================================\n\nexport interface AppOptions {\n gatewayUrl: string\n apiKey: string\n heartbeatInterval?: number\n maxConcurrentTasks?: number\n errorMessageTemplate?: string\n secure?: boolean\n /** Reconnection options for the gateway connection */\n reconnection?: ReconnectionOptions\n /** Enable remote logging to Loki via the gateway (default: false) */\n enableRemoteLogging?: boolean\n /** Minimum log level for remote logging (default: 'info') */\n remoteLogLevel?: 'debug' | 'info' | 'warn' | 'error'\n /** Number of logs to batch before sending (default: 100) */\n remoteLogBatchSize?: number\n /** Maximum seconds between log flushes (default: 5.0) */\n remoteLogFlushInterval?: number\n /** Sentry configuration (optional — omit or leave dsn empty to disable) */\n sentry?: import('./sentry.js').SentryConfig\n /** Braintrust configuration (optional — omit or leave apiKey empty to disable) */\n braintrust?: import('./braintrust.js').BraintrustConfig\n}\n\n// ============================================================================\n// HTTP Response Types\n// ============================================================================\n\nexport interface ConversationData {\n id: string\n userId: string\n agentId?: string\n awaitingRoute?: string\n pendingResponseSchema?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n metadata?: Record<string, unknown>\n}\n\nexport interface GetMessagesOptions {\n role?: string\n from?: string\n to?: string\n limit?: number\n offset?: number\n}\n\n// ============================================================================\n// Memory Types\n// ============================================================================\n\n/**\n * A message stored in ai-memory.\n * Matches the ai-memory API MessageStored schema.\n */\nexport interface MemoryMessage {\n id?: string\n role: 'user' | 'assistant' | 'system'\n content: string\n tenantId?: string\n userId?: string\n conversationId?: string\n metadata?: Record<string, unknown>\n timestamp?: string\n sequenceNumber?: number\n}\n\n/**\n * Options for memory search operations.\n */\nexport interface MemorySearchOptions {\n /** Maximum number of results (1-100, default: 10) */\n limit?: number\n /** Minimum similarity threshold (0-100, default: 70) */\n minSimilarity?: number\n /** Number of surrounding context messages to include (0-20, default: 0) */\n contextMessages?: number\n /** Tenant ID for multi-tenant filtering */\n tenantId?: string\n /** Tenant matching mode: 'exact' or 'hierarchy' */\n tenantMatch?: 'exact' | 'hierarchy'\n}\n\n/**\n * A memory search result with similarity score and optional context.\n */\nexport interface MemorySearchResult {\n message: MemoryMessage\n score: number\n context: MemoryMessage[]\n}\n\n/**\n * User conversation summary from ai-memory.\n */\nexport interface UserSummary {\n text: string\n lastUpdated: string\n messageCount: number\n version: number\n}\n","/**\n * Basion Agent SDK - Agent\n * Port of Python basion_agent/agent.py\n */\n\nimport { randomUUID } from 'crypto'\nimport { AsyncLocalStorage } from 'node:async_hooks'\n\nimport type { BasionAgentApp } from './app.js'\nimport { DedupLRU } from './dedup-lru.js'\nimport type { AttachmentClient } from './attachment-client.js'\nimport { logBraintrustError,withBraintrustSpan } from './braintrust.js'\nimport type { ConversationClient } from './conversation-client.js'\nimport { sendDlqEnvelope } from './dlq.js'\nimport { ConfigurationError } from './exceptions.js'\nimport type { GatewayClient } from './gateway-client.js'\nimport { type HeartbeatFailureCallback,HeartbeatManager } from './heartbeat.js'\nimport type { MemoryClient } from './memory-client.js'\nimport type { MemoryV2Client } from './memory-v2-client.js'\nimport { Message } from './message.js'\nimport type { RenderClient } from './render-client.js'\nimport { captureAgentError, setConversationId,withAgentSpan } from './sentry.js'\nimport { Streamer } from './streamer.js'\nimport { Tools } from './tools/container.js'\nimport type {\n AgentRegistrationData,\n KafkaMessage,\n MessageHandler,\n SenderFilterOptions,\n StreamerOptions,\n} from './types.js'\n\n/**\n * AsyncLocalStorage context set automatically by the agent message handler.\n * VercelAIMessageStore reads from this to embed cross-system IDs without\n * requiring agent developers to pass them explicitly.\n *\n * Stored on globalThis so that the same instance is shared across all bundle\n * chunks (tsup can include this module in multiple output files; globalThis\n * ensures they all reference the same AsyncLocalStorage object).\n *\n * @internal — consumed by VercelAIMessageStore; not part of the public API.\n */\nconst _msgCtxKey = Symbol.for('basion-ai-sdk.messageContext')\nif (!(globalThis as Record<symbol, unknown>)[_msgCtxKey]) {\n (globalThis as Record<symbol, unknown>)[_msgCtxKey] = new AsyncLocalStorage<{\n userMessageId?: string\n responseMessageId?: string\n }>()\n}\nexport const _messageContext = (globalThis as Record<symbol, unknown>)[_msgCtxKey] as AsyncLocalStorage<{\n userMessageId?: string\n responseMessageId?: string\n}>\n\n/**\n * Filter for matching message senders.\n */\nexport class SenderFilter {\n private readonly _include: Set<string> = new Set()\n private readonly _exclude: Set<string> = new Set()\n readonly matchAll: boolean\n\n constructor(senders?: string[]) {\n if (!senders || senders.length === 0) {\n this.matchAll = true\n } else {\n this.matchAll = false\n for (const sender of senders) {\n if (sender.startsWith('~')) {\n this._exclude.add(sender.slice(1))\n } else {\n this._include.add(sender)\n }\n }\n }\n }\n\n /**\n * Get included senders as an array (for compatibility with Python tests).\n */\n get include(): string[] {\n return Array.from(this._include)\n }\n\n /**\n * Get excluded senders as an array (for compatibility with Python tests).\n */\n get exclude(): string[] {\n return Array.from(this._exclude)\n }\n\n /**\n * Check if sender matches this filter.\n */\n matches(sender: string): boolean {\n // If excluded, reject\n if (this._exclude.has(sender)) {\n return false\n }\n // If match all or explicitly included\n if (this.matchAll || this._include.size === 0) {\n return true\n }\n return this._include.has(sender)\n }\n}\n\n// Message handlers stored as tuples: [SenderFilter, MessageHandler]\ntype RegisteredHandler = [SenderFilter, MessageHandler]\n\n/**\n * Core agent class that handles messaging via the Agent Gateway.\n */\nexport class Agent {\n readonly name: string\n readonly agentData: AgentRegistrationData\n sendErrorResponses: boolean = true\n errorMessageTemplate: string\n\n private readonly gatewayClient: GatewayClient\n private readonly conversationClient?: ConversationClient\n private readonly memoryClient?: MemoryClient\n private readonly memoryV2Client?: MemoryV2Client\n private readonly attachmentClient?: AttachmentClient\n private readonly renderClient?: RenderClient\n private readonly heartbeatInterval: number\n private readonly heartbeatFailureThreshold: number\n private readonly onHeartbeatFailure?: HeartbeatFailureCallback\n private readonly enableHeartbeat: boolean\n\n /** Set by BasionAgentApp.registerMe() to enable agent.call() */\n _app?: BasionAgentApp\n\n private messageHandlers: RegisteredHandler[] = []\n private heartbeatManager?: HeartbeatManager\n private initialized: boolean = false\n private running: boolean = false\n private _tools?: Tools\n private readonly dedup: DedupLRU = new DedupLRU(\n Number(process.env.BASION_SDK_DEDUP_LRU_SIZE) || 100_000,\n )\n\n /**\n * Live AbortControllers for in-flight turns, keyed by conversationId.\n * `assistantMessageId` scopes the controller to a specific turn so a cancel\n * for a previous turn cannot abort the current one.\n */\n private _cancelControllers = new Map<\n string,\n { controller: AbortController; assistantMessageId?: string }\n >()\n /**\n * Pre-cancel intents that arrived before the handler registered its controller.\n * Keyed by `assistantMessageId` (never by conversationId alone) so a stray\n * cancel from an earlier turn cannot poison the next turn. Entries self-expire\n * after PENDING_CANCEL_TTL_MS as defense-in-depth.\n */\n private _pendingCancels = new Map<string, { expiresAt: number }>()\n private static readonly PENDING_CANCEL_TTL_MS = 5000\n\n constructor(options: {\n name: string\n gatewayClient: GatewayClient\n agentData: AgentRegistrationData\n heartbeatInterval?: number\n heartbeatFailureThreshold?: number\n onHeartbeatFailure?: HeartbeatFailureCallback\n conversationClient?: ConversationClient\n memoryClient?: MemoryClient\n memoryV2Client?: MemoryV2Client\n attachmentClient?: AttachmentClient\n renderClient?: RenderClient\n errorMessageTemplate?: string\n enableHeartbeat?: boolean\n }) {\n this.name = options.name\n this.gatewayClient = options.gatewayClient\n this.agentData = options.agentData\n this.heartbeatInterval = options.heartbeatInterval ?? 60\n this.heartbeatFailureThreshold = options.heartbeatFailureThreshold ?? 3\n this.onHeartbeatFailure = options.onHeartbeatFailure\n this.conversationClient = options.conversationClient\n this.memoryClient = options.memoryClient\n this.memoryV2Client = options.memoryV2Client\n this.attachmentClient = options.attachmentClient\n this.renderClient = options.renderClient\n this.errorMessageTemplate = options.errorMessageTemplate ??\n 'I encountered an error while processing your message. Please try again or contact support if the issue persists.'\n this.enableHeartbeat = options.enableHeartbeat ?? true\n }\n\n /**\n * Create an AbortSignal for a conversation's current operation.\n * Call this before starting work (e.g. streamText) and pass the\n * returned signal so the operation can be cancelled externally.\n *\n * Pass `assistantMessageId` (typically `message.responseMessageId`) so\n * a cancel message targeting THIS turn can be matched precisely; a\n * cancel for any other turn will be ignored.\n *\n * If a cancel request arrived before this call AND carried a matching\n * `assistantMessageId`, the returned signal will already be aborted.\n */\n createAbortSignal(conversationId: string, assistantMessageId?: string): AbortSignal {\n // Replace any existing controller (defensive cleanup)\n const existing = this._cancelControllers.get(conversationId)\n if (existing) {\n existing.controller.abort()\n }\n\n const controller = new AbortController()\n this._cancelControllers.set(conversationId, { controller, assistantMessageId })\n\n this.purgeExpiredPendingCancels()\n\n // A pre-cancel only pre-aborts the new signal if it names the same turn.\n // Without this scoping, a stale cancel from a prior turn leaks across turns.\n if (assistantMessageId && this._pendingCancels.has(assistantMessageId)) {\n this._pendingCancels.delete(assistantMessageId)\n controller.abort()\n }\n\n return controller.signal\n }\n\n /**\n * Cancel the running operation for a conversation.\n * Called by the message interceptor when a cancel message arrives.\n *\n * If `assistantMessageId` is provided and does not match the live\n * controller's, the cancel is dropped as stale (prevents a late cancel\n * from a previous turn from aborting the current one).\n */\n cancelConversation(conversationId: string, assistantMessageId?: string): void {\n const entry = this._cancelControllers.get(conversationId)\n\n if (entry) {\n // Both IDs present and mismatched → stale cancel for a prior turn. Drop it.\n if (\n assistantMessageId &&\n entry.assistantMessageId &&\n assistantMessageId !== entry.assistantMessageId\n ) {\n return\n }\n entry.controller.abort()\n this._cancelControllers.delete(conversationId)\n return\n }\n\n // No live controller. Only remember cancels that name a specific turn;\n // blind conversation-scoped intent is what caused bug #1 and is removed.\n if (assistantMessageId) {\n this._pendingCancels.set(assistantMessageId, {\n expiresAt: Date.now() + Agent.PENDING_CANCEL_TTL_MS,\n })\n this.purgeExpiredPendingCancels()\n }\n }\n\n /**\n * Clean up the abort controller after the handler completes.\n * Also purges any matching pre-cancel intent so a stale entry cannot\n * outlive the turn it was meant for.\n */\n clearAbortController(conversationId: string, assistantMessageId?: string): void {\n this._cancelControllers.delete(conversationId)\n if (assistantMessageId) {\n this._pendingCancels.delete(assistantMessageId)\n }\n }\n\n private purgeExpiredPendingCancels(): void {\n const now = Date.now()\n for (const [id, { expiresAt }] of this._pendingCancels) {\n if (expiresAt <= now) {\n this._pendingCancels.delete(id)\n }\n }\n }\n\n /**\n * Access to tools (knowledge graph, etc.).\n *\n * Example:\n * const kg = agent.tools.knowledgeGraph\n * const diseases = await kg.searchDiseases({ name: \"Huntington\" })\n */\n get tools(): Tools {\n if (!this._tools) {\n this._tools = new Tools(this.gatewayClient)\n }\n return this._tools\n }\n\n /**\n * Register a message handler.\n * \n * Usage:\n * agent.onMessage(async (message, sender) => { ... })\n * agent.onMessage(handler, { senders: ['user'] })\n */\n onMessage(handler: MessageHandler): void\n onMessage(handler: MessageHandler, options: SenderFilterOptions): void\n onMessage(\n handler: MessageHandler,\n options?: SenderFilterOptions\n ): void {\n const filter = new SenderFilter(options?.senders)\n this.messageHandlers.push([filter, handler])\n }\n\n /**\n * Initialize agent after gateway connection is established.\n * Can be called multiple times (e.g., after reconnection) to re-register handlers.\n */\n initializeWithGateway(): void {\n // Register message handler with gateway\n // This is idempotent - calling it again just updates the handler\n const topic = `${this.name}.inbox`\n this.gatewayClient.registerHandler(topic, (msg) => {\n this.handleKafkaMessage(msg)\n })\n\n // Start heartbeat if not already created\n if (this.enableHeartbeat) {\n if (!this.heartbeatManager) {\n this.heartbeatManager = new HeartbeatManager(\n this.gatewayClient,\n this.name,\n this.heartbeatInterval,\n {\n failureThreshold: this.heartbeatFailureThreshold,\n onFailureThresholdExceeded: this.handleHeartbeatFailure.bind(this),\n }\n )\n }\n\n // Start or resume heartbeat (in case it was stopped during disconnect)\n if (!this.heartbeatManager.isRunning()) {\n this.heartbeatManager.start()\n } else if (this.heartbeatManager.isPaused()) {\n this.heartbeatManager.resume()\n }\n }\n\n this.initialized = true\n }\n\n /**\n * Handle heartbeat failure threshold exceeded.\n * This can indicate a stale connection that needs reconnection.\n */\n private handleHeartbeatFailure(consecutiveFailures: number, lastError?: Error): void {\n console.warn(\n `Agent '${this.name}' heartbeat failure threshold exceeded (${consecutiveFailures} failures)`\n )\n\n // Call custom callback if provided\n if (this.onHeartbeatFailure) {\n this.onHeartbeatFailure(consecutiveFailures, lastError)\n }\n\n // If gateway is not connected, try to trigger reconnection\n if (!this.gatewayClient.isConnected() && !this.gatewayClient.isReconnectingNow()) {\n console.warn('Gateway not connected, attempting force reconnect...')\n this.gatewayClient.forceReconnect().catch((err) => {\n console.error('Force reconnect from heartbeat failure failed:', err)\n })\n }\n }\n\n /**\n * Pause heartbeat sending (e.g., during reconnection).\n */\n pauseHeartbeat(): void {\n this.heartbeatManager?.pause()\n }\n\n /**\n * Resume heartbeat sending.\n */\n resumeHeartbeat(): void {\n this.heartbeatManager?.resume()\n }\n\n /**\n * Get sender from message headers.\n */\n private getSender(headers: Record<string, string>): string {\n return headers.from_ ?? headers.from ?? 'user'\n }\n\n /**\n * Find first handler that matches the sender.\n */\n private findMatchingHandler(sender: string): MessageHandler | undefined {\n for (const [filter, handler] of this.messageHandlers) {\n if (filter.matches(sender)) {\n return handler\n }\n }\n return undefined\n }\n\n /**\n * Handle incoming Kafka message.\n *\n * Consults the dedup LRU before dispatching to suppress duplicate deliveries.\n * The dedup key is `userMessageId` when present; otherwise falls back to\n * `${topic}:${partition}:${offset}` so at-most-once semantics hold even for\n * messages that pre-date the userMessageId header.\n *\n * Note: ConsumeAck is sent by GatewayClient BEFORE this method is called,\n * so dedup suppression here only prevents double handler dispatch — it does\n * not affect ack delivery.\n */\n private handleKafkaMessage(kafkaMsg: KafkaMessage): void {\n const dedupKey = kafkaMsg.headers?.userMessageId\n ?? `${kafkaMsg.topic}:${kafkaMsg.partition}:${kafkaMsg.offset}`\n if (this.dedup.checkAndAdd(dedupKey)) {\n console.debug(`[basion-sdk] dedup hit: ${dedupKey} — suppressing handler dispatch`)\n return\n }\n this.handleMessageAsync(kafkaMsg).catch((error) => {\n console.error('Error in message handler:', error)\n })\n }\n\n /**\n * Async message handling with error recovery.\n */\n private async handleMessageAsync(kafkaMsg: KafkaMessage): Promise<void> {\n let message: Message | undefined\n\n try {\n // Create message from Kafka data\n message = Message.fromKafkaMessage(\n kafkaMsg.body,\n kafkaMsg.headers,\n {\n conversationClient: this.conversationClient,\n memoryClient: this.memoryClient,\n memoryV2Client: this.memoryV2Client,\n attachmentClient: this.attachmentClient,\n agentName: this.name,\n gatewayClient: this.gatewayClient,\n }\n )\n\n // Find matching handler\n const sender = this.getSender(kafkaMsg.headers)\n const handler = this.findMatchingHandler(sender)\n\n if (!handler) {\n console.warn(`No handler matched for sender: ${sender}`)\n return\n }\n\n // Extract trace ID from Kafka headers for cross-agent Braintrust correlation\n const traceId = kafkaMsg.headers.traceId\n\n // Wrap handler execution in Sentry invoke_agent span\n // setConversationId must be called INSIDE the span so the conversation ID\n // is applied within the span's scope (see Sentry AI Agents docs)\n await withAgentSpan(\n `invoke_agent ${this.name}`,\n this.name,\n 'gen_ai.invoke_agent',\n async () => {\n setConversationId(message!.conversationId)\n\n // Also wrap in Braintrust span (nested under conversation trace)\n return withBraintrustSpan(\n `invoke_agent ${this.name}`,\n () => _messageContext.run(\n {\n userMessageId: message!.userMessageId,\n responseMessageId: message!.responseMessageId,\n },\n () => handler(message!, sender),\n ),\n {\n conversationId: message!.conversationId,\n agentName: this.name,\n traceId,\n type: 'llm',\n input: [{ role: 'user', content: message!.content }],\n metadata: {\n conversationId: message!.conversationId,\n agentName: this.name,\n sender,\n },\n },\n )\n },\n {\n 'gen_ai.operation.name': 'invoke_agent',\n 'gen_ai.conversation.id': message.conversationId,\n }\n )\n\n } catch (error) {\n console.error('Error handling message:', error)\n\n // Capture error in Sentry with agent context\n captureAgentError(error, this.name, {\n conversationId: message?.conversationId ?? 'unknown',\n })\n\n // Log error in Braintrust\n logBraintrustError(error, {\n conversationId: message?.conversationId ?? 'unknown',\n agentName: this.name,\n })\n\n // DLQ-capture: without this, a thrown handler is silent —\n // the gateway has already auto-committed the offset, so the\n // user message gets no reply and there's no record on the\n // platform side. The DLQ envelope flows through gatewayClient\n // to the dlq.events Kafka topic via the agent's existing\n // gRPC stream.\n try {\n sendDlqEnvelope(this.gatewayClient, {\n service: `agent-${this.name}`,\n source: { kind: 'kafka', topic: kafkaMsg.topic },\n stage: 'agent.handler_error',\n error,\n payloadHeaders: kafkaMsg.headers,\n payload: kafkaMsg.body,\n correlation: {\n conversationId: message?.conversationId,\n userMessageId: message?.userMessageId,\n assistantMessageId: kafkaMsg.headers?.assistantMessageId,\n },\n })\n } catch (dlqErr) {\n // sendDlqEnvelope is supposed to be fire-and-forget but\n // belt and suspenders — never let DLQ capture break the\n // already-failing error path.\n console.error('[basion-sdk] DLQ envelope send failed', dlqErr)\n }\n\n // Send error response if we have a message\n if (message && this.sendErrorResponses) {\n await this.sendErrorResponse(message, error as Error)\n }\n }\n }\n\n /**\n * Send error response to user when handler fails.\n */\n private async sendErrorResponse(message: Message, _error: Error): Promise<void> {\n try {\n const streamer = this.streamer(message)\n streamer.stream(this.errorMessageTemplate)\n await streamer.finish()\n } catch (err) {\n console.error('Failed to send error response:', err)\n }\n }\n\n /**\n * Report an error with agent context.\n *\n * Logs to console.error and captures in Sentry (if configured) in one call.\n * Use this for errors that don't involve streaming a response (e.g. background tasks).\n * For errors during streaming, use `streamer.streamError()` instead.\n *\n * @param error - The error to report\n * @param context - Optional additional context tags\n *\n * @example\n * ```typescript\n * try {\n * await riskyOperation()\n * } catch (e) {\n * agent.reportError(e)\n * }\n * ```\n */\n reportError(error: unknown, context?: Record<string, string>): void {\n console.error(`[${this.name}] Error:`, error)\n captureAgentError(error, this.name, context)\n }\n\n /**\n * Create a new Streamer for streaming responses.\n */\n streamer(message: Message, options: StreamerOptions = {}): Streamer {\n return new Streamer({\n agentName: this.name,\n originalMessage: message,\n gatewayClient: this.gatewayClient,\n conversationClient: this.conversationClient,\n attachmentClient: this.attachmentClient,\n renderClient: this.renderClient,\n awaiting: options.awaiting ?? false,\n })\n }\n\n /**\n * Call another agent and wait for its response.\n *\n * Sends a message to the target agent with isCall/callId headers.\n * The response is intercepted by the app's call interception logic\n * and returned as a string.\n *\n * @param agentName - Target agent name to call\n * @param conversationId - The conversation ID for context\n * @param content - Content to send to the target agent\n * @param timeout - Timeout in milliseconds (default: 30000)\n * @returns The target agent's response as a Message (with content, attachments, metadata, etc.)\n *\n * @example\n * ```typescript\n * const response = await agent.call('medical-agent', message.conversationId, 'Is ibuprofen safe?')\n * streamer.stream(`Medical agent says: ${response.content}`)\n * // Access attachments:\n * if (response.hasAttachments()) {\n * const bytes = await response.getAttachmentBytes()\n * }\n * ```\n */\n async call(\n agentName: string,\n conversationId: string,\n content: string,\n timeout: number = 30000,\n ): Promise<Message> {\n if (!this._app) {\n throw new Error(\n 'agent.call() requires a BasionAgentApp. ' +\n 'Ensure the agent was created via app.registerMe().'\n )\n }\n\n return withAgentSpan(\n `handoff from ${this.name} to ${agentName}`,\n this.name,\n 'gen_ai.handoff',\n async () => {\n const callId = randomUUID()\n\n // Create a promise that will be resolved when the response arrives\n let resolvePromise!: (value: Message) => void\n let rejectPromise!: (reason: Error) => void\n const resultPromise = new Promise<Message>((resolve, reject) => {\n resolvePromise = resolve\n rejectPromise = reject\n })\n\n // Register the pending call with the app\n this._app!._pendingCalls.set(callId, {\n resolve: resolvePromise,\n reject: rejectPromise,\n callerName: this.name,\n })\n\n try {\n // Produce the call message\n const headers: Record<string, string> = {\n conversationId,\n from_: this.name,\n to_: agentName,\n nextRoute: agentName,\n isCall: 'true',\n callId,\n }\n\n const body: Record<string, unknown> = {\n content,\n done: true,\n persist: false,\n }\n\n await this.gatewayClient.produce('router.inbox', conversationId, headers, body)\n\n // Wait for response with timeout\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error('TIMEOUT')), timeout)\n })\n\n const result = await Promise.race([resultPromise, timeoutPromise])\n return result\n } catch (error) {\n // Clean up on error/timeout\n this._app!._pendingCalls.delete(callId)\n this._app!._callContent.delete(callId)\n\n if (error instanceof Error && error.message === 'TIMEOUT') {\n throw new Error(`agent.call() to '${agentName}' timed out after ${timeout}ms`)\n }\n throw error\n }\n }\n ) as Promise<Message>\n }\n\n /**\n * Start a new conversation and send the first message as an agent.\n *\n * Creates a conversation in the conversation store and returns a Streamer\n * for sending the initial message. The message flows through the normal\n * Kafka pipeline (router.inbox → provider → Centrifugo).\n *\n * The user's reply will enter the normal handler pipeline via onMessage().\n *\n * @param options - Configuration for the new conversation\n * @returns Tuple of [conversationId, streamer]\n *\n * @example\n * ```typescript\n * const [convId, streamer] = await agent.startConversation({\n * userId: 'user-uuid',\n * title: 'Weekly Check-in',\n * })\n * streamer.stream('Hi! How are you feeling today?')\n * await streamer.finish()\n * ```\n */\n async startConversation(options: {\n userId: string\n title?: string\n awaiting?: boolean\n responseSchema?: Record<string, unknown>\n messageMetadata?: Record<string, unknown>\n metadata?: Record<string, unknown>\n }): Promise<[string, Streamer]> {\n if (!this.conversationClient) {\n throw new ConfigurationError(\n 'ConversationClient is required for startConversation. ' +\n 'Ensure the agent is created via BasionAgentApp.'\n )\n }\n\n // Create conversation with current_route and locked_by set atomically\n const conversation = await this.conversationClient.createConversation({\n userId: options.userId,\n title: options.title ?? 'Agent-initiated conversation',\n agentIdentifier: this.name,\n currentRoute: this.name,\n lockedBy: this.name,\n isNew: true,\n metadata: options.metadata,\n })\n\n const conversationId = String(conversation.id)\n\n // Create streamer with direct conversationId/userId (no originalMessage)\n const streamer = new Streamer({\n agentName: this.name,\n conversationId,\n userId: options.userId,\n gatewayClient: this.gatewayClient,\n conversationClient: this.conversationClient,\n attachmentClient: this.attachmentClient,\n renderClient: this.renderClient,\n awaiting: options.awaiting ?? false,\n })\n\n if (options.responseSchema) {\n streamer.setResponseSchema(options.responseSchema)\n }\n if (options.messageMetadata) {\n streamer.setMessageMetadata(options.messageMetadata)\n }\n\n return [conversationId, streamer]\n }\n\n /**\n * Mark agent as ready to consume messages.\n */\n startConsuming(): void {\n if (this.messageHandlers.length === 0) {\n throw new ConfigurationError('No message handlers registered')\n }\n\n if (!this.initialized) {\n throw new ConfigurationError('Agent not initialized with gateway')\n }\n\n this.running = true\n console.log(`Agent ${this.name} is now consuming messages`)\n }\n\n /**\n * Shutdown agent gracefully.\n */\n shutdown(): void {\n this.running = false\n\n if (this.heartbeatManager) {\n this.heartbeatManager.stop()\n }\n\n console.log(`Agent ${this.name} shutdown`)\n }\n\n /**\n * Check if agent is running.\n */\n isRunning(): boolean {\n return this.running\n }\n}\n","/**\n * Basion Agent SDK - Agent State Client\n * Port of Python basion_agent/agent_state_client.py\n */\n\nimport { APIException } from './exceptions.js'\n\nexport interface AgentStateResponse {\n conversationId: string\n namespace: string\n state: Record<string, unknown>\n createdAt?: string\n updatedAt?: string\n}\n\n/**\n * Async HTTP client for agent-state API.\n * Used for storing framework-specific state (e.g., Pydantic AI messages).\n */\nexport class AgentStateClient {\n private readonly baseUrl: string\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n }\n\n /**\n * Store or update agent state.\n */\n async put(\n conversationId: string,\n namespace: string,\n state: Record<string, unknown>\n ): Promise<{ conversationId: string; namespace: string; created: boolean }> {\n const url = `${this.baseUrl}/agent-state/${conversationId}`\n const payload = {\n namespace,\n state,\n }\n\n try {\n const response = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to store agent state: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversationId: string; namespace: string; created: boolean }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to store agent state: ${error}`)\n }\n }\n\n /**\n * Get agent state.\n */\n async get(\n conversationId: string,\n namespace: string\n ): Promise<AgentStateResponse | null> {\n const url = new URL(`${this.baseUrl}/agent-state/${conversationId}`)\n url.searchParams.set('namespace', namespace)\n\n try {\n const response = await fetch(url.toString())\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to get agent state: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as AgentStateResponse\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to get agent state: ${error}`)\n }\n }\n\n /**\n * Delete agent state.\n */\n async delete(\n conversationId: string,\n namespace?: string\n ): Promise<{ conversationId: string; namespace?: string; deleted: number }> {\n const url = new URL(`${this.baseUrl}/agent-state/${conversationId}`)\n if (namespace) {\n url.searchParams.set('namespace', namespace)\n }\n\n try {\n const response = await fetch(url.toString(), { method: 'DELETE' })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to delete agent state: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversationId: string; namespace?: string; deleted: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to delete agent state: ${error}`)\n }\n }\n\n // ── Vercel AI Messages (row-per-message store) ──────────────────────\n\n /**\n * Append Vercel AI messages to a conversation.\n */\n async appendVercelMessages(\n conversationId: string,\n messages: Record<string, unknown>[],\n ): Promise<{ conversation_id: string; appended: number; total: number }> {\n const url = `${this.baseUrl}/vercel-ai-messages/${conversationId}/append`\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ messages }),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to append vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; appended: number; total: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to append vercel messages: ${error}`)\n }\n }\n\n /**\n * Get Vercel AI messages for a conversation.\n * Returns AgentStateResponse-compatible shape or null if not found.\n */\n async getVercelMessages(\n conversationId: string,\n last?: number,\n ): Promise<AgentStateResponse | null> {\n const url = new URL(`${this.baseUrl}/vercel-ai-messages/${conversationId}`)\n if (last !== undefined) {\n url.searchParams.set('last', String(last))\n }\n\n try {\n const response = await fetch(url.toString())\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to get vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as AgentStateResponse\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to get vercel messages: ${error}`)\n }\n }\n\n /**\n * Prune Vercel AI messages before a given sequence number.\n * Pruned messages are excluded from load but retained for audit.\n */\n async pruneVercelMessages(\n conversationId: string,\n beforeSequence: number,\n ): Promise<{ conversation_id: string; pruned: number }> {\n const url = `${this.baseUrl}/vercel-ai-messages/${conversationId}/prune`\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ before_sequence: beforeSequence }),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to prune vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; pruned: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to prune vercel messages: ${error}`)\n }\n }\n\n /**\n * Delete all Vercel AI messages for a conversation.\n */\n async deleteVercelMessages(\n conversationId: string,\n ): Promise<{ conversation_id: string; deleted: number }> {\n const url = `${this.baseUrl}/vercel-ai-messages/${conversationId}`\n\n try {\n const response = await fetch(url, { method: 'DELETE' })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to delete vercel messages: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; deleted: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to delete vercel messages: ${error}`)\n }\n }\n\n // ── Filesystem Snapshots (row-per-snapshot store) ─────────────────────\n\n /**\n * Append filesystem snapshots to a conversation.\n */\n async appendFilesystemSnapshots(\n conversationId: string,\n snapshots: Record<string, unknown>[],\n ): Promise<{ conversation_id: string; appended: number; total: number }> {\n const url = `${this.baseUrl}/filesystem-snapshots/${conversationId}/append`\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ snapshots }),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to append filesystem snapshots: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as { conversation_id: string; appended: number; total: number }\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to append filesystem snapshots: ${error}`)\n }\n }\n\n /**\n * Get filesystem snapshots for a conversation.\n * Returns AgentStateResponse-compatible shape or null if not found.\n */\n async getFilesystemSnapshots(\n conversationId: string,\n ): Promise<AgentStateResponse | null> {\n const url = `${this.baseUrl}/filesystem-snapshots/${conversationId}`\n\n try {\n const response = await fetch(url.toString())\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new APIException(`Failed to get filesystem snapshots: ${response.status} - ${errorText}`, response.status)\n }\n\n return await response.json() as AgentStateResponse\n } catch (error) {\n if (error instanceof APIException) throw error\n throw new APIException(`Failed to get filesystem snapshots: ${error}`)\n }\n }\n}\n","/**\n * Basion Agent SDK - Vercel AI SDK Extension\n *\n * VercelAIMessageStore for persistent message history with Vercel AI SDK.\n *\n * @packageDocumentation\n */\n\nimport type { BasionAgentApp } from '../app.js'\nimport { _messageContext } from '../agent.js'\nimport { AgentStateClient } from '../agent-state-client.js'\n\n// Type definitions compatible with Vercel AI SDK's CoreMessage\ninterface CoreMessage {\n role: 'system' | 'user' | 'assistant' | 'tool'\n content: string | Array<{ type: string;[key: string]: unknown }>\n [key: string]: unknown\n}\n\n/**\n * Persistent message store for Vercel AI SDK.\n *\n * Stores and retrieves AI SDK message history using the agent-state API.\n * Cross-system message IDs (`_messageId`) are embedded automatically — no\n * manual ID passing required.\n *\n * @example\n * ```typescript\n * import { BasionAgentApp } from 'basion-ai-sdk'\n * import { VercelAIMessageStore } from 'basion-ai-sdk/extensions/vercel-ai'\n * import { streamText } from 'ai'\n * import { anthropic } from '@ai-sdk/anthropic'\n *\n * const app = new BasionAgentApp({ gatewayUrl: 'agent-gateway:8080', apiKey: 'your-key' })\n * const store = new VercelAIMessageStore(app)\n * const agent = await app.registerMe({ ... })\n *\n * agent.onMessage(async (message) => {\n * const history = await store.load(message.conversationId)\n *\n * const messages = [...history, { role: 'user', content: message.content }]\n *\n * const result = await streamText({ model: anthropic('claude-sonnet-4-6'), messages })\n *\n * // _messageId is embedded automatically — no IDs to pass\n * await store.append(message.conversationId, [\n * { role: 'user', content: message.content },\n * ...result.response.messages,\n * ])\n * })\n *\n * app.run()\n * ```\n */\nexport class VercelAIMessageStore {\n private static readonly NAMESPACE = 'vercel_ai'\n private readonly client: AgentStateClient\n\n constructor(app: BasionAgentApp) {\n this.client = new AgentStateClient(app.gatewayClient.conversationStoreUrl)\n }\n\n /**\n * Load message history for a conversation.\n *\n * @param conversationId - The conversation ID to load history for\n * @returns Array of CoreMessage objects (empty array if no history)\n */\n async load(conversationId: string): Promise<CoreMessage[]> {\n const result = await this.client.getVercelMessages(conversationId)\n\n if (!result) {\n return []\n }\n\n const state = result.state ?? {}\n const messages = state.messages as unknown[]\n\n if (!Array.isArray(messages)) {\n return []\n }\n\n return messages.filter(this.isValidMessage) as CoreMessage[]\n }\n\n /**\n * Append new messages to existing history.\n *\n * Cross-system `_messageId` fields are embedded automatically from the\n * current message handler context — no IDs need to be passed manually.\n *\n * @param conversationId - The conversation ID\n * @param newMessages - New messages to append (user turn + response messages)\n */\n async append(conversationId: string, newMessages: CoreMessage[]): Promise<void> {\n const ctx = _messageContext.getStore()\n const tagged = ctx ? this.embedMessageIds(newMessages, ctx) : newMessages\n await this.client.appendVercelMessages(\n conversationId,\n tagged.map(this.serializeMessage),\n )\n }\n\n /**\n * Clear message history for a conversation.\n *\n * @param conversationId - The conversation ID to clear history for\n */\n async clear(conversationId: string): Promise<void> {\n await this.client.deleteVercelMessages(conversationId)\n }\n\n /**\n * Prune messages before a given sequence number.\n *\n * Called after server-side compaction fires. Pruned messages are excluded\n * from `load()` but retained in the database for audit.\n *\n * @param conversationId - The conversation ID\n * @param beforeSequence - Prune all messages with sequence < this value\n * @returns Number of messages pruned\n */\n async pruneBeforeSequence(conversationId: string, beforeSequence: number): Promise<number> {\n const result = await this.client.pruneVercelMessages(conversationId, beforeSequence)\n return result.pruned\n }\n\n /**\n * Get the last N messages from history.\n *\n * @param conversationId - The conversation ID\n * @param count - Number of messages to retrieve\n */\n async getLastMessages(conversationId: string, count: number): Promise<CoreMessage[]> {\n const result = await this.client.getVercelMessages(conversationId, count)\n\n if (!result) {\n return []\n }\n\n const state = result.state ?? {}\n const messages = state.messages as unknown[]\n\n if (!Array.isArray(messages)) {\n return []\n }\n\n return messages.filter(this.isValidMessage) as CoreMessage[]\n }\n\n private embedMessageIds(\n messages: CoreMessage[],\n ids: { userMessageId?: string; responseMessageId?: string },\n ): CoreMessage[] {\n const result = [...messages]\n\n if (ids.userMessageId) {\n const userIdx = result.findIndex((m) => m.role === 'user')\n if (userIdx !== -1) {\n result[userIdx] = { ...result[userIdx], _messageId: ids.userMessageId }\n }\n }\n\n if (ids.responseMessageId) {\n for (let i = result.length - 1; i >= 0; i--) {\n const m = result[i]\n if (\n m.role === 'assistant' &&\n (typeof m.content === 'string'\n ? m.content.length > 0\n : (m.content as Array<{ type: string }>).some((c) => c.type === 'text'))\n ) {\n result[i] = { ...result[i], _messageId: ids.responseMessageId }\n break\n }\n }\n }\n\n return result\n }\n\n private isValidMessage(msg: unknown): msg is CoreMessage {\n if (typeof msg !== 'object' || msg === null) return false\n const m = msg as Record<string, unknown>\n return (\n typeof m.role === 'string' &&\n ['system', 'user', 'assistant', 'tool'].includes(m.role) &&\n (typeof m.content === 'string' || Array.isArray(m.content))\n )\n }\n\n private serializeMessage(msg: CoreMessage): Record<string, unknown> {\n return { ...msg }\n }\n}\n\nexport type { CoreMessage }\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { G as GatewayClient, S as StructuralStreamer, C as CheckboxConfig, F as Field, a as CheckboxGroupConfig, D as DatePickerConfig, b as FileFieldConfig, A as AttachmentInfo, H as HiddenConfig, M as MultiSelectConfig, N as NumberConfig, O as OptionDef, c as SelectConfig, d as SliderConfig, e as SwitchConfig, T as TextConfig, f as ConfirmationConfig, g as Message, h as ConfirmationResult, I as InferFields, V as ValidationResult, i as StepDef, j as ToDict, k as AccordionItemDef, l as AccordionConfig, m as AccordionComponentDict, n as CardVariant, o as CardSectionDef, p as CardButtonDef, q as CardConfig, r as CardComponentDict } from './app-mRFPU6oe.js';
2
- export { s as Agent, t as AgentInfo, u as AgentInventoryTool, v as AgentRegistrationData, w as AgentSqlResult, x as AppOptions, y as ApproveDraftResult, z as AttachmentClient, B as BasionAgentApp, E as BinaryResult, J as BraintrustConfig, K as BundleRenderOptions, L as Capability, P as CapabilityItem, Q as ChunkResult, R as ConnectionState, U as ConnectionStateListener, W as Conversation, X as ConversationClient, Y as ConversationData, Z as ConversationMessage, _ as DEFAULT_RECONNECTION_OPTIONS, $ as DraftDecision, a0 as DraftResult, a1 as Edge, a2 as Entity, a3 as ExpandValueSetResult, a4 as GenuiComponent, a5 as GenuiComponentDict, a6 as GetMessagesOptions, a7 as GroupedMatch, a8 as GroupedMatchEntry, a9 as HeartbeatFailureCallback, aa as HeartbeatManager, ab as HeartbeatOptions, ac as IngestResponse, ad as KafkaHeaders, ae as KafkaMessage, af as KnowledgeGraphTool, ag as LogEntry, ah as LogLevel, ai as LokiHandlerOptions, aj as LokiLogHandler, ak as LookupCodeResult, al as MatchingClient, am as MatchingTool, an as Memory, ao as MemoryClient, ap as MemoryIngestOptions, aq as MemoryMessage, ar as MemorySearchOptions, as as MemorySearchResult, at as MemoryV2, au as MemoryV2Client, av as MemoryV2IngestPayload, aw as MemoryV2SearchPayload, ax as MessageHandler, ay as Need, az as NeedItem, aA as NeedWithMatches, aB as PathStep, aC as PatientProfileClient, aD as PdfOptions, aE as ProduceAck, aF as Prompt, aG as QueryOptions, aH as ReconnectionOptions, aI as RegisterOptions, aJ as RelatedPage, aK as RenderBufferResult, aL as RenderClient, aM as RenderOptions, aN as RenderResult, aO as SearchMatch, aP as SearchOptions, aQ as SearchResult, aR as SenderFilter, aS as SenderFilterOptions, aT as SentryConfig, aU as SentryLoggerProxy, aV as SimilarDisease, aW as StreamOptions, aX as Streamer, aY as StreamerOptions, aZ as Tools, a_ as UserSummary, a$ as ValidateCodeResult, b0 as createAttachmentInfo, b1 as getSentryLogger, b2 as isBraintrustEnabled, b3 as isSentryEnabled, b4 as reportPath } from './app-mRFPU6oe.js';
1
+ import { G as GatewayClient, S as StructuralStreamer, C as CheckboxConfig, F as Field, a as CheckboxGroupConfig, D as DatePickerConfig, b as FileFieldConfig, A as AttachmentInfo, H as HiddenConfig, M as MultiSelectConfig, N as NumberConfig, O as OptionDef, c as SelectConfig, d as SliderConfig, e as SwitchConfig, T as TextConfig, f as ConfirmationConfig, g as Message, h as ConfirmationResult, I as InferFields, V as ValidationResult, i as StepDef, j as ToDict, k as AccordionItemDef, l as AccordionConfig, m as AccordionComponentDict, n as CardVariant, o as CardSectionDef, p as CardButtonDef, q as CardConfig, r as CardComponentDict, B as BasionAgentApp, s as Agent, J as JudgeHandler, t as MessageHandler } from './app-ChZ3-kr7.js';
2
+ export { u as AgentInfo, v as AgentInventoryTool, w as AgentRegistrationData, x as AgentSqlResult, y as AppOptions, z as ApproveDraftResult, E as AttachmentClient, K as BinaryResult, L as BraintrustConfig, P as BundleRenderOptions, Q as Capability, R as CapabilityItem, U as ChunkResult, W as ConnectionState, X as ConnectionStateListener, Y as Conversation, Z as ConversationClient, _ as ConversationData, $ as ConversationMessage, a0 as DEFAULT_RECONNECTION_OPTIONS, a1 as DraftDecision, a2 as DraftResult, a3 as Edge, a4 as Entity, a5 as ExpandValueSetResult, a6 as GenuiComponent, a7 as GenuiComponentDict, a8 as GetMessagesOptions, a9 as GroupedMatch, aa as GroupedMatchEntry, ab as HeartbeatFailureCallback, ac as HeartbeatManager, ad as HeartbeatOptions, ae as IngestResponse, af as JudgeContext, ag as JudgeMetricDef, ah as JudgeRequestPayload, ai as JudgeVerdict, aj as JudgeVerdictMetric, ak as KafkaHeaders, al as KafkaMessage, am as KnowledgeGraphTool, an as LogEntry, ao as LogLevel, ap as LokiHandlerOptions, aq as LokiLogHandler, ar as LookupCodeResult, as as MatchingClient, at as MatchingTool, au as Memory, av as MemoryClient, aw as MemoryIngestOptions, ax as MemoryMessage, ay as MemorySearchOptions, az as MemorySearchResult, aA as MemoryV2, aB as MemoryV2Client, aC as MemoryV2IngestPayload, aD as MemoryV2SearchPayload, aE as Need, aF as NeedItem, aG as NeedWithMatches, aH as PathStep, aI as PatientProfileClient, aJ as PdfOptions, aK as ProduceAck, aL as Prompt, aM as QueryOptions, aN as ReconnectionOptions, aO as RegisterJudgeOptions, aP as RegisterOptions, aQ as RelatedPage, aR as RenderBufferResult, aS as RenderClient, aT as RenderOptions, aU as RenderResult, aV as SearchMatch, aW as SearchOptions, aX as SearchResult, aY as SenderFilter, aZ as SenderFilterOptions, a_ as SentryConfig, a$ as SentryLoggerProxy, b0 as SimilarDisease, b1 as StreamOptions, b2 as Streamer, b3 as StreamerOptions, b4 as Tools, b5 as UserSummary, b6 as ValidateCodeResult, b7 as createAttachmentInfo, b8 as getSentryLogger, b9 as isBraintrustEnabled, ba as isSentryEnabled, bb as reportPath } from './app-ChZ3-kr7.js';
3
3
  export { V as VfsBatchOperation, a as VfsBranch, b as VfsClient, c as VfsCommit, d as VfsCreateBranchOptions, e as VfsCreateFilesystemOptions, f as VfsDeleteFileOptions, g as VfsDiffEntry, h as VfsDiffResult, i as VfsFilesystem, j as VfsMergeConflict, k as VfsMergeResult, l as VfsReadFileResult, m as VfsTreeEntry, n as VfsWriteFileOptions, o as VfsWriteFileResponse } from './vfs-client-CsHAFj1R.js';
4
4
  import 'zod';
5
5
 
@@ -829,6 +829,7 @@ declare const SERVICES: {
829
829
  readonly REPORT_GENERATOR: "report-generator";
830
830
  readonly MATCHING_ENGINE: "matching-engine";
831
831
  readonly VFS: "vfs";
832
+ readonly JUDGE: "judge";
832
833
  };
833
834
  type ServiceName = typeof SERVICES[keyof typeof SERVICES];
834
835
 
@@ -890,12 +891,32 @@ declare class ReconnectionError extends BasionAgentError {
890
891
  constructor(message: string, attempts: number, lastError?: Error);
891
892
  }
892
893
 
894
+ /**
895
+ * Basion Agent SDK - Judge Extension
896
+ *
897
+ * Wraps a JudgeHandler as a MessageHandler bound to a judge-request call.
898
+ * Mirrors companion's createUserMessageHandler:
899
+ * judgeAgent.onMessage(createJudgeHandler(app, judgeAgent, async (ctx) => {...}))
900
+ */
901
+
902
+ /**
903
+ * Create a MessageHandler that processes an incoming judge-request call,
904
+ * loads both views of the target conversation in parallel, runs the author's
905
+ * JudgeHandler, and transports the verdict back via `finish()` metadata.
906
+ *
907
+ * @param app - The BasionAgentApp instance (provides conversationClient, etc.)
908
+ * @param agent - The judge Agent instance (provides `streamer()`)
909
+ * @param handler - The author-supplied JudgeHandler function
910
+ * @returns A MessageHandler ready for `judgeAgent.onMessage(...)`
911
+ */
912
+ declare function createJudgeHandler(app: BasionAgentApp, agent: Agent, handler: JudgeHandler): MessageHandler;
913
+
893
914
  /**
894
915
  * Basion Agent SDK - TypeScript framework for building AI agents
895
916
  *
896
917
  * @packageDocumentation
897
918
  */
898
919
 
899
- declare const VERSION = "0.5.1";
920
+ declare const VERSION = "0.19.0";
900
921
 
901
- export { AI_INVENTORY_PATHS, APIException, Accordion, AccordionConfig, AccordionItemDef, AgentStateClient, type AgentStateResponse, Artifact, AttachmentInfo, BasionAgentError, CONVERSATION_STORE_PATHS, Card, CardButtonDef, CardConfig, CardSectionDef, CardVariant, CheckpointClient, type CheckpointConfig, type CheckpointData, type CheckpointMetadata, type CheckpointTuple, ConfigurationError, Confirmation, ConfirmationConfig, ConfirmationResult, type DlqCorrelation, type DlqInput, type DlqSource, type DlqSourceKind, Field, Form, GatewayClient, GatewayError, HeartbeatError, InferFields, KafkaError, MATCHING_ENGINE_PATHS, MEMORY_PATHS, MatchCard, Message, MultiStepForm, OptionDef, ReconnectionError, RegistrationError, SERVICES, type ServiceName, StepDef, Stepper, StructuralStreamer, Surface, TextBlock, VERSION, ValidationResult, accordion, card, checkbox, checkboxGroup, confirmation, datePicker, file, hidden, multiSelect, number, option, select, sendDlqEnvelope, slider, step, switchField, text };
922
+ export { AI_INVENTORY_PATHS, APIException, Accordion, AccordionConfig, AccordionItemDef, Agent, AgentStateClient, type AgentStateResponse, Artifact, AttachmentInfo, BasionAgentApp, BasionAgentError, CONVERSATION_STORE_PATHS, Card, CardButtonDef, CardConfig, CardSectionDef, CardVariant, CheckpointClient, type CheckpointConfig, type CheckpointData, type CheckpointMetadata, type CheckpointTuple, ConfigurationError, Confirmation, ConfirmationConfig, ConfirmationResult, type DlqCorrelation, type DlqInput, type DlqSource, type DlqSourceKind, Field, Form, GatewayClient, GatewayError, HeartbeatError, InferFields, JudgeHandler, KafkaError, MATCHING_ENGINE_PATHS, MEMORY_PATHS, MatchCard, Message, MessageHandler, MultiStepForm, OptionDef, ReconnectionError, RegistrationError, SERVICES, type ServiceName, StepDef, Stepper, StructuralStreamer, Surface, TextBlock, VERSION, ValidationResult, accordion, card, checkbox, checkboxGroup, confirmation, createJudgeHandler, datePicker, file, hidden, multiSelect, number, option, select, sendDlqEnvelope, slider, step, switchField, text };