@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 +224 -0
- package/agent/agent-client.d.ts +24 -1
- package/index.d.ts +3 -2
- package/package.json +39 -39
- package/thaliq-sdk.cjs +1 -1
- package/thaliq-sdk.cjs.map +1 -1
- package/thaliq-sdk.es.js +1 -1
- package/thaliq-sdk.es.js.map +1 -1
- package/types/agent.d.ts +2 -1
- package/types/events.d.ts +23 -1
- package/types/responses.d.ts +22 -0
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
|
package/agent/agent-client.d.ts
CHANGED
|
@@ -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 {
|
|
38
|
-
export type {
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|