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 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 to a session. Accepts plaintext content and optional
142
- * rich components. Encrypts content and components automatically.
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
- sendMessage(sessionId: string, content: string, opts?: {
147
- components?: ComponentNode[];
148
- priority?: string;
149
- deadlineMinutes?: number;
150
- replyToMessageId?: string;
151
- agentName?: string;
152
- }): Promise<SessionMessage>;
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 an added `content` field.
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
- * Send a message to a session. Accepts plaintext content and optional
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 encryptedContent = await this.crypto.encryptSessionMessage(content);
329
- const body = { encryptedContent };
330
- if (opts?.components && opts.components.length > 0) {
331
- body.encryptedComponents = await this.crypto.encryptSessionMessage(JSON.stringify(opts.components));
332
- // Extract metadata from components for server-side routing
333
- const hasResponseRequired = opts.components.some((n) => n.type === "button_group" &&
334
- n.data?.responseRequired === true);
335
- if (hasResponseRequired) {
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
- if (typeof item.encryptedContent === "string") {
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 an added `content` field.
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
- // Session message
670
- if (typeof payload.encryptedContent === "string") {
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" && data.encryptedContent && this.crypto) {
105
- const plain = await this.crypto.decryptSessionMessage(data.encryptedContent);
106
- if (plain !== null)
107
- data.content = plain;
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 encryptedContent = await this.crypto.encryptSessionMessage(content);
150
- this.send({ type: "session.send", sessionId, encryptedContent });
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hiloop-sdk",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "description": "TypeScript SDK for Hiloop — zero-trust human-AI agent interaction platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",