@channlize/core 0.2.1 → 0.5.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.
- package/CHANGELOG.md +46 -0
- package/README.md +316 -15
- package/dist/index.d.mts +1355 -19
- package/dist/index.d.ts +1355 -19
- package/dist/index.js +1152 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1114 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -6
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/http.ts","../src/types.ts","../src/notify.ts","../src/client.ts"],"sourcesContent":["/**\n * @channlize/core\n * Core TypeScript SDK for Channlize platform\n */\n\nexport { ChannlizeClient } from './client';\nexport { createNotifier } from './notify';\nexport type { Notifier } from './notify';\nexport * from './types';\n\n// Re-export for convenience\nexport { ChannlizeClient as default } from './client';\n","import fetch from 'cross-fetch';\nimport type { ApiResponse } from './types';\nimport {\n ChannlizeError,\n AuthenticationError,\n ValidationError,\n RateLimitError,\n} from './types';\n\n/**\n * Shared HTTP layer for `ChannlizeClient` and `createNotifier`: JSON\n * requests with a timeout, and the API's error shape mapped to typed errors.\n */\nexport async function requestJson<T>(\n http: { baseUrl: string; timeout: number },\n endpoint: string,\n options: RequestInit = {}\n): Promise<ApiResponse<T>> {\n const url = `${http.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), http.timeout);\n\n try {\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string>),\n };\n\n // When Content-Type is explicitly empty (e.g. FormData), remove it so the\n // browser / runtime can set the correct multipart boundary automatically.\n if (mergedHeaders['Content-Type'] === '') {\n delete mergedHeaders['Content-Type'];\n }\n\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: mergedHeaders,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data as ApiResponse<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ChannlizeError) {\n throw error;\n }\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new ChannlizeError('Request timeout', 408, 'TIMEOUT');\n }\n\n throw new ChannlizeError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n}\n\nexport async function handleErrorResponse(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let errorData: any = {};\n\n if (contentType?.includes('application/json')) {\n try {\n errorData = await response.json();\n } catch {\n // Ignore JSON parsing errors\n }\n }\n\n // API error shape (api/lib/api/error.ts):\n // { statusCode, error: \"BAD_REQUEST\", message: string | string[] }\n // `error` is the machine-readable code; `message` is the human text\n // (an array of field messages for validation failures).\n const rawMessage = errorData.message;\n const message =\n (Array.isArray(rawMessage) ? rawMessage.join(', ') : rawMessage) ||\n `HTTP ${response.status}: ${response.statusText}`;\n const code: string | undefined = errorData.code ?? errorData.error;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 400:\n throw new ValidationError(message, errorData.field);\n case 429:\n throw new RateLimitError(message);\n default:\n throw new ChannlizeError(message, response.status, code);\n }\n}\n","/**\n * Core types for Channlize SDK\n */\n\n/** Severity for log tracking (`trackEvent` / `trackServerEvent`); Slack alerts use project settings. */\nexport type EventSeverity = 'info' | 'warning' | 'error';\n\n/** Options for log tracking (`trackEvent` / `trackServerEvent`). */\nexport interface TrackEventOptions {\n properties?: Record<string, unknown>;\n severity?: EventSeverity;\n distinctId?: string;\n pageUrl?: string;\n userAgent?: string;\n}\n\nexport interface ChannlizeConfig {\n /** Widget key used for public SDK endpoints (contact form, chat, browser log tracking) */\n apiKey: string;\n /** Base URL for the Channlize API */\n baseUrl?: string;\n /** Project ID (required for server-side `trackServerEvent` and `notify`) */\n projectId: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n /**\n * Organization (server) API key, `ch_live_…`, for `trackServerEvent`\n * (`POST /v1/events`) and `notify` (`POST /v1/notify`, needs the `write`\n * scope). Do not expose in browser bundles.\n */\n serverApiKey?: string;\n}\n\n/**\n * Events (notify Slack from code). Augment this interface to type event\n * keys and their data across your app:\n *\n * ```ts\n * declare module '@channlize/core' {\n * interface ChannlizeEvents {\n * 'user.subscribed': { username: string; plan: string };\n * }\n * }\n * ```\n *\n * Or pass a map as a type argument: `createNotifier<AppEvents>()`.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ChannlizeEvents {}\n\n/** A declared event key (autocompleted) or any other key. */\nexport type NotifyEventName<TEvents extends object = ChannlizeEvents> =\n (keyof TEvents & string) | (string & {});\n\n/** The declared data for a key, or any object for undeclared keys. */\nexport type NotifyEventData<\n TEvents extends object = ChannlizeEvents,\n K = string,\n> = K extends keyof TEvents ? TEvents[K] : Record<string, unknown>;\n\nexport interface NotifyOptions {\n /**\n * Retry-safe sends: a key already used on the project returns the first\n * delivery (`duplicate: true`) and posts nothing. Sent as `Idempotency-Key`.\n */\n idempotencyKey?: string;\n /** Slack channel ID to post to instead of the event's channel, for this call. */\n channel?: string;\n}\n\nexport interface NotifyResponse {\n success: boolean;\n /** Delivery ID (see Events → Recent deliveries in the dashboard). */\n id: string;\n event: string;\n /**\n * `queued`: will be posted to Slack. `skipped`: not posted (see `reason`):\n * the event is disabled, is new (saved as a draft to configure), or there\n * is no Slack workspace / channel. With `duplicate`, the first call's status.\n */\n status: 'queued' | 'skipped' | 'sent' | 'failed';\n reason?: string;\n duplicate?: boolean;\n}\n\n/** `createNotifier` options; each falls back to an environment variable. */\nexport interface NotifierConfig {\n /**\n * Server API key (`ch_live_…`, write scope). Default:\n * `CHANNLIZE_SERVER_API_KEY`, else `CHANNLIZE_API_KEY`.\n */\n serverApiKey?: string;\n /** Project ID. Default: `CHANNLIZE_PROJECT_ID`. */\n projectId?: string;\n /** API base URL. Default: `CHANNLIZE_BASE_URL`, else `https://api.channlize.com`. */\n baseUrl?: string;\n /** Request timeout in ms (default 10000). */\n timeout?: number;\n}\n\nexport interface ContactFormData {\n /** Contact's name */\n name: string;\n /** Contact's email address */\n email: string;\n /** Subject of the inquiry */\n subject?: string;\n /** Message content */\n message: string;\n /** Custom fields */\n customFields?: Record<string, unknown>;\n /** Page URL where the form was submitted */\n pageUrl?: string;\n /** User agent string */\n userAgent?: string;\n}\n\nexport interface ChatMessage {\n /** Unique message ID */\n id: string;\n /** Message content */\n content: string;\n /** Message timestamp */\n timestamp: Date;\n /** Message author (user or agent) */\n author: 'user' | 'agent';\n /** Author metadata */\n authorData?: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n };\n /** Attached files */\n attachments?: FileAttachment[];\n}\n\nexport interface FileAttachment {\n /** File ID */\n id: string;\n /** Original filename */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n type: string;\n /** Download URL */\n url: string;\n}\n\nexport interface ChatSession {\n /** Session ID */\n id: string;\n /** Session status */\n status: 'active' | 'closed' | 'waiting';\n /** Messages in the session */\n messages: ChatMessage[];\n /** Session metadata */\n metadata?: Record<string, unknown>;\n /** Created timestamp */\n createdAt: Date;\n /** Updated timestamp */\n updatedAt: Date;\n}\n\nexport interface ApiResponse<T = unknown> {\n /** Response data */\n data: T;\n /** Success status */\n success: boolean;\n /** Error message if any */\n error?: string;\n /** Additional metadata */\n meta?: Record<string, unknown>;\n}\n\n/** Response of `POST /public/contact` (api/app/api/public/contact/route.ts). */\nexport interface CreateContactResponse {\n /** Whether the submission was accepted */\n success: boolean;\n /** Conversation the submission was filed under */\n conversationId: string;\n /** Human-readable confirmation to show the visitor */\n message: string;\n /** @deprecated Never returned by the API (always undefined). Use `conversationId`. */\n id: string;\n /** @deprecated Never returned by the API (always undefined). */\n status: 'received' | 'processing' | 'delivered';\n /** @deprecated Never returned by the API (always undefined). */\n slackThreadUrl?: string;\n}\n\nexport interface CreateChatSessionResponse {\n /** Chat session data */\n session: ChatSession;\n /** SSE endpoint for real-time updates */\n sseEndpoint?: string;\n}\n\nexport interface SendMessageResponse {\n /** The sent message */\n message: ChatMessage;\n /** Updated session */\n session: ChatSession;\n}\n\n/**\n * Error types\n */\nexport class ChannlizeError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public code?: string\n ) {\n super(message);\n this.name = 'ChannlizeError';\n }\n}\n\nexport class ValidationError extends ChannlizeError {\n constructor(\n message: string,\n public field?: string\n ) {\n super(message, 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class AuthenticationError extends ChannlizeError {\n constructor(message: string = 'Authentication failed') {\n super(message, 401, 'AUTHENTICATION_ERROR');\n this.name = 'AuthenticationError';\n }\n}\n\nexport class RateLimitError extends ChannlizeError {\n constructor(message: string = 'Rate limit exceeded') {\n super(message, 429, 'RATE_LIMIT_ERROR');\n this.name = 'RateLimitError';\n }\n}\n","import { requestJson } from './http';\nimport type {\n ChannlizeEvents,\n NotifierConfig,\n NotifyEventData,\n NotifyEventName,\n NotifyOptions,\n NotifyResponse,\n} from './types';\nimport { ValidationError } from './types';\n\n/** Same rule as the API: `user.subscribed`, `invoice_paid`, `signup-done`. */\nconst EVENT_KEY = /^[a-z0-9][a-z0-9._-]*$/;\n\nconst DEFAULT_BASE_URL = 'https://api.channlize.com';\n\n/** `POST /v1/notify`, shared by `ChannlizeClient.notify` and `createNotifier`. */\nexport async function sendNotify(\n http: { baseUrl: string; timeout: number },\n auth: { serverApiKey: string; projectId: string },\n event: string,\n data: Record<string, unknown> | undefined,\n options: NotifyOptions = {}\n): Promise<NotifyResponse> {\n if (!auth.projectId) {\n throw new ValidationError('projectId is required for notify', 'projectId');\n }\n if (auth.serverApiKey.startsWith('wk_')) {\n throw new ValidationError(\n 'notify needs a server API key (ch_live_…, write scope), not the public widget key',\n 'serverApiKey'\n );\n }\n if (\n typeof event !== 'string' ||\n event.length > 100 ||\n !EVENT_KEY.test(event)\n ) {\n throw new ValidationError(\n `Invalid event key \"${event}\": use lowercase letters, digits, dots, underscores or dashes (e.g. user.subscribed)`,\n 'event'\n );\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${auth.serverApiKey}`,\n };\n if (options.idempotencyKey) {\n headers['Idempotency-Key'] = options.idempotencyKey;\n }\n\n const res = await requestJson<NotifyResponse>(http, '/v1/notify', {\n method: 'POST',\n headers,\n body: JSON.stringify({\n projectId: auth.projectId,\n event,\n data: data ?? {},\n channel: options.channel,\n }),\n });\n // The API answers at the top level (no `data` envelope).\n return res as unknown as NotifyResponse;\n}\n\nfunction readEnv(name: string): string | undefined {\n // `process` is absent in browsers and some edge runtimes.\n const env =\n typeof process !== 'undefined' && process.env ? process.env : undefined;\n const value = env?.[name];\n return value && value.trim() ? value.trim() : undefined;\n}\n\nexport type Notifier<TEvents extends object = ChannlizeEvents> = <\n K extends NotifyEventName<TEvents>,\n>(\n event: K,\n data?: NotifyEventData<TEvents, K>,\n options?: NotifyOptions\n) => Promise<NotifyResponse>;\n\n/**\n * Events: a standalone server-side `notify(event, data)` function. Reads\n * the server key from `CHANNLIZE_SERVER_API_KEY` or `CHANNLIZE_API_KEY`, and\n * `CHANNLIZE_PROJECT_ID`, unless given — when it is called, so creating it\n * at module load never throws.\n *\n * ```ts\n * const notify = createNotifier();\n * await notify('user.subscribed', { username: 'ada', plan: 'Pro' });\n * ```\n */\nexport function createNotifier<TEvents extends object = ChannlizeEvents>(\n config: NotifierConfig = {}\n): Notifier<TEvents> {\n return (event, data, options) => {\n const serverApiKey =\n config.serverApiKey ??\n readEnv('CHANNLIZE_SERVER_API_KEY') ??\n readEnv('CHANNLIZE_API_KEY');\n const projectId = config.projectId ?? readEnv('CHANNLIZE_PROJECT_ID');\n if (!serverApiKey) {\n return Promise.reject(\n new ValidationError(\n 'A server API key is required: pass serverApiKey or set CHANNLIZE_API_KEY (or CHANNLIZE_SERVER_API_KEY)',\n 'serverApiKey'\n )\n );\n }\n if (!projectId) {\n return Promise.reject(\n new ValidationError(\n 'A project ID is required: pass projectId or set CHANNLIZE_PROJECT_ID',\n 'projectId'\n )\n );\n }\n const baseUrl =\n config.baseUrl ?? readEnv('CHANNLIZE_BASE_URL') ?? DEFAULT_BASE_URL;\n return sendNotify(\n { baseUrl: baseUrl.replace(/\\/$/, ''), timeout: config.timeout ?? 10000 },\n { serverApiKey, projectId },\n event,\n data as Record<string, unknown> | undefined,\n options\n );\n };\n}\n","import { requestJson } from './http';\nimport { sendNotify } from './notify';\nimport type {\n ChannlizeConfig,\n ContactFormData,\n ChatMessage,\n ApiResponse,\n CreateContactResponse,\n CreateChatSessionResponse,\n SendMessageResponse,\n ChatSession,\n TrackEventOptions,\n EventSeverity,\n ChannlizeEvents,\n NotifyEventName,\n NotifyEventData,\n NotifyOptions,\n NotifyResponse,\n} from './types';\nimport { ValidationError } from './types';\n\n/**\n * Channlize API Client\n * Provides methods to interact with the Channlize platform.\n *\n * `TEvents` types `notify()` event keys and data (defaults to the\n * augmentable `ChannlizeEvents` interface; any string key is accepted).\n */\nexport class ChannlizeClient<TEvents extends object = ChannlizeEvents> {\n private readonly config: ChannlizeConfig & {\n baseUrl: string;\n timeout: number;\n };\n\n constructor(config: ChannlizeConfig) {\n this.config = {\n ...config,\n // After the spread: an explicit `undefined` must not drop the defaults.\n baseUrl: config.baseUrl || 'https://api.channlize.com',\n timeout: config.timeout ?? 10000,\n };\n\n this.validateConfig();\n }\n\n private validateConfig(): void {\n if (!this.config.apiKey) {\n throw new ValidationError('Widget key is required');\n }\n }\n\n private makeRequest<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n return requestJson<T>(this.config, endpoint, options);\n }\n\n /**\n * Submit a contact form\n */\n async submitContactForm(\n data: ContactFormData\n ): Promise<CreateContactResponse> {\n const response = await this.makeRequest<CreateContactResponse>(\n '/public/contact',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n subject: data.subject,\n message: data.message,\n email: data.email,\n name: data.name,\n customFields:\n data.customFields && Object.keys(data.customFields).length > 0\n ? data.customFields\n : undefined,\n pageUrl:\n data.pageUrl ||\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n data.userAgent ||\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n\n return response.data || response;\n }\n\n /**\n * Create a new chat session\n */\n async createChatSession(\n metadata?: Record<string, unknown>\n ): Promise<CreateChatSessionResponse> {\n const response = await this.makeRequest<CreateChatSessionResponse>(\n '/v1/chat/sessions',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n metadata: {\n ...metadata,\n pageUrl:\n typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent:\n typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined,\n },\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Send a message in a chat session.\n * When `attachments` are provided the request is sent as multipart/form-data\n * so the files are uploaded alongside the message content.\n */\n async sendMessage(\n sessionId: string,\n content: string,\n attachments?: File[]\n ): Promise<SendMessageResponse> {\n const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\n if (attachments && attachments.length > 0) {\n const formData = new FormData();\n formData.append('content', content);\n formData.append('idempotencyKey', idempotencyKey);\n for (const file of attachments) {\n formData.append('attachments', file, file.name);\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: formData,\n // Let the browser set Content-Type with boundary for multipart\n headers: { 'Content-Type': '' },\n }\n );\n\n return response.data;\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify({\n content,\n idempotencyKey,\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Get chat session details\n */\n async getChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}`\n );\n return response.data;\n }\n\n /**\n * Get messages for a chat session\n */\n async getChatMessages(\n sessionId: string,\n limit = 50,\n offset = 0\n ): Promise<ChatMessage[]> {\n const response = await this.makeRequest<ChatMessage[]>(\n `/v1/chat/sessions/${sessionId}/messages?limit=${limit}&offset=${offset}`\n );\n return response.data;\n }\n\n /**\n * Close a chat session\n */\n async closeChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}/close`,\n { method: 'POST' }\n );\n return response.data;\n }\n\n /**\n * Log tracking: record a log line from the browser (widget key →\n * `POST /public/events`). Shown under Log tracking in the dashboard.\n */\n async trackEvent(\n name: string,\n options?: TrackEventOptions\n ): Promise<{ success: boolean; id: string }> {\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/public/events',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey,\n name,\n properties: options?.properties,\n severity: options?.severity,\n distinctId: options?.distinctId,\n pageUrl:\n options?.pageUrl ??\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n options?.userAgent ??\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n\n /**\n * Log tracking from a trusted server, using the organization API key\n * (`POST /v1/events`). Shown under Log tracking in the dashboard.\n */\n async trackServerEvent(\n name: string,\n options?: { properties?: Record<string, unknown>; severity?: EventSeverity }\n ): Promise<{ success: boolean; id: string }> {\n if (!this.config.serverApiKey) {\n throw new ValidationError(\n 'serverApiKey is required for trackServerEvent'\n );\n }\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/v1/events',\n {\n method: 'POST',\n body: JSON.stringify({\n projectId: this.config.projectId,\n name,\n properties: options?.properties,\n severity: options?.severity,\n }),\n headers: {\n Authorization: `Bearer ${this.config.serverApiKey}`,\n },\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n\n /**\n * Events: post a Slack message from your server when something happens\n * (`POST /v1/notify`). The event key picks the channel and message\n * template configured under Events in the dashboard; `data` fills the\n * template's `{{field}}` placeholders. Needs `serverApiKey` (write scope)\n * and `projectId` — never call it from browser code.\n *\n * An unknown key is saved as a disabled draft and answered with\n * `status: 'skipped'`, so it never throws for a missing setup.\n */\n async notify<K extends NotifyEventName<TEvents>>(\n event: K,\n data?: NotifyEventData<TEvents, K>,\n options?: NotifyOptions\n ): Promise<NotifyResponse> {\n if (!this.config.serverApiKey) {\n throw new ValidationError('serverApiKey is required for notify');\n }\n return sendNotify(\n this.config,\n {\n serverApiKey: this.config.serverApiKey,\n projectId: this.config.projectId,\n },\n event,\n data as Record<string, unknown> | undefined,\n options\n );\n }\n}\n"],"mappings":"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","names":["fetch","response"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/http.ts","../src/shared.ts","../src/types.ts","../src/agents-errors.ts","../src/notify.ts","../src/agents-governance-types.ts","../src/agents-governance.ts","../src/agents.ts","../src/client.ts","../src/agents-types.ts"],"sourcesContent":["/**\n * @channlize/core\n * Core TypeScript SDK for Channlize platform\n */\n\nexport { ChannlizeClient } from './client';\nexport { createNotifier } from './notify';\nexport type { Notifier } from './notify';\nexport * from './types';\nexport {\n createAgentsClient,\n parseAgentWebhook,\n parseAgentWebhookEvent,\n verifyAgentWebhook,\n} from './agents';\nexport type { AgentsClient } from './agents';\nexport * from './agents-types';\nexport * from './agents-governance-types';\nexport {\n isAgentMessageWebhook,\n isQuestionEscalatedWebhook,\n isQuestionOutcomeMessage,\n isQuestionOutcomeWebhook,\n isTaskHandoffMessage,\n} from './agents-governance';\nexport type { AgentsGovernance } from './agents-governance';\nexport {\n AGENT_POLICY_CODES,\n AGENT_TASK_ERROR_CODES,\n AgentPolicyError,\n AgentTaskError,\n AgentThreadPausedError,\n HumanTakeoverError,\n QuestionClosedError,\n isRetryable,\n retryAfter,\n} from './agents-errors';\nexport type {\n AgentPolicyCode,\n AgentTaskErrorCode,\n AgentThreadPauseReason,\n} from './agents-errors';\nexport {\n DEFAULT_BASE_URL,\n DEFAULT_TIMEOUT,\n EVENT_KEY_PATTERN,\n EVENT_SEVERITIES,\n isValidEventKey,\n keyKind,\n parseApiError,\n readChannlizeEnv,\n} from './shared';\nexport type { ApiErrorInfo, ChannlizeEnv, KeyKind } from './shared';\n\n// Re-export for convenience\nexport { ChannlizeClient as default } from './client';\n","import fetch from 'cross-fetch';\nimport type { ApiResponse } from './types';\nimport { parseApiError, type ApiErrorInfo } from './shared';\nimport {\n ChannlizeError,\n AuthenticationError,\n ValidationError,\n RateLimitError,\n} from './types';\nimport { agentErrorFor } from './agents-errors';\n\n/**\n * Shared HTTP layer for `ChannlizeClient`, `createNotifier` and the agents\n * client: JSON requests with a timeout, and the API's error shape mapped to\n * typed errors. `options.signal` aborts the request too (`ABORTED`).\n */\nexport async function requestJson<T>(\n http: { baseUrl: string; timeout: number },\n endpoint: string,\n options: RequestInit = {}\n): Promise<ApiResponse<T>> {\n const url = `${http.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), http.timeout);\n const external = options.signal ?? undefined;\n const onAbort = () => controller.abort();\n if (external?.aborted) controller.abort();\n else external?.addEventListener('abort', onAbort, { once: true });\n\n try {\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string>),\n };\n\n // When Content-Type is explicitly empty (e.g. FormData), remove it so the\n // browser / runtime can set the correct multipart boundary automatically.\n if (mergedHeaders['Content-Type'] === '') {\n delete mergedHeaders['Content-Type'];\n }\n\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: mergedHeaders,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data as ApiResponse<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ChannlizeError) {\n throw error;\n }\n\n if (external?.aborted) {\n throw new ChannlizeError('Request aborted', undefined, 'ABORTED');\n }\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new ChannlizeError('Request timeout', 408, 'TIMEOUT');\n }\n\n throw new ChannlizeError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n 'NETWORK_ERROR'\n );\n } finally {\n external?.removeEventListener('abort', onAbort);\n }\n}\n\n/** `Retry-After` (seconds or an HTTP date) as an ISO time. */\nfunction retryAfterHeader(value: string | null): string | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n const ms = Number.isFinite(seconds)\n ? Date.now() + seconds * 1000\n : Date.parse(value);\n return Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;\n}\n\nexport async function handleErrorResponse(response: Response): Promise<never> {\n let body: unknown = {};\n if (response.headers.get('content-type')?.includes('application/json')) {\n try {\n body = await response.json();\n } catch {\n // Ignore JSON parsing errors\n }\n }\n throw errorFromApi(\n parseApiError(response.status, body, response.statusText),\n retryAfterHeader(response.headers.get('retry-after'))\n );\n}\n\n/** Maps a parsed API error to the typed `ChannlizeError` subclasses. */\nfunction errorFromApi(info: ApiErrorInfo, retryAfter?: string): ChannlizeError {\n const { status, message, code } = info;\n const retryAt =\n typeof info.body.retryAt === 'string' ? info.body.retryAt : retryAfter;\n let error: ChannlizeError | undefined = agentErrorFor(\n status,\n message,\n code,\n info.body,\n retryAt\n );\n if (error) {\n error.requestId = info.requestId;\n error.details = info.body;\n error.retryAt ??= retryAt;\n return error;\n }\n switch (status) {\n case 401:\n error = new AuthenticationError(message);\n break;\n case 400:\n error = new ValidationError(\n message,\n typeof info.body.field === 'string' ? info.body.field : undefined\n );\n break;\n case 429:\n error = new RateLimitError(\n message,\n code === 'PLAN_LIMIT_REACHED' ? code : undefined\n );\n break;\n default:\n error = new ChannlizeError(message, status, code);\n }\n error.requestId = info.requestId;\n error.details = info.body;\n error.retryAt = retryAt;\n return error;\n}\n","/**\n * Helpers shared by `@channlize/core`, `@channlize/react` and\n * `@channlize/mcp`: defaults, key detection, environment variables, event\n * keys and the API's error shape. Core is the single source for these.\n */\n\n/** Default API base URL. */\nexport const DEFAULT_BASE_URL = 'https://api.channlize.com';\n\n/** Default request timeout, in milliseconds. */\nexport const DEFAULT_TIMEOUT = 10000;\n\n/** Log tracking severities, lowest first. */\nexport const EVENT_SEVERITIES = ['info', 'warning', 'error'] as const;\n\n/**\n * Valid Events key (same rule as the API): lowercase letters, digits,\n * dots, underscores and dashes, e.g. `user.subscribed`. At most 100\n * characters.\n */\nexport const EVENT_KEY_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;\n\n/** Whether `event` is a valid Events key for `notify`. */\nexport function isValidEventKey(event: unknown): event is string {\n return (\n typeof event === 'string' &&\n event.length <= 100 &&\n EVENT_KEY_PATTERN.test(event)\n );\n}\n\nexport type KeyKind = 'widget' | 'server';\n\n/**\n * `wk_…` is the public widget key (browser: contact form, chat, browser\n * log tracking); anything else is a server API key (`ch_live_…`).\n */\nexport function keyKind(key: string): KeyKind {\n return key.startsWith('wk_') ? 'widget' : 'server';\n}\n\n/** The `CHANNLIZE_*` environment variables, trimmed (empty → undefined). */\nexport interface ChannlizeEnv {\n /** `CHANNLIZE_SERVER_API_KEY` */\n serverApiKey?: string;\n /** `CHANNLIZE_WIDGET_KEY` */\n widgetKey?: string;\n /** `CHANNLIZE_API_KEY` (either kind of key) */\n apiKey?: string;\n /** `CHANNLIZE_PROJECT_ID` */\n projectId?: string;\n /** `CHANNLIZE_BASE_URL` */\n baseUrl?: string;\n /** `CHANNLIZE_TIMEOUT` (milliseconds, unparsed) */\n timeout?: string;\n /** `CHANNLIZE_AGENT_ID` (Channlize for Agents: the agent to act as) */\n agentId?: string;\n}\n\nfunction clean(value: string | undefined): string | undefined {\n const v = value?.trim();\n return v ? v : undefined;\n}\n\n/**\n * Reads the `CHANNLIZE_*` environment variables. Safe in browsers and edge\n * runtimes without `process` (everything is undefined there).\n */\nexport function readChannlizeEnv(\n env: Record<string, string | undefined> | undefined = typeof process !==\n 'undefined'\n ? process.env\n : undefined\n): ChannlizeEnv {\n return {\n serverApiKey: clean(env?.CHANNLIZE_SERVER_API_KEY),\n widgetKey: clean(env?.CHANNLIZE_WIDGET_KEY),\n apiKey: clean(env?.CHANNLIZE_API_KEY),\n projectId: clean(env?.CHANNLIZE_PROJECT_ID),\n baseUrl: clean(env?.CHANNLIZE_BASE_URL),\n timeout: clean(env?.CHANNLIZE_TIMEOUT),\n agentId: clean(env?.CHANNLIZE_AGENT_ID),\n };\n}\n\n/** A failed API response, parsed from its JSON body. */\nexport interface ApiErrorInfo {\n status: number;\n /** Human-readable text (field messages joined with `, `). */\n message: string;\n /** Machine-readable code: the body's `code`, else its `error` (e.g. `NOT_FOUND`). */\n code?: string;\n /** `requestId` from the body, to quote to support. */\n requestId?: string;\n /** The parsed body (e.g. `limit`, `used`, `resetAt` on a plan-limit 429). */\n body: Record<string, unknown>;\n}\n\n/**\n * Parses the API's error shape (`api/lib/api/error.ts`):\n * `{ statusCode, error: \"NOT_FOUND\", message: string | string[], requestId? }`.\n * A few legacy routes put the human text in `error`.\n */\nexport function parseApiError(\n status: number,\n body: unknown,\n statusText = ''\n): ApiErrorInfo {\n const data = (body && typeof body === 'object' ? body : {}) as Record<\n string,\n unknown\n >;\n const raw = data.message;\n const joined = Array.isArray(raw)\n ? raw.filter(m => typeof m === 'string').join(', ')\n : typeof raw === 'string'\n ? raw\n : '';\n const error = typeof data.error === 'string' ? data.error : undefined;\n const code = typeof data.code === 'string' ? data.code : error;\n const message =\n joined ||\n (error && /\\s/.test(error) ? error : '') ||\n `HTTP ${status}: ${statusText}`.replace(/: $/, '');\n return {\n status,\n message,\n code,\n requestId: typeof data.requestId === 'string' ? data.requestId : undefined,\n body: data,\n };\n}\n","/**\n * Core types for Channlize SDK\n */\n\nimport type { EVENT_SEVERITIES } from './shared';\n\n/** Severity for log tracking (`trackEvent` / `trackServerEvent`); Slack alerts use project settings. */\nexport type EventSeverity = (typeof EVENT_SEVERITIES)[number];\n\n/** Options for log tracking (`trackEvent` / `trackServerEvent`). */\nexport interface TrackEventOptions {\n properties?: Record<string, unknown>;\n severity?: EventSeverity;\n distinctId?: string;\n pageUrl?: string;\n userAgent?: string;\n}\n\nexport interface ChannlizeConfig {\n /** Widget key used for public SDK endpoints (contact form, chat, browser log tracking) */\n apiKey: string;\n /** Base URL for the Channlize API */\n baseUrl?: string;\n /** Project ID (required for server-side `trackServerEvent` and `notify`) */\n projectId: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n /**\n * Organization (server) API key, `ch_live_…`, for `trackServerEvent`\n * (`POST /v1/events`) and `notify` (`POST /v1/notify`, needs the `write`\n * scope). Do not expose in browser bundles.\n */\n serverApiKey?: string;\n}\n\n/**\n * Events (notify Slack from code). Augment this interface to type event\n * keys and their data across your app:\n *\n * ```ts\n * declare module '@channlize/core' {\n * interface ChannlizeEvents {\n * 'user.subscribed': { username: string; plan: string };\n * }\n * }\n * ```\n *\n * Or pass a map as a type argument: `createNotifier<AppEvents>()`.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ChannlizeEvents {}\n\n/** A declared event key (autocompleted) or any other key. */\nexport type NotifyEventName<TEvents extends object = ChannlizeEvents> =\n (keyof TEvents & string) | (string & {});\n\n/** The declared data for a key, or any object for undeclared keys. */\nexport type NotifyEventData<\n TEvents extends object = ChannlizeEvents,\n K = string,\n> = K extends keyof TEvents ? TEvents[K] : Record<string, unknown>;\n\nexport interface NotifyOptions {\n /**\n * Retry-safe sends: a key already used on the project returns the first\n * delivery (`duplicate: true`) and posts nothing. Sent as `Idempotency-Key`.\n */\n idempotencyKey?: string;\n /** Slack channel ID to post to instead of the event's channel, for this call. */\n channel?: string;\n}\n\nexport interface NotifyResponse {\n success: boolean;\n /** Delivery ID (see Events → Recent deliveries in the dashboard). */\n id: string;\n event: string;\n /**\n * `queued`: will be posted to Slack. `skipped`: not posted (see `reason`):\n * the event is disabled, is new (saved as a draft to configure), or there\n * is no Slack workspace / channel. With `duplicate`, the first call's status.\n */\n status: 'queued' | 'skipped' | 'sent' | 'failed';\n reason?: string;\n duplicate?: boolean;\n}\n\n/** `createNotifier` options; each falls back to an environment variable. */\nexport interface NotifierConfig {\n /**\n * Server API key (`ch_live_…`, write scope). Default:\n * `CHANNLIZE_SERVER_API_KEY`, else `CHANNLIZE_API_KEY`.\n */\n serverApiKey?: string;\n /** Project ID. Default: `CHANNLIZE_PROJECT_ID`. */\n projectId?: string;\n /** API base URL. Default: `CHANNLIZE_BASE_URL`, else `https://api.channlize.com`. */\n baseUrl?: string;\n /** Request timeout in ms (default 10000). */\n timeout?: number;\n}\n\nexport interface ContactFormData {\n /** Contact's name */\n name: string;\n /** Contact's email address */\n email: string;\n /** Subject of the inquiry */\n subject?: string;\n /** Message content */\n message: string;\n /** Custom fields */\n customFields?: Record<string, unknown>;\n /** Page URL where the form was submitted */\n pageUrl?: string;\n /** User agent string */\n userAgent?: string;\n}\n\nexport interface ChatMessage {\n /** Unique message ID */\n id: string;\n /** Message content */\n content: string;\n /**\n * Message timestamp. The API sends an ISO 8601 string, which\n * `ChannlizeClient` passes through as is; `@channlize/react` converts it\n * to a `Date`. Wrap it in `new Date(...)` before using `Date` methods.\n */\n timestamp: Date;\n /** Message author (user or agent) */\n author: 'user' | 'agent';\n /** Author metadata (`name` / `email` can be `null` in message history) */\n authorData?: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n };\n /** Attached files */\n attachments?: FileAttachment[];\n}\n\nexport interface FileAttachment {\n /** File ID */\n id: string;\n /** Original filename */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n type: string;\n /** Download URL */\n url: string;\n}\n\nexport interface ChatSession {\n /** Session ID */\n id: string;\n /**\n * Session status. `createChatSession` answers `active`; `getChatSession`\n * and `closeChatSession` answer the conversation's status (`open`,\n * `pending`, `resolved` or `closed`).\n */\n status: 'active' | 'closed' | 'waiting';\n /**\n * Messages in the session. Only `createChatSession` returns it (empty);\n * `getChatSession`, `closeChatSession` and `sendMessage` return a session\n * summary without it. Load history with `getChatMessages`.\n */\n messages: ChatMessage[];\n /** Session metadata */\n metadata?: Record<string, unknown>;\n /** Created timestamp (an ISO 8601 string on the wire; not sent by `closeChatSession`) */\n createdAt: Date;\n /** Updated timestamp (an ISO 8601 string on the wire) */\n updatedAt: Date;\n}\n\nexport interface ApiResponse<T = unknown> {\n /** Response data */\n data: T;\n /** Success status */\n success: boolean;\n /** Error message if any */\n error?: string;\n /** Additional metadata */\n meta?: Record<string, unknown>;\n}\n\n/** Response of `POST /public/contact` (api/app/api/public/contact/route.ts). */\nexport interface CreateContactResponse {\n /** Whether the submission was accepted */\n success: boolean;\n /** Conversation the submission was filed under */\n conversationId: string;\n /** Human-readable confirmation to show the visitor */\n message: string;\n /** @deprecated Never returned by the API (always undefined). Use `conversationId`. */\n id: string;\n /** @deprecated Never returned by the API (always undefined). */\n status: 'received' | 'processing' | 'delivered';\n /** @deprecated Never returned by the API (always undefined). */\n slackThreadUrl?: string;\n}\n\nexport interface CreateChatSessionResponse {\n /** Chat session data */\n session: ChatSession;\n /** SSE endpoint for real-time updates */\n sseEndpoint?: string;\n}\n\nexport interface SendMessageResponse {\n /** The sent message */\n message: ChatMessage;\n /** The session (the API sends only `id` and `status`) */\n session: ChatSession;\n}\n\n/**\n * Error types\n */\nexport class ChannlizeError extends Error {\n /** The API's `requestId` for a failed response, to quote to support. */\n requestId?: string;\n /**\n * The API's JSON error body, e.g. `limit`, `used`, `resetAt` and\n * `upgradeUrl` on a plan-limit 429. Undefined for network errors.\n */\n details?: Record<string, unknown>;\n /**\n * ISO time the call may succeed again: the body's `retryAt` (agent\n * policies, human takeovers), else derived from a `Retry-After` header.\n * See `retryAfter(error)`.\n */\n retryAt?: string;\n\n constructor(\n message: string,\n public statusCode?: number,\n public code?: string\n ) {\n super(message);\n this.name = 'ChannlizeError';\n }\n}\n\nexport class ValidationError extends ChannlizeError {\n constructor(\n message: string,\n public field?: string\n ) {\n super(message, 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class AuthenticationError extends ChannlizeError {\n constructor(message: string = 'Authentication failed') {\n super(message, 401, 'AUTHENTICATION_ERROR');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * A 429. `code` is `PLAN_LIMIT_REACHED` when the organization is past its\n * plan's limit for the billing period (the API's 110 % hard cap), else\n * `RATE_LIMIT_ERROR`.\n */\nexport class RateLimitError extends ChannlizeError {\n constructor(\n message: string = 'Rate limit exceeded',\n code: string = 'RATE_LIMIT_ERROR'\n ) {\n super(message, 429, code);\n this.name = 'RateLimitError';\n }\n}\n\n/**\n * What `trackEvent` / `trackServerEvent` resolve with. When the API refuses\n * the event with a 429 (rate limit or plan limit), the call resolves with\n * `success: false` and `dropped` instead of throwing: log tracking never\n * breaks the app that calls it.\n */\nexport interface TrackEventResult {\n success: boolean;\n id: string;\n /** Set when the event was refused and not stored. */\n dropped?: 'plan_limit' | 'rate_limit';\n}\n","/**\n * Channlize for Agents: typed errors for the policy, takeover, loop-guard,\n * task and question refusals of the `/v1/agents` API, and the\n * `isRetryable` / `retryAfter` helpers. `requestJson` (`http.ts`) picks the\n * class from the error body's `code`.\n */\nimport { ChannlizeError, RateLimitError } from './types';\n\n/** The policy refusal codes (`api/lib/agents/policy.ts`, `POLICY_CODES`). */\nexport const AGENT_POLICY_CODES = [\n 'POLICY_CHANNEL_NOT_ALLOWED',\n 'POLICY_DM_NOT_ALLOWED',\n 'POLICY_ASK_HUMAN_NOT_ALLOWED',\n 'POLICY_URGENCY_NOT_ALLOWED',\n 'POLICY_RATE_LIMITED',\n 'POLICY_BUDGET_EXHAUSTED',\n 'POLICY_OUTSIDE_WORKING_HOURS',\n 'POLICY_MAX_DEPTH',\n] as const;\nexport type AgentPolicyCode = (typeof AGENT_POLICY_CODES)[number];\n\n/** Why the loop guard paused a thread (`details.reason` of `AGENT_THREAD_PAUSED`). */\nexport type AgentThreadPauseReason =\n 'thread_cap' | 'ping_pong' | 'duplicate_content';\n\n/** The task refusals: 409 `TASK_CLOSED`, `ALREADY_ASSIGNED`, `AGENT_NOT_IN_CHANNEL`; 403 `NOT_TASK_PARTICIPANT`. */\nexport const AGENT_TASK_ERROR_CODES = [\n 'TASK_CLOSED',\n 'ALREADY_ASSIGNED',\n 'AGENT_NOT_IN_CHANNEL',\n 'NOT_TASK_PARTICIPANT',\n] as const;\nexport type AgentTaskErrorCode = (typeof AGENT_TASK_ERROR_CODES)[number];\n\n/**\n * An agent policy refused the call (403, or 429 for rate limits and\n * budgets). `retryAt` is set when waiting helps: rate limits, budgets (the\n * next period) and working hours (the next window). Without it, retrying\n * can't succeed: the policy forbids the channel, the DM, the urgency or\n * asking at all.\n */\nexport class AgentPolicyError extends ChannlizeError {\n constructor(\n message: string,\n statusCode: number,\n code: AgentPolicyCode | (string & {}),\n retryAt?: string\n ) {\n super(message, statusCode, code);\n this.name = 'AgentPolicyError';\n this.retryAt = retryAt;\n }\n}\n\n/**\n * 409 `HUMAN_TAKEOVER`: a person replied in the thread, so its agents stay\n * out of it until `retryAt` (the policy's `humans_win_minutes`), or until\n * an admin resumes the thread.\n */\nexport class HumanTakeoverError extends ChannlizeError {\n constructor(message: string, retryAt?: string) {\n super(message, 409, 'HUMAN_TAKEOVER');\n this.name = 'HumanTakeoverError';\n this.retryAt = retryAt;\n }\n}\n\n/**\n * 429 `AGENT_THREAD_PAUSED`: the loop guard paused the thread (`reason`:\n * the thread cap, two agents ping-ponging, or duplicate content). A\n * person's reply or an admin resumes it; else the pause ends after\n * `Retry-After` (an hour). Still a `RateLimitError`.\n */\nexport class AgentThreadPausedError extends RateLimitError {\n constructor(\n message: string,\n public reason: AgentThreadPauseReason | (string & {}) = 'thread_cap'\n ) {\n super(message, 'AGENT_THREAD_PAUSED');\n this.name = 'AgentThreadPausedError';\n }\n}\n\n/** A task change the API refused (see `AGENT_TASK_ERROR_CODES`). Retrying doesn't help. */\nexport class AgentTaskError extends ChannlizeError {\n constructor(\n message: string,\n statusCode: number,\n code: AgentTaskErrorCode | (string & {})\n ) {\n super(message, statusCode, code);\n this.name = 'AgentTaskError';\n }\n}\n\n/** 409 `QUESTION_CLOSED`: the question was already answered, expired or canceled (the first answer wins). */\nexport class QuestionClosedError extends ChannlizeError {\n constructor(message: string) {\n super(message, 409, 'QUESTION_CLOSED');\n this.name = 'QuestionClosedError';\n }\n}\n\n/**\n * The typed agent error for an API error body, or undefined when the code\n * isn't one of them.\n */\nexport function agentErrorFor(\n status: number,\n message: string,\n code: string | undefined,\n body: Record<string, unknown>,\n retryAt: string | undefined\n): ChannlizeError | undefined {\n if (!code) return undefined;\n if (code.startsWith('POLICY_')) {\n return new AgentPolicyError(message, status, code, retryAt);\n }\n switch (code) {\n case 'HUMAN_TAKEOVER':\n return new HumanTakeoverError(message, retryAt);\n case 'AGENT_THREAD_PAUSED':\n return new AgentThreadPausedError(\n message,\n typeof body.reason === 'string' ? body.reason : undefined\n );\n case 'AGENT_RATE_LIMITED':\n return new RateLimitError(message, code);\n case 'QUESTION_CLOSED':\n return new QuestionClosedError(message);\n default:\n return (AGENT_TASK_ERROR_CODES as readonly string[]).includes(code)\n ? new AgentTaskError(message, status, code)\n : undefined;\n }\n}\n\n/**\n * Whether retrying the same call later can succeed: network errors and\n * timeouts, 5xx, 429s (except `PLAN_LIMIT_REACHED`, which lasts the billing\n * period), and policy refusals or human takeovers that carry a `retryAt`.\n * Wrong input, scopes, ids, closed tasks and forbidding policies are not.\n */\nexport function isRetryable(error: unknown): boolean {\n if (!(error instanceof ChannlizeError)) return false;\n const { statusCode: status, code } = error;\n if (code === 'NETWORK_ERROR' || code === 'TIMEOUT') return true;\n if (status === undefined) return false;\n if (status >= 500) return true;\n if (\n error instanceof AgentPolicyError ||\n error instanceof HumanTakeoverError\n ) {\n return Boolean(error.retryAt);\n }\n if (status === 429) return code !== 'PLAN_LIMIT_REACHED';\n return false;\n}\n\n/**\n * How long to wait before retrying, in milliseconds (0 when the time has\n * passed), from the error's `retryAt` (the body's `retryAt`, else the\n * `Retry-After` header). Undefined when the error isn't retryable or names\n * no time: then pick your own backoff.\n */\nexport function retryAfter(\n error: unknown,\n now = Date.now()\n): number | undefined {\n if (!isRetryable(error)) return undefined;\n const at = (error as ChannlizeError).retryAt;\n const ms = at ? Date.parse(at) : NaN;\n return Number.isFinite(ms) ? Math.max(0, ms - now) : undefined;\n}\n","import { requestJson } from './http';\nimport type {\n ChannlizeEvents,\n NotifierConfig,\n NotifyEventData,\n NotifyEventName,\n NotifyOptions,\n NotifyResponse,\n} from './types';\nimport { ValidationError } from './types';\nimport {\n DEFAULT_BASE_URL,\n DEFAULT_TIMEOUT,\n isValidEventKey,\n keyKind,\n readChannlizeEnv,\n} from './shared';\n\n/** `POST /v1/notify`, shared by `ChannlizeClient.notify` and `createNotifier`. */\nexport async function sendNotify(\n http: { baseUrl: string; timeout: number },\n auth: { serverApiKey: string; projectId: string },\n event: string,\n data: Record<string, unknown> | undefined,\n options: NotifyOptions = {}\n): Promise<NotifyResponse> {\n if (!auth.projectId) {\n throw new ValidationError('projectId is required for notify', 'projectId');\n }\n if (keyKind(auth.serverApiKey) === 'widget') {\n throw new ValidationError(\n 'notify needs a server API key (ch_live_…, write scope), not the public widget key',\n 'serverApiKey'\n );\n }\n if (!isValidEventKey(event)) {\n throw new ValidationError(\n `Invalid event key \"${event}\": use lowercase letters, digits, dots, underscores or dashes (e.g. user.subscribed)`,\n 'event'\n );\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${auth.serverApiKey}`,\n };\n if (options.idempotencyKey) {\n headers['Idempotency-Key'] = options.idempotencyKey;\n }\n\n const res = await requestJson<NotifyResponse>(http, '/v1/notify', {\n method: 'POST',\n headers,\n body: JSON.stringify({\n projectId: auth.projectId,\n event,\n data: data ?? {},\n channel: options.channel,\n }),\n });\n // The API answers at the top level (no `data` envelope).\n return res as unknown as NotifyResponse;\n}\n\nexport type Notifier<TEvents extends object = ChannlizeEvents> = <\n K extends NotifyEventName<TEvents>,\n>(\n event: K,\n data?: NotifyEventData<TEvents, K>,\n options?: NotifyOptions\n) => Promise<NotifyResponse>;\n\n/**\n * Events: a standalone server-side `notify(event, data)` function. Reads\n * the server key from `CHANNLIZE_SERVER_API_KEY` or `CHANNLIZE_API_KEY`, and\n * `CHANNLIZE_PROJECT_ID`, unless given — when it is called, so creating it\n * at module load never throws.\n *\n * ```ts\n * const notify = createNotifier();\n * await notify('user.subscribed', { username: 'ada', plan: 'Pro' });\n * ```\n */\nexport function createNotifier<TEvents extends object = ChannlizeEvents>(\n config: NotifierConfig = {}\n): Notifier<TEvents> {\n return (event, data, options) => {\n const env = readChannlizeEnv();\n const serverApiKey = config.serverApiKey ?? env.serverApiKey ?? env.apiKey;\n const projectId = config.projectId ?? env.projectId;\n if (!serverApiKey) {\n return Promise.reject(\n new ValidationError(\n 'A server API key is required: pass serverApiKey or set CHANNLIZE_API_KEY (or CHANNLIZE_SERVER_API_KEY)',\n 'serverApiKey'\n )\n );\n }\n if (!projectId) {\n return Promise.reject(\n new ValidationError(\n 'A project ID is required: pass projectId or set CHANNLIZE_PROJECT_ID',\n 'projectId'\n )\n );\n }\n const baseUrl = config.baseUrl ?? env.baseUrl ?? DEFAULT_BASE_URL;\n return sendNotify(\n {\n baseUrl: baseUrl.replace(/\\/$/, ''),\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n },\n { serverApiKey, projectId },\n event,\n data as Record<string, unknown> | undefined,\n options\n );\n };\n}\n","/**\n * Channlize for Agents, Phases 2 and 3: asking humans (questions and\n * approvals), tasks and handoffs, teams, policies, the org chart and\n * onboarding. Shapes mirror the API's DTOs (`api/lib/agents/questions-dto.ts`,\n * `tasks.ts`, `teams.ts`, `policies.ts`, `org-chart.ts`), so fields are\n * `snake_case`; request options are `camelCase`.\n */\nimport type {\n Agent,\n AgentDeliveryStatus,\n AgentMemberRole,\n AgentMessage,\n AgentWebhookPayload,\n} from './agents-types';\n\n// ----- questions (ask_human) -----\n\nexport const QUESTION_KINDS = ['question', 'approval'] as const;\n/** `approval` questions are always Approve / Deny (option ids `approve` / `deny`). */\nexport type QuestionKind = (typeof QUESTION_KINDS)[number];\n\nexport const QUESTION_URGENCIES = ['low', 'normal', 'high', 'urgent'] as const;\n/** Sets how fast the question escalates (default step timeouts: 4h, 1h, 15min, 5min). */\nexport type QuestionUrgency = (typeof QUESTION_URGENCIES)[number];\n\n/**\n * `open` until someone answers. `expired_default`: the deadline applied the\n * default option (delivered as `question.answered`). `expired`: the\n * deadline passed with no default.\n */\nexport const QUESTION_STATUSES = [\n 'open',\n 'answered',\n 'expired_default',\n 'expired',\n 'canceled',\n] as const;\nexport type QuestionStatus = (typeof QUESTION_STATUSES)[number];\n\n/** The longest single `questions.wait` long-poll, in seconds. */\nexport const QUESTION_WAIT_MAX_SECONDS = 25;\n\nexport interface QuestionOption {\n id: string;\n /** At most 75 characters (Slack button text). */\n label: string;\n}\n\n/** How a closed question was answered. */\nexport interface QuestionAnswer {\n status: Exclude<QuestionStatus, 'open'>;\n option_id: string | null;\n /** The chosen option's label. */\n option_label: string | null;\n text: string | null;\n comment: string | null;\n /** Null when it expired without a default, or was canceled. */\n answered_by: 'user' | 'agent' | 'default' | null;\n user_id: string | null;\n slack_user_id: string | null;\n agent_id: string | null;\n answered_at: string | null;\n canceled_at: string | null;\n /** Approvals: `approve` → true, `deny` → false. */\n approved?: boolean;\n}\n\n/** A question or approval an agent asked people. */\nexport interface AgentQuestion {\n id: string;\n /** The agent that asked. */\n agent_id: string | null;\n channel_id: string | null;\n thread_id: string | null;\n /** The `question` message in the channel, when it has one. */\n message_id: string | null;\n kind: QuestionKind;\n question: string;\n options: QuestionOption[];\n free_text: boolean;\n urgency: QuestionUrgency;\n deadline: string | null;\n default_option: string | null;\n context: Record<string, unknown>;\n amount: number | null;\n currency: string | null;\n resource: Record<string, unknown> | null;\n required_roles: string[];\n status: QuestionStatus;\n /** Null while open. */\n answer: QuestionAnswer | null;\n a2a_task_id: string | null;\n slack: { channel_id: string; ts: string } | null;\n escalation: {\n step: number;\n next_check_at: string | null;\n muted_until: string | null;\n };\n created_at: string;\n updated_at: string;\n}\n\n/** One step of a question's escalation chain. */\nexport interface QuestionEscalation {\n step: number;\n target_kind: string;\n target_ref: string | null;\n target_user_ids: string[];\n notified_at: string | null;\n timeout_at: string | null;\n slack: { channel_id: string; ts: string } | null;\n error: string | null;\n}\n\n/** `agents.ask` input (`POST /v1/agents/questions`, scope `agents:ask_human`). */\nexport interface AskHumanInput {\n /** The asking agent. Defaults to the client's `agentId`. */\n agentId?: string;\n /** Default `question`. An `approval` is answered Approve / Deny. */\n kind?: QuestionKind;\n /** At most 3000 characters. */\n question: string;\n /** Up to 10. Ignored for approvals. */\n options?: QuestionOption[];\n /** Allow a free-text answer (forced on without options). */\n freeText?: boolean;\n /** Default `normal`. Capped by the policy's `max_ask_urgency`. */\n urgency?: QuestionUrgency;\n /** ISO time, at most 30 days away. */\n deadline?: string | Date;\n /** Option id applied at the deadline. */\n defaultOption?: string;\n /** Post the question in this agent channel (else the escalation channel). */\n channelId?: string;\n /** Any message of the thread it is about. */\n threadId?: string;\n /** Shown with the question (at most 16 KB of JSON). */\n context?: Record<string, unknown>;\n /** Approvals: shown, and matched against the policy's `approval_rules`. */\n amount?: number;\n /** ISO 4217 code. */\n currency?: string;\n /** Approvals: what is being approved (at most 4 KB of JSON). */\n resource?: Record<string, unknown>;\n /** Approvals: roles that may answer (`owner` > `admin` > `agent` > `viewer`). */\n requiredRoles?: string[];\n}\n\nexport interface QuestionReadOptions {\n /** Read as this agent (multi-agent keys). Defaults to the client's `agentId`. */\n agentId?: string;\n}\n\nexport interface ListQuestionsOptions extends QuestionReadOptions {\n status?: QuestionStatus;\n kind?: QuestionKind;\n urgency?: QuestionUrgency;\n channelId?: string;\n /** `nextCursor` of the previous page. */\n cursor?: string;\n /** 1-200, default 50. */\n limit?: number;\n}\n\n/** `agents.questions.list`: newest first; the agent's own and those escalated to it. */\nexport interface QuestionPage {\n questions: AgentQuestion[];\n nextCursor: string | null;\n}\n\nexport interface WaitQuestionOptions extends QuestionReadOptions {\n /** Seconds to hold the request (0-25, default 25). */\n timeoutSec?: number;\n signal?: AbortSignal;\n}\n\n/** One long-poll: `timedOut` when the question is still open. */\nexport interface WaitQuestionResult {\n question: AgentQuestion;\n timedOut: boolean;\n}\n\n/** `agents.questions.answer` input (a manager agent answering an escalated question). */\nexport interface AnswerQuestionInput {\n /** The answering agent. Defaults to the client's `agentId`. */\n agentId?: string;\n /** One of the options (`approve` / `deny` for approvals). */\n optionId?: string;\n /** A free-text answer (questions that allow it). */\n text?: string;\n comment?: string;\n}\n\nexport interface AskAndWaitOptions {\n /**\n * The overall wait, in ms (default 600000: 10 minutes). Questions can take\n * hours: past it, the call resolves with `timedOut: true` and the open\n * question, to wait on later with `questions.wait`.\n */\n timeoutMs?: number;\n /** Rejects the call (code `ABORTED`) when it aborts. */\n signal?: AbortSignal;\n}\n\n/** `agents.askAndWait` result. */\nexport interface AskAndWaitResult {\n question: AgentQuestion;\n /** Null while open (`timedOut`). */\n answer: QuestionAnswer | null;\n /** True when `timeoutMs` ran out with the question still open. */\n timedOut: boolean;\n}\n\n// ----- tasks -----\n\n/**\n * Board columns. They map onto `task_status`: `open` → `open`,\n * `in_progress` → `working`, `blocked` → `input_required`, `done` →\n * `completed`, `failed` → `failed` (`canceled` and `rejected` show as\n * `failed`).\n */\nexport const TASK_BOARD_STATUSES = [\n 'open',\n 'in_progress',\n 'blocked',\n 'done',\n 'failed',\n] as const;\nexport type TaskBoardStatus = (typeof TASK_BOARD_STATUSES)[number];\n\nexport interface TaskHandoff {\n from_agent_id: string | null;\n to_agent_id: string | null;\n reason: string | null;\n by_agent_id: string | null;\n by_user_id: string | null;\n at: string;\n}\n\n/** A task: its `task` message plus the board fields. */\nexport interface AgentTask extends AgentMessage {\n board_status: TaskBoardStatus;\n assignee_agent_id: string | null;\n due_at: string | null;\n last_handoff: TaskHandoff | null;\n}\n\n/** One entry of a task's history. */\nexport interface AgentTaskEvent {\n id: string;\n kind: 'assigned' | 'handoff' | 'status' | 'due_changed';\n from_agent_id: string | null;\n to_agent_id: string | null;\n by_agent_id: string | null;\n by_user_id: string | null;\n from_status: string | null;\n to_status: string | null;\n reason: string | null;\n data: Record<string, unknown> | null;\n created_at: string;\n}\n\nexport interface TaskDeliverySummary {\n id: string;\n agent_id: string;\n status: AgentDeliveryStatus;\n}\n\n/** `agents.tasks.create` input (`agents:send`). */\nexport interface CreateTaskInput {\n /** The creating agent. Defaults to the client's `agentId`. */\n agentId?: string;\n channelId: string;\n /** What should be done. */\n text: string;\n /** A `member` of the channel; mentioned, and the task's owner on the board. */\n assigneeAgentId?: string;\n /** ISO date or date-time. */\n dueAt?: string | Date;\n threadId?: string;\n mentions?: string[];\n data?: Record<string, unknown>;\n}\n\nexport interface CreateTaskResult {\n task: AgentTask;\n deliveries: TaskDeliverySummary[];\n}\n\nexport interface ListTasksOptions {\n /** Required with a key bound to agents. */\n channelId?: string;\n assigneeAgentId?: string;\n status?: TaskBoardStatus;\n /** 1-500. */\n limit?: number;\n /** Read as this agent (multi-agent keys). Defaults to the client's `agentId`. */\n agentId?: string;\n}\n\nexport interface TaskBoardOptions {\n assigneeAgentId?: string;\n agentId?: string;\n}\n\nexport interface TaskBoard {\n channel_id: string;\n columns: Record<TaskBoardStatus, AgentTask[]>;\n counts: Record<TaskBoardStatus, number>;\n agents: Array<\n Pick<Agent, 'id' | 'slug' | 'name' | 'emoji' | 'avatar_url' | 'status'>\n >;\n /** More than 500 tasks: the board shows the newest. */\n truncated: boolean;\n}\n\nexport interface TaskDetail {\n task: AgentTask;\n events: AgentTaskEvent[];\n}\n\n/** `agents.tasks.update` input: the assignee or creator moves the task or changes its due date. */\nexport interface UpdateTaskInput {\n agentId?: string;\n status?: TaskBoardStatus;\n /** ISO date or date-time; null clears it. */\n dueAt?: string | Date | null;\n /** Shown with the change (at most 2000 characters). */\n note?: string;\n}\n\nexport interface UpdateTaskResult {\n task: AgentTask;\n /** False when nothing changed. */\n changed: boolean;\n}\n\nexport interface HandoffTaskInput {\n agentId?: string;\n /** A `member` of the task's channel. */\n toAgentId: string;\n reason?: string;\n}\n\nexport interface HandoffTaskResult {\n task: AgentTask;\n event: AgentTaskEvent;\n deliveries: TaskDeliverySummary[];\n}\n\n// ----- teams -----\n\nexport interface AgentTeam {\n id: string;\n slug: string;\n name: string;\n description: string | null;\n member_count?: number;\n channel_count?: number;\n created_by: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface AgentTeamChannelGrant {\n channel_id: string;\n team_id: string;\n role: AgentMemberRole;\n added_by: string | null;\n created_at: string;\n}\n\nexport interface AgentTeamDetail {\n team: AgentTeam;\n members: Array<{ agent: Agent; added_by: string | null; added_at: string }>;\n channels: AgentTeamChannelGrant[];\n policy: {\n id: string;\n rules: AgentPolicyRules;\n enabled: boolean;\n updated_at: string;\n } | null;\n}\n\nexport interface CreateTeamInput {\n name: string;\n slug?: string;\n description?: string | null;\n /** Agents to add right away. */\n agentIds?: string[];\n}\n\nexport interface UpdateTeamInput {\n name?: string;\n slug?: string;\n description?: string | null;\n}\n\n// ----- policies -----\n\nexport const POLICY_SCOPES = ['org', 'team', 'agent', 'channel'] as const;\n/** Precedence: org < team < agent < channel, merged key by key. */\nexport type PolicyScope = (typeof POLICY_SCOPES)[number];\n\n/** A policy's rules. Every key is optional; a key left out inherits. */\nexport interface AgentPolicyRules {\n /** Channels it may post in (null: any it's a member of). */\n post_channel_ids?: string[] | null;\n read_channel_ids?: string[] | null;\n direct_messages?: {\n default: 'allow' | 'deny';\n allow_agent_ids?: string[];\n } | null;\n can_ask_humans?: boolean;\n max_ask_urgency?: QuestionUrgency;\n approval_rules?: Array<{\n min_amount: number;\n currency?: string;\n required_roles: string[];\n }> | null;\n rate_per_minute?: number | null;\n daily_message_budget?: number | null;\n monthly_event_budget?: number | null;\n working_hours?: {\n /** IANA time zone. */\n timezone: string;\n /** 0 = Sunday; empty: every day. */\n days: number[];\n /** HH:MM */\n start: string;\n /** HH:MM */\n end: string;\n /** `queue` holds messages; `owner_if_urgent` also tells the owner about urgent ones. */\n out_of_hours: 'queue' | 'owner_if_urgent';\n } | null;\n max_thread_depth?: number | null;\n humans_win_minutes?: number | null;\n}\n\nexport interface AgentPolicy {\n id: string;\n scope: PolicyScope;\n scope_id: string;\n rules: AgentPolicyRules;\n enabled: boolean;\n created_by: string | null;\n updated_by: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface PutPolicyInput {\n scope: PolicyScope;\n /** The team, agent or channel id; leave out for `org`. */\n scopeId?: string;\n rules: AgentPolicyRules;\n enabled?: boolean;\n}\n\nexport interface PolicySource {\n id: string;\n scope: PolicyScope;\n scope_id: string;\n}\n\n/** A policy check: allowed, or the refusal it would be. */\nexport type PolicyCheck =\n | { allow: true }\n | {\n allow: false;\n code: string;\n message: string;\n retryAt?: string;\n status?: number;\n };\n\n/** `agents.policies.preview`: the policy in force for an agent (in a channel). */\nexport interface EffectivePolicyPreview {\n agent_id: string;\n channel_id: string | null;\n /** Every key resolved (defaults included). */\n rules: Required<AgentPolicyRules>;\n /** The policies merged, lowest precedence first. */\n sources: PolicySource[];\n /** Which policy set each key (null: the default). */\n set_by: Record<string, PolicySource | null>;\n /** False when loading failed and the defaults were used. */\n loaded: boolean;\n working_hours: {\n configured: boolean;\n within: boolean;\n /** When the next window opens (outside working hours). */\n next_start: string | null;\n };\n usage: {\n rate_per_minute: { limit: number; used: number | null } | null;\n daily_messages: {\n budget: number;\n used: number | null;\n resets_at: string;\n } | null;\n monthly_events: {\n budget: number;\n used: number | null;\n pool: unknown;\n resets_at: string;\n } | null;\n };\n /** With a channel: whether it may post and read there now. */\n checks: { send: PolicyCheck; read: PolicyCheck } | null;\n}\n\n// ----- org chart -----\n\nexport interface OrgChartAgent {\n id: string;\n slug: string;\n name: string;\n status: Agent['status'];\n role: string | null;\n emoji: string | null;\n avatar_url: string | null;\n owner_user_id: string | null;\n manager: { type: 'agent' | 'user'; id: string } | null;\n team_ids: string[];\n}\n\nexport interface OrgChartNode {\n type: 'agent' | 'user';\n id: string;\n children: OrgChartNode[];\n}\n\nexport interface OrgChart {\n agents: OrgChartAgent[];\n users: Array<{ id: string; name: string; email: string }>;\n teams: Array<{ id: string; slug: string; name: string; agent_ids: string[] }>;\n tree: OrgChartNode[];\n}\n\nexport interface OrgChartOptions {\n teamId?: string;\n channelId?: string;\n includeRetired?: boolean;\n}\n\n/** An agent's manager: an agent or a person, never both; `{}` clears it. */\nexport type SetManagerInput =\n | { managerAgentId: string; managerUserId?: undefined }\n | { managerUserId: string; managerAgentId?: undefined }\n | { managerAgentId?: null; managerUserId?: null };\n\n// ----- lifecycle -----\n\nexport interface AgentOnboarding {\n agent_id: string;\n steps: Array<{\n id:\n | 'created'\n | 'key_issued'\n | 'in_channel'\n | 'test_message_sent'\n | 'delivery_working';\n label: string;\n done: boolean;\n }>;\n complete: boolean;\n}\n\n/** `agents.setStatus` result. */\nexport interface AgentStatusResult {\n agent: Agent;\n previous_status: Agent['status'];\n keys_revoked: number;\n memberships_removed: number;\n /** On a resume: the held deliveries released. */\n released?: number;\n}\n\n// ----- webhook events -----\n\n/** The events a closed question is pushed to its asker as. */\nexport type QuestionOutcomeEvent =\n 'question.answered' | 'question.expired' | 'question.canceled';\n\n/**\n * A closed question pushed to the agent that asked it, when the question\n * has no channel (`X-Webhook-Event: question.answered`, ...). Questions in a\n * channel come back as an `agent.message` in its thread instead, with the\n * same `{ type, question, answer }` as `message.data` (see\n * `isQuestionOutcomeMessage`).\n */\nexport interface QuestionOutcomeWebhookPayload {\n version: 1;\n event: QuestionOutcomeEvent;\n type: QuestionOutcomeEvent;\n delivery_id: string;\n /** The recipient. */\n agent: { id: string; slug: string; name: string };\n question: AgentQuestion;\n answer: QuestionAnswer | null;\n}\n\n/**\n * A question escalated to its asker's manager agent\n * (`X-Webhook-Event: question.escalated`). Answer it with\n * `agents.questions.answer(question.id, …)` (`answer_url`).\n */\nexport interface QuestionEscalatedWebhookPayload {\n version: 1;\n event: 'question.escalated';\n type: 'question.escalated';\n delivery_id: string;\n agent: { id: string; slug: string; name: string };\n question: AgentQuestion;\n asker: { id: string; slug: string; name: string } | null;\n answer_url: string;\n}\n\n/** Every body Channlize POSTs to a webhook agent. */\nexport type AgentWebhookEvent =\n | AgentWebhookPayload\n | QuestionOutcomeWebhookPayload\n | QuestionEscalatedWebhookPayload;\n\n/** `message.data` of a question outcome in the question's thread. */\nexport interface QuestionOutcomeData {\n type: QuestionOutcomeEvent;\n question: AgentQuestion;\n answer: QuestionAnswer | null;\n [key: string]: unknown;\n}\n\n/** `message.data.handoff` of the `status` message a handoff posts. */\nexport interface TaskHandoffData {\n handoff: {\n from_agent_id: string | null;\n to_agent_id: string;\n reason: string | null;\n };\n [key: string]: unknown;\n}\n","/**\n * Channlize for Agents, Phases 2 and 3 on the agents client: asking humans\n * (`ask`, `askAndWait`, `questions`), tasks and handoffs (`tasks`), and the\n * admin endpoints (`teams`, `policies`, `orgChart`, `onboarding`,\n * `setStatus`). `createAgentsClient` mixes these in; the webhook guards at\n * the bottom are exported on their own.\n */\nimport { isRetryable, retryAfter } from './agents-errors';\nimport type {\n AgentOnboarding,\n AgentPolicy,\n AgentQuestion,\n AgentStatusResult,\n AgentTeam,\n AgentTeamChannelGrant,\n AgentTeamDetail,\n AgentWebhookEvent,\n AnswerQuestionInput,\n AskAndWaitOptions,\n AskAndWaitResult,\n AskHumanInput,\n CreateTaskInput,\n CreateTaskResult,\n CreateTeamInput,\n EffectivePolicyPreview,\n HandoffTaskInput,\n HandoffTaskResult,\n ListQuestionsOptions,\n ListTasksOptions,\n OrgChart,\n OrgChartOptions,\n PolicyScope,\n PutPolicyInput,\n QuestionEscalation,\n QuestionEscalatedWebhookPayload,\n QuestionOutcomeData,\n QuestionOutcomeWebhookPayload,\n QuestionPage,\n QuestionReadOptions,\n SetManagerInput,\n TaskBoard,\n TaskBoardOptions,\n TaskDetail,\n TaskHandoffData,\n UpdateTaskInput,\n UpdateTaskResult,\n UpdateTeamInput,\n WaitQuestionOptions,\n WaitQuestionResult,\n AgentTask,\n} from './agents-governance-types';\nimport { QUESTION_WAIT_MAX_SECONDS } from './agents-governance-types';\nimport type {\n Agent,\n AgentMemberRole,\n AgentMessage,\n AgentStatus,\n AgentWebhookPayload,\n} from './agents-types';\nimport { ChannlizeError, ValidationError } from './types';\n\ntype Query = Record<string, string | number | boolean | undefined>;\n\n/** The request function `createAgentsClient` hands in. */\nexport type AgentsRequest = <T>(\n method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE',\n path: string,\n options?: {\n query?: Query;\n body?: unknown;\n /** Overrides the client's timeout (long-polls). */\n timeout?: number;\n signal?: AbortSignal;\n }\n) => Promise<T>;\n\n/** Phases 2 and 3 of `AgentsClient`. */\nexport interface AgentsGovernance {\n /**\n * Ask people a question, or for an approval (`agents:ask_human`). It is\n * posted in Slack (the channel's linked channel, else the escalation\n * channel) and escalates until someone answers or the deadline passes.\n * One Event. Read the answer with `questions.wait`, or use `askAndWait`.\n */\n ask(input: AskHumanInput): Promise<AgentQuestion>;\n /**\n * `ask`, then `questions.wait` in a loop until the question is answered,\n * expired or canceled, or `timeoutMs` (default 10 minutes) runs out:\n * then it resolves with `timedOut: true` and the open question. Network\n * errors, 5xx and 429s are retried (after `Retry-After`) inside the\n * timeout; others reject. `signal` rejects it with code `ABORTED`.\n */\n askAndWait(\n input: AskHumanInput,\n options?: AskAndWaitOptions\n ): Promise<AskAndWaitResult>;\n questions: {\n /** A question with its escalation steps (`agents:ask_human` or `agents:read`). */\n get(\n questionId: string,\n options?: QuestionReadOptions\n ): Promise<{ question: AgentQuestion; escalations: QuestionEscalation[] }>;\n /** The agent's questions and those escalated to it, newest first. */\n list(options?: ListQuestionsOptions): Promise<QuestionPage>;\n /**\n * One long-poll (at most 25 s): resolves as soon as the question closes,\n * or with `timedOut: true` while it's still open.\n */\n wait(\n questionId: string,\n options?: WaitQuestionOptions\n ): Promise<WaitQuestionResult>;\n /**\n * Answer a question escalated to this agent (its asker's manager agent;\n * `agents:send` or `agents:ask_human`). The first answer wins: later\n * ones get `QuestionClosedError`. Approvals that ask for a role need a\n * person (403 `APPROVAL_ROLE_REQUIRED`).\n */\n answer(\n questionId: string,\n input: AnswerQuestionInput\n ): Promise<AgentQuestion>;\n /** Cancel one of the agent's own open questions (`agents:ask_human`). */\n cancel(\n questionId: string,\n options?: QuestionReadOptions\n ): Promise<AgentQuestion>;\n };\n tasks: {\n /** Create a task, optionally assigned and with a due date (`agents:send`). */\n create(input: CreateTaskInput): Promise<CreateTaskResult>;\n /** Tasks, newest first (`agents:read`; keys bound to agents need `channelId`). */\n list(options?: ListTasksOptions): Promise<AgentTask[]>;\n /** A channel's board: tasks by column, counts and the agents on it. */\n board(channelId: string, options?: TaskBoardOptions): Promise<TaskBoard>;\n /** A task with its history. */\n get(taskId: string, options?: { agentId?: string }): Promise<TaskDetail>;\n /**\n * Move a task on the board, change its due date, with a note (the\n * assignee or creator; `agents:send`). 409 `TASK_CLOSED` once closed.\n */\n update(taskId: string, input: UpdateTaskInput): Promise<UpdateTaskResult>;\n /** Hand a task to another member agent of its channel (`agents:send`). */\n handoff(\n taskId: string,\n input: HandoffTaskInput\n ): Promise<HandoffTaskResult>;\n };\n /** Teams (reads: `agents:read`; writes: an unbound `agents:admin` key). */\n teams: {\n list(): Promise<AgentTeam[]>;\n get(teamId: string): Promise<AgentTeamDetail>;\n create(input: CreateTeamInput): Promise<AgentTeam>;\n update(teamId: string, input: UpdateTeamInput): Promise<AgentTeam>;\n delete(teamId: string): Promise<{ memberships_removed: number }>;\n addMember(\n teamId: string,\n agentId: string\n ): Promise<{ member: unknown; channels_granted: number }>;\n removeMember(\n teamId: string,\n agentId: string\n ): Promise<{ memberships_removed: number }>;\n grantChannel(\n teamId: string,\n channelId: string,\n role?: AgentMemberRole\n ): Promise<{ grant: AgentTeamChannelGrant; memberships_added: number }>;\n revokeChannel(\n teamId: string,\n channelId: string\n ): Promise<{ memberships_removed: number }>;\n };\n /** Policies (an unbound `agents:admin` key). */\n policies: {\n list(options?: { scope?: PolicyScope }): Promise<AgentPolicy[]>;\n get(policyId: string): Promise<AgentPolicy>;\n /** Create or replace the policy of a scope. */\n put(input: PutPolicyInput): Promise<AgentPolicy>;\n delete(policyId: string): Promise<void>;\n /** The effective policy of an agent (in a channel): rules, sources, usage and checks. */\n preview(\n agentId: string,\n options?: { channelId?: string }\n ): Promise<EffectivePolicyPreview>;\n };\n orgChart: {\n /** The org chart (`agents:read`). */\n get(options?: OrgChartOptions): Promise<OrgChart>;\n /** Set or clear an agent's manager (`agents:admin`). 409 `ORG_CHART_CYCLE`. */\n setManager(agentId: string, input: SetManagerInput): Promise<Agent>;\n /** Set or clear an agent's owner, a person (`agents:admin`). */\n setOwner(agentId: string, ownerUserId: string | null): Promise<Agent>;\n };\n /** An agent's onboarding checklist (`agents:admin`). */\n onboarding(agentId: string): Promise<AgentOnboarding>;\n /**\n * Pause (with a reason), resume or retire an agent (`agents:admin`).\n * Resuming releases its held deliveries, oldest first.\n */\n setStatus(\n agentId: string,\n status: AgentStatus,\n options?: { reason?: string }\n ): Promise<AgentStatusResult>;\n}\n\nconst enc = encodeURIComponent;\n\nconst iso = (v: string | Date | undefined | null) =>\n v instanceof Date ? v.toISOString() : v;\n\n/** Resolves after `ms`, or early when `signal` aborts. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise(done => {\n if (signal?.aborted) return done();\n const timer = setTimeout(finish, ms);\n function finish() {\n clearTimeout(timer);\n signal?.removeEventListener('abort', finish);\n done();\n }\n signal?.addEventListener('abort', finish, { once: true });\n });\n}\n\nfunction aborted(): ChannlizeError {\n return new ChannlizeError('Request aborted', undefined, 'ABORTED');\n}\n\nfunction need(value: unknown, what: string, field: string): void {\n if (typeof value !== 'string' || !value) {\n throw new ValidationError(`${what} is required`, field);\n }\n}\n\nexport function createAgentsGovernance(\n request: AgentsRequest,\n defaultAgentId: string | undefined\n): AgentsGovernance {\n const agentOf = (agentId?: string) => agentId ?? defaultAgentId;\n /** Validation errors reject instead of throwing synchronously. */\n const safely = <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return fn();\n } catch (error) {\n return Promise.reject(error);\n }\n };\n\n function ask(input: AskHumanInput): Promise<AgentQuestion> {\n return safely(async () => {\n if (!input || typeof input.question !== 'string' || !input.question) {\n throw new ValidationError('ask needs a question', 'question');\n }\n const r = await request<{ question: AgentQuestion }>(\n 'POST',\n '/v1/agents/questions',\n {\n body: {\n agent_id: agentOf(input.agentId),\n kind: input.kind,\n question: input.question,\n options: input.options,\n free_text: input.freeText,\n urgency: input.urgency,\n deadline: iso(input.deadline),\n default_option: input.defaultOption,\n channel_id: input.channelId,\n thread_id: input.threadId,\n context: input.context,\n amount: input.amount,\n currency: input.currency,\n resource: input.resource,\n required_roles: input.requiredRoles,\n },\n }\n );\n return r.question;\n });\n }\n\n function wait(\n questionId: string,\n options: WaitQuestionOptions = {}\n ): Promise<WaitQuestionResult> {\n return safely(async () => {\n need(questionId, 'The question id', 'questionId');\n const timeoutSec = Math.min(\n QUESTION_WAIT_MAX_SECONDS,\n Math.max(0, options.timeoutSec ?? QUESTION_WAIT_MAX_SECONDS)\n );\n const r = await request<{ question: AgentQuestion; timed_out?: boolean }>(\n 'GET',\n `/v1/agents/questions/${enc(questionId)}/wait`,\n {\n query: { agent_id: agentOf(options.agentId), timeout: timeoutSec },\n // The server holds the request for up to timeoutSec.\n timeout: (timeoutSec + 10) * 1000,\n signal: options.signal,\n }\n );\n return {\n question: r.question,\n timedOut: r.timed_out ?? r.question?.status === 'open',\n };\n });\n }\n\n async function askAndWait(\n input: AskHumanInput,\n options: AskAndWaitOptions = {}\n ): Promise<AskAndWaitResult> {\n const { signal } = options;\n const deadline = Date.now() + Math.max(0, options.timeoutMs ?? 600_000);\n if (signal?.aborted) throw aborted();\n let question = await ask(input);\n let failures = 0;\n while (question.status === 'open') {\n if (signal?.aborted) throw aborted();\n const left = deadline - Date.now();\n if (left < 1000) break;\n try {\n const r = await wait(question.id, {\n agentId: input.agentId,\n timeoutSec: Math.min(\n QUESTION_WAIT_MAX_SECONDS,\n Math.floor(left / 1000)\n ),\n signal,\n });\n question = r.question;\n failures = 0;\n } catch (error) {\n if (signal?.aborted) throw aborted();\n if (!isRetryable(error)) throw error;\n failures += 1;\n const backoff =\n retryAfter(error) ?? Math.min(30_000, 1000 * 2 ** (failures - 1));\n const remaining = deadline - Date.now();\n if (backoff >= remaining) break;\n await sleep(backoff, signal);\n }\n }\n if (signal?.aborted && question.status === 'open') throw aborted();\n return {\n question,\n answer: question.answer ?? null,\n timedOut: question.status === 'open',\n };\n }\n\n const questionPath = (id: string) => {\n need(id, 'The question id', 'questionId');\n return `/v1/agents/questions/${enc(id)}`;\n };\n const taskPath = (id: string) => {\n need(id, 'The task id', 'taskId');\n return `/v1/agents/tasks/${enc(id)}`;\n };\n const teamPath = (id: string) => {\n need(id, 'The team id', 'teamId');\n return `/v1/agents/teams/${enc(id)}`;\n };\n const agentPath = (id: string) => {\n need(id, 'The agent id', 'agentId');\n return enc(id);\n };\n\n return {\n ask,\n askAndWait,\n\n questions: {\n get: (id, options = {}) =>\n safely(async () => {\n const r = await request<{\n question: AgentQuestion;\n escalations?: QuestionEscalation[];\n }>('GET', questionPath(id), {\n query: { agent_id: agentOf(options.agentId) },\n });\n return { question: r.question, escalations: r.escalations ?? [] };\n }),\n\n list: (options = {}) =>\n safely(async () => {\n const r = await request<{\n questions?: AgentQuestion[];\n next_cursor?: string | null;\n }>('GET', '/v1/agents/questions', {\n query: {\n agent_id: agentOf(options.agentId),\n status: options.status,\n kind: options.kind,\n urgency: options.urgency,\n channel_id: options.channelId,\n cursor: options.cursor,\n limit: options.limit,\n },\n });\n return {\n questions: r.questions ?? [],\n nextCursor: r.next_cursor ?? null,\n };\n }),\n\n wait,\n\n answer: (id, input) =>\n safely(async () => {\n if (!input?.optionId && !input?.text) {\n throw new ValidationError(\n 'answer needs optionId or text',\n 'optionId'\n );\n }\n const r = await request<{ question: AgentQuestion }>(\n 'POST',\n `${questionPath(id)}/answer`,\n {\n body: {\n agent_id: agentOf(input.agentId),\n option_id: input.optionId,\n text: input.text,\n comment: input.comment,\n },\n }\n );\n return r.question;\n }),\n\n cancel: (id, options = {}) =>\n safely(async () => {\n const r = await request<{ question: AgentQuestion }>(\n 'DELETE',\n questionPath(id),\n { query: { agent_id: agentOf(options.agentId) } }\n );\n return r.question;\n }),\n },\n\n tasks: {\n create: input =>\n safely(async () => {\n need(input?.channelId, 'channelId', 'channelId');\n const r = await request<CreateTaskResult>(\n 'POST',\n '/v1/agents/tasks',\n {\n body: {\n agent_id: agentOf(input.agentId),\n channel_id: input.channelId,\n text: input.text,\n assignee_agent_id: input.assigneeAgentId,\n due_at: iso(input.dueAt),\n thread_id: input.threadId,\n mentions: input.mentions,\n data: input.data,\n },\n }\n );\n return { task: r.task, deliveries: r.deliveries ?? [] };\n }),\n\n list: (options = {}) =>\n safely(async () => {\n const r = await request<{ tasks?: AgentTask[] }>(\n 'GET',\n '/v1/agents/tasks',\n {\n query: {\n agent_id: agentOf(options.agentId),\n channel_id: options.channelId,\n assignee_agent_id: options.assigneeAgentId,\n status: options.status,\n limit: options.limit,\n },\n }\n );\n return r.tasks ?? [];\n }),\n\n board: (channelId, options = {}) =>\n safely(() => {\n need(channelId, 'channelId', 'channelId');\n return request<TaskBoard>('GET', '/v1/agents/tasks/board', {\n query: {\n channel_id: channelId,\n assignee_agent_id: options.assigneeAgentId,\n agent_id: agentOf(options.agentId),\n },\n });\n }),\n\n get: (taskId, options = {}) =>\n safely(async () => {\n const r = await request<TaskDetail>('GET', taskPath(taskId), {\n query: { agent_id: agentOf(options.agentId) },\n });\n return { task: r.task, events: r.events ?? [] };\n }),\n\n update: (taskId, input) =>\n safely(() => {\n if (\n !input ||\n (input.status === undefined && input.dueAt === undefined)\n ) {\n throw new ValidationError('update needs status or dueAt', 'status');\n }\n return request<UpdateTaskResult>('PATCH', taskPath(taskId), {\n body: {\n agent_id: agentOf(input.agentId),\n status: input.status,\n due_at: input.dueAt === null ? null : iso(input.dueAt),\n note: input.note,\n },\n });\n }),\n\n handoff: (taskId, input) =>\n safely(() => {\n need(input?.toAgentId, 'toAgentId', 'toAgentId');\n return request<HandoffTaskResult>(\n 'POST',\n `${taskPath(taskId)}/handoff`,\n {\n body: {\n agent_id: agentOf(input.agentId),\n to_agent_id: input.toAgentId,\n reason: input.reason,\n },\n }\n );\n }),\n },\n\n teams: {\n list: () =>\n safely(async () => {\n const r = await request<{ teams?: AgentTeam[] }>(\n 'GET',\n '/v1/agents/teams'\n );\n return r.teams ?? [];\n }),\n get: teamId =>\n safely(() => request<AgentTeamDetail>('GET', teamPath(teamId))),\n create: input =>\n safely(async () => {\n const r = await request<{ team: AgentTeam }>(\n 'POST',\n '/v1/agents/teams',\n {\n body: {\n name: input.name,\n slug: input.slug,\n description: input.description,\n agent_ids: input.agentIds,\n },\n }\n );\n return r.team;\n }),\n update: (teamId, input) =>\n safely(async () => {\n const r = await request<{ team: AgentTeam }>(\n 'PATCH',\n teamPath(teamId),\n { body: input }\n );\n return r.team;\n }),\n delete: teamId =>\n safely(async () => {\n const r = await request<{ memberships_removed?: number }>(\n 'DELETE',\n teamPath(teamId)\n );\n return { memberships_removed: r.memberships_removed ?? 0 };\n }),\n addMember: (teamId, agentId) =>\n safely(() => {\n need(agentId, 'agentId', 'agentId');\n return request<{ member: unknown; channels_granted: number }>(\n 'POST',\n `${teamPath(teamId)}/members`,\n { body: { agent_id: agentId } }\n );\n }),\n removeMember: (teamId, agentId) =>\n safely(async () => {\n const r = await request<{ memberships_removed?: number }>(\n 'DELETE',\n `${teamPath(teamId)}/members/${agentPath(agentId)}`\n );\n return { memberships_removed: r.memberships_removed ?? 0 };\n }),\n grantChannel: (teamId, channelId, role) =>\n safely(() => {\n need(channelId, 'channelId', 'channelId');\n return request<{\n grant: AgentTeamChannelGrant;\n memberships_added: number;\n }>('POST', `${teamPath(teamId)}/channels`, {\n body: { channel_id: channelId, role },\n });\n }),\n revokeChannel: (teamId, channelId) =>\n safely(async () => {\n need(channelId, 'channelId', 'channelId');\n const r = await request<{ memberships_removed?: number }>(\n 'DELETE',\n `${teamPath(teamId)}/channels/${enc(channelId)}`\n );\n return { memberships_removed: r.memberships_removed ?? 0 };\n }),\n },\n\n policies: {\n list: (options = {}) =>\n safely(async () => {\n const r = await request<{ policies?: AgentPolicy[] }>(\n 'GET',\n '/v1/agents/policies',\n { query: { scope: options.scope } }\n );\n return r.policies ?? [];\n }),\n get: policyId =>\n safely(async () => {\n need(policyId, 'The policy id', 'policyId');\n const r = await request<{ policy: AgentPolicy }>(\n 'GET',\n `/v1/agents/policies/${enc(policyId)}`\n );\n return r.policy;\n }),\n put: input =>\n safely(async () => {\n const r = await request<{ policy: AgentPolicy }>(\n 'PUT',\n '/v1/agents/policies',\n {\n body: {\n scope: input.scope,\n scope_id: input.scopeId,\n rules: input.rules,\n enabled: input.enabled,\n },\n }\n );\n return r.policy;\n }),\n delete: policyId =>\n safely(async () => {\n need(policyId, 'The policy id', 'policyId');\n await request('DELETE', `/v1/agents/policies/${enc(policyId)}`);\n }),\n preview: (agentId, options = {}) =>\n safely(() => {\n need(agentId, 'agentId', 'agentId');\n return request<EffectivePolicyPreview>(\n 'GET',\n '/v1/agents/policies/preview',\n { query: { agent_id: agentId, channel_id: options.channelId } }\n );\n }),\n },\n\n orgChart: {\n get: (options = {}) =>\n request<OrgChart>('GET', '/v1/agents/org-chart', {\n query: {\n team_id: options.teamId,\n channel_id: options.channelId,\n include_retired: options.includeRetired ? 'true' : undefined,\n },\n }),\n setManager: (agentId, input) =>\n safely(async () => {\n if (input?.managerAgentId && input?.managerUserId) {\n throw new ValidationError(\n 'Set managerAgentId or managerUserId, not both',\n 'managerAgentId'\n );\n }\n const r = await request<{ agent: Agent }>(\n 'PUT',\n `/v1/agents/org-chart/${agentPath(agentId)}/manager`,\n {\n body: {\n manager_agent_id: input?.managerAgentId ?? null,\n manager_user_id: input?.managerUserId ?? null,\n },\n }\n );\n return r.agent;\n }),\n setOwner: (agentId, ownerUserId) =>\n safely(async () => {\n const r = await request<{ agent: Agent }>(\n 'PUT',\n `/v1/agents/org-chart/${agentPath(agentId)}/owner`,\n { body: { owner_user_id: ownerUserId ?? null } }\n );\n return r.agent;\n }),\n },\n\n onboarding: agentId =>\n safely(() =>\n request<AgentOnboarding>(\n 'GET',\n `/v1/agents/${agentPath(agentId)}/onboarding`\n )\n ),\n\n setStatus: (agentId, status, options = {}) =>\n safely(() =>\n request<AgentStatusResult>(\n 'POST',\n `/v1/agents/${agentPath(agentId)}/status`,\n { body: { status, reason: options.reason } }\n )\n ),\n };\n}\n\n// ----- webhook guards -----\n\nconst QUESTION_OUTCOMES = new Set([\n 'question.answered',\n 'question.expired',\n 'question.canceled',\n]);\n\n/** An `agent.message` delivery (a channel message, task or status). */\nexport function isAgentMessageWebhook(\n payload: AgentWebhookEvent | null | undefined\n): payload is AgentWebhookPayload {\n return payload?.event === 'agent.message';\n}\n\n/** A closed question pushed to its asker (`question.answered` / `expired` / `canceled`). */\nexport function isQuestionOutcomeWebhook(\n payload: AgentWebhookEvent | null | undefined\n): payload is QuestionOutcomeWebhookPayload {\n return Boolean(payload && QUESTION_OUTCOMES.has(payload.event));\n}\n\n/** A question escalated to this (manager) agent: answer it with `questions.answer`. */\nexport function isQuestionEscalatedWebhook(\n payload: AgentWebhookEvent | null | undefined\n): payload is QuestionEscalatedWebhookPayload {\n return payload?.event === 'question.escalated';\n}\n\n/**\n * A question's outcome posted in its thread (questions asked with a\n * `channelId`): `message.data` is `{ type, question, answer }`.\n */\nexport function isQuestionOutcomeMessage(\n message: AgentMessage | null | undefined\n): message is AgentMessage & { data: QuestionOutcomeData } {\n const data = message?.data as Record<string, unknown> | undefined;\n return Boolean(\n data &&\n typeof data.type === 'string' &&\n QUESTION_OUTCOMES.has(data.type) &&\n data.question &&\n typeof data.question === 'object'\n );\n}\n\n/**\n * The `status` message a task handoff posts (delivered to the previous and\n * new assignees): `message.data.handoff` says who to whom, and why.\n */\nexport function isTaskHandoffMessage(\n message: AgentMessage | null | undefined\n): message is AgentMessage & { data: TaskHandoffData } {\n const handoff = (message?.data as Record<string, unknown> | undefined)\n ?.handoff as Record<string, unknown> | undefined;\n return Boolean(\n message?.type === 'status' &&\n handoff &&\n typeof handoff === 'object' &&\n typeof handoff.to_agent_id === 'string'\n );\n}\n","import { requestJson } from './http';\nimport {\n createAgentsGovernance,\n type AgentsGovernance,\n type AgentsRequest,\n} from './agents-governance';\nimport type { AgentWebhookEvent } from './agents-governance-types';\nimport { ChannlizeError, ValidationError } from './types';\nimport type {\n Agent,\n AgentAckInput,\n AgentAckResult,\n AgentChannel,\n AgentChannelFeed,\n AgentChannelFeedOptions,\n AgentChannelMember,\n AgentChannelReadOptions,\n AgentDetail,\n AgentInboxItem,\n AgentInboxOptions,\n AgentInboxPage,\n AgentMessageHandler,\n AgentPollOptions,\n AgentsClientConfig,\n AgentWebhookPayload,\n SendAgentMessageInput,\n SendAgentMessageResult,\n} from './agents-types';\nimport {\n DEFAULT_BASE_URL,\n DEFAULT_TIMEOUT,\n keyKind,\n readChannlizeEnv,\n} from './shared';\n\ntype Query = Record<string, string | number | boolean | undefined>;\n\n/**\n * Channlize for Agents: post to agent channels, read the inbox and the\n * directory, ask humans and wait for the answer, work on tasks, as one of\n * your agents; and, with an `agents:admin` key, manage teams, policies and\n * the org chart. Create it with `createAgentsClient`, or use\n * `ChannlizeClient.agents`.\n */\nexport interface AgentsClient extends AgentsGovernance {\n /** The default agent id (`agentId` option or `CHANNLIZE_AGENT_ID`). */\n readonly agentId: string | undefined;\n /** Post a message, task or task status to a channel (`agents:send`). One Event per message. */\n send(input: SendAgentMessageInput): Promise<SendAgentMessageResult>;\n /** The agent's unacked messages, oldest first (`agents:read`). */\n inbox(agentId?: string, options?: AgentInboxOptions): Promise<AgentInboxPage>;\n /** Acknowledge inbox messages so they leave the inbox (`agents:read`). */\n ack(\n agentId: string | undefined,\n input: AgentAckInput\n ): Promise<AgentAckResult>;\n /**\n * Read the inbox in a loop, call `handler` for each message (oldest\n * first) and ack it once the handler resolves. Errors back off\n * exponentially; the failed message isn't acked, so it comes back. 400,\n * 401, 403 and 404 stop the loop (the returned promise rejects): retrying\n * can't fix a wrong key, scope or agent. Resolves when `signal` aborts.\n */\n poll(\n agentId: string | undefined,\n handler: AgentMessageHandler,\n options?: AgentPollOptions\n ): Promise<void>;\n /** The organization's agents (`agents:read`). */\n list(options?: { includeRetired?: boolean }): Promise<Agent[]>;\n /** One agent (`agents:read`); an `agents:admin` key also gets the detail. */\n get(agentId: string): Promise<AgentDetail>;\n channels: {\n /** Channels the key's agents are members or observers of (all of them for an `agents:admin` key). */\n list(options?: { includeArchived?: boolean }): Promise<AgentChannel[]>;\n /** A channel and its members. */\n get(\n channelId: string,\n options?: AgentChannelReadOptions\n ): Promise<{ channel: AgentChannel; members: AgentChannelMember[] }>;\n /** A channel's members and roles. */\n members(\n channelId: string,\n options?: AgentChannelReadOptions\n ): Promise<AgentChannelMember[]>;\n /** A channel's messages: newest first with `cursor`, or newer than `after` (polling). */\n messages(\n channelId: string,\n options?: AgentChannelFeedOptions\n ): Promise<AgentChannelFeed>;\n };\n}\n\ninterface ResolvedAgentsConfig {\n apiKey: string;\n agentId?: string;\n baseUrl: string;\n timeout: number;\n}\n\nfunction resolve(config: AgentsClientConfig): ResolvedAgentsConfig {\n const env = readChannlizeEnv();\n const apiKey = config.apiKey ?? env.serverApiKey ?? env.apiKey;\n if (!apiKey) {\n throw new ValidationError(\n 'An agent API key is required: pass apiKey or set CHANNLIZE_API_KEY (or CHANNLIZE_SERVER_API_KEY)',\n 'apiKey'\n );\n }\n if (keyKind(apiKey) === 'widget') {\n throw new ValidationError(\n 'Agents need an agent key or a multi-agent key (ch_live_…, agents:send / agents:read scopes), not the public widget key',\n 'apiKey'\n );\n }\n return {\n apiKey,\n agentId: config.agentId ?? env.agentId,\n baseUrl: (config.baseUrl ?? env.baseUrl ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n ),\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n };\n}\n\nconst enc = encodeURIComponent;\n\n/** Errors the poll loop doesn't retry: the request itself is wrong. */\nfunction isFatal(error: unknown): boolean {\n return (\n error instanceof ChannlizeError &&\n [400, 401, 403, 404].includes(error.statusCode ?? 0)\n );\n}\n\n/** Resolves after `ms`, or early when `signal` aborts. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise(done => {\n if (signal?.aborted) return done();\n const timer = setTimeout(finish, ms);\n function finish() {\n clearTimeout(timer);\n signal?.removeEventListener('abort', finish);\n done();\n }\n signal?.addEventListener('abort', finish, { once: true });\n });\n}\n\n/**\n * Channlize for Agents client. Reads the key from `CHANNLIZE_SERVER_API_KEY`\n * or `CHANNLIZE_API_KEY`, and the default agent from `CHANNLIZE_AGENT_ID`,\n * unless given. Server-side only: never ship an agent key to a browser.\n *\n * ```ts\n * const agents = createAgentsClient({ agentId: process.env.CHANNLIZE_AGENT_ID });\n * await agents.send({ channel: channelId, text: 'Deploy finished' });\n * ```\n */\nexport function createAgentsClient(\n config: AgentsClientConfig = {}\n): AgentsClient {\n const cfg = resolve(config);\n\n const request: AgentsRequest = async <T>(\n method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE',\n path: string,\n options: {\n query?: Query;\n body?: unknown;\n timeout?: number;\n signal?: AbortSignal;\n } = {}\n ): Promise<T> => {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(options.query ?? {})) {\n if (v !== undefined && v !== '') params.set(k, String(v));\n }\n const qs = params.toString();\n const http = { ...cfg, timeout: options.timeout ?? cfg.timeout };\n const res = await requestJson<T>(http, `${path}${qs ? `?${qs}` : ''}`, {\n method,\n headers: { Authorization: `Bearer ${cfg.apiKey}` },\n body:\n options.body === undefined ? undefined : JSON.stringify(options.body),\n signal: options.signal,\n });\n // The agents API answers at the top level (no `data` envelope).\n return res as unknown as T;\n };\n\n function agentFor(agentId: string | undefined, what: string): string {\n const id = agentId ?? cfg.agentId;\n if (!id) {\n throw new ValidationError(\n `${what} needs an agent id: pass it or set agentId (CHANNLIZE_AGENT_ID)`,\n 'agentId'\n );\n }\n return id;\n }\n\n async function inbox(\n agentId?: string,\n options: AgentInboxOptions = {}\n ): Promise<AgentInboxPage> {\n const id = agentFor(agentId, 'inbox');\n const r = await request<{\n messages?: AgentInboxItem[];\n next_cursor?: string | null;\n has_more?: boolean;\n }>('GET', `/v1/agents/${enc(id)}/inbox`, {\n query: { cursor: options.cursor, limit: options.limit },\n });\n return {\n messages: r.messages ?? [],\n nextCursor: r.next_cursor ?? null,\n hasMore: Boolean(r.has_more),\n };\n }\n\n async function ack(\n agentId: string | undefined,\n input: AgentAckInput\n ): Promise<AgentAckResult> {\n const id = agentFor(agentId, 'ack');\n let body: { up_to: string } | { ids: string[] };\n if ('upTo' in input && input.upTo) body = { up_to: input.upTo };\n else if ('ids' in input && input.ids?.length) body = { ids: input.ids };\n else {\n throw new ValidationError(\n 'ack needs upTo or a non-empty ids list',\n 'ids'\n );\n }\n return request<AgentAckResult>('POST', `/v1/agents/${enc(id)}/inbox/ack`, {\n body,\n });\n }\n\n async function poll(\n agentId: string | undefined,\n handler: AgentMessageHandler,\n options: AgentPollOptions = {}\n ): Promise<void> {\n const id = agentFor(agentId, 'poll');\n const interval = Math.max(0, options.intervalMs ?? 5000);\n const maxBackoff = Math.max(interval, options.maxBackoffMs ?? 60_000);\n const { signal } = options;\n let failures = 0;\n\n while (!signal?.aborted) {\n let failed = false;\n let more = false;\n try {\n const page = await inbox(id, { limit: options.limit });\n more = page.hasMore;\n const done: string[] = [];\n try {\n for (const item of page.messages) {\n if (signal?.aborted) break;\n try {\n await handler(item.message, item);\n } catch (error) {\n failed = true;\n options.onError?.(error, item);\n break;\n }\n done.push(item.delivery_id);\n }\n } finally {\n if (done.length) await ack(id, { ids: done });\n }\n } catch (error) {\n if (isFatal(error)) throw error;\n failed = true;\n options.onError?.(error);\n }\n\n if (failed) {\n failures += 1;\n const backoff = Math.min(\n maxBackoff,\n (interval || 1000) * 2 ** (failures - 1)\n );\n await sleep(backoff, signal);\n } else {\n failures = 0;\n if (!more) await sleep(interval, signal);\n }\n }\n }\n\n const channelRead = (options: AgentChannelReadOptions = {}) => ({\n agent_id: options.agentId ?? cfg.agentId,\n });\n\n return {\n ...createAgentsGovernance(request, cfg.agentId),\n agentId: cfg.agentId,\n\n send(input) {\n if (!input || typeof input.channel !== 'string' || !input.channel) {\n return Promise.reject(\n new ValidationError('send needs a channel id', 'channel')\n );\n }\n return request<SendAgentMessageResult>('POST', '/v1/agents/messages', {\n body: {\n agent_id: input.agentId ?? cfg.agentId,\n channel_id: input.channel,\n type: input.type,\n text: input.text,\n thread_id: input.threadId,\n mentions: input.mentions,\n data: input.data,\n tokens: input.tokens,\n cost: input.cost,\n task_id: input.taskId,\n task_status: input.taskStatus,\n },\n });\n },\n\n inbox,\n ack,\n poll,\n\n async list(options = {}) {\n const r = await request<{ agents?: Agent[] }>('GET', '/v1/agents', {\n query: { include_retired: options.includeRetired || undefined },\n });\n return r.agents ?? [];\n },\n\n get(agentId) {\n return request<AgentDetail>('GET', `/v1/agents/${enc(agentId)}`);\n },\n\n channels: {\n async list(options = {}) {\n const r = await request<{ channels?: AgentChannel[] }>(\n 'GET',\n '/v1/agents/channels',\n { query: { include_archived: options.includeArchived || undefined } }\n );\n return r.channels ?? [];\n },\n\n get(channelId, options) {\n return request<{\n channel: AgentChannel;\n members: AgentChannelMember[];\n }>('GET', `/v1/agents/channels/${enc(channelId)}`, {\n query: channelRead(options),\n });\n },\n\n async members(channelId, options) {\n const r = await request<{ members?: AgentChannelMember[] }>(\n 'GET',\n `/v1/agents/channels/${enc(channelId)}/members`,\n { query: channelRead(options) }\n );\n return r.members ?? [];\n },\n\n async messages(channelId, options = {}) {\n const r = await request<{\n channel: AgentChannel;\n messages?: AgentChannelFeed['messages'];\n next_cursor?: string | null;\n latest_cursor?: string | null;\n has_more?: boolean;\n }>('GET', `/v1/agents/channels/${enc(channelId)}/messages`, {\n query: {\n ...channelRead(options),\n cursor: options.cursor,\n after: options.after,\n limit: options.limit,\n sender_agent_id: options.senderAgentId,\n type: options.type,\n thread_id: options.threadId,\n },\n });\n return {\n channel: r.channel,\n messages: r.messages ?? [],\n nextCursor: r.next_cursor ?? null,\n latestCursor: r.latest_cursor ?? null,\n hasMore: Boolean(r.has_more),\n };\n },\n },\n };\n}\n\n// ----- webhook signatures -----\n\n/**\n * Node's `crypto` module, loaded only when needed. The specifier is a\n * variable (and marked ignored) so browser and edge bundlers never try to\n * resolve `node:crypto`; the import only runs where Web Crypto is missing.\n */\nconst NODE_CRYPTO = 'node:crypto';\n\n/** Web Crypto: `globalThis.crypto.subtle`, else Node's `webcrypto` (Node 18). */\nasync function getSubtle(): Promise<SubtleCrypto> {\n const subtle = (globalThis as { crypto?: Crypto }).crypto?.subtle;\n if (subtle) return subtle;\n try {\n const mod = (await import(\n /* webpackIgnore: true */ /* @vite-ignore */ NODE_CRYPTO\n )) as { webcrypto?: { subtle?: SubtleCrypto } };\n if (mod.webcrypto?.subtle) return mod.webcrypto.subtle;\n } catch {\n // Not Node: fall through to the error below.\n }\n throw new ChannlizeError(\n 'verifyAgentWebhook needs the Web Crypto API (globalThis.crypto.subtle, or node:crypto webcrypto on Node.js 18)'\n );\n}\n\nfunction hexToBytes(hex: string): Uint8Array<ArrayBuffer> | null {\n if (hex.length === 0 || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {\n return null;\n }\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\nfunction toBytes(body: string | Uint8Array): Uint8Array<ArrayBuffer> {\n if (typeof body === 'string') return new TextEncoder().encode(body);\n const copy = new Uint8Array(body.byteLength);\n copy.set(body);\n return copy;\n}\n\n/**\n * Checks the `X-Webhook-Signature` header of a webhook agent delivery:\n * `sha256=<hex HMAC-SHA256 of the raw body>`, keyed with the agent's secret\n * (shown once when the agent is created or its secret rotated).\n *\n * Pass the body **exactly as received** (a string or bytes), before any JSON\n * parsing: re-serializing changes the bytes and the signature no longer\n * matches. The comparison is constant-time (Web Crypto `verify`). Uses\n * `globalThis.crypto.subtle`, or `node:crypto`'s `webcrypto` on Node.js 18.\n *\n * ```ts\n * const raw = await req.text();\n * if (!(await verifyAgentWebhook(raw, req.headers.get('x-webhook-signature'), secret))) {\n * return new Response('bad signature', { status: 401 });\n * }\n * const payload = JSON.parse(raw) as AgentWebhookPayload;\n * ```\n */\nexport async function verifyAgentWebhook(\n rawBody: string | Uint8Array,\n signatureHeader: string | string[] | null | undefined,\n secret: string\n): Promise<boolean> {\n if (!secret)\n throw new ValidationError('The agent secret is required', 'secret');\n const header = Array.isArray(signatureHeader)\n ? signatureHeader[0]\n : signatureHeader;\n const m = /^sha256=([0-9a-f]+)$/i.exec(header?.trim() ?? '');\n const expected = m ? hexToBytes(m[1]!) : null;\n if (!expected || expected.length !== 32) return false;\n const subtle = await getSubtle();\n const key = await subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['verify']\n );\n return subtle.verify('HMAC', key, expected, toBytes(rawBody));\n}\n\n/**\n * `verifyAgentWebhook`, then the parsed `agent.message` payload. Throws a\n * 401 `ChannlizeError` (`INVALID_SIGNATURE`) when the signature doesn't\n * match. A webhook agent that asks humans (or manages agents) also gets\n * question events: use `parseAgentWebhookEvent` for those.\n */\nexport async function parseAgentWebhook(\n rawBody: string | Uint8Array,\n signatureHeader: string | string[] | null | undefined,\n secret: string\n): Promise<AgentWebhookPayload> {\n return (await parseAgentWebhookEvent(\n rawBody,\n signatureHeader,\n secret\n )) as AgentWebhookPayload;\n}\n\n/**\n * `verifyAgentWebhook`, then any webhook body: `agent.message`,\n * `question.answered` / `question.expired` / `question.canceled` or\n * `question.escalated` (the `X-Webhook-Event` header). Narrow it with\n * `isAgentMessageWebhook`, `isQuestionOutcomeWebhook` and\n * `isQuestionEscalatedWebhook`.\n */\nexport async function parseAgentWebhookEvent(\n rawBody: string | Uint8Array,\n signatureHeader: string | string[] | null | undefined,\n secret: string\n): Promise<AgentWebhookEvent> {\n if (!(await verifyAgentWebhook(rawBody, signatureHeader, secret))) {\n throw new ChannlizeError(\n 'Invalid agent webhook signature',\n 401,\n 'INVALID_SIGNATURE'\n );\n }\n const text =\n typeof rawBody === 'string' ? rawBody : new TextDecoder().decode(rawBody);\n return JSON.parse(text) as AgentWebhookEvent;\n}\n","import { requestJson } from './http';\nimport { sendNotify } from './notify';\nimport { createAgentsClient, type AgentsClient } from './agents';\nimport type {\n ChannlizeConfig,\n ContactFormData,\n ChatMessage,\n ApiResponse,\n CreateContactResponse,\n CreateChatSessionResponse,\n SendMessageResponse,\n ChatSession,\n TrackEventOptions,\n EventSeverity,\n ChannlizeEvents,\n NotifyEventName,\n NotifyEventData,\n NotifyOptions,\n NotifyResponse,\n TrackEventResult,\n} from './types';\nimport { RateLimitError, ValidationError } from './types';\nimport { DEFAULT_BASE_URL, DEFAULT_TIMEOUT } from './shared';\n\n/**\n * Channlize API Client\n * Provides methods to interact with the Channlize platform.\n *\n * `TEvents` types `notify()` event keys and data (defaults to the\n * augmentable `ChannlizeEvents` interface; any string key is accepted).\n */\nexport class ChannlizeClient<TEvents extends object = ChannlizeEvents> {\n private readonly config: ChannlizeConfig & {\n baseUrl: string;\n timeout: number;\n };\n\n constructor(config: ChannlizeConfig) {\n this.config = {\n ...config,\n // After the spread: an explicit `undefined` must not drop the defaults.\n baseUrl: config.baseUrl || DEFAULT_BASE_URL,\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n };\n\n this.validateConfig();\n }\n\n private validateConfig(): void {\n if (!this.config.apiKey) {\n throw new ValidationError('Widget key is required');\n }\n }\n\n private agentsClient?: AgentsClient;\n\n /**\n * Channlize for Agents (`agents.send`, `agents.inbox`, `agents.poll`, ...),\n * authenticated with `serverApiKey`: an agent key or a multi-agent key.\n * Same as `createAgentsClient({ apiKey: serverApiKey, baseUrl, timeout })`.\n */\n get agents(): AgentsClient {\n if (!this.config.serverApiKey) {\n throw new ValidationError(\n 'serverApiKey (an agent or multi-agent key) is required for agents',\n 'serverApiKey'\n );\n }\n this.agentsClient ??= createAgentsClient({\n apiKey: this.config.serverApiKey,\n baseUrl: this.config.baseUrl,\n timeout: this.config.timeout,\n });\n return this.agentsClient;\n }\n\n private makeRequest<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n return requestJson<T>(this.config, endpoint, options);\n }\n\n /**\n * Submit a contact form\n */\n async submitContactForm(\n data: ContactFormData\n ): Promise<CreateContactResponse> {\n const response = await this.makeRequest<CreateContactResponse>(\n '/public/contact',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n subject: data.subject,\n message: data.message,\n email: data.email,\n name: data.name,\n customFields:\n data.customFields && Object.keys(data.customFields).length > 0\n ? data.customFields\n : undefined,\n pageUrl:\n data.pageUrl ||\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n data.userAgent ||\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n\n return response.data || response;\n }\n\n /**\n * Create a new chat session\n */\n async createChatSession(\n metadata?: Record<string, unknown>\n ): Promise<CreateChatSessionResponse> {\n const response = await this.makeRequest<CreateChatSessionResponse>(\n '/v1/chat/sessions',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n metadata: {\n ...metadata,\n pageUrl:\n typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent:\n typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined,\n },\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Send a message in a chat session.\n * When `attachments` are provided the request is sent as multipart/form-data\n * so the files are uploaded alongside the message content.\n */\n async sendMessage(\n sessionId: string,\n content: string,\n attachments?: File[]\n ): Promise<SendMessageResponse> {\n const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\n if (attachments && attachments.length > 0) {\n const formData = new FormData();\n formData.append('content', content);\n formData.append('idempotencyKey', idempotencyKey);\n for (const file of attachments) {\n formData.append('attachments', file, file.name);\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: formData,\n // Let the browser set Content-Type with boundary for multipart\n headers: { 'Content-Type': '' },\n }\n );\n\n return response.data;\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify({\n content,\n idempotencyKey,\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Get chat session details\n */\n async getChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}`\n );\n return response.data;\n }\n\n /**\n * Get messages for a chat session\n */\n async getChatMessages(\n sessionId: string,\n limit = 50,\n offset = 0\n ): Promise<ChatMessage[]> {\n const response = await this.makeRequest<ChatMessage[]>(\n `/v1/chat/sessions/${sessionId}/messages?limit=${limit}&offset=${offset}`\n );\n return response.data;\n }\n\n /**\n * Close a chat session\n */\n async closeChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}/close`,\n { method: 'POST' }\n );\n return response.data;\n }\n\n /**\n * Log tracking: record a log line from the browser (widget key →\n * `POST /public/events`). Shown under Log tracking in the dashboard.\n */\n async trackEvent(\n name: string,\n options?: TrackEventOptions\n ): Promise<TrackEventResult> {\n return this.nonFatalTracking(() => this.sendBrowserEvent(name, options));\n }\n\n private async sendBrowserEvent(\n name: string,\n options?: TrackEventOptions\n ): Promise<TrackEventResult> {\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/public/events',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey,\n name,\n properties: options?.properties,\n severity: options?.severity,\n distinctId: options?.distinctId,\n pageUrl:\n options?.pageUrl ??\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n options?.userAgent ??\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n\n /**\n * Log tracking from a trusted server, using the organization API key\n * (`POST /v1/events`). Shown under Log tracking in the dashboard.\n */\n async trackServerEvent(\n name: string,\n options?: { properties?: Record<string, unknown>; severity?: EventSeverity }\n ): Promise<TrackEventResult> {\n if (!this.config.serverApiKey) {\n throw new ValidationError(\n 'serverApiKey is required for trackServerEvent'\n );\n }\n return this.nonFatalTracking(() => this.sendServerEvent(name, options));\n }\n\n /**\n * Log tracking must never break the caller: a 429 (rate limit, or the\n * plan's limit for the billing period) resolves with `success: false`\n * and `dropped` instead of throwing, with one console warning per client.\n */\n private async nonFatalTracking(\n send: () => Promise<TrackEventResult>\n ): Promise<TrackEventResult> {\n try {\n return await send();\n } catch (error) {\n if (!(error instanceof RateLimitError)) throw error;\n const dropped =\n error.code === 'PLAN_LIMIT_REACHED' ? 'plan_limit' : 'rate_limit';\n if (!this.warnedDropped && typeof console !== 'undefined') {\n this.warnedDropped = true;\n console.warn(`[channlize] log event dropped: ${error.message}`);\n }\n return { success: false, id: '', dropped };\n }\n }\n\n private warnedDropped = false;\n\n private async sendServerEvent(\n name: string,\n options?: { properties?: Record<string, unknown>; severity?: EventSeverity }\n ): Promise<TrackEventResult> {\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/v1/events',\n {\n method: 'POST',\n body: JSON.stringify({\n projectId: this.config.projectId,\n name,\n properties: options?.properties,\n severity: options?.severity,\n }),\n headers: {\n Authorization: `Bearer ${this.config.serverApiKey}`,\n },\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n\n /**\n * Events: post a Slack message from your server when something happens\n * (`POST /v1/notify`). The event key picks the channel and message\n * template configured under Events in the dashboard; `data` fills the\n * template's `{{field}}` placeholders. Needs `serverApiKey` (write scope)\n * and `projectId` — never call it from browser code.\n *\n * An unknown key is saved as a disabled draft and answered with\n * `status: 'skipped'`, so it never throws for a missing setup.\n */\n async notify<K extends NotifyEventName<TEvents>>(\n event: K,\n data?: NotifyEventData<TEvents, K>,\n options?: NotifyOptions\n ): Promise<NotifyResponse> {\n if (!this.config.serverApiKey) {\n throw new ValidationError('serverApiKey is required for notify');\n }\n return sendNotify(\n this.config,\n {\n serverApiKey: this.config.serverApiKey,\n projectId: this.config.projectId,\n },\n event,\n data as Record<string, unknown> | undefined,\n options\n );\n }\n}\n","/**\n * Channlize for Agents: the JSON shapes of the `/v1/agents` API. They mirror\n * the API's DTOs (`api/lib/agents/dto.ts`), so fields are `snake_case` as\n * the API sends them. Request options are `camelCase`, like the rest of the\n * SDK.\n */\n\n/** An agent's lifecycle status. Only `active` agents can send. */\nexport const AGENT_STATUSES = ['active', 'paused', 'retired'] as const;\nexport type AgentStatus = (typeof AGENT_STATUSES)[number];\n\n/** How channel messages reach an agent. */\nexport const AGENT_DELIVERY_MODES = ['webhook', 'inbox', 'a2a'] as const;\nexport type AgentDeliveryMode = (typeof AGENT_DELIVERY_MODES)[number];\n\n/** Every message type a channel can hold. */\nexport const AGENT_MESSAGE_TYPES = [\n 'message',\n 'task',\n 'question',\n 'escalation',\n 'status',\n] as const;\nexport type AgentMessageType = (typeof AGENT_MESSAGE_TYPES)[number];\n\n/** The message types an agent can send (`agents.send`). */\nexport const SENDABLE_AGENT_MESSAGE_TYPES = [\n 'message',\n 'task',\n 'status',\n] as const;\nexport type SendableAgentMessageType =\n (typeof SENDABLE_AGENT_MESSAGE_TYPES)[number];\n\n/**\n * A task's lifecycle. A `task` starts `open`; a `status` message moves it.\n * `completed`, `failed`, `canceled` and `rejected` are final.\n */\nexport const AGENT_TASK_STATUSES = [\n 'open',\n 'working',\n 'input_required',\n 'completed',\n 'failed',\n 'canceled',\n 'rejected',\n] as const;\nexport type AgentTaskStatus = (typeof AGENT_TASK_STATUSES)[number];\n\n/** Agents are `member`s (post and read) or `observer`s (read); people are `supervisor`s. */\nexport type AgentMemberRole = 'member' | 'observer' | 'supervisor';\n\n/**\n * Where one delivery stands: `inbox` (waiting to be pulled), `pending`\n * (webhook or A2A push queued), `delivered`, `failed` (pushes gave up; still\n * in the inbox), `held` (recipient paused), `pending_a2a`, `skipped`.\n */\nexport type AgentDeliveryStatus =\n | 'pending'\n | 'inbox'\n | 'delivered'\n | 'failed'\n | 'held'\n | 'pending_a2a'\n | 'skipped';\n\n/** An agent of the directory (`GET /v1/agents`). Never carries its secret. */\nexport interface Agent {\n id: string;\n slug: string;\n name: string;\n /** `webhook` (run by you) or `builtin` (run by Channlize). */\n type: string;\n delivery: AgentDeliveryMode;\n status: AgentStatus;\n enabled: boolean;\n avatar_url: string | null;\n emoji: string | null;\n role: string | null;\n description: string | null;\n owner_user_id: string | null;\n manager_agent_id: string | null;\n manager_user_id: string | null;\n webhook_url: string | null;\n a2a_agent_card_url: string | null;\n a2a_agent_card: unknown;\n a2a_agent_card_fetched_at: string | null;\n has_secret: boolean;\n last_seen_at: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** Slack mirroring of a channel. Default: everything on. */\nexport interface AgentMirrorConfig {\n enabled: boolean;\n types: Record<AgentMessageType, boolean>;\n /** Each task (and its status updates) gets its own Slack thread. */\n thread_per_task: boolean;\n}\n\n/** An agent channel. */\nexport interface AgentChannel {\n id: string;\n slug: string;\n name: string;\n description: string | null;\n slack_channel_id: string | null;\n mirror: AgentMirrorConfig;\n archived: boolean;\n archived_at: string | null;\n created_by: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** A channel member: an agent (`member` / `observer`) or a person (`supervisor`). */\nexport interface AgentChannelMember {\n id: string;\n channel_id: string;\n kind: 'agent' | 'user';\n agent_id: string | null;\n user_id: string | null;\n role: AgentMemberRole;\n name: string | null;\n agent_slug?: string | null;\n agent_status?: string | null;\n user_email?: string | null;\n created_at: string;\n}\n\n/**\n * Who sent a message. `slack_user`: a person replying in Slack who isn't\n * matched to a Channlize user. `deleted`: the sender no longer exists.\n */\nexport type AgentMessageSender =\n | { kind: 'agent'; agent_id: string }\n | { kind: 'user'; user_id: string }\n | { kind: 'slack_user'; slack_user_id: string; name: string }\n | { kind: 'deleted' };\n\n/** A message in an agent channel. */\nexport interface AgentMessage {\n id: string;\n /** Position in the channel feed. */\n seq: string;\n channel_id: string;\n /** Root message of its thread; null on a root. */\n thread_id: string | null;\n sender: AgentMessageSender;\n type: AgentMessageType;\n text: string;\n data: Record<string, unknown>;\n /** Mentioned agent ids. */\n mentions: string[];\n /** On a `status` message: the task it updates. */\n task_id: string | null;\n /** On a `task`: its current status. On a `status` message: the status it reported. */\n task_status: AgentTaskStatus | null;\n tokens: number | null;\n cost: number | null;\n slack_ts: string | null;\n a2a: {\n context_id: string | null;\n task_id: string | null;\n message_id: string | null;\n };\n created_at: string;\n}\n\n/** One message for one recipient agent. */\nexport interface AgentDelivery {\n id: string;\n message_id: string;\n agent_id: string;\n status: AgentDeliveryStatus;\n attempts: number;\n last_error: string | null;\n delivered_at: string | null;\n acked_at: string | null;\n created_at: string;\n}\n\n// ----- requests and responses -----\n\n/** `agents.send` input. */\nexport interface SendAgentMessageInput {\n /**\n * The agent that sends. Required with a multi-agent key (unless it lists\n * one agent); optional with an agent key. Defaults to the client's `agentId`.\n */\n agentId?: string;\n /** The channel id. The agent must be a `member` (observers can't post). */\n channel: string;\n /** Default `message`. A `task` starts `open`; a `status` updates `taskId`. */\n type?: SendableAgentMessageType;\n text: string;\n /** Reply in this thread (any message of it). */\n threadId?: string;\n /** Agent ids to mention. */\n mentions?: string[];\n /** Structured data, stored and delivered with the message. */\n data?: Record<string, unknown>;\n /** Tokens the agent used for this message, for reporting. */\n tokens?: number;\n /** Cost the agent reports for this message, for reporting. */\n cost?: number;\n /** On a `status` message: the task to update. */\n taskId?: string;\n /** On a `status` message: the task's new status. */\n taskStatus?: AgentTaskStatus;\n}\n\n/** `agents.send` result (`201`). */\nexport interface SendAgentMessageResult {\n message: AgentMessage;\n /** One per recipient agent. */\n deliveries: Array<{\n id: string;\n agent_id: string;\n status: AgentDeliveryStatus;\n }>;\n}\n\n/** One unacked message of an agent's inbox. */\nexport interface AgentInboxItem {\n delivery_id: string;\n /** This item's position; pass it as `cursor` to read after it. */\n cursor: string;\n status: AgentDeliveryStatus;\n attempts: number;\n message: AgentMessage;\n}\n\nexport interface AgentInboxOptions {\n /** Read after this cursor (`nextCursor` of the previous page). Default: the oldest unacked message. */\n cursor?: string;\n /** Page size (1-200, default 50). */\n limit?: number;\n}\n\n/** `agents.inbox` result: unacked messages, oldest first. */\nexport interface AgentInboxPage {\n messages: AgentInboxItem[];\n /** Pass as `cursor` for the next page; the same cursor when nothing is newer. */\n nextCursor: string | null;\n hasMore: boolean;\n}\n\n/** `agents.ack` input: everything up to and including one id, or a list of ids (delivery or message ids). */\nexport type AgentAckInput = { upTo: string } | { ids: string[] };\n\nexport interface AgentAckResult {\n /** How many were newly acked (acking twice is harmless). */\n acked: number;\n}\n\n/**\n * `agents.get` result. An `agents:admin` key also gets the dashboard detail:\n * recent messages, delivery stats, keys and channels.\n */\nexport interface AgentDetail {\n agent: Agent;\n recent_messages?: AgentMessage[];\n delivery_stats?: Record<string, unknown>;\n keys?: Array<Record<string, unknown>>;\n channels?: AgentChannel[];\n}\n\nexport interface AgentChannelReadOptions {\n /** Read as this agent (multi-agent keys). Defaults to the client's `agentId`. */\n agentId?: string;\n}\n\nexport interface AgentChannelFeedOptions extends AgentChannelReadOptions {\n /** Older page: `nextCursor` of the previous page. */\n cursor?: string;\n /** Newer messages only: `latestCursor` of the previous read (for polling). */\n after?: string;\n /** Page size (1-200, default 50). */\n limit?: number;\n /** Only messages from this agent. */\n senderAgentId?: string;\n type?: AgentMessageType;\n /** Only this thread. */\n threadId?: string;\n}\n\n/** `agents.channels.messages` result. */\nexport interface AgentChannelFeed {\n channel: AgentChannel;\n messages: AgentMessage[];\n /** Pass as `cursor` for the older page (null when there is none). */\n nextCursor: string | null;\n /** Pass as `after` on the next poll. */\n latestCursor: string | null;\n hasMore: boolean;\n}\n\n// ----- webhook -----\n\n/**\n * The body Channlize POSTs to a webhook agent (`version: 1`), with the\n * headers `X-Webhook-Event: agent.message`, `X-Webhook-Delivery-ID` and\n * `X-Webhook-Signature` (check it with `verifyAgentWebhook`). Answer 2xx to\n * ack it; anything else is retried with backoff, and the message stays in\n * the agent's inbox.\n */\nexport interface AgentWebhookPayload {\n version: 1;\n event: 'agent.message';\n delivery_id: string;\n /** The recipient. */\n agent: { id: string; slug: string; name: string };\n channel: { id: string; slug: string; name: string };\n sender:\n | { kind: 'agent'; id: string; slug: string | null; name: string | null }\n | { kind: 'user'; id: string }\n | { kind: 'slack_user'; id: string; name: string }\n | { kind: 'deleted' };\n message: AgentMessage;\n ack_url: string;\n}\n\n// ----- client config -----\n\n/** `createAgentsClient` options; each falls back to an environment variable. */\nexport interface AgentsClientConfig {\n /**\n * An agent key, a multi-agent key, or an `agents:admin` key (read\n * helpers only). Default: `CHANNLIZE_SERVER_API_KEY`, else `CHANNLIZE_API_KEY`.\n */\n apiKey?: string;\n /**\n * The agent this client acts as by default (needed with a multi-agent\n * key, and for `inbox` / `ack` / `poll`). Default: `CHANNLIZE_AGENT_ID`.\n */\n agentId?: string;\n /** API base URL. Default: `CHANNLIZE_BASE_URL`, else `https://api.channlize.com`. */\n baseUrl?: string;\n /** Request timeout in ms (default 10000). */\n timeout?: number;\n}\n\n/** `agents.poll` options. */\nexport interface AgentPollOptions {\n /** Wait between polls when the inbox is empty (default 5000 ms). */\n intervalMs?: number;\n /** Upper bound of the backoff after errors (default 60000 ms). */\n maxBackoffMs?: number;\n /** Inbox page size (default 50). */\n limit?: number;\n /** Stops the loop; `poll` then resolves. */\n signal?: AbortSignal;\n /**\n * Called when reading the inbox, the handler or the ack fails (`item` is\n * set for a handler failure). The loop backs off and retries; the failed\n * message isn't acked, so it comes back.\n */\n onError?: (error: unknown, item?: AgentInboxItem) => void;\n}\n\n/** What `agents.poll` calls for each message, oldest first. Acked once it resolves. */\nexport type AgentMessageHandler = (\n message: AgentMessage,\n item: AgentInboxItem\n) => unknown | Promise<unknown>;\n"],"mappings":"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","names":["fetch","retryAfter","enc","sleep","response"]}
|