@happyrobot-ai/sdk 0.1.2 → 0.1.4

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 CHANGED
@@ -113,15 +113,16 @@ const { workflow } = await createFromTemplate(client, {
113
113
  | `client.knowledgeBases` | `KnowledgeBasesResource` | Knowledge base document management |
114
114
  | `client.workflowFolders` | `WorkflowFoldersResource` | Workflow folder organization |
115
115
  | `client.mcp` | `MCPResource` | MCP server management |
116
- | `client.usage` | `UsageResource` | Usage metrics (call minutes, tokens, etc.) |
117
116
  | `client.billing` | `BillingResource` | Billing usage details and totals |
118
117
  | `client.apiKey` | `ApiKeyResource` | API key introspection |
118
+ | `client.artifacts` | `ArtifactsResource` | Resolve fresh artifact URLs |
119
119
  | `client.adversarialSuites` | `AdversarialSuitesResource` | Adversarial suite management and execution |
120
120
  | `client.adversarialTests` | `AdversarialTestsResource` | Adversarial test management and execution |
121
121
  | `client.northstars` | `NorthstarsResource` | Northstar quality criteria management |
122
122
  | `client.customEvals` | `CustomEvalsResource` | Custom eval management and execution |
123
123
  | `client.issues` | `IssuesResource` | Quality issue (flag) status management |
124
124
  | `client.auditRemarks` | `AuditRemarksResource` | Audit remark feedback management |
125
+ | `client.chat` | `ChatResource` | Chat session management, messaging, and file uploads |
125
126
 
126
127
  ---
127
128
 
@@ -301,6 +302,20 @@ await client.messages.createFlag("message-id", { type: "incorrect", note: "..."
301
302
 
302
303
  ---
303
304
 
305
+ ## `client.artifacts`
306
+
307
+ ```ts
308
+ const resolved = await client.artifacts.resolve({
309
+ s3_keys: ["artifacts/<media_id>/extracted_text.txt"],
310
+ });
311
+ ```
312
+
313
+ | Method | HTTP | Path | Description |
314
+ |---|---|---|---|
315
+ | `resolve(body)` | POST | `/artifacts/resolve` | Resolve fresh presigned URLs for artifacts |
316
+
317
+ ---
318
+
304
319
  ## `client.variables`
305
320
 
306
321
  Variables are scoped to a workflow.
@@ -507,28 +522,6 @@ await client.mcp.refresh("mcp-id");
507
522
 
508
523
  ---
509
524
 
510
- ## `client.usage`
511
-
512
- All methods accept a `UsageQuery` with `workflow_id`, `start_date`, and `end_date`.
513
-
514
- ```ts
515
- const data = await client.usage.callMinutes({ workflow_id: "wf-id", start_date: "2024-01-01", end_date: "2024-01-31" });
516
- await client.usage.llmTokens({ ... });
517
- await client.usage.textCount({ ... });
518
- await client.usage.ocrCount({ ... });
519
- await client.usage.rpaMinutes({ ... });
520
- ```
521
-
522
- | Method | HTTP | Path | Description |
523
- |---|---|---|---|
524
- | `callMinutes(query)` | GET | `/usage/call-minutes` | Voice call minutes consumed |
525
- | `llmTokens(query)` | GET | `/usage/llm-tokens` | LLM tokens consumed |
526
- | `textCount(query)` | GET | `/usage/text-count` | Text messages sent |
527
- | `ocrCount(query)` | GET | `/usage/ocr-count` | OCR operations performed |
528
- | `rpaMinutes(query)` | GET | `/usage/rpa-minutes` | RPA automation minutes consumed |
529
-
530
- ---
531
-
532
525
  ## `client.billing`
533
526
 
534
527
  ```ts
@@ -729,6 +722,154 @@ await client.auditRemarks.deleteFeedback("audit-remark-id");
729
722
 
730
723
  ---
731
724
 
725
+ ## Chat (`client.chat` + `HappyRobotChatClient`)
726
+
727
+ Build custom chat UIs powered by your workflows. Uses a two-tier auth model:
728
+
729
+ 1. **Your server** creates a scoped client token using the API key (keeps it secret)
730
+ 2. **Your browser** uses `HappyRobotChatClient` with that token for all chat operations
731
+
732
+ ### Server-side: Create a client token
733
+
734
+ ```ts
735
+ // --- YOUR SERVER (e.g. Next.js API route, Express handler) ---
736
+ import { HappyRobotClient } from "@happyrobot-ai/sdk";
737
+
738
+ const client = new HappyRobotClient({ apiKey: process.env.HAPPYROBOT_API_KEY });
739
+
740
+ app.post("/api/chat-token", async (req, res) => {
741
+ const { token, expires_at } = await client.chat.createToken({
742
+ workflow_id: "your-workflow-id",
743
+ });
744
+ res.json({ token, expires_at });
745
+ });
746
+ ```
747
+
748
+ ### Browser-side: `HappyRobotChatClient`
749
+
750
+ ```ts
751
+ // --- YOUR BROWSER CODE (React, Vue, vanilla JS, etc.) ---
752
+ import { HappyRobotChatClient } from "@happyrobot-ai/sdk";
753
+
754
+ // 1. Get a client token from your server
755
+ const { token } = await fetch("/api/chat-token", { method: "POST" }).then(r => r.json());
756
+
757
+ // 2. Initialize the browser-side client
758
+ const chat = new HappyRobotChatClient({ token });
759
+
760
+ // 3. Create a session
761
+ const { session_id } = await chat.createSession();
762
+
763
+ // 4. Connect bidirectional WebSocket
764
+ const connection = chat.connect(session_id, {
765
+ onResponseStart: () => {
766
+ // Show typing indicator
767
+ },
768
+ onResponseChunk: (content) => {
769
+ // Append streamed text to the UI
770
+ },
771
+ onResponseEnd: (content) => {
772
+ // Full response complete — hide typing indicator
773
+ },
774
+ onSessionClosed: (event) => {
775
+ // Session ended: event.status, event.reason, event.duration
776
+ },
777
+ onTokenExpired: () => {
778
+ // Token expired — fetch a new token from your server and reconnect
779
+ },
780
+ });
781
+
782
+ // 5. Send messages through the WebSocket
783
+ const ack = await connection.sendMessage({ content: "Hello!" });
784
+ console.log(ack.message.id); // server-assigned message ID
785
+
786
+ // 6. Send messages with file attachments
787
+ const artifact = await chat.uploadFile(fileBlob, "photo.png", "image/png");
788
+ await connection.sendMessage({ content: "Here's the photo", artifacts: [artifact] });
789
+
790
+ // 7. Get history (page reload, reconnect)
791
+ const { messages } = await chat.getHistory(session_id);
792
+
793
+ // 8. Clean up
794
+ connection.close();
795
+ ```
796
+
797
+ ### Browser-side: File uploads
798
+
799
+ ```ts
800
+ // Option A: Use the convenience method (handles all 3 steps)
801
+ const artifact = await chat.uploadFile(fileBlob, "photo.png", "image/png");
802
+ await connection.sendMessage({
803
+ content: "Here's the photo",
804
+ artifacts: [artifact],
805
+ });
806
+
807
+ // Option B: Manual 3-step flow for more control
808
+ const upload = await chat.getPresignedUpload({ filename: "photo.png", mime_type: "image/png" });
809
+ await fetch(upload.upload_url, { method: "PUT", body: fileBlob, headers: { "Content-Type": "image/png" } });
810
+ await chat.completeUpload({
811
+ artifact_id: upload.artifact_id,
812
+ s3_uri: upload.s3_uri,
813
+ filename: "photo.png",
814
+ mime_type: "image/png",
815
+ size_bytes: fileBlob.size,
816
+ });
817
+ await connection.sendMessage({
818
+ content: "Here's the photo",
819
+ artifacts: [{ media_id: upload.artifact_id, mime_type: "image/png", filename: "photo.png", size_bytes: fileBlob.size }],
820
+ });
821
+ ```
822
+
823
+ ### `HappyRobotClient` (server-side)
824
+
825
+ | Method | Description |
826
+ |---|---|
827
+ | `client.chat.createToken({ workflow_id, env? })` | Create a scoped client token (1 hour expiry) |
828
+
829
+ ### `HappyRobotChatClient` (browser-side)
830
+
831
+ | Method | Description |
832
+ |---|---|
833
+ | `chat.createSession()` | Create a new chat session |
834
+ | `chat.connect(sessionId, handlers)` | Open bidirectional WebSocket — returns `ChatConnection` |
835
+ | `chat.sendMessage(sessionId, { content, artifacts? })` | Send a user message via HTTP (fallback if WS unavailable) |
836
+ | `chat.getHistory(sessionId)` | Get message history |
837
+ | `chat.uploadFile(file, filename, mimeType)` | Upload a file (convenience method) |
838
+ | `chat.getPresignedUpload({ filename, mime_type })` | Get presigned S3 upload URL |
839
+ | `chat.completeUpload({ artifact_id, s3_uri, ... })` | Register uploaded artifact |
840
+
841
+ ### `ChatConnection`
842
+
843
+ | Method | Description |
844
+ |---|---|
845
+ | `connection.sendMessage({ content, artifacts? })` | Send a message via WebSocket. Returns a `Promise` that resolves with the server ack |
846
+ | `connection.close()` | Close the WebSocket connection |
847
+ | `connection.ws` | The underlying `WebSocket` instance |
848
+
849
+ ### WebSocket Events
850
+
851
+ **Server → Client:**
852
+
853
+ | Event | Fields | Description |
854
+ |---|---|---|
855
+ | `connected` | `session_id` | Connection established |
856
+ | `response-start` | `content` | AI started generating a response |
857
+ | `response-chunk` | `content` | Partial response text |
858
+ | `response-end` | `content` | Complete response text |
859
+ | `session-closed` | `session_id`, `status`, `reason`, `duration`, `timestamp` | Session ended |
860
+ | `token-expired` | — | JWT expired — reconnect with a fresh token |
861
+ | `message-ack` | `id`, `message` | Server confirmed the message was sent |
862
+ | `message-error` | `id`, `error` | Server failed to send the message |
863
+ | `heartbeat` | — | Keep-alive (every 15s) |
864
+
865
+ **Client → Server:**
866
+
867
+ | Event | Fields | Description |
868
+ |---|---|---|
869
+ | `message` | `content`, `artifacts?`, `id?` | Send a user message. `id` is echoed back in ack/error |
870
+
871
+ ---
872
+
732
873
  ## Pagination
733
874
 
734
875
  List methods that are paginated return a `PaginatedResponse<T>`:
@@ -0,0 +1,106 @@
1
+ /**
2
+ * HappyRobotChatClient — lightweight browser-side client for chat sessions.
3
+ *
4
+ * Initialized with a scoped client token (created server-side via
5
+ * `client.chat.createToken()`). Does NOT require an API key.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { HappyRobotChatClient } from "@happyrobot-ai/sdk";
10
+ *
11
+ * // Token comes from your server
12
+ * const chat = new HappyRobotChatClient({ token });
13
+ *
14
+ * const { session_id } = await chat.createSession();
15
+ *
16
+ * chat.connect(session_id, {
17
+ * onResponseStart: () => { },
18
+ * onResponseChunk: (content) => { },
19
+ * onResponseEnd: (content) => { },
20
+ * onSessionClosed: (event) => { },
21
+ * });
22
+ *
23
+ * await chat.sendMessage(session_id, { content: "Hello!" });
24
+ * ```
25
+ */
26
+ import type { ChatClientConfig, CreateChatSessionResponse, SendChatMessageBody, SendChatMessageResponse, ChatHistoryResponse, PresignedUploadQuery, PresignedUploadResponse, CompleteUploadBody, CompleteUploadResponse, ChatWsSessionClosedEvent, ChatWsMessageAckEvent } from "./types/chat.types";
27
+ export interface ChatConnectionHandlers {
28
+ /** AI started generating a response. */
29
+ onResponseStart?: () => void;
30
+ /** Partial response text chunk. */
31
+ onResponseChunk?: (content: string) => void;
32
+ /** Complete response text. */
33
+ onResponseEnd?: (content: string) => void;
34
+ /** Session was closed by the server. */
35
+ onSessionClosed?: (event: ChatWsSessionClosedEvent) => void;
36
+ /** Connection established. */
37
+ onConnected?: (sessionId: string) => void;
38
+ /** Token expired — reconnect with a fresh token to continue. */
39
+ onTokenExpired?: () => void;
40
+ /** WebSocket error. */
41
+ onError?: (error: Event) => void;
42
+ /** WebSocket closed. */
43
+ onClose?: (event: {
44
+ code: number;
45
+ reason: string;
46
+ }) => void;
47
+ }
48
+ export interface ChatConnection {
49
+ /** Close the WebSocket connection only (no server-side cleanup). */
50
+ close(): void;
51
+ /**
52
+ * End the session on the server (stops the AI agent) and close the WebSocket.
53
+ * Use this when the user is done with the conversation.
54
+ */
55
+ endSession(): Promise<void>;
56
+ /** Send a message through the WebSocket. Returns the ack from the server. */
57
+ sendMessage(body: SendChatMessageBody): Promise<ChatWsMessageAckEvent>;
58
+ /** The underlying WebSocket instance. */
59
+ readonly ws: WebSocket;
60
+ }
61
+ export declare class HappyRobotChatClient {
62
+ private readonly token;
63
+ private readonly baseUrl;
64
+ private readonly fetchFn;
65
+ constructor(config: ChatClientConfig);
66
+ /** Create a new chat session. */
67
+ createSession(): Promise<CreateChatSessionResponse>;
68
+ /** Send a user message to a chat session. */
69
+ sendMessage(sessionId: string, body: SendChatMessageBody): Promise<SendChatMessageResponse>;
70
+ /** Get message history for a chat session. */
71
+ getHistory(sessionId: string): Promise<ChatHistoryResponse>;
72
+ /**
73
+ * Get a presigned S3 URL for file upload.
74
+ *
75
+ * Upload flow:
76
+ * 1. `getPresignedUpload()` — get the upload URL
77
+ * 2. `PUT` the file directly to `upload_url`
78
+ * 3. `completeUpload()` — register the artifact
79
+ * 4. Include the artifact in `sendMessage()` via the `artifacts` array
80
+ */
81
+ getPresignedUpload(query: PresignedUploadQuery): Promise<PresignedUploadResponse>;
82
+ /** Register an uploaded artifact after direct S3 upload. */
83
+ completeUpload(body: CompleteUploadBody): Promise<CompleteUploadResponse>;
84
+ /**
85
+ * Upload a file and return the artifact metadata ready for `sendMessage()`.
86
+ *
87
+ * Convenience method that combines `getPresignedUpload()`, S3 upload, and
88
+ * `completeUpload()` into a single call.
89
+ */
90
+ uploadFile(file: Blob, filename: string, mimeType: string): Promise<{
91
+ media_id: string;
92
+ mime_type: string;
93
+ filename: string;
94
+ size_bytes: number;
95
+ }>;
96
+ /**
97
+ * Connect a bidirectional WebSocket for sending messages and receiving
98
+ * real-time AI response streaming.
99
+ *
100
+ * Returns a `ChatConnection` with `sendMessage()` and `close()` methods.
101
+ * Pass event handlers to react to streaming chunks, completed responses,
102
+ * and session lifecycle events.
103
+ */
104
+ connect(sessionId: string, handlers?: ChatConnectionHandlers): ChatConnection;
105
+ private request;
106
+ }
package/chat-client.js ADDED
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ /**
3
+ * HappyRobotChatClient — lightweight browser-side client for chat sessions.
4
+ *
5
+ * Initialized with a scoped client token (created server-side via
6
+ * `client.chat.createToken()`). Does NOT require an API key.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { HappyRobotChatClient } from "@happyrobot-ai/sdk";
11
+ *
12
+ * // Token comes from your server
13
+ * const chat = new HappyRobotChatClient({ token });
14
+ *
15
+ * const { session_id } = await chat.createSession();
16
+ *
17
+ * chat.connect(session_id, {
18
+ * onResponseStart: () => { },
19
+ * onResponseChunk: (content) => { },
20
+ * onResponseEnd: (content) => { },
21
+ * onSessionClosed: (event) => { },
22
+ * });
23
+ *
24
+ * await chat.sendMessage(session_id, { content: "Hello!" });
25
+ * ```
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.HappyRobotChatClient = void 0;
29
+ const DEFAULT_BASE_URL = "https://platform.happyrobot.ai/api/v2";
30
+ class HappyRobotChatClient {
31
+ token;
32
+ baseUrl;
33
+ fetchFn;
34
+ constructor(config) {
35
+ if (!config.token) {
36
+ throw new Error("token is required");
37
+ }
38
+ this.token = config.token;
39
+ const customBaseUrl = config.baseUrl;
40
+ const isTesting = typeof process !== "undefined" && process.env?.HR_TESTING === "true";
41
+ if (customBaseUrl && !isTesting) {
42
+ throw new Error("baseUrl can not be overridden");
43
+ }
44
+ this.baseUrl = (customBaseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
45
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
46
+ }
47
+ /** Create a new chat session. */
48
+ async createSession() {
49
+ return this.request("POST", "/chat/sessions");
50
+ }
51
+ /** Send a user message to a chat session. */
52
+ async sendMessage(sessionId, body) {
53
+ return this.request("POST", `/chat/sessions/${enc(sessionId)}/messages`, body);
54
+ }
55
+ /** Get message history for a chat session. */
56
+ async getHistory(sessionId) {
57
+ return this.request("GET", `/chat/sessions/${enc(sessionId)}/history`);
58
+ }
59
+ /**
60
+ * Get a presigned S3 URL for file upload.
61
+ *
62
+ * Upload flow:
63
+ * 1. `getPresignedUpload()` — get the upload URL
64
+ * 2. `PUT` the file directly to `upload_url`
65
+ * 3. `completeUpload()` — register the artifact
66
+ * 4. Include the artifact in `sendMessage()` via the `artifacts` array
67
+ */
68
+ async getPresignedUpload(query) {
69
+ const params = new URLSearchParams({
70
+ filename: query.filename,
71
+ mime_type: query.mime_type,
72
+ });
73
+ return this.request("GET", `/chat/upload/presigned?${params.toString()}`);
74
+ }
75
+ /** Register an uploaded artifact after direct S3 upload. */
76
+ async completeUpload(body) {
77
+ return this.request("POST", "/chat/upload/complete", body);
78
+ }
79
+ /**
80
+ * Upload a file and return the artifact metadata ready for `sendMessage()`.
81
+ *
82
+ * Convenience method that combines `getPresignedUpload()`, S3 upload, and
83
+ * `completeUpload()` into a single call.
84
+ */
85
+ async uploadFile(file, filename, mimeType) {
86
+ const upload = await this.getPresignedUpload({
87
+ filename,
88
+ mime_type: mimeType,
89
+ });
90
+ await this.fetchFn(upload.upload_url, {
91
+ method: "PUT",
92
+ body: file,
93
+ headers: { "Content-Type": mimeType },
94
+ });
95
+ const completed = await this.completeUpload({
96
+ artifact_id: upload.artifact_id,
97
+ s3_uri: upload.s3_uri,
98
+ filename,
99
+ mime_type: mimeType,
100
+ size_bytes: file.size,
101
+ });
102
+ return {
103
+ media_id: completed.artifact_id,
104
+ mime_type: mimeType,
105
+ filename,
106
+ size_bytes: file.size,
107
+ };
108
+ }
109
+ /**
110
+ * Connect a bidirectional WebSocket for sending messages and receiving
111
+ * real-time AI response streaming.
112
+ *
113
+ * Returns a `ChatConnection` with `sendMessage()` and `close()` methods.
114
+ * Pass event handlers to react to streaming chunks, completed responses,
115
+ * and session lifecycle events.
116
+ */
117
+ connect(sessionId, handlers = {}) {
118
+ const wsBase = this.baseUrl
119
+ .replace(/^http:/, "ws:")
120
+ .replace(/^https:/, "wss:");
121
+ const url = `${wsBase}/chat/sessions/${enc(sessionId)}/ws?token=${enc(this.token)}`;
122
+ const ws = new WebSocket(url);
123
+ // Pending message-ack/message-error callbacks keyed by client-generated id.
124
+ const pending = new Map();
125
+ ws.onmessage = (event) => {
126
+ try {
127
+ const data = JSON.parse(event.data);
128
+ switch (data.type) {
129
+ case "connected":
130
+ handlers.onConnected?.(data.session_id);
131
+ break;
132
+ case "response-start":
133
+ handlers.onResponseStart?.();
134
+ break;
135
+ case "response-chunk":
136
+ handlers.onResponseChunk?.(data.content);
137
+ break;
138
+ case "response-end":
139
+ handlers.onResponseEnd?.(data.content);
140
+ break;
141
+ case "session-closed":
142
+ handlers.onSessionClosed?.(data);
143
+ break;
144
+ case "token-expired":
145
+ handlers.onTokenExpired?.();
146
+ break;
147
+ case "message-ack": {
148
+ const id = data.id;
149
+ if (id && pending.has(id)) {
150
+ pending.get(id).resolve(data);
151
+ pending.delete(id);
152
+ }
153
+ break;
154
+ }
155
+ case "message-error": {
156
+ const id = data.id;
157
+ if (id && pending.has(id)) {
158
+ pending.get(id).reject(new Error(data.error));
159
+ pending.delete(id);
160
+ }
161
+ break;
162
+ }
163
+ case "heartbeat":
164
+ break;
165
+ }
166
+ }
167
+ catch {
168
+ // Ignore malformed messages
169
+ }
170
+ };
171
+ ws.onerror = (event) => {
172
+ handlers.onError?.(event);
173
+ };
174
+ ws.onclose = (event) => {
175
+ // Reject all pending messages on close
176
+ for (const [, { reject }] of pending) {
177
+ reject(new Error("WebSocket closed"));
178
+ }
179
+ pending.clear();
180
+ handlers.onClose?.(event);
181
+ };
182
+ const sendMessage = (body) => {
183
+ return new Promise((resolve, reject) => {
184
+ if (ws.readyState !== WebSocket.OPEN) {
185
+ reject(new Error("WebSocket is not open"));
186
+ return;
187
+ }
188
+ const id = crypto.randomUUID();
189
+ pending.set(id, { resolve, reject });
190
+ ws.send(JSON.stringify({
191
+ type: "message",
192
+ id,
193
+ content: body.content,
194
+ ...(body.artifacts && body.artifacts.length > 0
195
+ ? { artifacts: body.artifacts }
196
+ : {}),
197
+ }));
198
+ });
199
+ };
200
+ const endSession = async () => {
201
+ await this.request("POST", `/chat/sessions/${enc(sessionId)}/close`);
202
+ ws.close();
203
+ };
204
+ return {
205
+ close: () => ws.close(),
206
+ endSession,
207
+ sendMessage,
208
+ get ws() {
209
+ return ws;
210
+ },
211
+ };
212
+ }
213
+ // ── Internal ──
214
+ async request(method, path, body) {
215
+ const url = `${this.baseUrl}${path}`;
216
+ const headers = {
217
+ Authorization: `Bearer ${this.token}`,
218
+ Accept: "application/json",
219
+ };
220
+ if (body !== undefined) {
221
+ headers["Content-Type"] = "application/json";
222
+ }
223
+ const response = await this.fetchFn(url, {
224
+ method,
225
+ headers,
226
+ body: body !== undefined ? JSON.stringify(body) : undefined,
227
+ });
228
+ if (!response.ok) {
229
+ const errorBody = await response.text().catch(() => "");
230
+ let parsed = {};
231
+ try {
232
+ parsed = JSON.parse(errorBody);
233
+ }
234
+ catch {
235
+ // ignore
236
+ }
237
+ throw new Error(parsed.error ||
238
+ parsed.message ||
239
+ `Request failed with status ${response.status}`);
240
+ }
241
+ const text = await response.text();
242
+ if (!text)
243
+ return undefined;
244
+ return JSON.parse(text);
245
+ }
246
+ }
247
+ exports.HappyRobotChatClient = HappyRobotChatClient;
248
+ function enc(s) {
249
+ return encodeURIComponent(s);
250
+ }
251
+ //# sourceMappingURL=chat-client.js.map
@@ -0,0 +1,3 @@
1
+ // ESM wrapper — re-exports CJS module
2
+ export { default } from "./chat-client.js";
3
+ export * from "./chat-client.js";
package/client.d.ts CHANGED
@@ -24,7 +24,6 @@ import { ContactsResource } from "./resources/contacts";
24
24
  import { KnowledgeBasesResource } from "./resources/knowledge-bases";
25
25
  import { WorkflowFoldersResource } from "./resources/workflow-folders";
26
26
  import { MCPResource } from "./resources/mcp";
27
- import { UsageResource } from "./resources/usage";
28
27
  import { BillingResource } from "./resources/billing";
29
28
  import { ApiKeyResource } from "./resources/api-key";
30
29
  import { AdversarialSuitesResource } from "./resources/adversarial-suites";
@@ -33,6 +32,8 @@ import { NorthstarsResource } from "./resources/northstars";
33
32
  import { CustomEvalsResource } from "./resources/custom-evals";
34
33
  import { IssuesResource } from "./resources/issues";
35
34
  import { AuditRemarksResource } from "./resources/audit-remarks";
35
+ import { ChatResource } from "./resources/chat";
36
+ import { ArtifactsResource } from "./resources/artifacts";
36
37
  export declare class HappyRobotClient {
37
38
  private readonly http;
38
39
  /** Workflow CRUD, publishing, runs, and templates. */
@@ -63,8 +64,6 @@ export declare class HappyRobotClient {
63
64
  readonly workflowFolders: WorkflowFoldersResource;
64
65
  /** MCP server management. */
65
66
  readonly mcp: MCPResource;
66
- /** Usage metrics (call minutes, tokens, etc.). */
67
- readonly usage: UsageResource;
68
67
  /** Billing usage details and totals. */
69
68
  readonly billing: BillingResource;
70
69
  /** API key introspection. */
@@ -81,5 +80,9 @@ export declare class HappyRobotClient {
81
80
  readonly issues: IssuesResource;
82
81
  /** Audit remark feedback management. */
83
82
  readonly auditRemarks: AuditRemarksResource;
83
+ /** Chat session management and messaging. */
84
+ readonly chat: ChatResource;
85
+ /** Artifact URL resolution. */
86
+ readonly artifacts: ArtifactsResource;
84
87
  constructor(config: ClientConfig);
85
88
  }
package/client.js CHANGED
@@ -27,7 +27,6 @@ const contacts_1 = require("./resources/contacts");
27
27
  const knowledge_bases_1 = require("./resources/knowledge-bases");
28
28
  const workflow_folders_1 = require("./resources/workflow-folders");
29
29
  const mcp_1 = require("./resources/mcp");
30
- const usage_1 = require("./resources/usage");
31
30
  const billing_1 = require("./resources/billing");
32
31
  const api_key_1 = require("./resources/api-key");
33
32
  const adversarial_suites_1 = require("./resources/adversarial-suites");
@@ -36,6 +35,8 @@ const northstars_1 = require("./resources/northstars");
36
35
  const custom_evals_1 = require("./resources/custom-evals");
37
36
  const issues_1 = require("./resources/issues");
38
37
  const audit_remarks_1 = require("./resources/audit-remarks");
38
+ const chat_1 = require("./resources/chat");
39
+ const artifacts_1 = require("./resources/artifacts");
39
40
  class HappyRobotClient {
40
41
  http;
41
42
  /** Workflow CRUD, publishing, runs, and templates. */
@@ -66,8 +67,6 @@ class HappyRobotClient {
66
67
  workflowFolders;
67
68
  /** MCP server management. */
68
69
  mcp;
69
- /** Usage metrics (call minutes, tokens, etc.). */
70
- usage;
71
70
  /** Billing usage details and totals. */
72
71
  billing;
73
72
  /** API key introspection. */
@@ -84,6 +83,10 @@ class HappyRobotClient {
84
83
  issues;
85
84
  /** Audit remark feedback management. */
86
85
  auditRemarks;
86
+ /** Chat session management and messaging. */
87
+ chat;
88
+ /** Artifact URL resolution. */
89
+ artifacts;
87
90
  constructor(config) {
88
91
  this.http = new http_1.HttpClient(config);
89
92
  this.workflows = new workflows_1.WorkflowsResource(this.http);
@@ -100,7 +103,6 @@ class HappyRobotClient {
100
103
  this.knowledgeBases = new knowledge_bases_1.KnowledgeBasesResource(this.http);
101
104
  this.workflowFolders = new workflow_folders_1.WorkflowFoldersResource(this.http);
102
105
  this.mcp = new mcp_1.MCPResource(this.http);
103
- this.usage = new usage_1.UsageResource(this.http);
104
106
  this.billing = new billing_1.BillingResource(this.http);
105
107
  this.apiKey = new api_key_1.ApiKeyResource(this.http);
106
108
  this.adversarialSuites = new adversarial_suites_1.AdversarialSuitesResource(this.http);
@@ -109,6 +111,8 @@ class HappyRobotClient {
109
111
  this.customEvals = new custom_evals_1.CustomEvalsResource(this.http);
110
112
  this.issues = new issues_1.IssuesResource(this.http);
111
113
  this.auditRemarks = new audit_remarks_1.AuditRemarksResource(this.http);
114
+ this.chat = new chat_1.ChatResource(this.http);
115
+ this.artifacts = new artifacts_1.ArtifactsResource(this.http);
112
116
  }
113
117
  }
114
118
  exports.HappyRobotClient = HappyRobotClient;
package/core/http.js CHANGED
@@ -22,8 +22,9 @@ class HttpClient {
22
22
  }
23
23
  this.apiKey = config.apiKey;
24
24
  const customBaseUrl = config.baseUrl;
25
- if (customBaseUrl && !config.apiKey.startsWith("sk_test_")) {
26
- throw new Error("baseUrl can only be overridden with a test API key (sk_test_...)");
25
+ const isTesting = typeof process !== "undefined" && process.env?.HR_TESTING === "true";
26
+ if (customBaseUrl && !isTesting) {
27
+ throw new Error("baseUrl can not be overridden");
27
28
  }
28
29
  this.baseUrl = (customBaseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
29
30
  this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
package/index.d.ts CHANGED
@@ -22,6 +22,8 @@
22
22
  * ```
23
23
  */
24
24
  export { HappyRobotClient } from "./client";
25
+ export { HappyRobotChatClient } from "./chat-client";
26
+ export type { ChatConnectionHandlers, ChatConnection } from "./chat-client";
25
27
  export type { ClientConfig, RequestConfig, PaginatedResponse, PaginationMetadata, PaginationQuery } from "./core/types";
26
28
  export { HappyRobotError, ApiError, AuthenticationError, NotFoundError, ValidationError, RateLimitError, TimeoutError, NetworkError, } from "./core/errors";
27
29
  export type { ApiErrorBody } from "./core/errors";
@@ -43,7 +45,6 @@ export { ContactsResource } from "./resources/contacts";
43
45
  export { KnowledgeBasesResource } from "./resources/knowledge-bases";
44
46
  export { WorkflowFoldersResource } from "./resources/workflow-folders";
45
47
  export { MCPResource } from "./resources/mcp";
46
- export { UsageResource } from "./resources/usage";
47
48
  export { BillingResource } from "./resources/billing";
48
49
  export { ApiKeyResource } from "./resources/api-key";
49
50
  export { AdversarialSuitesResource } from "./resources/adversarial-suites";
@@ -52,6 +53,8 @@ export { NorthstarsResource } from "./resources/northstars";
52
53
  export { CustomEvalsResource } from "./resources/custom-evals";
53
54
  export { IssuesResource } from "./resources/issues";
54
55
  export { AuditRemarksResource } from "./resources/audit-remarks";
56
+ export { ChatResource } from "./resources/chat";
57
+ export { ArtifactsResource } from "./resources/artifacts";
55
58
  export type * from "./types/workflows.types";
56
59
  export type * from "./types/versions.types";
57
60
  export type * from "./types/nodes.types";
@@ -62,7 +65,6 @@ export type * from "./types/sip-trunks.types";
62
65
  export type * from "./types/integrations.types";
63
66
  export type * from "./types/contacts.types";
64
67
  export type * from "./types/knowledge-bases.types";
65
- export type * from "./types/usage.types";
66
68
  export type * from "./types/variables.types";
67
69
  export type * from "./types/messages.types";
68
70
  export type * from "./types/mcp.types";
@@ -73,3 +75,5 @@ export type * from "./types/northstars.types";
73
75
  export type * from "./types/custom-evals.types";
74
76
  export type * from "./types/issues.types";
75
77
  export type * from "./types/audit-remarks.types";
78
+ export type * from "./types/chat.types";
79
+ export type * from "./types/artifacts.types";
package/index.js CHANGED
@@ -23,10 +23,12 @@
23
23
  * ```
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.AuditRemarksResource = exports.IssuesResource = exports.CustomEvalsResource = exports.NorthstarsResource = exports.AdversarialTestsResource = exports.AdversarialSuitesResource = exports.ApiKeyResource = exports.BillingResource = exports.UsageResource = exports.MCPResource = exports.WorkflowFoldersResource = exports.KnowledgeBasesResource = exports.ContactsResource = exports.IntegrationsResource = exports.SipTrunksResource = exports.PhoneNumbersResource = exports.VariablesResource = exports.MessagesResource = exports.SessionsResource = exports.RunsResource = exports.NodesResource = exports.VersionsResource = exports.WorkflowsResource = exports.iterateSSE = exports.paginate = exports.NetworkError = exports.TimeoutError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.AuthenticationError = exports.ApiError = exports.HappyRobotError = exports.HappyRobotClient = void 0;
27
- // ── Client ──
26
+ exports.ArtifactsResource = exports.ChatResource = exports.AuditRemarksResource = exports.IssuesResource = exports.CustomEvalsResource = exports.NorthstarsResource = exports.AdversarialTestsResource = exports.AdversarialSuitesResource = exports.ApiKeyResource = exports.BillingResource = exports.MCPResource = exports.WorkflowFoldersResource = exports.KnowledgeBasesResource = exports.ContactsResource = exports.IntegrationsResource = exports.SipTrunksResource = exports.PhoneNumbersResource = exports.VariablesResource = exports.MessagesResource = exports.SessionsResource = exports.RunsResource = exports.NodesResource = exports.VersionsResource = exports.WorkflowsResource = exports.iterateSSE = exports.paginate = exports.NetworkError = exports.TimeoutError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.AuthenticationError = exports.ApiError = exports.HappyRobotError = exports.HappyRobotChatClient = exports.HappyRobotClient = void 0;
27
+ // ── Clients ──
28
28
  var client_1 = require("./client");
29
29
  Object.defineProperty(exports, "HappyRobotClient", { enumerable: true, get: function () { return client_1.HappyRobotClient; } });
30
+ var chat_client_1 = require("./chat-client");
31
+ Object.defineProperty(exports, "HappyRobotChatClient", { enumerable: true, get: function () { return chat_client_1.HappyRobotChatClient; } });
30
32
  var errors_1 = require("./core/errors");
31
33
  Object.defineProperty(exports, "HappyRobotError", { enumerable: true, get: function () { return errors_1.HappyRobotError; } });
32
34
  Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return errors_1.ApiError; } });
@@ -69,8 +71,6 @@ var workflow_folders_1 = require("./resources/workflow-folders");
69
71
  Object.defineProperty(exports, "WorkflowFoldersResource", { enumerable: true, get: function () { return workflow_folders_1.WorkflowFoldersResource; } });
70
72
  var mcp_1 = require("./resources/mcp");
71
73
  Object.defineProperty(exports, "MCPResource", { enumerable: true, get: function () { return mcp_1.MCPResource; } });
72
- var usage_1 = require("./resources/usage");
73
- Object.defineProperty(exports, "UsageResource", { enumerable: true, get: function () { return usage_1.UsageResource; } });
74
74
  var billing_1 = require("./resources/billing");
75
75
  Object.defineProperty(exports, "BillingResource", { enumerable: true, get: function () { return billing_1.BillingResource; } });
76
76
  var api_key_1 = require("./resources/api-key");
@@ -87,4 +87,8 @@ var issues_1 = require("./resources/issues");
87
87
  Object.defineProperty(exports, "IssuesResource", { enumerable: true, get: function () { return issues_1.IssuesResource; } });
88
88
  var audit_remarks_1 = require("./resources/audit-remarks");
89
89
  Object.defineProperty(exports, "AuditRemarksResource", { enumerable: true, get: function () { return audit_remarks_1.AuditRemarksResource; } });
90
+ var chat_1 = require("./resources/chat");
91
+ Object.defineProperty(exports, "ChatResource", { enumerable: true, get: function () { return chat_1.ChatResource; } });
92
+ var artifacts_1 = require("./resources/artifacts");
93
+ Object.defineProperty(exports, "ArtifactsResource", { enumerable: true, get: function () { return artifacts_1.ArtifactsResource; } });
90
94
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyrobot-ai/sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "TypeScript SDK for the HappyRobot Public API",
5
5
  "main": "./index.js",
6
6
  "module": "./index.mjs",
@@ -37,7 +37,8 @@
37
37
  "license": "MIT",
38
38
  "repository": {
39
39
  "type": "git",
40
- "url": "https://github.com/happyrobot-ai/app-v2"
40
+ "url": "git+https://github.com/happyrobot-ai/app-v2.git",
41
+ "directory": "packages/public_api"
41
42
  },
42
43
  "dependencies": {}
43
44
  }
@@ -0,0 +1,7 @@
1
+ import type { HttpClient } from "../core/http";
2
+ import type { ResolveArtifactsBody, ResolveArtifactsResponse } from "../types/artifacts.types";
3
+ export declare class ArtifactsResource {
4
+ private readonly http;
5
+ constructor(http: HttpClient);
6
+ resolve(body: ResolveArtifactsBody): Promise<ResolveArtifactsResponse>;
7
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArtifactsResource = void 0;
4
+ class ArtifactsResource {
5
+ http;
6
+ constructor(http) {
7
+ this.http = http;
8
+ }
9
+ async resolve(body) {
10
+ return this.http.request({
11
+ method: "POST",
12
+ path: "/artifacts/resolve",
13
+ body,
14
+ });
15
+ }
16
+ }
17
+ exports.ArtifactsResource = ArtifactsResource;
18
+ //# sourceMappingURL=artifacts.js.map
@@ -0,0 +1,3 @@
1
+ // ESM wrapper — re-exports CJS module
2
+ export { default } from "./artifacts.js";
3
+ export * from "./artifacts.js";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Chat resource — client.chat.*
3
+ *
4
+ * Server-side only. Creates scoped client tokens that are passed to the
5
+ * browser-side `HappyRobotChatClient`.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const client = new HappyRobotClient({ apiKey: "sk_live_..." });
10
+ * const { token } = await client.chat.createToken({ workflow_id: "..." });
11
+ * // Pass `token` to the browser → new HappyRobotChatClient({ token })
12
+ * ```
13
+ */
14
+ import type { HttpClient } from "../core/http";
15
+ import type { CreateChatTokenBody, CreateChatTokenResponse } from "../types/chat.types";
16
+ export declare class ChatResource {
17
+ private readonly http;
18
+ constructor(http: HttpClient);
19
+ /**
20
+ * Create a scoped client token for browser-side chat operations.
21
+ *
22
+ * Call this from your server, then pass the returned `token` to the browser
23
+ * where it can be used to initialize `HappyRobotChatClient`.
24
+ */
25
+ createToken(body: CreateChatTokenBody): Promise<CreateChatTokenResponse>;
26
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Chat resource — client.chat.*
4
+ *
5
+ * Server-side only. Creates scoped client tokens that are passed to the
6
+ * browser-side `HappyRobotChatClient`.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const client = new HappyRobotClient({ apiKey: "sk_live_..." });
11
+ * const { token } = await client.chat.createToken({ workflow_id: "..." });
12
+ * // Pass `token` to the browser → new HappyRobotChatClient({ token })
13
+ * ```
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ChatResource = void 0;
17
+ class ChatResource {
18
+ http;
19
+ constructor(http) {
20
+ this.http = http;
21
+ }
22
+ /**
23
+ * Create a scoped client token for browser-side chat operations.
24
+ *
25
+ * Call this from your server, then pass the returned `token` to the browser
26
+ * where it can be used to initialize `HappyRobotChatClient`.
27
+ */
28
+ async createToken(body) {
29
+ return this.http.request({
30
+ method: "POST",
31
+ path: "/chat/tokens",
32
+ body,
33
+ });
34
+ }
35
+ }
36
+ exports.ChatResource = ChatResource;
37
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1,3 @@
1
+ // ESM wrapper — re-exports CJS module
2
+ export { default } from "./chat.js";
3
+ export * from "./chat.js";
@@ -1,27 +0,0 @@
1
- /**
2
- * Usage resource — client.usage.*
3
- *
4
- * Maps to:
5
- * GET /usage/call-minutes
6
- * GET /usage/llm-tokens
7
- * GET /usage/text-count
8
- * GET /usage/ocr-count
9
- * GET /usage/rpa-minutes
10
- */
11
- import type { HttpClient } from "../core/http";
12
- import type { UsageQuery, DetailedUsageResponse } from "../types/usage.types";
13
- export declare class UsageResource {
14
- private readonly http;
15
- constructor(http: HttpClient);
16
- /** Get call minutes usage. */
17
- callMinutes(query: UsageQuery): Promise<DetailedUsageResponse>;
18
- /** Get LLM token usage. */
19
- llmTokens(query: UsageQuery): Promise<DetailedUsageResponse>;
20
- /** Get text message count usage. */
21
- textCount(query: UsageQuery): Promise<DetailedUsageResponse>;
22
- /** Get OCR count usage. */
23
- ocrCount(query: UsageQuery): Promise<DetailedUsageResponse>;
24
- /** Get RPA minutes usage. */
25
- rpaMinutes(query: UsageQuery): Promise<DetailedUsageResponse>;
26
- private toQuery;
27
- }
@@ -1,70 +0,0 @@
1
- "use strict";
2
- /**
3
- * Usage resource — client.usage.*
4
- *
5
- * Maps to:
6
- * GET /usage/call-minutes
7
- * GET /usage/llm-tokens
8
- * GET /usage/text-count
9
- * GET /usage/ocr-count
10
- * GET /usage/rpa-minutes
11
- */
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.UsageResource = void 0;
14
- class UsageResource {
15
- http;
16
- constructor(http) {
17
- this.http = http;
18
- }
19
- /** Get call minutes usage. */
20
- async callMinutes(query) {
21
- return this.http.request({
22
- method: "GET",
23
- path: "/usage/call-minutes",
24
- query: this.toQuery(query),
25
- });
26
- }
27
- /** Get LLM token usage. */
28
- async llmTokens(query) {
29
- return this.http.request({
30
- method: "GET",
31
- path: "/usage/llm-tokens",
32
- query: this.toQuery(query),
33
- });
34
- }
35
- /** Get text message count usage. */
36
- async textCount(query) {
37
- return this.http.request({
38
- method: "GET",
39
- path: "/usage/text-count",
40
- query: this.toQuery(query),
41
- });
42
- }
43
- /** Get OCR count usage. */
44
- async ocrCount(query) {
45
- return this.http.request({
46
- method: "GET",
47
- path: "/usage/ocr-count",
48
- query: this.toQuery(query),
49
- });
50
- }
51
- /** Get RPA minutes usage. */
52
- async rpaMinutes(query) {
53
- return this.http.request({
54
- method: "GET",
55
- path: "/usage/rpa-minutes",
56
- query: this.toQuery(query),
57
- });
58
- }
59
- toQuery(query) {
60
- const { workflow_id, start_date, end_date } = query;
61
- const ids = Array.isArray(workflow_id) ? workflow_id : [workflow_id];
62
- return {
63
- workflow_id: ids.join(","),
64
- start_date,
65
- end_date,
66
- };
67
- }
68
- }
69
- exports.UsageResource = UsageResource;
70
- //# sourceMappingURL=usage.js.map
@@ -1,3 +0,0 @@
1
- // ESM wrapper — re-exports CJS module
2
- export { default } from "./usage.js";
3
- export * from "./usage.js";