@thaliq/sdk 1.0.1 → 1.1.1

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
@@ -1,10 +1,19 @@
1
1
  import { Provider } from '../types/provider';
2
2
  import { ChatOptions, StreamOptions, AgentStream, RespondToActionOptions } from '../types/agent';
3
- import { ChatResponse } from '../types/responses';
3
+ import { ChatResponse, MessageFeedback } from '../types/responses';
4
4
  import { UserIdentity } from '../types/identity';
5
5
  import { ResolvedConfig, SDKEventMap } from '../types/config';
6
6
  import { TypedEventEmitter } from '../utils/event-emitter';
7
7
  import { ConversationManager } from './conversation-manager';
8
+ /** Options for submitting feedback */
9
+ export interface FeedbackOptions {
10
+ /** Conversation ID */
11
+ conversationId: string;
12
+ /** Message ID (from ResponseMetadata.messageId) */
13
+ messageId: string;
14
+ /** Feedback value */
15
+ feedback: MessageFeedback;
16
+ }
8
17
  export declare class AgentClient {
9
18
  private readonly config;
10
19
  private readonly provider;
@@ -38,6 +47,20 @@ export declare class AgentClient {
38
47
  * ```
39
48
  */
40
49
  respondToAction(options: RespondToActionOptions): AgentStream;
50
+ /**
51
+ * Submit feedback (thumbs up/down) for an assistant message.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const response = await thaliq.agent.chat('Hello!');
56
+ * await thaliq.agent.feedback({
57
+ * conversationId: response.conversationId,
58
+ * messageId: response.metadata.messageId!,
59
+ * feedback: 'positive',
60
+ * });
61
+ * ```
62
+ */
63
+ feedback(options: FeedbackOptions): Promise<void>;
41
64
  /** Build the ProviderRequest from message + options + identity */
42
65
  private buildRequest;
43
66
  /** Resolve all headers from config + identity */
package/index.d.ts CHANGED
@@ -34,8 +34,9 @@ export { ConversationManager } from './agent/conversation-manager';
34
34
  export type { ThaliqConfig, ResolvedConfig, RetryConfig, ResolvedRetryConfig, SDKEventMap, Logger, LogLevel, } from './types/config';
35
35
  export type { UserIdentity } from './types/identity';
36
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';
37
+ export type { FeedbackOptions } from './agent/agent-client';
38
+ export type { StreamEvent, MetaEvent, StatusEvent, ContentDeltaEvent, ToolStartEvent, ToolEndEvent, ActionEvent, HandoffEvent, ResponseCompletedEvent, MessageStopEvent, RateLimitEvent, ErrorEvent, KeepaliveEvent, } from './types/events';
39
+ export type { ChatResponse, MessagePart, ResponseMetadata, MessageFeedback, Insight, InsightType, InsightSeverity, } from './types/responses';
39
40
  export type { ActionType, RejectBehavior, FieldType, ActionOption, ActionField, PendingAction, ActionResponse, } from './types/actions';
40
41
  export type { ConversationRef } from './types/conversations';
41
42
  export type { Provider, ProviderRequest, ResolvedHeaders, } from './types/provider';
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
- {
2
- "name": "@thaliq/sdk",
3
- "version": "1.0.1",
4
- "description": "Headless, type-safe, provider-agnostic SDK for Thaliq AI agents",
5
- "type": "module",
6
- "main": "./thaliq-sdk.cjs",
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"
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
- }
1
+ {
2
+ "name": "@thaliq/sdk",
3
+ "version": "1.1.1",
4
+ "description": "Headless, type-safe, provider-agnostic SDK for Thaliq AI agents",
5
+ "type": "module",
6
+ "main": "./thaliq-sdk.cjs",
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"
14
+ }
15
+ },
16
+ "sideEffects": false,
17
+ "license": "MIT",
18
+ "author": "Thaliq \u003chello@thaliq.com\u003e (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": "\u003e=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
+ }
package/thaliq-sdk.cjs CHANGED
@@ -1,2 +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;
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 r extends t{constructor(t){super(t,"VALIDATION_ERROR",400),this.name="ValidationError"}}class n 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 r(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 n(i);default:return new t(i,"UNKNOWN_ERROR",o)}}class l{constructor(t,e,s,r){this._conversationId=null,this._status="streaming",this._pendingAction=null,this._consumed=!1,this._source=t,this._abortController=e,this._onEvent=s,this._onComplete=r}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"),"handoff"===t.type&&(this._status="handoff"),"response.completed"===t.type&&(this._status="completed"),"error"===t.type&&(this._status="error"),"rate_limit"===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,r="",n="",i=[];const a=[];for await(const l of this)switch(l.type){case"meta":n=l.conversationId;break;case"content.delta":{r+=l.delta;const t=a[a.length-1];t&&"text"===t.type?t.content+=l.delta:a.push({type:"text",content:l.delta});break}case"tool.start":a.push({type:"tool_call",name:l.tool,status:"running"});break;case"tool.end":for(let t=a.length-1;t>=0;t--){const e=a[t];if("tool_call"===e.type&&e.name===l.tool&&"running"===e.status){a[t]={...e,status:l.success?"success":"error"};break}}break;case"action":e=l.action;break;case"response.completed":r=l.message,n=l.conversationId,i=l.insights,t=l.metadata;break;case"error":throw new o(l.message);case"rate_limit":throw new s(l.message,l.retryAfter)}const c={message:r,conversationId:n,insights:i,metadata:t??{conversationId:n,model:"unknown",promptTokens:0,completionTokens:0,processingTimeMs:0,generatedAt:(new Date).toISOString()},parts:a};return e&&(c.action=e),c}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);if("rate_limit"===e.type)throw new s(e.message,e.retryAfter)}return t}}function h(o){return!(o instanceof t)||!(o instanceof e)&&(!(o instanceof r)&&(!(o instanceof s)&&(o instanceof n||(o instanceof a||(o instanceof i||!!(o.status&&o.status>=500))))))}function d(t,e,s){const r=e*Math.pow(2,t),n=Math.min(r,s),o=n*Math.random()*.5;return Math.floor(n+o)}function u(t){return new Promise(e=>setTimeout(e,t))}async function p(e,s){const{config:r,logger:n,onRetry:o,signal:i}=s;let a;for(let l=0;l<=r.maxRetries;l++)try{return await e()}catch(c){if(a=c,l>=r.maxRetries)break;if(!h(c))break;if(i?.aborted)break;const e=d(l,r.baseDelay,r.maxDelay);n.warn(`Retry ${l+1}/${r.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 m{constructor(t,e,s,r,n){this.config=t,this.provider=e,this.getIdentity=s,this.emitter=r,this.conversationManager=n}async chat(t,e){const s=this.buildRequest(t,e),r=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 r.conversationId&&this.conversationManager.setActive(r.conversationId),r}stream(e,r){const n=new AbortController,o=this.buildRequest(e,r,n.signal),i=this.provider.stream(o);this.emitter.emit("stream.start",r?.conversationId??null);return new l(i,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)}if("rate_limit"===e.type){const t=new s(e.message,e.retryAfter);this.emitter.emit("rateLimit",t),this.emitter.emit("error",t)}r?.onEvent&&r.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})}async feedback(e){const s=this.getIdentity(),r=this.resolveHeaders(s),n=await fetch(`${this.config.baseUrl}/api/agent/feedback`,{method:"POST",headers:{"content-type":"application/json","x-api-key":r["x-api-key"],"x-integration-type":r["x-integration-type"],...r["x-user-id"]?{"x-user-id":r["x-user-id"]}:{},...r.authorization?{authorization:r.authorization}:{}},body:JSON.stringify({conversationId:e.conversationId,messageId:e.messageId,feedback:e.feedback})});if(!n.ok){const e=await n.text().catch(()=>`HTTP ${n.status}`);throw new t(e,"UNKNOWN_ERROR")}}buildRequest(t,e,s){const r=this.getIdentity(),n=e?.signal??s;return{message:t,conversationId:e?.conversationId,agentType:e?.agentType,actionResponse:e?.actionResponse,headers:this.resolveHeaders(r),signal:n,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,r]of Object.entries(this.config.defaultHeaders))e[s]=r;return e}}class g{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,r=new AbortController;let n;t.signal&&t.signal.addEventListener("abort",()=>r.abort(t.signal?.reason),{once:!0}),n=setTimeout(()=>{r.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:r.signal})}catch(c){if(c instanceof i)throw c;if(c instanceof DOMException&&"AbortError"===c.name){if(r.signal.reason instanceof i)throw r.signal.reason;throw c}throw new a(c instanceof Error?c.message:"Connection failed")}finally{void 0!==n&&clearTimeout(n)}}}function y(t){let e="",s="",r="";const n=t.split("\n");for(const o of n)o.startsWith("event:")?e=o.slice(6).trim():o.startsWith("data:")?s=o.slice(5).trim():o.startsWith("id:")&&(r=o.slice(3).trim());return e||s?{event:e,data:s,id:r}:null}function x(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"handoff":return{type:"handoff",message:s.message,agentName:s.agentName,reason:s.reason};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};case"message_stop":return{type:"message_stop",model:s.model,usage:s.usage??{inputTokens:0,outputTokens:0}};case"rate_limit":return{type:"rate_limit",message:s.content??s.message??"Rate limit exceeded",retryAfter:s.retryAfter??60};default:return null}}catch{return null}}class v{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 r="";try{for(;;){const{done:t,value:n}=await e.read();if(t)break;r+=s.decode(n,{stream:!0});const o=r.split("\n\n");r=o.pop()??"";for(const e of o){const t=y(e);if(!t)continue;const s=x(t);s&&(yield s)}}if(r.trim()){const t=y(r);if(t){const e=x(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 b="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=g,exports.RateLimitError=s,exports.ServiceError=n,exports.StreamError=o,exports.Thaliq=class{constructor(t){this.emitter=new w,this.identity=null,this.config=function(t){if(!t.apiKey)throw new r("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),n=t.maxRetries??t.retry?.maxRetries??2;return{apiKey:t.apiKey,baseUrl:(t.baseUrl??b).replace(/\/+$/,""),integrationType:t.integrationType??"sdk",timeout:t.timeout??3e4,maxRetries:n,retry:{maxRetries:n,baseDelay:t.retry?.baseDelay??500,maxDelay:t.retry?.maxDelay??1e4},logger:s,logLevel:e,defaultHeaders:t.defaultHeaders??{}}}(t),this.provider=t.provider??new v({baseUrl:this.config.baseUrl,timeout:this.config.timeout,logger:this.config.logger}),this.conversations=new g,this.agent=new m(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 r("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=v,exports.TimeoutError=i,exports.TypedEventEmitter=w,exports.ValidationError=r,exports.calculateBackoff=d,exports.isRetryableError=h,exports.withRetry=p;
2
2
  //# sourceMappingURL=thaliq-sdk.cjs.map