hiloop-sdk 0.8.5 → 0.8.6
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/dist/client.d.ts +20 -13
- package/dist/client.js +62 -23
- package/dist/index.d.ts +1 -1
- package/dist/ws.d.ts +1 -1
- package/dist/ws.js +24 -7
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export declare class HiloopError extends Error {
|
|
|
5
5
|
statusCode: number;
|
|
6
6
|
constructor(statusCode: number, message: string);
|
|
7
7
|
}
|
|
8
|
+
export interface SendOptions {
|
|
9
|
+
agentName?: string;
|
|
10
|
+
priority?: string;
|
|
11
|
+
deadlineMinutes?: number;
|
|
12
|
+
replyToMessageId?: string;
|
|
13
|
+
}
|
|
8
14
|
export interface HiloopClientOptions {
|
|
9
15
|
apiKey: string;
|
|
10
16
|
/** Agent name. Auto-creates the agent if it doesn't exist (space API keys only). */
|
|
@@ -137,19 +143,19 @@ export declare class HiloopClient {
|
|
|
137
143
|
page?: number;
|
|
138
144
|
}): Promise<ConvSession[]>;
|
|
139
145
|
closeConvSession(sessionId: string): Promise<ConvSession>;
|
|
146
|
+
/** Options for send / sendText. */
|
|
147
|
+
private sendComponents;
|
|
140
148
|
/**
|
|
141
|
-
* Send a message
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* This is the primary method for agent-to-human communication.
|
|
149
|
+
* Send a plain text message. Wraps the text in a `{ type: "text" }` component.
|
|
150
|
+
* Agent and session are auto-created if they don't exist.
|
|
145
151
|
*/
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
sendText(sessionId: string, text: string, opts?: SendOptions): Promise<SessionMessage>;
|
|
153
|
+
/**
|
|
154
|
+
* Send a message composed of components. Everything is encrypted in the
|
|
155
|
+
* component tree — no separate content field.
|
|
156
|
+
* Agent and session are auto-created if they don't exist.
|
|
157
|
+
*/
|
|
158
|
+
send(sessionId: string, components: ComponentNode[], opts?: SendOptions): Promise<SessionMessage>;
|
|
153
159
|
/**
|
|
154
160
|
* Long-poll for a response to a specific message.
|
|
155
161
|
* Returns the updated message once the human has responded, or after timeout.
|
|
@@ -340,11 +346,12 @@ export declare class HiloopClient {
|
|
|
340
346
|
decryptSessionMessage(encryptedContent: string): Promise<string | null>;
|
|
341
347
|
/**
|
|
342
348
|
* Decrypt a webhook payload in place.
|
|
343
|
-
* Decrypts encryptedContent from session messages.
|
|
344
|
-
* Returns the payload with
|
|
349
|
+
* Decrypts encryptedComponents (and legacy encryptedContent) from session messages.
|
|
350
|
+
* Returns the payload with added `content` and `components` fields.
|
|
345
351
|
*/
|
|
346
352
|
decryptWebhookPayload<T extends Record<string, unknown>>(payload: T): Promise<T & {
|
|
347
353
|
content?: string;
|
|
354
|
+
components?: ComponentNode[];
|
|
348
355
|
}>;
|
|
349
356
|
static generateKeyPair(): KeyPair;
|
|
350
357
|
/** Encrypt plaintext with AES-256-GCM for session messages. */
|
package/dist/client.js
CHANGED
|
@@ -317,25 +317,17 @@ export class HiloopClient {
|
|
|
317
317
|
return parseConvSession(data);
|
|
318
318
|
}
|
|
319
319
|
// -- Unified Message API ----------------------------------------------------
|
|
320
|
-
/**
|
|
321
|
-
|
|
322
|
-
* rich components. Encrypts content and components automatically.
|
|
323
|
-
*
|
|
324
|
-
* This is the primary method for agent-to-human communication.
|
|
325
|
-
*/
|
|
326
|
-
async sendMessage(sessionId, content, opts) {
|
|
320
|
+
/** Options for send / sendText. */
|
|
321
|
+
async sendComponents(sessionId, components, opts) {
|
|
327
322
|
await this.ensureInitialized(opts?.agentName);
|
|
328
|
-
const
|
|
329
|
-
const body = {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
body.responseRequired = true;
|
|
337
|
-
body.category = "decide";
|
|
338
|
-
}
|
|
323
|
+
const encryptedComponents = await this.crypto.encryptSessionMessage(JSON.stringify(components));
|
|
324
|
+
const body = { encryptedComponents };
|
|
325
|
+
// Extract metadata from components for server-side routing
|
|
326
|
+
const hasResponseRequired = components.some((n) => n.type === "button_group" &&
|
|
327
|
+
n.data?.responseRequired === true);
|
|
328
|
+
if (hasResponseRequired) {
|
|
329
|
+
body.responseRequired = true;
|
|
330
|
+
body.category = "decide";
|
|
339
331
|
}
|
|
340
332
|
if (opts?.priority)
|
|
341
333
|
body.priority = opts.priority;
|
|
@@ -346,6 +338,21 @@ export class HiloopClient {
|
|
|
346
338
|
const data = await this.request("POST", `/agent/sessions/${sessionId}/messages`, { body, agentName: opts?.agentName });
|
|
347
339
|
return parseSessionMessage(data);
|
|
348
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Send a plain text message. Wraps the text in a `{ type: "text" }` component.
|
|
343
|
+
* Agent and session are auto-created if they don't exist.
|
|
344
|
+
*/
|
|
345
|
+
async sendText(sessionId, text, opts) {
|
|
346
|
+
return this.sendComponents(sessionId, [{ type: "text", data: { content: text } }], opts);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Send a message composed of components. Everything is encrypted in the
|
|
350
|
+
* component tree — no separate content field.
|
|
351
|
+
* Agent and session are auto-created if they don't exist.
|
|
352
|
+
*/
|
|
353
|
+
async send(sessionId, components, opts) {
|
|
354
|
+
return this.sendComponents(sessionId, components, opts);
|
|
355
|
+
}
|
|
349
356
|
/**
|
|
350
357
|
* Long-poll for a response to a specific message.
|
|
351
358
|
* Returns the updated message once the human has responded, or after timeout.
|
|
@@ -396,7 +403,25 @@ export class HiloopClient {
|
|
|
396
403
|
// Decrypt timeline items (session messages)
|
|
397
404
|
if (Array.isArray(result.items)) {
|
|
398
405
|
await Promise.all(result.items.map(async (item) => {
|
|
399
|
-
|
|
406
|
+
// Decrypt components (new format)
|
|
407
|
+
if (typeof item.encryptedComponents === "string") {
|
|
408
|
+
const plain = await this.crypto.decryptSessionMessage(item.encryptedComponents);
|
|
409
|
+
if (plain !== null) {
|
|
410
|
+
try {
|
|
411
|
+
const nodes = JSON.parse(plain);
|
|
412
|
+
item.components = nodes;
|
|
413
|
+
// Extract text from first text component for convenience
|
|
414
|
+
const textNode = nodes.find((n) => n.type === "text");
|
|
415
|
+
if (textNode)
|
|
416
|
+
item.content = textNode.data?.content ?? null;
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
item.components = [];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Fall back to legacy encryptedContent
|
|
424
|
+
if (item.content === undefined && typeof item.encryptedContent === "string") {
|
|
400
425
|
const plain = await this.crypto.decryptSessionMessage(item.encryptedContent);
|
|
401
426
|
if (plain !== null)
|
|
402
427
|
item.content = plain;
|
|
@@ -660,14 +685,28 @@ export class HiloopClient {
|
|
|
660
685
|
}
|
|
661
686
|
/**
|
|
662
687
|
* Decrypt a webhook payload in place.
|
|
663
|
-
* Decrypts encryptedContent from session messages.
|
|
664
|
-
* Returns the payload with
|
|
688
|
+
* Decrypts encryptedComponents (and legacy encryptedContent) from session messages.
|
|
689
|
+
* Returns the payload with added `content` and `components` fields.
|
|
665
690
|
*/
|
|
666
691
|
async decryptWebhookPayload(payload) {
|
|
667
692
|
await this.ensureInitialized();
|
|
668
693
|
const result = { ...payload };
|
|
669
|
-
//
|
|
670
|
-
if (typeof payload.
|
|
694
|
+
// New format: components
|
|
695
|
+
if (typeof payload.encryptedComponents === "string") {
|
|
696
|
+
const plain = await this.crypto.decryptSessionMessage(payload.encryptedComponents);
|
|
697
|
+
if (plain !== null) {
|
|
698
|
+
try {
|
|
699
|
+
const nodes = JSON.parse(plain);
|
|
700
|
+
result.components = nodes;
|
|
701
|
+
const textNode = nodes.find((n) => n.type === "text");
|
|
702
|
+
if (textNode)
|
|
703
|
+
result.content = textNode.data?.content ?? undefined;
|
|
704
|
+
}
|
|
705
|
+
catch { /* ignore */ }
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
// Legacy: encryptedContent
|
|
709
|
+
if (result.content === undefined && typeof payload.encryptedContent === "string") {
|
|
671
710
|
const plain = await this.crypto.decryptSessionMessage(payload.encryptedContent);
|
|
672
711
|
if (plain !== null)
|
|
673
712
|
result.content = plain;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { HiloopClient, HiloopError } from "./client.js";
|
|
2
|
-
export type { HiloopClientOptions } from "./client.js";
|
|
2
|
+
export type { HiloopClientOptions, SendOptions } from "./client.js";
|
|
3
3
|
export { HiloopWsClient } from "./ws.js";
|
|
4
4
|
export type { HiloopWsClientOptions, WsEventType, WsEventHandler } from "./ws.js";
|
|
5
5
|
export { generateKeyPair, deriveKeyPairFromApiKey, computeFingerprint, encrypt, decrypt, encryptAes, decryptAes, deriveWrappingKey, CryptoContext } from "./crypto.js";
|
package/dist/ws.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export declare class HiloopWsClient {
|
|
|
46
46
|
subscribe(topic: string): void;
|
|
47
47
|
/** Unsubscribe from a topic. */
|
|
48
48
|
unsubscribe(topic: string): void;
|
|
49
|
-
/** Send a session message (auto-encrypts). */
|
|
49
|
+
/** Send a text session message (auto-encrypts as component). */
|
|
50
50
|
sendSessionMessage(sessionId: string, content: string): Promise<void>;
|
|
51
51
|
/** Send a typing indicator. */
|
|
52
52
|
sendTyping(sessionId: string, isTyping: boolean): void;
|
package/dist/ws.js
CHANGED
|
@@ -101,10 +101,26 @@ export class HiloopWsClient {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
// Auto-decrypt session messages
|
|
104
|
-
if (data.type === "session.message.new" &&
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
|
|
104
|
+
if (data.type === "session.message.new" && this.crypto) {
|
|
105
|
+
// New format: components
|
|
106
|
+
if (data.encryptedComponents) {
|
|
107
|
+
const plain = await this.crypto.decryptSessionMessage(data.encryptedComponents);
|
|
108
|
+
if (plain !== null) {
|
|
109
|
+
try {
|
|
110
|
+
data.components = JSON.parse(plain);
|
|
111
|
+
const textNode = data.components?.find((n) => n.type === "text");
|
|
112
|
+
if (textNode)
|
|
113
|
+
data.content = textNode.data?.content ?? null;
|
|
114
|
+
}
|
|
115
|
+
catch { /* ignore */ }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Legacy: encryptedContent
|
|
119
|
+
if (data.content === undefined && data.encryptedContent) {
|
|
120
|
+
const plain = await this.crypto.decryptSessionMessage(data.encryptedContent);
|
|
121
|
+
if (plain !== null)
|
|
122
|
+
data.content = plain;
|
|
123
|
+
}
|
|
108
124
|
}
|
|
109
125
|
this.emit(data.type, data);
|
|
110
126
|
}
|
|
@@ -143,11 +159,12 @@ export class HiloopWsClient {
|
|
|
143
159
|
unsubscribe(topic) {
|
|
144
160
|
this.send({ type: "unsubscribe", topic });
|
|
145
161
|
}
|
|
146
|
-
/** Send a session message (auto-encrypts). */
|
|
162
|
+
/** Send a text session message (auto-encrypts as component). */
|
|
147
163
|
async sendSessionMessage(sessionId, content) {
|
|
148
164
|
await this.ensureCrypto();
|
|
149
|
-
const
|
|
150
|
-
this.
|
|
165
|
+
const components = [{ type: "text", data: { content } }];
|
|
166
|
+
const encryptedComponents = await this.crypto.encryptSessionMessage(JSON.stringify(components));
|
|
167
|
+
this.send({ type: "session.send", sessionId, encryptedComponents });
|
|
151
168
|
}
|
|
152
169
|
/** Send a typing indicator. */
|
|
153
170
|
sendTyping(sessionId, isTyping) {
|