import EventEmitter from 'eventemitter3'; /** * Agent SDK types — mirrors the rendezvous server protocol. */ interface AnycastAgentOptions { /** WebSocket URL of the rendezvous server (e.g., wss://agents.anycast.com/rendezvous) */ rendezvousUrl: string; /** Agent auth token from the Anycast portal */ token: string; /** Human-readable agent name (e.g., 'anycast-observer') */ name?: string; /** Unique machine identifier (auto-generated if not provided) */ machineId?: string; /** Public key for end-to-end encryption */ publicKey?: string; /** Agent version string */ version?: string; /** Auto-reconnect on disconnect (default: true) */ reconnect?: boolean; /** Reconnect interval in ms (default: 3000) */ reconnectInterval?: number; /** Connection timeout in ms (default: 30000) */ timeout?: number; /** Heartbeat interval in ms (default: 25000) */ heartbeatInterval?: number; /** Portal base URL for preflight (default: https://agents.anycast.com) */ portalUrl?: string; /** Internal API key for usage tracking (POST /api/internal/agents/usage) */ internalKey?: string; /** Auto-save context on disconnect, auto-restore on connect (default: false) */ persistContext?: boolean; /** Discovery mode: 'rendezvous' (default), 'auto' (DHT with fallback), 'dht' (DHT only) */ discoveryMode?: 'rendezvous' | 'dht' | 'auto'; /** Enable WireGuard tunnel support (default: false) */ wireguard?: boolean; } declare enum ConnectionState { DISCONNECTED = "DISCONNECTED", CONNECTING = "CONNECTING", CONNECTED = "CONNECTED", RECONNECTING = "RECONNECTING" } interface AnycastAgentEvents { connect: (info: { agentId: string; tenantId: string; name: string; popIata?: string; }) => void; disconnect: (reason: string) => void; reconnecting: (attempt: number) => void; 'context_restored': (context: Record | null) => void; message: (payload: { messageId: string; text: string; context?: Record; reply: (text: string) => void; }) => void; peer: (agentId: string, status: 'online' | 'offline') => void; 'connect_incoming': (sessionId: string, fromAgentId: string, fromAgentName: string) => void; 'connect_accepted': (sessionId: string, agentId: string, publicKey: string) => void; 'connect_rejected': (sessionId: string, reason: string) => void; signal: (sessionId: string, fromAgentId: string, signal: { type: string; data: string; }) => void; 'relay_assigned': (sessionId: string, relayHost: string, relayPort: number, relayToken: string) => void; error: (error: AnycastError) => void; stateChange: (state: ConnectionState) => void; interrupted: (data: { reason: string; }) => void; discovery: (info: { mode: 'rendezvous' | 'dht' | 'auto'; fallback: boolean; }) => void; tunnel: (info: { tunnelId: string; localIP: string; peerIP: string; peerAgentId: string; }) => void; } declare class AnycastError extends Error { code: string; fatal: boolean; constructor(message: string, code: string, fatal?: boolean); } interface AnycastPluginContext { agentId?: string; agentName?: string; agentToken: string; sessionId?: string; attributes: Record; emit: (eventName: string, data?: Record) => Promise; } interface AnycastPlugin { name: string; onSessionStart?: (ctx: AnycastPluginContext) => void | Promise; onToolUse?: (ctx: AnycastPluginContext, toolName: string, input?: unknown) => void | Promise; onResult?: (ctx: AnycastPluginContext, usage: { promptTokens: number; completionTokens: number; cacheReadTokens: number; cacheCreationTokens: number; costUsd: number; numTurns: number; durationMs: number; }) => void | Promise; onSessionEnd?: (ctx: AnycastPluginContext) => void | Promise; } /** Shared memory client interface */ interface AgentMemoryClient { get(key: string, options?: { scope?: 'agent' | 'tenant'; }): Promise; set(key: string, value: unknown, options?: { scope?: 'agent' | 'tenant'; ttl?: number; }): Promise; delete(key: string, options?: { scope?: 'agent' | 'tenant'; }): Promise; list(prefix?: string, options?: { scope?: 'agent' | 'tenant'; limit?: number; }): Promise>; } /** Session context client interface */ interface AgentContextClient { save(data: Record): Promise; restore(): Promise | null>; clear(): Promise; } /** * AnycastAgent — connects to the Anycast Agents rendezvous server * for P2P connectivity, signaling, and relay fallback. * * @example * ```typescript * const agent = new AnycastAgent({ * rendezvousUrl: 'wss://agents.anycast.com/rendezvous', * token: 'agt_...', * }); * * agent.on('connect', ({ agentId }) => { * console.log(`Connected as ${agentId}`); * }); * * agent.on('peer', (peerId, status) => { * console.log(`Peer ${peerId} is ${status}`); * }); * * await agent.connect(); * ``` */ declare class AnycastAgent extends EventEmitter { private ws; private state; private heartbeatTimer; private reconnectTimer; private reconnectAttempt; private intentionalClose; /** Agent ID assigned by the server after registration */ agentId: string | null; /** Tenant ID from the server */ tenantId: string | null; /** Agent name from the server */ agentName: string | null; /** PoP IATA code the agent is connected through */ popIata: string | null; /** WAN IP (NAT gateway / public IP as seen by portal) */ wanIp: string | null; /** Internal IP (agent's own network interface) */ internalIp: string | null; /** Shared memory client for persistent key-value storage */ readonly memory: AgentMemoryClient; /** Session context client for save/restore across restarts */ readonly context: AgentContextClient; private _contextData; private _persistContext; /** WireGuard Curve25519 keypair for tunnel negotiation */ private wgKeyPair; private readonly opts; constructor(options: AnycastAgentOptions); /** * Set in-memory context data that will be auto-saved on disconnect * (only effective when persistContext: true). */ setContext(data: Record): void; /** * Get the WireGuard public key for this agent (null if WireGuard is disabled). */ getWireguardPublicKey(): string | null; /** * Request a WireGuard tunnel to a target agent. * Creates a tunnel record via the portal API and returns tunnel metadata. * The actual WireGuard interface is brought up when the peer accepts (tunnel_accepted). */ requestTunnel(targetAgentId: string): Promise<{ tunnelId: string; localIP: string; peerIP: string; active: boolean; close: () => Promise; }>; /** Current connection state */ get connectionState(): ConnectionState; /** Whether the agent is connected */ get connected(): boolean; /** * Connect to the rendezvous server. * Resolves when registered, rejects on timeout or fatal error. */ connect(): Promise; /** * Disconnect from the rendezvous server. */ disconnect(): Promise; /** * Request a connection to a peer agent. * @returns The session ID for this connection */ requestConnection(targetAgentId: string): string; /** * Send a WebRTC signal to a peer via the rendezvous server. */ sendSignal(sessionId: string, targetAgentId: string, signal: { type: 'offer' | 'answer' | 'ice'; data: string; }): void; /** * Disconnect a session. */ disconnectSession(sessionId: string): void; /** * Reply to a portal_message received via the 'message' event. * Must be called within the timeout window (default 30s). */ replyToMessage(messageId: string, text: string): void; /** * Track LLM token usage for this agent. Reports to the portal for cost tracking. */ trackUsage(usage: { model: string; promptTokens: number; completionTokens: number; triggeredBy?: string; triggeredByName?: string; context?: string; }): Promise; /** * Wrap an Anthropic SDK client to auto-report token usage after every call. * Does NOT require @anthropic-ai/sdk as a dependency — works via Proxy. */ wrapAnthropic(client: T, options?: { triggeredBy?: string; triggeredByName?: string; context?: string; }): T; /** * Wrap an OpenAI SDK client to auto-report token usage after every call. * Does NOT require openai as a dependency — works via Proxy. */ wrapOpenAI(client: T, options?: { triggeredBy?: string; triggeredByName?: string; context?: string; }): T; /** * Generic wrapper for any async function — extracts usage via a callback. */ tracked(fn: () => Promise, options: { model: string; extractUsage: (result: R) => { promptTokens: number; completionTokens: number; }; triggeredBy?: string; triggeredByName?: string; context?: string; }): Promise; private handleMessage; private send; private startHeartbeat; private stopHeartbeat; private scheduleReconnect; private cleanup; private setState; private assertConnected; } /** * withAnycast() — transparent observability wrapper for @anthropic-ai/claude-agent-sdk sessions. * * Intercepts the session's stream() to forward usage, tool-call, and custom event data * to the Anycast portal without modifying any event data or blocking the stream. * * Features: * - Telemetry: usage + tool calls forwarded automatically * - Attributes: arbitrary KV attached to all telemetry (OTel-aligned) * - emit(): custom event emission at any point * - Plugins: lifecycle hooks (onSessionStart, onToolUse, onResult, onSessionEnd) * - Memory: persistent KV via portal API * - Session persistence: auto-save session_id for resume */ interface AgentSDKContentBlock { type: 'text' | 'tool_use' | string; text?: string; name?: string; id?: string; input?: unknown; } interface AgentSDKEvent { type: 'assistant' | 'tool_use_summary' | 'result' | string; message?: { content: AgentSDKContentBlock[]; }; summary?: string; session_id?: string; usage?: { input_tokens?: number; output_tokens?: number; cache_read_input_tokens?: number; cache_creation_input_tokens?: number; }; total_cost_usd?: number; num_turns?: number; duration_ms?: number; duration_api_ms?: number; } interface AgentSDKSession { stream(): AsyncIterable; send(prompt: string): Promise; close(): void; sessionId?: string; } interface WithAnycastOptions { agentToken: string; portalUrl?: string; agentName?: string; debug?: boolean; enableMemory?: boolean; memoryScope?: 'agent' | 'tenant'; persistSession?: boolean; agentId?: string; /** Key index from resolveApiKey() — forwarded in usage reports for per-key tracking */ _resolvedKeyIndex?: number; /** Arbitrary attributes attached to all telemetry. Use OTel semantic conventions. */ attributes?: Record; /** Lifecycle hook plugins */ plugins?: AnycastPlugin[]; } declare class AnycastMemory { private agentToken; private portalUrl; private scope; private debug; constructor(agentToken: string, portalUrl: string, scope: 'agent' | 'tenant', debug?: boolean); get(key: string): Promise; set(key: string, value: unknown, options?: { ttl?: number; }): Promise; list(prefix?: string): Promise; delete(key: string): Promise; } declare function withAnycast(session: T, options: WithAnycastOptions): T & { memory: AnycastMemory; emit: (eventName: string, data?: Record) => Promise; }; declare namespace withAnycast { var getLastSessionId: (agentToken: string, options?: { portalUrl?: string; agentId?: string; }) => Promise; var resolveApiKey: (options: { agentToken: string; portalUrl?: string; model?: string; provider?: string; }) => Promise<{ apiKey: string; provider: string; providerName: string; model: string | null; keyIndex: number; providerId: string; }>; } /** * withAnycastOpenAI() — transparent telemetry wrapper for @openai/agents streams. * * Tested against @openai/agents 0.x * OpenAI Agents SDK is evolving rapidly. If this wrapper breaks after an SDK update, * check: https://github.com/openai/openai-agents-js * and update the event type interface below. */ interface OpenAIAgentStreamEvent { type: 'raw_model_stream_event' | 'run_item_stream_event' | 'agent_updated_stream_event' | 'run_stream_done' | string; item?: { type: 'message_output_item' | 'tool_call_item' | 'tool_call_output_item' | string; rawItem?: { name?: string; type?: string; }; }; finalOutput?: { rawResponses?: Array<{ usage?: { input_tokens?: number; output_tokens?: number; total_tokens?: number; }; }>; }; } interface OpenAIAgentStream { [Symbol.asyncIterator](): AsyncIterator; } interface WithAnycastOpenAIOptions { /** Agent token from the Anycast portal */ agentToken: string; /** Portal URL (default: https://agents.anycast.com) */ portalUrl?: string; /** Display name in portal */ agentName?: string; /** Log forwarded events to console */ debug?: boolean; /** Attach .memory API to the stream (default: false) */ enableMemory?: boolean; /** Auto-persist session state (default: false) */ persistSession?: boolean; } /** * Wrap an OpenAI Agents SDK stream with Anycast telemetry. * * @example * ```js * const { run } = require('@openai/agents') * const { withAnycastOpenAI } = require('@anycast/agent') * * const stream = withAnycastOpenAI( * await run(agent, prompt, { stream: true }), * { agentToken: process.env.ANYCAST_TOKEN } * ) * for await (const event of stream) { ... } * ``` */ declare function withAnycastOpenAI(stream: T, options: WithAnycastOpenAIOptions): T & { memory: AnycastMemory; }; interface SlackPluginOptions { userId: string; userName: string; channelId: string; channelName: string; threadTs: string; teamId?: string; } /** * Slack plugin for withAnycast() — emits structured Slack context events. * * @example * ```js * const session = withAnycast(rawSession, { * agentToken: process.env.ANYCAST_TOKEN, * attributes: { * 'user.id': event.user, * 'messaging.system': 'slack', * 'messaging.destination': channelName, * 'messaging.destination_id': channelId, * 'messaging.conversation_id': threadTs, * }, * plugins: [slackPlugin({ userId, userName, channelId, channelName, threadTs })] * }) * ``` */ declare function slackPlugin(opts: SlackPluginOptions): AnycastPlugin; export { type AgentContextClient, type AgentMemoryClient, AnycastAgent, type AnycastAgentEvents, type AnycastAgentOptions, AnycastError, AnycastMemory, type AnycastPlugin, type AnycastPluginContext, ConnectionState, type SlackPluginOptions, type WithAnycastOpenAIOptions, type WithAnycastOptions, slackPlugin, withAnycast, withAnycastOpenAI };