hiloop-sdk 0.7.0 → 0.8.0
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 +49 -561
- package/dist/client.js +67 -709
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/types.d.ts +30 -51
- package/dist/types.js +22 -33
- package/dist/ws.d.ts +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Hiloop SDK client for agents to interact with the Hiloop platform. */
|
|
2
2
|
import { CryptoContext, computeFingerprint, decryptAes, deriveKeyPairFromApiKey, deriveWrappingKey, encryptAes, generateKeyPair } from "./crypto.js";
|
|
3
|
-
import { parseChannel, parseChannelMessage, parseChannelParticipant, parseConvSession, parseConvSessionMessage, parseGuestToken,
|
|
3
|
+
import { parseChannel, parseChannelMessage, parseChannelParticipant, parseConvSession, parseConvSessionMessage, parseGuestToken, parseSessionMessage, } from "./types.js";
|
|
4
4
|
export class HiloopError extends Error {
|
|
5
5
|
constructor(statusCode, message) {
|
|
6
6
|
super(`HTTP ${statusCode}: ${message}`);
|
|
@@ -201,242 +201,6 @@ export class HiloopClient {
|
|
|
201
201
|
clearTimeout(timer);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
-
// -- Interactions -----------------------------------------------------------
|
|
205
|
-
async createInteraction(opts) {
|
|
206
|
-
// Validate form schema before encrypting
|
|
207
|
-
if (opts.type === "form" && opts.formSchema === undefined) {
|
|
208
|
-
throw new Error("Form interactions require a formSchema with at least one field.");
|
|
209
|
-
}
|
|
210
|
-
if (opts.formSchema !== undefined) {
|
|
211
|
-
validateFormSchema(opts.formSchema);
|
|
212
|
-
}
|
|
213
|
-
// Encrypt all fields with a SINGLE shared content key to avoid
|
|
214
|
-
// DB unique constraint on (interaction_id, scope, recipient_key_id).
|
|
215
|
-
const { encrypted, contentWrappings } = await this.encryptFields({
|
|
216
|
-
encryptedTitle: opts.title,
|
|
217
|
-
encryptedDescription: opts.description,
|
|
218
|
-
encryptedOptions: opts.options,
|
|
219
|
-
encryptedContext: opts.context,
|
|
220
|
-
encryptedFormSchema: opts.formSchema,
|
|
221
|
-
});
|
|
222
|
-
const body = {
|
|
223
|
-
type: opts.type,
|
|
224
|
-
priority: opts.priority ?? "normal",
|
|
225
|
-
contentWrappings,
|
|
226
|
-
...encrypted,
|
|
227
|
-
};
|
|
228
|
-
if (opts.routeToUser !== undefined)
|
|
229
|
-
body.routeToUser = opts.routeToUser;
|
|
230
|
-
if (opts.routeToTeam !== undefined)
|
|
231
|
-
body.routeToTeam = opts.routeToTeam;
|
|
232
|
-
if (opts.deadlineMinutes !== undefined)
|
|
233
|
-
body.deadlineMinutes = opts.deadlineMinutes;
|
|
234
|
-
if (opts.callbackUrl !== undefined)
|
|
235
|
-
body.callbackUrl = opts.callbackUrl;
|
|
236
|
-
if (opts.convSessionId !== undefined)
|
|
237
|
-
body.convSessionId = opts.convSessionId;
|
|
238
|
-
if (opts.presentation !== undefined)
|
|
239
|
-
body.presentation = opts.presentation;
|
|
240
|
-
const data = await this.request("POST", "/agent/interactions", { body });
|
|
241
|
-
return parseInteraction(data);
|
|
242
|
-
}
|
|
243
|
-
async getInteraction(interactionId) {
|
|
244
|
-
const data = await this.request("GET", `/agent/interactions/${interactionId}`);
|
|
245
|
-
return this.decryptInteractionFields(parseInteraction(data), data);
|
|
246
|
-
}
|
|
247
|
-
/** Try to decrypt encrypted interaction fields using content wrappings. */
|
|
248
|
-
async decryptInteractionFields(interaction, raw) {
|
|
249
|
-
const wrappings = raw.contentWrappings;
|
|
250
|
-
if (!wrappings?.length)
|
|
251
|
-
return interaction;
|
|
252
|
-
const tryDecrypt = async (ciphertext) => {
|
|
253
|
-
if (!ciphertext)
|
|
254
|
-
return undefined;
|
|
255
|
-
const plain = await this.crypto.decryptWrappedContent(ciphertext, wrappings);
|
|
256
|
-
return plain ?? ciphertext;
|
|
257
|
-
};
|
|
258
|
-
return {
|
|
259
|
-
...interaction,
|
|
260
|
-
title: (await tryDecrypt(interaction.title)) ?? "",
|
|
261
|
-
description: await tryDecrypt(interaction.description),
|
|
262
|
-
options: await tryDecrypt(interaction.options),
|
|
263
|
-
context: await tryDecrypt(interaction.context),
|
|
264
|
-
formSchema: await tryDecrypt(interaction.formSchema),
|
|
265
|
-
response: await tryDecrypt(interaction.response),
|
|
266
|
-
comment: await tryDecrypt(interaction.comment),
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
async cancelInteraction(interactionId) {
|
|
270
|
-
const data = await this.request("POST", `/agent/interactions/${interactionId}/cancel`);
|
|
271
|
-
return parseInteraction(data);
|
|
272
|
-
}
|
|
273
|
-
/** Update description/context on an interaction. Accepts plaintext — auto-encrypts. */
|
|
274
|
-
async updateInteractionContext(interactionId, opts) {
|
|
275
|
-
const { encrypted, contentWrappings } = await this.encryptFields({
|
|
276
|
-
encryptedDescription: opts.description,
|
|
277
|
-
encryptedContext: opts.context,
|
|
278
|
-
});
|
|
279
|
-
return await this.request("PATCH", `/agent/interactions/${interactionId}/context`, { body: { contentWrappings, ...encrypted } });
|
|
280
|
-
}
|
|
281
|
-
async acknowledgeInteraction(interactionId) {
|
|
282
|
-
const data = await this.request("POST", `/agent/interactions/${interactionId}/acknowledge`);
|
|
283
|
-
return parseInteraction(data);
|
|
284
|
-
}
|
|
285
|
-
/** AG-018/019: Update priority, deadline, or context metadata on a pending interaction. */
|
|
286
|
-
async updateInteractionMetadata(interactionId, opts) {
|
|
287
|
-
return await this.request("PATCH", `/agent/interactions/${interactionId}/metadata`, { body: opts });
|
|
288
|
-
}
|
|
289
|
-
/** Get the transition log for an interaction. */
|
|
290
|
-
async getInteractionTransitions(interactionId) {
|
|
291
|
-
return await this.request("GET", `/agent/interactions/${interactionId}/transitions`);
|
|
292
|
-
}
|
|
293
|
-
/** HU-073 (agent side): Read pending task control signal (pause/resume/cancel). */
|
|
294
|
-
async getTaskControl(interactionId) {
|
|
295
|
-
return await this.request("GET", `/agent/interactions/${interactionId}/task-control`);
|
|
296
|
-
}
|
|
297
|
-
/** HU-073 (agent side): Acknowledge task control signal, clearing it. */
|
|
298
|
-
async ackTaskControl(interactionId) {
|
|
299
|
-
return await this.request("POST", `/agent/interactions/${interactionId}/task-control/ack`);
|
|
300
|
-
}
|
|
301
|
-
/** Single long-poll for a response (server-side timeout, max ~30s). */
|
|
302
|
-
async awaitResponse(interactionId, timeout = 30) {
|
|
303
|
-
const data = await this.request("GET", `/agent/interactions/${interactionId}/await`, { params: { timeout: String(timeout) }, timeout: (timeout + 5) * 1000 });
|
|
304
|
-
return parseInteraction(data);
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Wait for a human to respond to an interaction, polling repeatedly.
|
|
308
|
-
* Returns the interaction once it has a response (status is responded/completed),
|
|
309
|
-
* or throws after the timeout expires.
|
|
310
|
-
*
|
|
311
|
-
* @param interactionId - The interaction to wait for
|
|
312
|
-
* @param timeoutSeconds - Maximum wait time in seconds (default 300, max 3600)
|
|
313
|
-
*/
|
|
314
|
-
async waitForResponse(interactionId, timeoutSeconds = 300) {
|
|
315
|
-
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
316
|
-
const pollInterval = Math.min(30, timeoutSeconds); // server long-poll max 30s
|
|
317
|
-
while (Date.now() < deadline) {
|
|
318
|
-
const remaining = Math.ceil((deadline - Date.now()) / 1000);
|
|
319
|
-
if (remaining <= 0)
|
|
320
|
-
break;
|
|
321
|
-
const pollTimeout = Math.min(pollInterval, remaining);
|
|
322
|
-
const interaction = await this.awaitResponse(interactionId, pollTimeout);
|
|
323
|
-
// Check if the human has responded
|
|
324
|
-
if (interaction.status === "responded" ||
|
|
325
|
-
interaction.status === "completed" ||
|
|
326
|
-
interaction.status === "cancelled" ||
|
|
327
|
-
interaction.status === "expired") {
|
|
328
|
-
return interaction;
|
|
329
|
-
}
|
|
330
|
-
// Still pending — loop and poll again
|
|
331
|
-
}
|
|
332
|
-
// Final check before timeout
|
|
333
|
-
const final = await this.getInteraction(interactionId);
|
|
334
|
-
if (final.status !== "created" && final.status !== "pending" && final.status !== "viewed") {
|
|
335
|
-
return final;
|
|
336
|
-
}
|
|
337
|
-
throw new HiloopError(408, `Timed out waiting for response after ${timeoutSeconds}s`);
|
|
338
|
-
}
|
|
339
|
-
async listInteractions(filters) {
|
|
340
|
-
const params = {};
|
|
341
|
-
if (filters?.status !== undefined)
|
|
342
|
-
params.status = filters.status;
|
|
343
|
-
if (filters?.type !== undefined)
|
|
344
|
-
params.type = filters.type;
|
|
345
|
-
params.page = String(filters?.page ?? 1);
|
|
346
|
-
params.pageSize = String(filters?.pageSize ?? 20);
|
|
347
|
-
const data = await this.request("GET", "/agent/interactions", { params });
|
|
348
|
-
return {
|
|
349
|
-
items: data.items.map((i) => parseInteraction(i)),
|
|
350
|
-
total: data.total,
|
|
351
|
-
page: data.page,
|
|
352
|
-
pageSize: data.pageSize,
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
// -- Messages ---------------------------------------------------------------
|
|
356
|
-
async sendMessage(interactionId, content) {
|
|
357
|
-
const { encrypted, contentWrappings } = await this.encryptFields({
|
|
358
|
-
encryptedContent: content,
|
|
359
|
-
});
|
|
360
|
-
const data = await this.request("POST", `/agent/interactions/${interactionId}/messages`, { body: { contentWrappings, ...encrypted } });
|
|
361
|
-
return parseMessage(data);
|
|
362
|
-
}
|
|
363
|
-
/** Push content blocks to an interaction. */
|
|
364
|
-
async pushContentBlocks(interactionId, blocks) {
|
|
365
|
-
await this.ensureInitialized();
|
|
366
|
-
const encryptedBlocks = await Promise.all(blocks.map(async (block) => {
|
|
367
|
-
const plaintext = JSON.stringify(block.data);
|
|
368
|
-
const wrapped = await this.crypto.encryptWithWrapping(plaintext);
|
|
369
|
-
return {
|
|
370
|
-
blockType: block.blockType,
|
|
371
|
-
encryptedData: wrapped.ciphertext,
|
|
372
|
-
contentWrappings: wrapped.wrappings,
|
|
373
|
-
position: block.position,
|
|
374
|
-
};
|
|
375
|
-
}));
|
|
376
|
-
return await this.request("POST", `/agent/interactions/${interactionId}/blocks`, { body: { blocks: encryptedBlocks } });
|
|
377
|
-
}
|
|
378
|
-
/** List content blocks for an interaction. */
|
|
379
|
-
async listContentBlocks(interactionId) {
|
|
380
|
-
return await this.request("GET", `/agent/interactions/${interactionId}/blocks`);
|
|
381
|
-
}
|
|
382
|
-
/** Update a content block. */
|
|
383
|
-
async updateContentBlock(interactionId, blockId, data) {
|
|
384
|
-
return await this.request("PUT", `/agent/interactions/${interactionId}/blocks/${blockId}`, { body: { data } });
|
|
385
|
-
}
|
|
386
|
-
/** Delete a content block. */
|
|
387
|
-
async deleteContentBlock(interactionId, blockId) {
|
|
388
|
-
return await this.request("DELETE", `/agent/interactions/${interactionId}/blocks/${blockId}`);
|
|
389
|
-
}
|
|
390
|
-
/** Upload a file attachment to an interaction (base64-encoded). */
|
|
391
|
-
async uploadAttachment(interactionId, opts) {
|
|
392
|
-
return await this.request("POST", `/agent/interactions/${interactionId}/attachments`, { body: opts });
|
|
393
|
-
}
|
|
394
|
-
/** List attachments for an interaction, optionally filtered by messageId. */
|
|
395
|
-
async listAttachments(interactionId, messageId) {
|
|
396
|
-
const qs = messageId ? `?messageId=${encodeURIComponent(messageId)}` : "";
|
|
397
|
-
return await this.request("GET", `/agent/interactions/${interactionId}/attachments${qs}`);
|
|
398
|
-
}
|
|
399
|
-
/** Download raw binary content of a specific attachment. */
|
|
400
|
-
async getAttachment(interactionId, fileId) {
|
|
401
|
-
return await this.requestBinary(`/agent/interactions/${interactionId}/attachments/${fileId}`);
|
|
402
|
-
}
|
|
403
|
-
/** List all reactions for messages in an interaction. */
|
|
404
|
-
async listInteractionReactions(interactionId) {
|
|
405
|
-
return await this.request("GET", `/agent/interactions/${interactionId}/reactions`);
|
|
406
|
-
}
|
|
407
|
-
/** Add a reaction emoji to a message in an interaction. */
|
|
408
|
-
async addInteractionReaction(interactionId, messageId, emoji) {
|
|
409
|
-
return await this.request("POST", `/agent/interactions/${interactionId}/messages/${messageId}/reactions`, { body: { emoji } });
|
|
410
|
-
}
|
|
411
|
-
/** Remove a reaction emoji from a message in an interaction. */
|
|
412
|
-
async removeInteractionReaction(interactionId, messageId, emoji) {
|
|
413
|
-
return await this.request("DELETE", `/agent/interactions/${interactionId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`);
|
|
414
|
-
}
|
|
415
|
-
/** Mark all messages in an interaction as read by this agent. */
|
|
416
|
-
async markInteractionRead(interactionId) {
|
|
417
|
-
return await this.request("POST", `/agent/interactions/${interactionId}/messages/read`);
|
|
418
|
-
}
|
|
419
|
-
/** Send a typing indicator for an interaction. */
|
|
420
|
-
async sendInteractionTyping(interactionId, isTyping) {
|
|
421
|
-
return await this.request("POST", `/agent/interactions/${interactionId}/typing`, { body: { isTyping } });
|
|
422
|
-
}
|
|
423
|
-
async listMessages(interactionId, page = 1, pageSize = 50) {
|
|
424
|
-
const params = { page: String(page), pageSize: String(pageSize) };
|
|
425
|
-
const data = await this.request("GET", `/agent/interactions/${interactionId}/messages`, { params });
|
|
426
|
-
const rawItems = data.messages ?? data.items ?? [];
|
|
427
|
-
const items = rawItems.map((m) => parseMessage(m));
|
|
428
|
-
// Try to decrypt messages using content wrappings
|
|
429
|
-
await Promise.all(items.map(async (msg, i) => {
|
|
430
|
-
const raw = rawItems[i];
|
|
431
|
-
const wrappings = raw.contentWrappings;
|
|
432
|
-
if (wrappings?.length && msg.content) {
|
|
433
|
-
const plain = await this.crypto.decryptWrappedContent(msg.content, wrappings);
|
|
434
|
-
if (plain !== null)
|
|
435
|
-
msg.content = plain;
|
|
436
|
-
}
|
|
437
|
-
}));
|
|
438
|
-
return { items, total: data.total, page: data.page, pageSize: data.pageSize };
|
|
439
|
-
}
|
|
440
204
|
/** AD-060: Push agent telemetry activity status + plan to the platform. */
|
|
441
205
|
async pushTelemetry(opts) {
|
|
442
206
|
return await this.request("POST", "/agent/activity", { body: opts });
|
|
@@ -544,19 +308,60 @@ export class HiloopClient {
|
|
|
544
308
|
const data = await this.request("POST", `/agent/sessions/${sessionId}/close`);
|
|
545
309
|
return parseConvSession(data);
|
|
546
310
|
}
|
|
311
|
+
// -- Unified Message API ----------------------------------------------------
|
|
547
312
|
/**
|
|
548
|
-
* Send a message to a
|
|
549
|
-
*
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
* Send a message in a conv session. Accepts plaintext — encrypts automatically
|
|
553
|
-
* using the session message key (`{agentPub}:{AES-GCM ciphertext}` format).
|
|
313
|
+
* Send a message to a session. Accepts plaintext content and optional
|
|
314
|
+
* rich components. Encrypts content and components automatically.
|
|
315
|
+
*
|
|
316
|
+
* This is the primary method for agent-to-human communication.
|
|
554
317
|
*/
|
|
555
|
-
async
|
|
318
|
+
async sendMessage(sessionId, content, opts) {
|
|
556
319
|
await this.ensureInitialized();
|
|
557
320
|
const encryptedContent = await this.crypto.encryptSessionMessage(content);
|
|
558
|
-
const
|
|
559
|
-
|
|
321
|
+
const body = { encryptedContent };
|
|
322
|
+
if (opts?.components && opts.components.length > 0) {
|
|
323
|
+
body.encryptedComponents = await this.crypto.encryptSessionMessage(JSON.stringify(opts.components));
|
|
324
|
+
// Extract metadata from components for server-side routing
|
|
325
|
+
const hasResponseRequired = opts.components.some((n) => n.type === "button_group" &&
|
|
326
|
+
n.data?.responseRequired === true);
|
|
327
|
+
if (hasResponseRequired) {
|
|
328
|
+
body.responseRequired = true;
|
|
329
|
+
body.category = "decide";
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (opts?.priority)
|
|
333
|
+
body.priority = opts.priority;
|
|
334
|
+
if (opts?.deadlineMinutes)
|
|
335
|
+
body.deadlineMinutes = opts.deadlineMinutes;
|
|
336
|
+
if (opts?.replyToMessageId)
|
|
337
|
+
body.replyToMessageId = opts.replyToMessageId;
|
|
338
|
+
const data = await this.request("POST", `/agent/sessions/${sessionId}/messages`, { body, agentName: opts?.agentName });
|
|
339
|
+
return parseSessionMessage(data);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Long-poll for a response to a specific message.
|
|
343
|
+
* Returns the updated message once the human has responded, or after timeout.
|
|
344
|
+
*/
|
|
345
|
+
async awaitResponse(sessionId, messageId, timeoutSeconds = 300) {
|
|
346
|
+
const data = await this.request("GET", `/agent/sessions/${sessionId}/messages/${messageId}/await`, {
|
|
347
|
+
params: { timeout: String(Math.min(timeoutSeconds, 30)) },
|
|
348
|
+
timeout: (Math.min(timeoutSeconds, 30) + 5) * 1000,
|
|
349
|
+
});
|
|
350
|
+
return parseSessionMessage(data);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Acknowledge a message (mark it as seen/handled by the agent).
|
|
354
|
+
*/
|
|
355
|
+
async acknowledge(sessionId, messageId) {
|
|
356
|
+
const data = await this.request("POST", `/agent/sessions/${sessionId}/messages/${messageId}/acknowledge`);
|
|
357
|
+
return parseSessionMessage(data);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Cancel a previously sent message (e.g. withdraw an approval request).
|
|
361
|
+
*/
|
|
362
|
+
async cancel(sessionId, messageId) {
|
|
363
|
+
const data = await this.request("POST", `/agent/sessions/${sessionId}/messages/${messageId}/cancel`);
|
|
364
|
+
return parseSessionMessage(data);
|
|
560
365
|
}
|
|
561
366
|
/** Send a typing indicator to a conv session (HU-034). */
|
|
562
367
|
async sendConvSessionTyping(sessionId, typing) {
|
|
@@ -580,7 +385,7 @@ export class HiloopClient {
|
|
|
580
385
|
if (opts?.after !== undefined)
|
|
581
386
|
params.after = opts.after;
|
|
582
387
|
const result = await this.request("GET", `/agent/sessions/${sessionId}/timeline`, { params });
|
|
583
|
-
// Decrypt timeline items (session messages
|
|
388
|
+
// Decrypt timeline items (session messages)
|
|
584
389
|
if (Array.isArray(result.items)) {
|
|
585
390
|
await Promise.all(result.items.map(async (item) => {
|
|
586
391
|
if (item.kind === "message" && typeof item.encryptedContent === "string") {
|
|
@@ -590,20 +395,6 @@ export class HiloopClient {
|
|
|
590
395
|
else
|
|
591
396
|
item.content = "[Encrypted]";
|
|
592
397
|
}
|
|
593
|
-
if (item.kind === "interaction") {
|
|
594
|
-
const wrappings = item.contentWrappings;
|
|
595
|
-
if (wrappings?.length) {
|
|
596
|
-
for (const field of ["encryptedTitle", "encryptedDescription", "encryptedOptions", "encryptedFormSchema", "encryptedContext"]) {
|
|
597
|
-
if (typeof item[field] === "string") {
|
|
598
|
-
const plain = await this.crypto.decryptWrappedContent(item[field], wrappings);
|
|
599
|
-
if (plain !== null) {
|
|
600
|
-
const readableKey = field.replace(/^encrypted/, "").replace(/^./, (c) => c.toLowerCase());
|
|
601
|
-
item[readableKey] = plain;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
398
|
}));
|
|
608
399
|
}
|
|
609
400
|
return result;
|
|
@@ -634,117 +425,31 @@ export class HiloopClient {
|
|
|
634
425
|
async revokeGuestToken(sessionId, tokenId) {
|
|
635
426
|
await this.request("DELETE", `/human/sessions/${sessionId}/guest-tokens/${tokenId}`);
|
|
636
427
|
}
|
|
637
|
-
/** AG-060: Create multiple interactions in a single API call. */
|
|
638
|
-
async createInteractionBatch(interactions) {
|
|
639
|
-
const items = await Promise.all(interactions.map(async (opts) => {
|
|
640
|
-
const { encrypted, contentWrappings } = await this.encryptFields({
|
|
641
|
-
encryptedTitle: opts.title,
|
|
642
|
-
encryptedDescription: opts.description,
|
|
643
|
-
encryptedOptions: opts.options,
|
|
644
|
-
encryptedContext: opts.context,
|
|
645
|
-
});
|
|
646
|
-
const body = {
|
|
647
|
-
type: opts.type,
|
|
648
|
-
priority: opts.priority ?? "normal",
|
|
649
|
-
contentWrappings,
|
|
650
|
-
...encrypted,
|
|
651
|
-
};
|
|
652
|
-
if (opts.routeToUser !== undefined)
|
|
653
|
-
body.routeToUser = opts.routeToUser;
|
|
654
|
-
if (opts.routeToTeam !== undefined)
|
|
655
|
-
body.routeToTeam = opts.routeToTeam;
|
|
656
|
-
if (opts.deadlineMinutes !== undefined)
|
|
657
|
-
body.deadlineMinutes = opts.deadlineMinutes;
|
|
658
|
-
if (opts.convSessionId !== undefined)
|
|
659
|
-
body.convSessionId = opts.convSessionId;
|
|
660
|
-
return body;
|
|
661
|
-
}));
|
|
662
|
-
return this.request("POST", "/agent/interactions/batch", { body: { interactions: items } });
|
|
663
|
-
}
|
|
664
428
|
/** Archive a conversation session. */
|
|
665
429
|
async archiveConvSession(sessionId) {
|
|
666
430
|
const data = await this.request("POST", `/agent/sessions/${sessionId}/archive`);
|
|
667
431
|
return parseConvSession(data);
|
|
668
432
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
encryptedContext: opts.context,
|
|
675
|
-
});
|
|
676
|
-
const body = {
|
|
677
|
-
type: opts.type,
|
|
678
|
-
priority: opts.priority ?? "normal",
|
|
679
|
-
participants,
|
|
680
|
-
contentWrappings,
|
|
681
|
-
...encrypted,
|
|
682
|
-
};
|
|
683
|
-
if (opts.routeToUser !== undefined)
|
|
684
|
-
body.routeToUser = opts.routeToUser;
|
|
685
|
-
if (opts.routeToTeam !== undefined)
|
|
686
|
-
body.routeToTeam = opts.routeToTeam;
|
|
687
|
-
if (opts.deadlineMinutes !== undefined)
|
|
688
|
-
body.deadlineMinutes = opts.deadlineMinutes;
|
|
689
|
-
if (opts.callbackUrl !== undefined)
|
|
690
|
-
body.callbackUrl = opts.callbackUrl;
|
|
691
|
-
return this.request("POST", "/agent/interactions/group", { body });
|
|
692
|
-
}
|
|
693
|
-
/** Add a reference from one interaction to another (AG-062). */
|
|
694
|
-
async addInteractionReference(interactionId, targetInteractionId, opts) {
|
|
695
|
-
return this.request("POST", `/agent/interactions/${interactionId}/references`, {
|
|
696
|
-
body: { targetInteractionId, referenceType: opts?.referenceType, note: opts?.note },
|
|
697
|
-
});
|
|
433
|
+
// -- Session attachments -----------------------------------------------------
|
|
434
|
+
/** Upload an attachment to a session. Content must be base64-encoded. */
|
|
435
|
+
async uploadSessionAttachment(sessionId, opts) {
|
|
436
|
+
const data = await this.request("POST", `/agent/sessions/${sessionId}/attachments`, { body: opts });
|
|
437
|
+
return data.attachment;
|
|
698
438
|
}
|
|
699
|
-
/** List
|
|
700
|
-
async
|
|
701
|
-
|
|
439
|
+
/** List attachments for a session. */
|
|
440
|
+
async listSessionAttachments(sessionId) {
|
|
441
|
+
const data = await this.request("GET", `/agent/sessions/${sessionId}/attachments`);
|
|
442
|
+
return data.attachments;
|
|
702
443
|
}
|
|
703
|
-
/**
|
|
704
|
-
async
|
|
705
|
-
return this.
|
|
444
|
+
/** Download an attachment's binary content. */
|
|
445
|
+
async downloadSessionAttachment(sessionId, fileId) {
|
|
446
|
+
return this.requestBinary(`/agent/sessions/${sessionId}/attachments/${fileId}`);
|
|
706
447
|
}
|
|
707
448
|
// -- Public agent routes (no auth required) ---------------------------------
|
|
708
449
|
/** Get public metadata for a public agent by slug. */
|
|
709
450
|
async getPublicAgentInfo(slug) {
|
|
710
451
|
return this.request("GET", `/public/${slug}/info`);
|
|
711
452
|
}
|
|
712
|
-
/** Create an interaction as a public (anonymous) user on a public agent. */
|
|
713
|
-
async createPublicInteraction(slug, opts) {
|
|
714
|
-
return this.request("POST", `/public/${slug}/interactions`, { body: opts });
|
|
715
|
-
}
|
|
716
|
-
/** Get the status of a public interaction by slug + ID. */
|
|
717
|
-
async getPublicInteraction(slug, interactionId) {
|
|
718
|
-
return this.request("GET", `/public/${slug}/interactions/${interactionId}`);
|
|
719
|
-
}
|
|
720
|
-
/** Long-poll for a response on a public interaction (returns when responded/completed/cancelled/expired). */
|
|
721
|
-
async awaitPublicInteraction(slug, interactionId, timeoutMs = 30000) {
|
|
722
|
-
return this.request("GET", `/public/${slug}/interactions/${interactionId}/await`, {
|
|
723
|
-
params: { timeout: String(timeoutMs) },
|
|
724
|
-
timeout: timeoutMs + 5000,
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
/** Send a message to a public interaction as an anonymous user. */
|
|
728
|
-
async sendPublicMessage(slug, interactionId, encryptedContent) {
|
|
729
|
-
return this.request("POST", `/public/${slug}/interactions/${interactionId}/messages`, { body: { encryptedContent } });
|
|
730
|
-
}
|
|
731
|
-
/** List interactions created on a public agent (paginated). */
|
|
732
|
-
async listPublicInteractions(slug, opts) {
|
|
733
|
-
const params = {};
|
|
734
|
-
if (opts?.page !== undefined)
|
|
735
|
-
params.page = String(opts.page);
|
|
736
|
-
if (opts?.pageSize !== undefined)
|
|
737
|
-
params.pageSize = String(opts.pageSize);
|
|
738
|
-
return this.request("GET", `/public/${slug}/interactions`, Object.keys(params).length > 0 ? { params } : undefined);
|
|
739
|
-
}
|
|
740
|
-
/** Cancel a public interaction (anonymous user side). */
|
|
741
|
-
async cancelPublicInteraction(slug, interactionId) {
|
|
742
|
-
return this.request("DELETE", `/public/${slug}/interactions/${interactionId}`);
|
|
743
|
-
}
|
|
744
|
-
/** Get the transition log for a public interaction. */
|
|
745
|
-
async getPublicInteractionTransitions(slug, interactionId) {
|
|
746
|
-
return this.request("GET", `/public/${slug}/interactions/${interactionId}/transitions`);
|
|
747
|
-
}
|
|
748
453
|
/** Look up a public session by its public token (no auth required). */
|
|
749
454
|
async getPublicSession(publicToken) {
|
|
750
455
|
return this.request("GET", `/public/sessions/${publicToken}`);
|
|
@@ -753,298 +458,6 @@ export class HiloopClient {
|
|
|
753
458
|
async joinPublicSession(publicToken, displayName) {
|
|
754
459
|
return this.request("POST", `/public/sessions/${publicToken}/join`, { body: { displayName } });
|
|
755
460
|
}
|
|
756
|
-
// -- Human Interaction Detail -----------------------------------------------
|
|
757
|
-
/** Get a human-facing interaction detail view. */
|
|
758
|
-
async getHumanInteraction(interactionId) {
|
|
759
|
-
return this.request("GET", `/human/interactions/${interactionId}`);
|
|
760
|
-
}
|
|
761
|
-
/** Mark an interaction as viewed by the authenticated user. */
|
|
762
|
-
async viewInteraction(interactionId) {
|
|
763
|
-
return this.request("POST", `/human/interactions/${interactionId}/view`);
|
|
764
|
-
}
|
|
765
|
-
/** Submit a response to an interaction. */
|
|
766
|
-
async respondToHumanInteraction(interactionId, opts) {
|
|
767
|
-
return this.request("POST", `/human/interactions/${interactionId}/respond`, { body: opts });
|
|
768
|
-
}
|
|
769
|
-
/** Snooze an interaction until a given ISO datetime. */
|
|
770
|
-
async snoozeInteraction(interactionId, snoozedUntil) {
|
|
771
|
-
return this.request("POST", `/human/interactions/${interactionId}/snooze`, { body: { snoozedUntil } });
|
|
772
|
-
}
|
|
773
|
-
/** Toggle the pinned state of an interaction. */
|
|
774
|
-
async pinInteraction(interactionId, pinned) {
|
|
775
|
-
return this.request("POST", `/human/interactions/${interactionId}/pin`, { body: { pinned } });
|
|
776
|
-
}
|
|
777
|
-
// -- Human Inbox / Feed ----------------------------------------------------
|
|
778
|
-
// getInbox removed -- use getFeed with view param
|
|
779
|
-
/** Get the human user's activity feed (all interactions). */
|
|
780
|
-
async getFeed(opts) {
|
|
781
|
-
const params = new URLSearchParams();
|
|
782
|
-
if (opts?.page)
|
|
783
|
-
params.set("page", String(opts.page));
|
|
784
|
-
if (opts?.pageSize)
|
|
785
|
-
params.set("pageSize", String(opts.pageSize));
|
|
786
|
-
if (opts?.view)
|
|
787
|
-
params.set("view", opts.view);
|
|
788
|
-
if (opts?.q)
|
|
789
|
-
params.set("q", opts.q);
|
|
790
|
-
if (opts?.type)
|
|
791
|
-
params.set("type", opts.type);
|
|
792
|
-
if (opts?.priority)
|
|
793
|
-
params.set("priority", opts.priority);
|
|
794
|
-
if (opts?.pinned !== undefined)
|
|
795
|
-
params.set("pinned", String(opts.pinned));
|
|
796
|
-
if (opts?.agentId)
|
|
797
|
-
params.set("agentId", opts.agentId);
|
|
798
|
-
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
799
|
-
return this.request("GET", `/human/feed${qs}`);
|
|
800
|
-
}
|
|
801
|
-
// searchInteractions removed -- use getFeed with q param
|
|
802
|
-
// getHistory removed -- use getFeed with view param
|
|
803
|
-
/** Get badge counts (e.g. open interactions) for the authenticated user. */
|
|
804
|
-
async getBadges() {
|
|
805
|
-
return this.request("GET", "/human/badges");
|
|
806
|
-
}
|
|
807
|
-
// -- Account ----------------------------------------------------------------
|
|
808
|
-
/** Get the authenticated user's current availability status. */
|
|
809
|
-
async getMyAvailability() {
|
|
810
|
-
return this.request("GET", "/human/availability");
|
|
811
|
-
}
|
|
812
|
-
/** Update the authenticated user's availability status. */
|
|
813
|
-
async updateMyAvailability(status) {
|
|
814
|
-
return this.request("PATCH", "/human/availability", { body: { status } });
|
|
815
|
-
}
|
|
816
|
-
/** List delegation rules for the authenticated user. */
|
|
817
|
-
async listDelegations() {
|
|
818
|
-
return this.request("GET", "/human/delegations");
|
|
819
|
-
}
|
|
820
|
-
/** Create a delegation rule for the authenticated user. */
|
|
821
|
-
async createDelegation(opts) {
|
|
822
|
-
return this.request("POST", "/human/delegations", { body: opts });
|
|
823
|
-
}
|
|
824
|
-
/** Remove a delegation rule by delegateId. */
|
|
825
|
-
async deleteDelegation(delegateId) {
|
|
826
|
-
return this.request("DELETE", `/human/delegations/${delegateId}`);
|
|
827
|
-
}
|
|
828
|
-
// -- Notifications ----------------------------------------------------------
|
|
829
|
-
/** List sent notifications for the authenticated user. */
|
|
830
|
-
async listNotifications() {
|
|
831
|
-
return this.request("GET", "/human/notifications");
|
|
832
|
-
}
|
|
833
|
-
/** Get notification preferences for the authenticated user. */
|
|
834
|
-
async getNotificationPreferences() {
|
|
835
|
-
return this.request("GET", "/human/notifications/preferences");
|
|
836
|
-
}
|
|
837
|
-
/** Update notification preference for a specific channel. */
|
|
838
|
-
async updateNotificationPreference(channel, opts) {
|
|
839
|
-
return this.request("PUT", `/human/notifications/preferences/${channel}`, { body: opts });
|
|
840
|
-
}
|
|
841
|
-
/** Register a push token for the authenticated user's device. */
|
|
842
|
-
async registerPushToken(opts) {
|
|
843
|
-
return this.request("POST", "/human/notifications/push-tokens", { body: opts });
|
|
844
|
-
}
|
|
845
|
-
/** Remove the push token for the authenticated user's device. */
|
|
846
|
-
async deletePushToken(opts) {
|
|
847
|
-
return this.request("DELETE", "/human/notifications/push-tokens", { body: opts });
|
|
848
|
-
}
|
|
849
|
-
/** Save (upsert) a response draft for an interaction. */
|
|
850
|
-
async saveDraft(interactionId, encryptedDraft) {
|
|
851
|
-
return this.request("PUT", `/human/interactions/${interactionId}/draft`, { body: { encryptedDraft } });
|
|
852
|
-
}
|
|
853
|
-
/** Get the saved response draft for an interaction. */
|
|
854
|
-
async getDraft(interactionId) {
|
|
855
|
-
return this.request("GET", `/human/interactions/${interactionId}/draft`);
|
|
856
|
-
}
|
|
857
|
-
/** Delete the saved response draft for an interaction. */
|
|
858
|
-
async deleteDraft(interactionId) {
|
|
859
|
-
return this.request("DELETE", `/human/interactions/${interactionId}/draft`);
|
|
860
|
-
}
|
|
861
|
-
/** Unsnooze an interaction before its scheduled snooze time. */
|
|
862
|
-
async unsnoozeInteraction(interactionId) {
|
|
863
|
-
return this.request("POST", `/human/interactions/${interactionId}/unsnooze`);
|
|
864
|
-
}
|
|
865
|
-
// -- Collaboration (milestone alerts + approval chain) -----------------------
|
|
866
|
-
/** Create a milestone alert for an interaction. */
|
|
867
|
-
async createMilestoneAlert(interactionId, opts) {
|
|
868
|
-
return this.request("POST", `/human/interactions/${interactionId}/milestone-alerts`, { body: opts });
|
|
869
|
-
}
|
|
870
|
-
/** List milestone alerts for an interaction. */
|
|
871
|
-
async listMilestoneAlerts(interactionId) {
|
|
872
|
-
return this.request("GET", `/human/interactions/${interactionId}/milestone-alerts`);
|
|
873
|
-
}
|
|
874
|
-
/** Delete a milestone alert. */
|
|
875
|
-
async deleteMilestoneAlert(interactionId, alertId) {
|
|
876
|
-
return this.request("DELETE", `/human/interactions/${interactionId}/milestone-alerts/${alertId}`);
|
|
877
|
-
}
|
|
878
|
-
/** Get the approval chain for an interaction. */
|
|
879
|
-
async getApprovalChain(interactionId) {
|
|
880
|
-
return this.request("GET", `/human/interactions/${interactionId}/approval-chain`);
|
|
881
|
-
}
|
|
882
|
-
/** Respond to an approval chain step. */
|
|
883
|
-
async respondToApprovalChain(interactionId, opts) {
|
|
884
|
-
return this.request("POST", `/human/interactions/${interactionId}/approval-chain/respond`, { body: opts });
|
|
885
|
-
}
|
|
886
|
-
// -- Phase 628: Comments + review feedback ------------------------------------
|
|
887
|
-
/** List comments on an interaction. */
|
|
888
|
-
async listInteractionComments(interactionId) {
|
|
889
|
-
return this.request("GET", `/human/interactions/${interactionId}/comments`);
|
|
890
|
-
}
|
|
891
|
-
/** Post a comment on an interaction. */
|
|
892
|
-
async createInteractionComment(interactionId, opts) {
|
|
893
|
-
return this.request("POST", `/human/interactions/${interactionId}/comments`, { body: opts });
|
|
894
|
-
}
|
|
895
|
-
/** Mark a comment as resolved. */
|
|
896
|
-
async resolveInteractionComment(interactionId, commentId) {
|
|
897
|
-
return this.request("PATCH", `/human/interactions/${interactionId}/comments/${commentId}/resolve`);
|
|
898
|
-
}
|
|
899
|
-
/** Delete a comment. */
|
|
900
|
-
async deleteInteractionComment(interactionId, commentId) {
|
|
901
|
-
return this.request("DELETE", `/human/interactions/${interactionId}/comments/${commentId}`);
|
|
902
|
-
}
|
|
903
|
-
/** Submit review feedback for an interaction. */
|
|
904
|
-
async submitReviewFeedback(interactionId, opts) {
|
|
905
|
-
return this.request("POST", `/human/interactions/${interactionId}/review-feedback`, { body: opts });
|
|
906
|
-
}
|
|
907
|
-
// -- Phase 629: Voting + tags --------------------------------------------------
|
|
908
|
-
/** Cast a vote on an interaction. */
|
|
909
|
-
async voteOnInteraction(interactionId, opts) {
|
|
910
|
-
return this.request("POST", `/human/interactions/${interactionId}/vote`, { body: opts });
|
|
911
|
-
}
|
|
912
|
-
/** Get aggregate vote results for an interaction. */
|
|
913
|
-
async getVoteResults(interactionId) {
|
|
914
|
-
return this.request("GET", `/human/interactions/${interactionId}/vote/results`);
|
|
915
|
-
}
|
|
916
|
-
/** Get the current user's vote on an interaction. */
|
|
917
|
-
async getMyVote(interactionId) {
|
|
918
|
-
return this.request("GET", `/human/interactions/${interactionId}/vote/mine`);
|
|
919
|
-
}
|
|
920
|
-
/** List tags on an interaction. */
|
|
921
|
-
async getInteractionTags(interactionId) {
|
|
922
|
-
return this.request("GET", `/human/interactions/${interactionId}/tags`);
|
|
923
|
-
}
|
|
924
|
-
/** Add a tag to an interaction. */
|
|
925
|
-
async addInteractionTag(interactionId, tag) {
|
|
926
|
-
return this.request("POST", `/human/interactions/${interactionId}/tags`, { body: { tag } });
|
|
927
|
-
}
|
|
928
|
-
// -- Phase 630: More tags + response templates ---------------------------------
|
|
929
|
-
/** Remove a tag from an interaction. */
|
|
930
|
-
async deleteInteractionTag(interactionId, tag) {
|
|
931
|
-
return this.request("DELETE", `/human/interactions/${interactionId}/tags/${encodeURIComponent(tag)}`);
|
|
932
|
-
}
|
|
933
|
-
/** List all space-wide tags. */
|
|
934
|
-
async listSpaceTags() {
|
|
935
|
-
return this.request("GET", "/human/tags");
|
|
936
|
-
}
|
|
937
|
-
/** List response templates for the current user. */
|
|
938
|
-
async listResponseTemplates() {
|
|
939
|
-
return this.request("GET", "/human/response-templates");
|
|
940
|
-
}
|
|
941
|
-
/** Create a response template. */
|
|
942
|
-
async createResponseTemplate(opts) {
|
|
943
|
-
return this.request("POST", "/human/response-templates", { body: opts });
|
|
944
|
-
}
|
|
945
|
-
/** Delete a response template. */
|
|
946
|
-
async deleteResponseTemplate(templateId) {
|
|
947
|
-
return this.request("DELETE", `/human/response-templates/${templateId}`);
|
|
948
|
-
}
|
|
949
|
-
// -- Phase 631: Review feedback + template use + message routes ----------------
|
|
950
|
-
/** Get review feedback for an interaction. */
|
|
951
|
-
async getReviewFeedback(interactionId) {
|
|
952
|
-
return this.request("GET", `/human/interactions/${interactionId}/review-feedback`);
|
|
953
|
-
}
|
|
954
|
-
/** Use a response template (increments usage count). */
|
|
955
|
-
async useResponseTemplate(templateId) {
|
|
956
|
-
return this.request("POST", `/human/response-templates/${templateId}/use`);
|
|
957
|
-
}
|
|
958
|
-
/** List messages for a human interaction. */
|
|
959
|
-
async listInteractionMessages(interactionId, opts) {
|
|
960
|
-
const params = {};
|
|
961
|
-
if (opts?.before !== undefined)
|
|
962
|
-
params.before = opts.before;
|
|
963
|
-
if (opts?.limit !== undefined)
|
|
964
|
-
params.limit = String(opts.limit);
|
|
965
|
-
return this.request("GET", `/human/interactions/${interactionId}/messages`, { params });
|
|
966
|
-
}
|
|
967
|
-
/** Send a message to a human interaction thread. */
|
|
968
|
-
async sendInteractionMessage(interactionId, opts) {
|
|
969
|
-
return this.request("POST", `/human/interactions/${interactionId}/messages`, { body: opts });
|
|
970
|
-
}
|
|
971
|
-
/** Mark messages as read for an interaction. */
|
|
972
|
-
async markMessagesRead(interactionId) {
|
|
973
|
-
return this.request("POST", `/human/interactions/${interactionId}/messages/read`);
|
|
974
|
-
}
|
|
975
|
-
// -- Phase 632: Reactions + agent activity + references ------------------------
|
|
976
|
-
/** Get aggregated reactions for all messages in an interaction. */
|
|
977
|
-
async getInteractionReactions(interactionId) {
|
|
978
|
-
return this.request("GET", `/human/interactions/${interactionId}/reactions`);
|
|
979
|
-
}
|
|
980
|
-
/** Add a reaction to a specific message. */
|
|
981
|
-
async addMessageReaction(interactionId, messageId, emoji) {
|
|
982
|
-
return this.request("POST", `/human/interactions/${interactionId}/messages/${messageId}/reactions`, { body: { emoji } });
|
|
983
|
-
}
|
|
984
|
-
/** Remove a reaction from a specific message. */
|
|
985
|
-
async removeMessageReaction(interactionId, messageId, emoji) {
|
|
986
|
-
return this.request("DELETE", `/human/interactions/${interactionId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`);
|
|
987
|
-
}
|
|
988
|
-
/** Get agent activity data for the space (for human dashboard). */
|
|
989
|
-
async getAgentActivity() {
|
|
990
|
-
return this.request("GET", "/human/agents/activity");
|
|
991
|
-
}
|
|
992
|
-
/** List interaction references for a human interaction. */
|
|
993
|
-
async listHumanInteractionReferences(interactionId) {
|
|
994
|
-
return this.request("GET", `/human/interactions/${interactionId}/references`);
|
|
995
|
-
}
|
|
996
|
-
// -- Phase 633: References + usage + feature gates + presence ------------------
|
|
997
|
-
/** Add a reference link between two interactions. */
|
|
998
|
-
async addHumanInteractionReference(interactionId, opts) {
|
|
999
|
-
return this.request("POST", `/human/interactions/${interactionId}/references`, { body: opts });
|
|
1000
|
-
}
|
|
1001
|
-
/** Get current usage and quota for the space. */
|
|
1002
|
-
async getUsage() {
|
|
1003
|
-
return this.request("GET", "/human/usage");
|
|
1004
|
-
}
|
|
1005
|
-
/** Get feature gate flags for the current user. */
|
|
1006
|
-
async getFeatureGates() {
|
|
1007
|
-
return this.request("GET", "/human/feature-gates");
|
|
1008
|
-
}
|
|
1009
|
-
/** Track that the current user is viewing an interaction. */
|
|
1010
|
-
async trackInteractionViewing(interactionId) {
|
|
1011
|
-
return this.request("POST", `/human/interactions/${interactionId}/viewing`);
|
|
1012
|
-
}
|
|
1013
|
-
/** Get who is currently viewing an interaction. */
|
|
1014
|
-
async getInteractionViewers(interactionId) {
|
|
1015
|
-
return this.request("GET", `/human/interactions/${interactionId}/viewers`);
|
|
1016
|
-
}
|
|
1017
|
-
// -- Phase 634: More account routes + inbox grouped + members ------------------
|
|
1018
|
-
/** Stop tracking viewing presence for an interaction. */
|
|
1019
|
-
async stopTrackingViewing(interactionId) {
|
|
1020
|
-
return this.request("DELETE", `/human/interactions/${interactionId}/viewing`);
|
|
1021
|
-
}
|
|
1022
|
-
/** Get human-assigned tasks (interactions requiring action). */
|
|
1023
|
-
async getHumanTasks(opts) {
|
|
1024
|
-
const params = {};
|
|
1025
|
-
if (opts?.page !== undefined)
|
|
1026
|
-
params.page = String(opts.page);
|
|
1027
|
-
if (opts?.pageSize !== undefined)
|
|
1028
|
-
params.pageSize = String(opts.pageSize);
|
|
1029
|
-
return this.request("GET", "/human/tasks", { params });
|
|
1030
|
-
}
|
|
1031
|
-
// getInboxGrouped removed -- use getFeed with view param
|
|
1032
|
-
/** List space members (humans). */
|
|
1033
|
-
async getHumanMembers(opts) {
|
|
1034
|
-
const params = {};
|
|
1035
|
-
if (opts?.page !== undefined)
|
|
1036
|
-
params.page = String(opts.page);
|
|
1037
|
-
if (opts?.pageSize !== undefined)
|
|
1038
|
-
params.pageSize = String(opts.pageSize);
|
|
1039
|
-
return this.request("GET", "/human/members", { params });
|
|
1040
|
-
}
|
|
1041
|
-
/** Get aggregated notification digest for the current user. */
|
|
1042
|
-
async getNotificationDigest(opts) {
|
|
1043
|
-
const params = {};
|
|
1044
|
-
if (opts?.since !== undefined)
|
|
1045
|
-
params.since = opts.since;
|
|
1046
|
-
return this.request("GET", "/human/notifications/digest", { params });
|
|
1047
|
-
}
|
|
1048
461
|
// -- Channels ---------------------------------------------------------------
|
|
1049
462
|
/** Create a new agent-to-agent channel. */
|
|
1050
463
|
async createChannel(opts) {
|
|
@@ -1238,30 +651,19 @@ export class HiloopClient {
|
|
|
1238
651
|
return this.crypto.decryptSessionMessage(encryptedContent);
|
|
1239
652
|
}
|
|
1240
653
|
/**
|
|
1241
|
-
* Decrypt
|
|
1242
|
-
* Decrypts
|
|
1243
|
-
* Returns the payload with added `
|
|
654
|
+
* Decrypt a webhook payload in place.
|
|
655
|
+
* Decrypts encryptedContent from session messages.
|
|
656
|
+
* Returns the payload with an added `content` field.
|
|
1244
657
|
*/
|
|
1245
658
|
async decryptWebhookPayload(payload) {
|
|
1246
659
|
await this.ensureInitialized();
|
|
1247
660
|
const result = { ...payload };
|
|
1248
|
-
const wrappings = payload.contentWrappings;
|
|
1249
661
|
// Session message
|
|
1250
662
|
if (typeof payload.encryptedContent === "string") {
|
|
1251
663
|
const plain = await this.crypto.decryptSessionMessage(payload.encryptedContent);
|
|
1252
664
|
if (plain !== null)
|
|
1253
665
|
result.content = plain;
|
|
1254
666
|
}
|
|
1255
|
-
// Interaction fields (need content wrappings)
|
|
1256
|
-
if (wrappings?.length) {
|
|
1257
|
-
for (const [encrypted, decrypted] of [["encryptedTitle", "title"], ["encryptedResponse", "response"], ["encryptedComment", "comment"]]) {
|
|
1258
|
-
if (typeof payload[encrypted] === "string") {
|
|
1259
|
-
const plain = await this.crypto.decryptWrappedContent(payload[encrypted], wrappings);
|
|
1260
|
-
if (plain !== null)
|
|
1261
|
-
result[decrypted] = plain;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
667
|
return result;
|
|
1266
668
|
}
|
|
1267
669
|
// -- Crypto -----------------------------------------------------------------
|
|
@@ -1277,47 +679,3 @@ export class HiloopClient {
|
|
|
1277
679
|
return decryptAes(ciphertextBase64, keyBase64);
|
|
1278
680
|
}
|
|
1279
681
|
}
|
|
1280
|
-
/**
|
|
1281
|
-
* Validate a form schema JSON string before encrypting.
|
|
1282
|
-
* - Must be valid JSON with a non-empty `fields` array.
|
|
1283
|
-
* - select / multi-select / radio fields must have at least 2 options.
|
|
1284
|
-
*/
|
|
1285
|
-
function validateFormSchema(schemaJson) {
|
|
1286
|
-
if (schemaJson === undefined || schemaJson.trim() === "") {
|
|
1287
|
-
throw new Error("formSchema must not be empty.");
|
|
1288
|
-
}
|
|
1289
|
-
let parsed;
|
|
1290
|
-
try {
|
|
1291
|
-
parsed = JSON.parse(schemaJson);
|
|
1292
|
-
}
|
|
1293
|
-
catch {
|
|
1294
|
-
throw new Error("formSchema must be valid JSON.");
|
|
1295
|
-
}
|
|
1296
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1297
|
-
throw new Error("formSchema must be a JSON object.");
|
|
1298
|
-
}
|
|
1299
|
-
const schema = parsed;
|
|
1300
|
-
const fields = schema["fields"];
|
|
1301
|
-
if (!Array.isArray(fields) || fields.length === 0) {
|
|
1302
|
-
throw new Error("formSchema must contain a non-empty 'fields' array. " +
|
|
1303
|
-
"A form requires at least one input field.");
|
|
1304
|
-
}
|
|
1305
|
-
for (const field of fields) {
|
|
1306
|
-
if (typeof field !== "object" || field === null)
|
|
1307
|
-
continue;
|
|
1308
|
-
const f = field;
|
|
1309
|
-
const type = f["type"];
|
|
1310
|
-
if (type === "select" || type === "multi-select" || type === "radio") {
|
|
1311
|
-
const options = f["options"];
|
|
1312
|
-
if (!Array.isArray(options) || options.length < 2) {
|
|
1313
|
-
const name = f["name"] ?? "(unnamed)";
|
|
1314
|
-
throw new Error(`Form field '${name}' of type '${type}' must have at least 2 options.`);
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
/**
|
|
1320
|
-
* Return all wrappings as-is. Each encrypted field has its own random content
|
|
1321
|
-
* key, so every wrapping is unique even when scope + recipientKeyId match.
|
|
1322
|
-
* The frontend tries each wrapping until one successfully decrypts.
|
|
1323
|
-
*/
|