@thaliq/sdk 1.0.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/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # @thaliq/sdk
2
+
3
+ Headless, type-safe, provider-agnostic SDK for building applications with [Thaliq](https://thaliq.com) AI agents.
4
+
5
+ - **Streaming-first** — `AsyncIterable<StreamEvent>` as primary primitive
6
+ - **Zero dependencies** — Only native runtime APIs (`fetch`, `ReadableStream`)
7
+ - **Provider-agnostic** — Swap backends via the `Provider` interface
8
+ - **Human-in-the-Loop** — Built-in HITL action support (consent, confirm, select, form)
9
+ - **Automatic retry** — Exponential backoff on server/network errors
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @thaliq/sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { Thaliq } from '@thaliq/sdk';
21
+
22
+ const thaliq = new Thaliq({ apiKey: 'tq_live_xxx' });
23
+
24
+ // Non-streaming
25
+ const response = await thaliq.agent.chat('Hello!');
26
+ console.log(response.message);
27
+
28
+ // Streaming
29
+ const stream = thaliq.agent.stream('Analyze Q4 sales');
30
+ for await (const event of stream) {
31
+ if (event.type === 'content.delta') {
32
+ process.stdout.write(event.delta);
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Streaming
38
+
39
+ Every stream event has a `type` field for type narrowing:
40
+
41
+ ```typescript
42
+ const stream = thaliq.agent.stream('Hello');
43
+
44
+ for await (const event of stream) {
45
+ switch (event.type) {
46
+ case 'meta':
47
+ console.log('Conversation:', event.conversationId);
48
+ break;
49
+ case 'status':
50
+ console.log('Status:', event.text);
51
+ break;
52
+ case 'content.delta':
53
+ process.stdout.write(event.delta);
54
+ break;
55
+ case 'tool.start':
56
+ console.log(`Running tool: ${event.tool}`);
57
+ break;
58
+ case 'tool.end':
59
+ console.log(`Tool ${event.tool}: ${event.success ? 'ok' : 'failed'}`);
60
+ break;
61
+ case 'response.completed':
62
+ console.log('\nDone. Tokens:', event.metadata.completionTokens);
63
+ break;
64
+ case 'error':
65
+ console.error('Error:', event.message);
66
+ break;
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### Convenience methods
72
+
73
+ ```typescript
74
+ // Get just the text
75
+ const text = await thaliq.agent.stream('Summarize this').text();
76
+
77
+ // Get the full response (like chat, but streamed)
78
+ const response = await thaliq.agent.stream('Summarize this').finalResponse();
79
+ ```
80
+
81
+ ## User Identity
82
+
83
+ For authenticated B2B scenarios, identify the user before sending messages:
84
+
85
+ ```typescript
86
+ thaliq.identify({
87
+ userId: 'usr_123',
88
+ email: 'user@company.com',
89
+ token: 'eyJ...', // JWT passthrough for MCP servers
90
+ mcpTokens: { 'my-mcp-server': 'server-specific-token' },
91
+ });
92
+
93
+ // Clear on logout
94
+ thaliq.reset();
95
+ ```
96
+
97
+ ## Human-in-the-Loop (HITL)
98
+
99
+ When the agent requires user approval before executing a tool:
100
+
101
+ ```typescript
102
+ import { ActionResponseBuilder } from '@thaliq/sdk';
103
+
104
+ const stream = thaliq.agent.stream('Transfer $500 to Alice');
105
+ for await (const event of stream) {
106
+ if (event.type === 'content.delta') process.stdout.write(event.delta);
107
+ }
108
+
109
+ if (stream.pendingAction) {
110
+ // User approved — resume the stream
111
+ const resumed = thaliq.agent.respondToAction({
112
+ conversationId: stream.conversationId!,
113
+ originalMessage: 'Transfer $500 to Alice',
114
+ actionResponse: ActionResponseBuilder.accept(stream.pendingAction.instructionId),
115
+ });
116
+
117
+ for await (const event of resumed) {
118
+ if (event.type === 'content.delta') process.stdout.write(event.delta);
119
+ }
120
+ }
121
+ ```
122
+
123
+ Action response helpers:
124
+
125
+ ```typescript
126
+ ActionResponseBuilder.accept(instructionId); // consent/confirm
127
+ ActionResponseBuilder.reject(instructionId); // reject
128
+ ActionResponseBuilder.select(instructionId, 'option_a'); // select
129
+ ActionResponseBuilder.submitForm(instructionId, { amount: '500' }); // form
130
+ ```
131
+
132
+ ## Conversations
133
+
134
+ Conversations are auto-tracked across streams:
135
+
136
+ ```typescript
137
+ const stream = thaliq.agent.stream('Hello');
138
+ for await (const event of stream) { /* ... */ }
139
+
140
+ // Continue the conversation
141
+ const followUp = thaliq.agent.stream('Tell me more', {
142
+ conversationId: thaliq.conversations.active!.id,
143
+ });
144
+ ```
145
+
146
+ ## Configuration
147
+
148
+ ```typescript
149
+ const thaliq = new Thaliq({
150
+ apiKey: 'tq_live_xxx', // Required
151
+ baseUrl: 'https://api.thaliq.com', // Default
152
+ integrationType: 'sdk', // 'sdk' | 'widget'
153
+ timeout: 30000, // Request timeout (ms)
154
+ maxRetries: 2, // Automatic retries
155
+ retry: {
156
+ baseDelay: 500, // Initial retry delay
157
+ maxDelay: 10000, // Max retry delay
158
+ },
159
+ logLevel: 'warn', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
160
+ defaultHeaders: { // Custom headers on every request
161
+ 'x-tenant-id': 'abc',
162
+ },
163
+ });
164
+ ```
165
+
166
+ ## Events
167
+
168
+ ```typescript
169
+ thaliq.on('error', (err) => console.error(err.code, err.message));
170
+ thaliq.on('retry', (attempt, err, delayMs) => console.warn(`Retry #${attempt}`));
171
+ thaliq.on('stream.start', (convId) => { /* ... */ });
172
+ thaliq.on('stream.end', (convId) => { /* ... */ });
173
+ thaliq.on('identify', (userId) => { /* ... */ });
174
+ thaliq.on('reset', () => { /* ... */ });
175
+ ```
176
+
177
+ ## Error Handling
178
+
179
+ All SDK errors extend `ThaliqError` with a `code` property:
180
+
181
+ ```typescript
182
+ import { ThaliqError, AuthError, RateLimitError } from '@thaliq/sdk';
183
+
184
+ try {
185
+ await thaliq.agent.chat('Hello');
186
+ } catch (err) {
187
+ if (err instanceof RateLimitError) {
188
+ console.log(`Rate limited. Retry after ${err.retryAfter}s`);
189
+ } else if (err instanceof AuthError) {
190
+ console.log('Invalid API key');
191
+ } else if (err instanceof ThaliqError) {
192
+ console.log(err.code, err.message);
193
+ }
194
+ }
195
+ ```
196
+
197
+ | Error Class | Code | HTTP Status |
198
+ |-------------|------|-------------|
199
+ | `AuthError` | `AUTH_ERROR` | 401, 403 |
200
+ | `RateLimitError` | `RATE_LIMIT` | 429 |
201
+ | `ValidationError` | `VALIDATION_ERROR` | 400 |
202
+ | `ServiceError` | `SERVICE_ERROR` | 503 |
203
+ | `StreamError` | `STREAM_ERROR` | — |
204
+ | `TimeoutError` | `TIMEOUT_ERROR` | — |
205
+ | `ConnectionError` | `CONNECTION_ERROR` | — |
206
+
207
+ ## Compatibility
208
+
209
+ | Runtime | Minimum |
210
+ |---------|---------|
211
+ | Node.js | 18+ (native `fetch`) |
212
+ | Browsers | Chrome 89+, Firefox 89+, Safari 15+ |
213
+ | Deno | 1.28+ |
214
+ | Bun | 1.0+ |
215
+
216
+ ## Documentation
217
+
218
+ - [Full SDK Documentation](https://docs.thaliq.com/sdk)
219
+ - [SDK Protocol Specification](https://docs.thaliq.com/sdk/protocol)
220
+ - [Integration Guide](https://docs.thaliq.com/integration)
221
+
222
+ ## License
223
+
224
+ MIT
@@ -0,0 +1,20 @@
1
+ import { Logger } from '../types/config';
2
+ import { Provider, ProviderRequest } from '../types/provider';
3
+ import { ChatResponse } from '../types/responses';
4
+ import { StreamEvent } from '../types/events';
5
+ /** Create a mock logger that captures calls */
6
+ export declare function createMockLogger(): Logger & {
7
+ calls: Record<string, unknown[][]>;
8
+ };
9
+ /** Encode SSE events into a ReadableStream<Uint8Array> */
10
+ export declare function createSSEStream(events: Array<{
11
+ event: string;
12
+ data: string;
13
+ }>): ReadableStream<Uint8Array>;
14
+ /** Create a mock provider for testing */
15
+ export declare function createMockProvider(overrides?: {
16
+ chat?: (request: ProviderRequest) => Promise<ChatResponse>;
17
+ stream?: (request: ProviderRequest) => AsyncIterable<StreamEvent>;
18
+ }): Provider;
19
+ /** Sleep for a given number of milliseconds */
20
+ export declare function delay(ms: number): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import { Provider } from '../types/provider';
2
+ import { ChatOptions, StreamOptions, AgentStream, RespondToActionOptions } from '../types/agent';
3
+ import { ChatResponse } from '../types/responses';
4
+ import { UserIdentity } from '../types/identity';
5
+ import { ResolvedConfig, SDKEventMap } from '../types/config';
6
+ import { TypedEventEmitter } from '../utils/event-emitter';
7
+ import { ConversationManager } from './conversation-manager';
8
+ export declare class AgentClient {
9
+ private readonly config;
10
+ private readonly provider;
11
+ private readonly getIdentity;
12
+ private readonly emitter;
13
+ private readonly conversationManager;
14
+ constructor(config: ResolvedConfig, provider: Provider, getIdentity: () => UserIdentity | null, emitter: TypedEventEmitter<SDKEventMap>, conversationManager: ConversationManager);
15
+ /** Send a message and wait for the complete response (with automatic retry) */
16
+ chat(message: string, options?: ChatOptions): Promise<ChatResponse>;
17
+ /** Send a message with streaming — returns an AgentStream */
18
+ stream(message: string, options?: StreamOptions): AgentStream;
19
+ /**
20
+ * Respond to a pending HITL action and resume the conversation.
21
+ *
22
+ * This is a convenience method that creates a new stream with the
23
+ * action response, continuing the conversation where it left off.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const stream = thaliq.agent.stream('Transfer $500');
28
+ * for await (const event of stream) { ... }
29
+ *
30
+ * if (stream.pendingAction) {
31
+ * const resumed = thaliq.agent.respondToAction({
32
+ * conversationId: stream.conversationId!,
33
+ * originalMessage: 'Transfer $500',
34
+ * actionResponse: ActionResponseBuilder.accept(stream.pendingAction.instructionId),
35
+ * });
36
+ * for await (const event of resumed) { ... }
37
+ * }
38
+ * ```
39
+ */
40
+ respondToAction(options: RespondToActionOptions): AgentStream;
41
+ /** Build the ProviderRequest from message + options + identity */
42
+ private buildRequest;
43
+ /** Resolve all headers from config + identity */
44
+ private resolveHeaders;
45
+ }
@@ -0,0 +1,22 @@
1
+ import { AgentStream as IAgentStream, StreamStatus } from '../types/agent';
2
+ import { StreamEvent } from '../types/events';
3
+ import { ChatResponse } from '../types/responses';
4
+ import { PendingAction } from '../types/actions';
5
+ export declare class AgentStreamImpl implements IAgentStream {
6
+ private _conversationId;
7
+ private _status;
8
+ private _pendingAction;
9
+ private readonly _source;
10
+ private readonly _abortController;
11
+ private readonly _onEvent?;
12
+ private readonly _onComplete?;
13
+ private _consumed;
14
+ constructor(source: AsyncIterable<StreamEvent>, abortController: AbortController, onEvent?: (event: StreamEvent) => void, onComplete?: (conversationId: string | null, status: StreamStatus) => void);
15
+ get conversationId(): string | null;
16
+ get status(): StreamStatus;
17
+ get pendingAction(): PendingAction | null;
18
+ abort(): void;
19
+ [Symbol.asyncIterator](): AsyncIterator<StreamEvent>;
20
+ finalResponse(): Promise<ChatResponse>;
21
+ text(): Promise<string>;
22
+ }
@@ -0,0 +1,19 @@
1
+ import { ConversationRef } from '../types/conversations';
2
+ export declare class ConversationManager {
3
+ private _active;
4
+ private readonly _history;
5
+ /** Create a new conversation reference */
6
+ create(): ConversationRef;
7
+ /** Get the most recently active conversation */
8
+ get active(): ConversationRef | null;
9
+ /** Get all known conversation references (in-memory only) */
10
+ get history(): readonly ConversationRef[];
11
+ /**
12
+ * Set the active conversation by ID (called internally after stream completes).
13
+ * If the ID matches an existing ref, that ref becomes active.
14
+ * Otherwise a new ref is created.
15
+ */
16
+ setActive(conversationId: string): void;
17
+ /** Clear all state */
18
+ clear(): void;
19
+ }
@@ -0,0 +1,3 @@
1
+ export { AgentClient } from './agent-client';
2
+ export { AgentStreamImpl } from './agent-stream';
3
+ export { ConversationManager } from './conversation-manager';
package/client.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { ThaliqConfig, SDKEventMap } from './types/config';
2
+ import { UserIdentity } from './types/identity';
3
+ import { AgentClient } from './agent/agent-client';
4
+ import { ConversationManager } from './agent/conversation-manager';
5
+ export declare class Thaliq {
6
+ private readonly config;
7
+ private readonly provider;
8
+ private readonly emitter;
9
+ private identity;
10
+ /** Agent client — use `thaliq.agent.chat()` and `thaliq.agent.stream()` */
11
+ readonly agent: AgentClient;
12
+ /** Conversations manager — create, track, and list conversations */
13
+ readonly conversations: ConversationManager;
14
+ constructor(userConfig: ThaliqConfig);
15
+ /** Register an event listener */
16
+ on<K extends keyof SDKEventMap>(event: K, listener: SDKEventMap[K]): this;
17
+ /** Register a one-time event listener */
18
+ once<K extends keyof SDKEventMap>(event: K, listener: SDKEventMap[K]): this;
19
+ /** Remove an event listener */
20
+ off<K extends keyof SDKEventMap>(event: K, listener: SDKEventMap[K]): this;
21
+ /** Set the user identity for subsequent requests */
22
+ identify(user: UserIdentity): void;
23
+ /** Clear user identity and local state */
24
+ reset(): void;
25
+ }
package/index.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @thaliq/sdk — Agentic SDK for Thaliq Platform
3
+ *
4
+ * Headless, type-safe, provider-agnostic SDK for building
5
+ * applications with Thaliq AI agents.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { Thaliq } from '@thaliq/sdk';
10
+ *
11
+ * const thaliq = new Thaliq({ apiKey: 'tq_live_xxx' });
12
+ *
13
+ * // Events
14
+ * thaliq.on('error', (err) => console.error(err));
15
+ * thaliq.on('retry', (attempt, err, delay) => console.warn(`Retry #${attempt}`));
16
+ *
17
+ * // Stream with auto-tracking
18
+ * const stream = thaliq.agent.stream('Hello!');
19
+ * for await (const event of stream) {
20
+ * if (event.type === 'content.delta') {
21
+ * process.stdout.write(event.delta);
22
+ * }
23
+ * }
24
+ *
25
+ * console.log(thaliq.conversations.active);
26
+ * ```
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+ export { Thaliq } from './client';
31
+ export { ThaliqNativeProvider } from './providers/thaliq-native';
32
+ export type { ThaliqNativeProviderConfig } from './providers/thaliq-native';
33
+ export { ConversationManager } from './agent/conversation-manager';
34
+ export type { ThaliqConfig, ResolvedConfig, RetryConfig, ResolvedRetryConfig, SDKEventMap, Logger, LogLevel, } from './types/config';
35
+ export type { UserIdentity } from './types/identity';
36
+ export type { AgentType, ChatOptions, StreamOptions, AgentStream, StreamStatus, RespondToActionOptions, } from './types/agent';
37
+ export type { StreamEvent, MetaEvent, StatusEvent, ContentDeltaEvent, ToolStartEvent, ToolEndEvent, ActionEvent, ResponseCompletedEvent, ErrorEvent, KeepaliveEvent, } from './types/events';
38
+ export type { ChatResponse, ResponseMetadata, Insight, InsightType, InsightSeverity, } from './types/responses';
39
+ export type { ActionType, RejectBehavior, FieldType, ActionOption, ActionField, PendingAction, ActionResponse, } from './types/actions';
40
+ export type { ConversationRef } from './types/conversations';
41
+ export type { Provider, ProviderRequest, ResolvedHeaders, } from './types/provider';
42
+ export { ActionResponseBuilder } from './utils/action-response-builder';
43
+ export { TypedEventEmitter } from './utils/event-emitter';
44
+ export { isRetryableError, calculateBackoff, withRetry } from './utils/retry';
45
+ export type { RetryOptions } from './utils/retry';
46
+ export { ThaliqError, AuthError, RateLimitError, ValidationError, ServiceError, StreamError, TimeoutError, ConnectionError, } from './types/errors';
47
+ export type { ErrorCode } from './types/errors';
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@thaliq/sdk",
3
+ "version": "1.0.0",
4
+ "description": "Headless, type-safe, provider-agnostic SDK for Thaliq AI agents",
5
+ "type": "module",
6
+ "main": "./thaliq-sdk.cjs.js",
7
+ "module": "./thaliq-sdk.es.js",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./thaliq-sdk.es.js",
13
+ "require": "./thaliq-sdk.cjs.js"
14
+ }
15
+ },
16
+ "sideEffects": false,
17
+ "license": "MIT",
18
+ "author": "Thaliq <hello@thaliq.com> (https://thaliq.com)",
19
+ "homepage": "https://docs.thaliq.com/sdk",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/thaliq/sdk"
23
+ },
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "keywords": [
28
+ "thaliq",
29
+ "ai",
30
+ "agents",
31
+ "sdk",
32
+ "streaming",
33
+ "sse",
34
+ "llm",
35
+ "ai-agents",
36
+ "hitl",
37
+ "human-in-the-loop"
38
+ ]
39
+ }
@@ -0,0 +1,26 @@
1
+ import { Logger } from '../../types/config';
2
+ export interface HttpClientConfig {
3
+ baseUrl: string;
4
+ timeout: number;
5
+ logger: Logger;
6
+ }
7
+ export interface HttpRequestOptions {
8
+ method: 'GET' | 'POST';
9
+ path: string;
10
+ headers: Record<string, string | undefined>;
11
+ body?: unknown;
12
+ signal?: AbortSignal;
13
+ timeout: number;
14
+ }
15
+ export declare class HttpClient {
16
+ private readonly baseUrl;
17
+ private readonly defaultTimeout;
18
+ private readonly logger;
19
+ constructor(config: HttpClientConfig);
20
+ /** Execute a JSON request and return the parsed response */
21
+ json<T>(options: HttpRequestOptions): Promise<T>;
22
+ /** Execute a request and return the raw Response (for SSE streaming) */
23
+ stream(options: HttpRequestOptions): Promise<Response>;
24
+ /** Low-level fetch with timeout and error wrapping */
25
+ private rawFetch;
26
+ }
@@ -0,0 +1,2 @@
1
+ export { ThaliqNativeProvider } from './thaliq-native.provider';
2
+ export type { ThaliqNativeProviderConfig } from './thaliq-native.provider';
@@ -0,0 +1,6 @@
1
+ import { StreamEvent } from '../../types/events';
2
+ /**
3
+ * Parse a ReadableStream into SSE events.
4
+ * Yields StreamEvent objects as they arrive.
5
+ */
6
+ export declare function parseSSEStream(stream: ReadableStream<Uint8Array>): AsyncIterable<StreamEvent>;
@@ -0,0 +1,19 @@
1
+ import { Provider, ProviderRequest } from '../../types/provider';
2
+ import { ChatResponse } from '../../types/responses';
3
+ import { StreamEvent } from '../../types/events';
4
+ import { Logger } from '../../types/config';
5
+ export interface ThaliqNativeProviderConfig {
6
+ baseUrl: string;
7
+ timeout: number;
8
+ logger: Logger;
9
+ }
10
+ export declare class ThaliqNativeProvider implements Provider {
11
+ readonly name = "thaliq-native";
12
+ private readonly http;
13
+ private readonly logger;
14
+ constructor(config: ThaliqNativeProviderConfig);
15
+ /** POST /api/agent/chat — non-streaming */
16
+ chat(request: ProviderRequest): Promise<ChatResponse>;
17
+ /** GET /api/agent/stream — SSE streaming */
18
+ stream(request: ProviderRequest): AsyncIterable<StreamEvent>;
19
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class t extends Error{constructor(t,e,s){super(t),this.name="ThaliqError",this.code=e,this.status=s}}class e extends t{constructor(t,e=401){super(t,"AUTH_ERROR",e),this.name="AuthError"}}class s extends t{constructor(t,e=60){super(t,"RATE_LIMIT",429),this.name="RateLimitError",this.retryAfter=e}}class n extends t{constructor(t){super(t,"VALIDATION_ERROR",400),this.name="ValidationError"}}class r extends t{constructor(t){super(t,"SERVICE_ERROR",503),this.name="ServiceError"}}class o extends t{constructor(t){super(t,"STREAM_ERROR"),this.name="StreamError"}}class i extends t{constructor(t="Request timed out"){super(t,"TIMEOUT_ERROR"),this.name="TimeoutError"}}class a extends t{constructor(t="Connection failed"){super(t,"CONNECTION_ERROR"),this.name="ConnectionError"}}function c(o,i,a){switch(o){case 400:return new n(i);case 401:case 403:return new e(i,o);case 429:{const t=a?.get("retry-after");return new s(i,t?parseInt(t,10):60)}case 503:return new r(i);default:return new t(i,"UNKNOWN_ERROR",o)}}class l{constructor(t,e,s,n){this._conversationId=null,this._status="streaming",this._pendingAction=null,this._consumed=!1,this._source=t,this._abortController=e,this._onEvent=s,this._onComplete=n}get conversationId(){return this._conversationId}get status(){return this._status}get pendingAction(){return this._pendingAction}abort(){this._status="aborted",this._abortController.abort()}async*[Symbol.asyncIterator](){if(this._consumed)throw new o("AgentStream can only be consumed once");this._consumed=!0;try{for await(const t of this._source)"meta"===t.type&&(this._conversationId=t.conversationId),"response.completed"!==t.type||this._conversationId||(this._conversationId=t.conversationId),"action"===t.type&&(this._pendingAction=t.action,this._status="awaiting_action"),"response.completed"===t.type&&(this._status="completed"),"error"===t.type&&(this._status="error"),this._onEvent&&this._onEvent(t),yield t;"streaming"===this._status&&(this._status=this._pendingAction?"awaiting_action":"completed")}finally{this._onComplete&&this._onComplete(this._conversationId,this._status)}}async finalResponse(){let t,e,s="",n="",r=[];for await(const a of this)switch(a.type){case"meta":n=a.conversationId;break;case"content.delta":s+=a.delta;break;case"action":e=a.action;break;case"response.completed":s=a.message,n=a.conversationId,r=a.insights,t=a.metadata;break;case"error":throw new o(a.message)}const i={message:s,conversationId:n,insights:r,metadata:t??{conversationId:n,model:"unknown",promptTokens:0,completionTokens:0,processingTimeMs:0,generatedAt:(new Date).toISOString()}};return e&&(i.action=e),i}async text(){let t="";for await(const e of this){if("content.delta"===e.type&&(t+=e.delta),"response.completed"===e.type)return e.message;if("error"===e.type)throw new o(e.message)}return t}}function h(o){return!(o instanceof t)||!(o instanceof e)&&(!(o instanceof n)&&(!(o instanceof s)&&(o instanceof r||(o instanceof a||(o instanceof i||!!(o.status&&o.status>=500))))))}function d(t,e,s){const n=e*Math.pow(2,t),r=Math.min(n,s),o=r*Math.random()*.5;return Math.floor(r+o)}function u(t){return new Promise(e=>setTimeout(e,t))}async function p(e,s){const{config:n,logger:r,onRetry:o,signal:i}=s;let a;for(let l=0;l<=n.maxRetries;l++)try{return await e()}catch(c){if(a=c,l>=n.maxRetries)break;if(!h(c))break;if(i?.aborted)break;const e=d(l,n.baseDelay,n.maxDelay);r.warn(`Retry ${l+1}/${n.maxRetries} after ${e}ms`,c instanceof Error?c.message:c),o&&c instanceof t&&o(l+1,c,e),await u(e)}throw a}class g{constructor(t,e,s,n,r){this.config=t,this.provider=e,this.getIdentity=s,this.emitter=n,this.conversationManager=r}async chat(t,e){const s=this.buildRequest(t,e),n=await p(()=>this.provider.chat(s),{config:this.config.retry,logger:this.config.logger,signal:e?.signal,onRetry:(t,e,s)=>{this.emitter.emit("retry",t,e,s)}});return n.conversationId&&this.conversationManager.setActive(n.conversationId),n}stream(e,s){const n=new AbortController,r=this.buildRequest(e,s,n.signal),o=this.provider.stream(r);this.emitter.emit("stream.start",s?.conversationId??null);return new l(o,n,e=>{if("meta"===e.type&&e.conversationId&&this.conversationManager.setActive(e.conversationId),"error"===e.type){const s=new t(e.message,"STREAM_ERROR");this.emitter.emit("error",s)}s?.onEvent&&s.onEvent(e)},t=>{this.emitter.emit("stream.end",t)})}respondToAction(t){return this.stream(t.originalMessage,{conversationId:t.conversationId,agentType:t.agentType,actionResponse:t.actionResponse,onEvent:t.onEvent,signal:t.signal})}buildRequest(t,e,s){const n=this.getIdentity(),r=e?.signal??s;return{message:t,conversationId:e?.conversationId,agentType:e?.agentType,actionResponse:e?.actionResponse,headers:this.resolveHeaders(n),signal:r,timeout:this.config.timeout}}resolveHeaders(t){const e={"x-api-key":this.config.apiKey,"x-integration-type":this.config.integrationType};t&&(t.userId&&(e["x-user-id"]=t.userId),t.participantId&&(e["x-participant-id"]=t.participantId),t.token&&(e.authorization=`Bearer ${t.token}`),t.mcpTokens&&Object.keys(t.mcpTokens).length>0&&(e["x-mcp-tokens"]=JSON.stringify(t.mcpTokens)));for(const[s,n]of Object.entries(this.config.defaultHeaders))e[s]=n;return e}}class m{constructor(){this._active=null,this._history=[]}create(){const t={id:"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)}),createdAt:new Date};return this._history.push(t),t}get active(){return this._active}get history(){return this._history}setActive(t){const e=this._history.find(e=>e.id===t);if(e)this._active=e;else{const e={id:t,createdAt:new Date};this._history.push(e),this._active=e}}clear(){this._active=null,this._history.length=0}}class f{constructor(t){this.baseUrl=t.baseUrl.replace(/\/+$/,""),this.defaultTimeout=t.timeout,this.logger=t.logger}async json(t){const e=await this.rawFetch(t);if(!e.ok){const t=await e.text().catch(()=>"Unknown error");let s;try{const e=JSON.parse(t);s=e.message??e.error??t}catch{s=t}throw c(e.status,s,e.headers)}return e.json()}async stream(t){const e=await this.rawFetch(t);if(!e.ok){const t=await e.text().catch(()=>"Unknown error");let s;try{const e=JSON.parse(t);s=e.message??e.error??t}catch{s=t}throw c(e.status,s,e.headers)}return e}async rawFetch(t){const e=`${this.baseUrl}${t.path}`,s=t.timeout??this.defaultTimeout,n=new AbortController;let r;t.signal&&t.signal.addEventListener("abort",()=>n.abort(t.signal?.reason),{once:!0}),r=setTimeout(()=>{n.abort(new i(`Request timed out after ${s}ms`))},s);const o={"content-type":"application/json"};for(const[i,a]of Object.entries(t.headers))void 0!==a&&(o[i]=a);this.logger.debug(`${t.method} ${e}`);try{return await fetch(e,{method:t.method,headers:o,body:t.body?JSON.stringify(t.body):void 0,signal:n.signal})}catch(c){if(c instanceof i)throw c;if(c instanceof DOMException&&"AbortError"===c.name){if(n.signal.reason instanceof i)throw n.signal.reason;throw c}throw new a(c instanceof Error?c.message:"Connection failed")}finally{void 0!==r&&clearTimeout(r)}}}function y(t){let e="",s="",n="";const r=t.split("\n");for(const o of r)o.startsWith("event:")?e=o.slice(6).trim():o.startsWith("data:")?s=o.slice(5).trim():o.startsWith("id:")&&(n=o.slice(3).trim());return e||s?{event:e,data:s,id:n}:null}function v(t){const e=t.event;try{const s=t.data?JSON.parse(t.data):{};switch(e){case"meta":return{type:"meta",conversationId:s.conversationId};case"status":return{type:"status",text:s.text};case"content.delta":return{type:"content.delta",delta:s.delta};case"tool.start":return{type:"tool.start",tool:s.tool};case"tool.end":return{type:"tool.end",tool:s.tool,success:s.success};case"action":return{type:"action",action:s};case"response.completed":return{type:"response.completed",message:s.message,conversationId:s.conversationId,insights:s.insights??[],metadata:s.metadata};case"error":return{type:"error",message:s.message};case"keepalive":return{type:"keepalive",ts:s.ts};default:return null}}catch{return null}}class x{constructor(t){this.name="thaliq-native",this.logger=t.logger,this.http=new f({baseUrl:t.baseUrl,timeout:t.timeout,logger:t.logger})}async chat(t){const e={message:t.message};return t.conversationId&&(e.conversationId=t.conversationId),t.agentType&&(e.agentType=t.agentType),t.actionResponse&&(e.actionResponse=t.actionResponse),this.http.json({method:"POST",path:"/api/agent/chat",headers:t.headers,body:e,signal:t.signal,timeout:t.timeout})}async*stream(t){const e=new URLSearchParams;e.set("q",t.message),t.conversationId&&e.set("conversationId",t.conversationId),t.agentType&&e.set("agentType",t.agentType),t.actionResponse&&e.set("actionResponse",JSON.stringify(t.actionResponse));const s=await this.http.stream({method:"GET",path:`/api/agent/stream?${e.toString()}`,headers:{...t.headers,accept:"text/event-stream"},signal:t.signal,timeout:t.timeout});if(!s.body)throw new o("Response body is null — streaming not supported");this.logger.debug("SSE stream connected"),yield*async function*(t){const e=t.getReader(),s=new TextDecoder;let n="";try{for(;;){const{done:t,value:r}=await e.read();if(t)break;n+=s.decode(r,{stream:!0});const o=n.split("\n\n");n=o.pop()??"";for(const e of o){const t=y(e);if(!t)continue;const s=v(t);s&&(yield s)}}if(n.trim()){const t=y(n);if(t){const e=v(t);e&&(yield e)}}}finally{e.releaseLock()}}(s.body)}}class w{constructor(){this._listeners=new Map}on(t,e){return this._listeners.has(t)||this._listeners.set(t,new Set),this._listeners.get(t).add(e),this}once(t,e){return e._once=!0,this.on(t,e)}off(t,e){const s=this._listeners.get(t);return s&&(s.delete(e),0===s.size&&this._listeners.delete(t)),this}emit(t,...e){const s=this._listeners.get(t);if(s){for(const t of s)t(...e),t._once&&s.delete(t);0===s.size&&this._listeners.delete(t)}}removeAllListeners(t){return t?this._listeners.delete(t):this._listeners.clear(),this}listenerCount(t){return this._listeners.get(t)?.size??0}}const _={debug:0,info:1,warn:2,error:3,silent:4};const R="https://api.thaliq.com";exports.ActionResponseBuilder=class{static accept(t){return{instructionId:t,accepted:!0}}static reject(t){return{instructionId:t,accepted:!1}}static select(t,e){return{instructionId:t,accepted:!0,selectedValue:e}}static submitForm(t,e){return{instructionId:t,accepted:!0,formValues:e}}},exports.AuthError=e,exports.ConnectionError=a,exports.ConversationManager=m,exports.RateLimitError=s,exports.ServiceError=r,exports.StreamError=o,exports.Thaliq=class{constructor(t){this.emitter=new w,this.identity=null,this.config=function(t){if(!t.apiKey)throw new n("apiKey is required");const e=t.logLevel??"warn",s=t.logger??function(t){const e=_[t];return{debug(t,...s){e<=_.debug&&console.debug(`[thaliq] ${t}`,...s)},info(t,...s){e<=_.info&&console.info(`[thaliq] ${t}`,...s)},warn(t,...s){e<=_.warn&&console.warn(`[thaliq] ${t}`,...s)},error(t,...s){e<=_.error&&console.error(`[thaliq] ${t}`,...s)}}}(e),r=t.maxRetries??t.retry?.maxRetries??2;return{apiKey:t.apiKey,baseUrl:(t.baseUrl??R).replace(/\/+$/,""),integrationType:t.integrationType??"sdk",timeout:t.timeout??3e4,maxRetries:r,retry:{maxRetries:r,baseDelay:t.retry?.baseDelay??500,maxDelay:t.retry?.maxDelay??1e4},logger:s,logLevel:e,defaultHeaders:t.defaultHeaders??{}}}(t),this.provider=t.provider??new x({baseUrl:this.config.baseUrl,timeout:this.config.timeout,logger:this.config.logger}),this.conversations=new m,this.agent=new g(this.config,this.provider,()=>this.identity,this.emitter,this.conversations)}on(t,e){return this.emitter.on(t,e),this}once(t,e){return this.emitter.once(t,e),this}off(t,e){return this.emitter.off(t,e),this}identify(t){if(!t.userId)throw new n("userId is required for identify()");this.identity=t,this.config.logger.info(`User identified: ${t.userId}`),this.emitter.emit("identify",t.userId)}reset(){this.identity=null,this.conversations.clear(),this.config.logger.info("SDK state reset"),this.emitter.emit("reset")}},exports.ThaliqError=t,exports.ThaliqNativeProvider=x,exports.TimeoutError=i,exports.TypedEventEmitter=w,exports.ValidationError=n,exports.calculateBackoff=d,exports.isRetryableError=h,exports.withRetry=p;
2
+ //# sourceMappingURL=thaliq-sdk.cjs.js.map