hiloop-sdk 0.8.5 → 0.8.7
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 +24 -13
- package/dist/client.js +66 -23
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -0
- 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,16 @@ 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
|
+
/** Client-supplied correlation key (e.g. tool_use_id). Unique per session. */
|
|
14
|
+
referenceId?: string;
|
|
15
|
+
/** Resolve a referenceId to a message and set replyToMessageId automatically. */
|
|
16
|
+
replyToReferenceId?: string;
|
|
17
|
+
}
|
|
8
18
|
export interface HiloopClientOptions {
|
|
9
19
|
apiKey: string;
|
|
10
20
|
/** Agent name. Auto-creates the agent if it doesn't exist (space API keys only). */
|
|
@@ -137,19 +147,19 @@ export declare class HiloopClient {
|
|
|
137
147
|
page?: number;
|
|
138
148
|
}): Promise<ConvSession[]>;
|
|
139
149
|
closeConvSession(sessionId: string): Promise<ConvSession>;
|
|
150
|
+
/** Options for send / sendText. */
|
|
151
|
+
private sendComponents;
|
|
140
152
|
/**
|
|
141
|
-
* Send a message
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* This is the primary method for agent-to-human communication.
|
|
153
|
+
* Send a plain text message. Wraps the text in a `{ type: "text" }` component.
|
|
154
|
+
* Agent and session are auto-created if they don't exist.
|
|
145
155
|
*/
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
sendText(sessionId: string, text: string, opts?: SendOptions): Promise<SessionMessage>;
|
|
157
|
+
/**
|
|
158
|
+
* Send a message composed of components. Everything is encrypted in the
|
|
159
|
+
* component tree — no separate content field.
|
|
160
|
+
* Agent and session are auto-created if they don't exist.
|
|
161
|
+
*/
|
|
162
|
+
send(sessionId: string, components: ComponentNode[], opts?: SendOptions): Promise<SessionMessage>;
|
|
153
163
|
/**
|
|
154
164
|
* Long-poll for a response to a specific message.
|
|
155
165
|
* Returns the updated message once the human has responded, or after timeout.
|
|
@@ -340,11 +350,12 @@ export declare class HiloopClient {
|
|
|
340
350
|
decryptSessionMessage(encryptedContent: string): Promise<string | null>;
|
|
341
351
|
/**
|
|
342
352
|
* Decrypt a webhook payload in place.
|
|
343
|
-
* Decrypts encryptedContent from session messages.
|
|
344
|
-
* Returns the payload with
|
|
353
|
+
* Decrypts encryptedComponents (and legacy encryptedContent) from session messages.
|
|
354
|
+
* Returns the payload with added `content` and `components` fields.
|
|
345
355
|
*/
|
|
346
356
|
decryptWebhookPayload<T extends Record<string, unknown>>(payload: T): Promise<T & {
|
|
347
357
|
content?: string;
|
|
358
|
+
components?: ComponentNode[];
|
|
348
359
|
}>;
|
|
349
360
|
static generateKeyPair(): KeyPair;
|
|
350
361
|
/** 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;
|
|
@@ -343,9 +335,28 @@ export class HiloopClient {
|
|
|
343
335
|
body.deadlineMinutes = opts.deadlineMinutes;
|
|
344
336
|
if (opts?.replyToMessageId)
|
|
345
337
|
body.replyToMessageId = opts.replyToMessageId;
|
|
338
|
+
if (opts?.referenceId)
|
|
339
|
+
body.referenceId = opts.referenceId;
|
|
340
|
+
if (opts?.replyToReferenceId)
|
|
341
|
+
body.replyToReferenceId = opts.replyToReferenceId;
|
|
346
342
|
const data = await this.request("POST", `/agent/sessions/${sessionId}/messages`, { body, agentName: opts?.agentName });
|
|
347
343
|
return parseSessionMessage(data);
|
|
348
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Send a plain text message. Wraps the text in a `{ type: "text" }` component.
|
|
347
|
+
* Agent and session are auto-created if they don't exist.
|
|
348
|
+
*/
|
|
349
|
+
async sendText(sessionId, text, opts) {
|
|
350
|
+
return this.sendComponents(sessionId, [{ type: "text", data: { content: text } }], opts);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Send a message composed of components. Everything is encrypted in the
|
|
354
|
+
* component tree — no separate content field.
|
|
355
|
+
* Agent and session are auto-created if they don't exist.
|
|
356
|
+
*/
|
|
357
|
+
async send(sessionId, components, opts) {
|
|
358
|
+
return this.sendComponents(sessionId, components, opts);
|
|
359
|
+
}
|
|
349
360
|
/**
|
|
350
361
|
* Long-poll for a response to a specific message.
|
|
351
362
|
* Returns the updated message once the human has responded, or after timeout.
|
|
@@ -396,7 +407,25 @@ export class HiloopClient {
|
|
|
396
407
|
// Decrypt timeline items (session messages)
|
|
397
408
|
if (Array.isArray(result.items)) {
|
|
398
409
|
await Promise.all(result.items.map(async (item) => {
|
|
399
|
-
|
|
410
|
+
// Decrypt components (new format)
|
|
411
|
+
if (typeof item.encryptedComponents === "string") {
|
|
412
|
+
const plain = await this.crypto.decryptSessionMessage(item.encryptedComponents);
|
|
413
|
+
if (plain !== null) {
|
|
414
|
+
try {
|
|
415
|
+
const nodes = JSON.parse(plain);
|
|
416
|
+
item.components = nodes;
|
|
417
|
+
// Extract text from first text component for convenience
|
|
418
|
+
const textNode = nodes.find((n) => n.type === "text");
|
|
419
|
+
if (textNode)
|
|
420
|
+
item.content = textNode.data?.content ?? null;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
item.components = [];
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Fall back to legacy encryptedContent
|
|
428
|
+
if (item.content === undefined && typeof item.encryptedContent === "string") {
|
|
400
429
|
const plain = await this.crypto.decryptSessionMessage(item.encryptedContent);
|
|
401
430
|
if (plain !== null)
|
|
402
431
|
item.content = plain;
|
|
@@ -660,14 +689,28 @@ export class HiloopClient {
|
|
|
660
689
|
}
|
|
661
690
|
/**
|
|
662
691
|
* Decrypt a webhook payload in place.
|
|
663
|
-
* Decrypts encryptedContent from session messages.
|
|
664
|
-
* Returns the payload with
|
|
692
|
+
* Decrypts encryptedComponents (and legacy encryptedContent) from session messages.
|
|
693
|
+
* Returns the payload with added `content` and `components` fields.
|
|
665
694
|
*/
|
|
666
695
|
async decryptWebhookPayload(payload) {
|
|
667
696
|
await this.ensureInitialized();
|
|
668
697
|
const result = { ...payload };
|
|
669
|
-
//
|
|
670
|
-
if (typeof payload.
|
|
698
|
+
// New format: components
|
|
699
|
+
if (typeof payload.encryptedComponents === "string") {
|
|
700
|
+
const plain = await this.crypto.decryptSessionMessage(payload.encryptedComponents);
|
|
701
|
+
if (plain !== null) {
|
|
702
|
+
try {
|
|
703
|
+
const nodes = JSON.parse(plain);
|
|
704
|
+
result.components = nodes;
|
|
705
|
+
const textNode = nodes.find((n) => n.type === "text");
|
|
706
|
+
if (textNode)
|
|
707
|
+
result.content = textNode.data?.content ?? undefined;
|
|
708
|
+
}
|
|
709
|
+
catch { /* ignore */ }
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
// Legacy: encryptedContent
|
|
713
|
+
if (result.content === undefined && typeof payload.encryptedContent === "string") {
|
|
671
714
|
const plain = await this.crypto.decryptSessionMessage(payload.encryptedContent);
|
|
672
715
|
if (plain !== null)
|
|
673
716
|
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/types.d.ts
CHANGED
|
@@ -139,6 +139,7 @@ export interface SessionMessage {
|
|
|
139
139
|
priority?: string | null;
|
|
140
140
|
status?: string | null;
|
|
141
141
|
deadlineAt?: string | null;
|
|
142
|
+
referenceId?: string | null;
|
|
142
143
|
replyToMessageId?: string | null;
|
|
143
144
|
respondedAt?: string | null;
|
|
144
145
|
viewedAt?: string | null;
|
package/dist/types.js
CHANGED
|
@@ -100,6 +100,7 @@ export function parseSessionMessage(data) {
|
|
|
100
100
|
priority: m.priority ?? null,
|
|
101
101
|
status: m.status ?? null,
|
|
102
102
|
deadlineAt: m.deadlineAt ?? null,
|
|
103
|
+
referenceId: m.referenceId ?? null,
|
|
103
104
|
replyToMessageId: m.replyToMessageId ?? null,
|
|
104
105
|
respondedAt: m.respondedAt ?? null,
|
|
105
106
|
viewedAt: m.viewedAt ?? null,
|
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) {
|