@andocorp/sdk 0.0.1 → 0.2.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/agent-endpoints.d.ts +89 -0
  4. package/dist/agent-endpoints.d.ts.map +1 -0
  5. package/dist/agent-endpoints.js +229 -0
  6. package/dist/agent-endpoints.js.map +1 -0
  7. package/dist/agent-types.d.ts +81 -0
  8. package/dist/agent-types.d.ts.map +1 -0
  9. package/dist/agent-types.js +3 -0
  10. package/dist/agent-types.js.map +1 -0
  11. package/dist/client.d.ts +133 -10
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/client.js +525 -57
  14. package/dist/client.js.map +1 -1
  15. package/dist/generated/contracts.d.ts +326 -11
  16. package/dist/generated/contracts.d.ts.map +1 -1
  17. package/dist/generated/contracts.js +21 -1
  18. package/dist/generated/contracts.js.map +1 -1
  19. package/dist/generated/public-api.d.ts +856 -0
  20. package/dist/generated/public-api.d.ts.map +1 -0
  21. package/dist/generated/public-api.js +382 -0
  22. package/dist/generated/public-api.js.map +1 -0
  23. package/dist/index.d.ts +8 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +25 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/realtime-events.d.ts +9 -4
  28. package/dist/realtime-events.d.ts.map +1 -1
  29. package/dist/realtime-events.js +113 -157
  30. package/dist/realtime-events.js.map +1 -1
  31. package/dist/realtime-runtime.d.ts +6 -17
  32. package/dist/realtime-runtime.d.ts.map +1 -1
  33. package/dist/realtime-runtime.js +45 -62
  34. package/dist/realtime-runtime.js.map +1 -1
  35. package/dist/realtime-types.d.ts +60 -77
  36. package/dist/realtime-types.d.ts.map +1 -1
  37. package/dist/realtime-types.js +2 -0
  38. package/dist/realtime-types.js.map +1 -1
  39. package/dist/realtime.d.ts +6 -5
  40. package/dist/realtime.d.ts.map +1 -1
  41. package/dist/realtime.js +282 -144
  42. package/dist/realtime.js.map +1 -1
  43. package/dist/targets.d.ts.map +1 -1
  44. package/dist/targets.js.map +1 -1
  45. package/dist/types.d.ts +61 -20
  46. package/dist/types.d.ts.map +1 -1
  47. package/package.json +24 -16
package/dist/client.js CHANGED
@@ -1,15 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AndoClient = void 0;
3
+ exports.ANDO_PUBLIC_API_CLIENT_METHODS = exports.AndoClient = exports.AndoApiError = exports.DEFAULT_ANDO_REALTIME_HOST = exports.DEFAULT_ANDO_PUBLIC_API_BASE_URL = exports.DEFAULT_ANDO_BASE_URL = void 0;
4
+ exports.requestAndoPublicApi = requestAndoPublicApi;
5
+ exports.resolveAndoPublicApiBaseUrl = resolveAndoPublicApiBaseUrl;
6
+ const agent_endpoints_1 = require("./agent-endpoints");
7
+ exports.DEFAULT_ANDO_BASE_URL = "https://api.ando.so";
8
+ exports.DEFAULT_ANDO_PUBLIC_API_BASE_URL = "https://api.ando.so/v1";
9
+ exports.DEFAULT_ANDO_REALTIME_HOST = "realtime.ando.so";
4
10
  class AndoApiError extends Error {
5
- constructor(message, status) {
6
- super(message);
11
+ constructor(params) {
12
+ const detail = params.bodyText === "" ? "" : ` ${params.bodyText}`;
13
+ super(`[ando-sdk] ${params.method} ${params.path} failed: ${params.status}${detail}`);
14
+ this.body = params.body;
15
+ this.bodyText = params.bodyText;
16
+ this.code = params.code;
17
+ this.method = params.method;
18
+ this.path = params.path;
19
+ this.requestId = params.requestId;
7
20
  this.name = "AndoApiError";
8
- this.status = status;
21
+ this.status = params.status;
9
22
  }
10
23
  }
24
+ exports.AndoApiError = AndoApiError;
11
25
  const DEFAULT_HISTORY_LIMIT = 8;
12
26
  const DEFAULT_THREAD_HISTORY_LIMIT = 12;
27
+ const TRANSIENT_API_RETRY_DELAYS_MS = [100, 250];
13
28
  function toIsoString(value) {
14
29
  return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
15
30
  }
@@ -18,33 +33,271 @@ function isNullConversationMessageError(error) {
18
33
  error.message.includes("/conversation-messages/") &&
19
34
  error.message.includes("returned null"));
20
35
  }
36
+ function isOptionalContextHistoryError(error) {
37
+ return (error instanceof AndoApiError &&
38
+ (error.status === 404 ||
39
+ error.status === 410 ||
40
+ error.status === 422 ||
41
+ error.status >= 500));
42
+ }
43
+ function isRequestRetryable(method, headers) {
44
+ if (method === "GET" || method === "HEAD") {
45
+ return true;
46
+ }
47
+ return headers?.["Idempotency-Key"] != null;
48
+ }
49
+ function isOptimisticConcurrencyFailure(error) {
50
+ return (error.code === "OptimisticConcurrencyControlFailure" ||
51
+ error.bodyText.includes("OptimisticConcurrencyControlFailure"));
52
+ }
53
+ function isRetryableApiError(error) {
54
+ if (!(error instanceof AndoApiError)) {
55
+ return false;
56
+ }
57
+ if (isOptimisticConcurrencyFailure(error)) {
58
+ return error.status === 409 || error.status >= 500;
59
+ }
60
+ return error.status === 429 || error.status >= 500;
61
+ }
62
+ function isRetryableNetworkError(error) {
63
+ return error instanceof TypeError && error.message === "fetch failed";
64
+ }
65
+ function isRetryableRequestError(error) {
66
+ return isRetryableApiError(error) || isRetryableNetworkError(error);
67
+ }
68
+ function createAbortError(signal) {
69
+ if (signal.reason instanceof Error) {
70
+ return signal.reason;
71
+ }
72
+ const error = new Error("[ando-sdk] request aborted");
73
+ error.name = "AbortError";
74
+ return error;
75
+ }
76
+ async function waitForRetryDelay(ms, signal) {
77
+ if (signal?.aborted) {
78
+ throw createAbortError(signal);
79
+ }
80
+ await new Promise((resolve, reject) => {
81
+ const timeout = setTimeout(() => {
82
+ cleanup();
83
+ resolve();
84
+ }, ms);
85
+ const abort = () => {
86
+ cleanup();
87
+ reject(createAbortError(signal));
88
+ };
89
+ const cleanup = () => {
90
+ clearTimeout(timeout);
91
+ signal?.removeEventListener("abort", abort);
92
+ };
93
+ signal?.addEventListener("abort", abort, { once: true });
94
+ });
95
+ }
96
+ function createJsonRequestInit(params) {
97
+ const requestInit = {
98
+ method: params.method,
99
+ headers: {
100
+ Authorization: params.authHeaderValue,
101
+ "Content-Type": "application/json",
102
+ ...params.headers,
103
+ },
104
+ };
105
+ if (params.signal != null) {
106
+ requestInit.signal = params.signal;
107
+ }
108
+ if (params.body != null) {
109
+ requestInit.body = JSON.stringify(params.body);
110
+ }
111
+ return requestInit;
112
+ }
113
+ function parseResponseBody(bodyText) {
114
+ if (bodyText.trim() === "") {
115
+ return null;
116
+ }
117
+ const trimmedBody = bodyText.trim();
118
+ if (trimmedBody.startsWith("{") || trimmedBody.startsWith("[")) {
119
+ try {
120
+ return JSON.parse(bodyText);
121
+ }
122
+ catch {
123
+ return bodyText;
124
+ }
125
+ }
126
+ return bodyText;
127
+ }
128
+ async function requestAndoPublicApi(params) {
129
+ const fetchImpl = params.fetchImpl ?? fetch;
130
+ const response = await fetchImpl(params.url, createJsonRequestInit({
131
+ authHeaderValue: `Bearer ${params.apiKey}`,
132
+ body: params.body,
133
+ headers: {
134
+ Accept: "application/json",
135
+ ...params.headers,
136
+ },
137
+ method: params.method,
138
+ signal: params.signal,
139
+ }));
140
+ const bodyText = await response.text();
141
+ return {
142
+ body: parseResponseBody(bodyText),
143
+ bodyText,
144
+ empty: bodyText.trim() === "",
145
+ ok: response.ok,
146
+ status: response.status,
147
+ statusText: response.statusText,
148
+ };
149
+ }
21
150
  function isDefined(value) {
22
151
  return value != null;
23
152
  }
153
+ function stripTrailingSlashes(value) {
154
+ let end = value.length;
155
+ while (end > 0 && value[end - 1] === "/") {
156
+ end -= 1;
157
+ }
158
+ return value.slice(0, end);
159
+ }
160
+ function normalizeConfiguredHost(value, fallback) {
161
+ return value == null || value === ""
162
+ ? fallback
163
+ : stripTrailingSlashes(value) || fallback;
164
+ }
165
+ function createIdempotencyKey(operation) {
166
+ const random = globalThis.crypto.randomUUID();
167
+ return `ando-sdk-${operation}-${random}`;
168
+ }
169
+ function requireIdempotencyKey(operation, idempotencyKey) {
170
+ const resolved = idempotencyKey ?? createIdempotencyKey(operation);
171
+ if (resolved.trim() === "") {
172
+ throw new Error(`[ando-sdk] ${operation} idempotency key cannot be empty`);
173
+ }
174
+ if (resolved.length > 255) {
175
+ throw new Error(`[ando-sdk] ${operation} idempotency key must be 255 characters or fewer`);
176
+ }
177
+ return resolved;
178
+ }
179
+ function parseErrorBody(bodyText) {
180
+ if (bodyText.trim() === "") {
181
+ return null;
182
+ }
183
+ try {
184
+ return JSON.parse(bodyText);
185
+ }
186
+ catch {
187
+ return bodyText;
188
+ }
189
+ }
190
+ function getErrorDetails(body) {
191
+ if (body == null || typeof body !== "object" || !("error" in body)) {
192
+ return {};
193
+ }
194
+ const error = body.error;
195
+ if (typeof error === "string") {
196
+ const code = body.error_code;
197
+ return typeof code === "string" ? { code } : {};
198
+ }
199
+ if (error == null || typeof error !== "object") {
200
+ return {};
201
+ }
202
+ const code = error.code;
203
+ const requestId = error.request_id;
204
+ return {
205
+ ...(typeof code === "string" ? { code } : {}),
206
+ ...(typeof requestId === "string" ? { requestId } : {}),
207
+ };
208
+ }
209
+ function emitDiagnostic(diagnostics, event) {
210
+ try {
211
+ const result = diagnostics?.(event);
212
+ if (result != null) {
213
+ result.then(() => undefined, () => undefined);
214
+ }
215
+ }
216
+ catch {
217
+ // Diagnostics should never affect SDK behavior.
218
+ }
219
+ }
220
+ function createRequestSignal(params) {
221
+ if (params.timeoutMs == null || params.timeoutMs <= 0) {
222
+ return {
223
+ signal: params.signal,
224
+ cleanup: () => undefined,
225
+ };
226
+ }
227
+ const controller = new AbortController();
228
+ let timeout = setTimeout(() => {
229
+ controller.abort(new Error(params.timeoutMessage));
230
+ }, params.timeoutMs);
231
+ const abortFromCaller = () => {
232
+ controller.abort(params.signal?.reason);
233
+ };
234
+ if (params.signal?.aborted) {
235
+ abortFromCaller();
236
+ }
237
+ else {
238
+ params.signal?.addEventListener("abort", abortFromCaller, { once: true });
239
+ }
240
+ return {
241
+ signal: controller.signal,
242
+ cleanup: () => {
243
+ if (timeout != null) {
244
+ clearTimeout(timeout);
245
+ timeout = undefined;
246
+ }
247
+ params.signal?.removeEventListener("abort", abortFromCaller);
248
+ },
249
+ };
250
+ }
24
251
  function buildMessageText(message) {
25
252
  // Context generation prefers one canonical body plus lightweight
26
253
  // placeholders for non-text inputs.
27
- const body = message.markdown_content?.trim();
254
+ const body = (message.markdown_content ?? message.content)?.trim();
28
255
  if (body) {
29
256
  return body;
30
257
  }
31
258
  const imageCount = message.image_urls?.length ?? 0;
32
259
  if (imageCount > 0) {
33
- return imageCount === 1
34
- ? "[image attachment]"
35
- : `[${imageCount} image attachments]`;
260
+ return imageCount === 1 ? "[image attachment]" : `[${imageCount} image attachments]`;
36
261
  }
37
262
  const fileCount = message.files?.length ?? 0;
38
263
  if (fileCount > 0) {
39
- return fileCount === 1
40
- ? "[file attachment]"
41
- : `[${fileCount} file attachments]`;
264
+ return fileCount === 1 ? "[file attachment]" : `[${fileCount} file attachments]`;
42
265
  }
43
266
  if (message.call_id != null) {
44
267
  return "[Jam started]";
45
268
  }
46
269
  return null;
47
270
  }
271
+ function resolveAndoPublicApiBaseUrl(apiHost) {
272
+ const url = new URL(apiHost);
273
+ url.search = "";
274
+ url.hash = "";
275
+ const trimmedPath = url.pathname.replace(/\/+$/, "");
276
+ if (trimmedPath.endsWith("/v1") || trimmedPath.endsWith("/api/v1")) {
277
+ url.pathname = trimmedPath;
278
+ return url.toString().replace(/\/+$/, "");
279
+ }
280
+ if (url.hostname === "api.ando.so") {
281
+ url.pathname = "/v1";
282
+ return url.toString().replace(/\/+$/, "");
283
+ }
284
+ url.pathname = `${trimmedPath === "" ? "" : trimmedPath}/v1`;
285
+ return url.toString().replace(/\/+$/, "");
286
+ }
287
+ function publicApiPathForConfiguredBase(baseUrl, publicPath) {
288
+ const basePath = new URL(baseUrl).pathname.replace(/\/+$/, "");
289
+ if (publicPath.startsWith("/v1/") &&
290
+ (basePath.endsWith("/v1") || basePath.endsWith("/api/v1"))) {
291
+ return publicPath.slice("/v1".length);
292
+ }
293
+ return publicPath;
294
+ }
295
+ function messageAuthorId(message) {
296
+ return message.author?.id ?? message.author_id ?? "unknown";
297
+ }
298
+ function messageAuthorName(message) {
299
+ return message.author?.display_name ?? message.author_name ?? null;
300
+ }
48
301
  function buildHistoryEntry(message) {
49
302
  const body = buildMessageText(message);
50
303
  if (!body) {
@@ -52,8 +305,8 @@ function buildHistoryEntry(message) {
52
305
  }
53
306
  return {
54
307
  message_id: message.id,
55
- author_id: message.author.id,
56
- author_name: message.author.display_name,
308
+ author_id: messageAuthorId(message),
309
+ author_name: messageAuthorName(message),
57
310
  body,
58
311
  created_at: toIsoString(message.created_at),
59
312
  };
@@ -65,57 +318,127 @@ function joinBodies(messages) {
65
318
  if (!body) {
66
319
  return null;
67
320
  }
68
- return `${message.author.display_name ?? message.author.id}: ${body}`;
321
+ return `${messageAuthorName(message) ?? messageAuthorId(message)}: ${body}`;
69
322
  })
70
323
  .filter(Boolean);
71
324
  return lines.length > 0 ? lines.join("\n\n") : null;
72
325
  }
73
326
  class AndoClient {
74
327
  constructor(options) {
75
- this.baseUrl = options.baseUrl.replace(/\/+$/, "");
76
- this.realtimeHost = options.realtimeHost?.replace(/\/+$/, "") ?? null;
328
+ this.baseUrl = normalizeConfiguredHost(options.baseUrl, exports.DEFAULT_ANDO_BASE_URL);
329
+ this.realtimeHost = normalizeConfiguredHost(options.realtimeHost, exports.DEFAULT_ANDO_REALTIME_HOST);
77
330
  this.authHeaderValue = `Bearer ${options.auth.apiKey}`;
331
+ this.diagnostics = options.diagnostics;
78
332
  this.fetchImpl = options.fetch ?? fetch;
333
+ this.requestTimeoutMs = options.requestTimeoutMs;
79
334
  this.realtime = {
80
335
  subscribeMember: async (realtimeOptions) => {
81
- if (!this.realtimeHost) {
82
- throw new Error("[ando-sdk] realtimeHost is required for realtime subscriptions");
83
- }
84
- const { AndoRealtimeClient } = await import("./realtime.js");
85
- const realtimeClient = new AndoRealtimeClient({
86
- realtimeHost: this.realtimeHost,
87
- getMe: (signal) => this.getMe(signal),
88
- getMemberGroupIdByMemberId: (memberId, signal) => this.getMemberGroupIdByMemberId(memberId, signal),
89
- getMyMemberships: (params) => this.getMyMemberships(params),
90
- createWebSocketToken: (room, signal) => this.createWebSocketToken(room, signal),
91
- getMessageContext: (messageId, signal) => this.getMessageContext(messageId, signal),
336
+ const realtimeClient = await this.createRealtimeClient();
337
+ return realtimeClient.subscribeMember({
338
+ ...realtimeOptions,
339
+ diagnostics: realtimeOptions?.diagnostics ?? this.diagnostics,
340
+ });
341
+ },
342
+ on: async (event, realtimeOptions) => {
343
+ const realtimeClient = await this.createRealtimeClient();
344
+ return realtimeClient.on(event, {
345
+ ...realtimeOptions,
346
+ diagnostics: realtimeOptions?.diagnostics ?? this.diagnostics,
92
347
  });
93
- return realtimeClient.subscribeMember(realtimeOptions);
94
348
  },
95
349
  };
96
350
  }
351
+ async createRealtimeClient() {
352
+ const { AndoRealtimeClient } = await import("./realtime.js");
353
+ return new AndoRealtimeClient({
354
+ diagnostics: this.diagnostics,
355
+ openRealtimeConnection: (request, signal) => this.openRealtimeConnection(request, signal),
356
+ getMessageContext: (messageId, signal, contextOptions) => this.getMessageContext(messageId, signal, contextOptions),
357
+ getConversation: (conversationId, signal) => this.getConversation(conversationId, signal),
358
+ });
359
+ }
97
360
  async fetchJson(path, options = {}) {
98
361
  const url = new URL(`${this.baseUrl}${path}`);
362
+ const method = options.method ?? "GET";
99
363
  for (const [key, value] of Object.entries(options.query ?? {})) {
100
364
  if (value == null || value === "") {
101
365
  continue;
102
366
  }
103
367
  url.searchParams.set(key, String(value));
104
368
  }
105
- const response = await this.fetchImpl(url, {
106
- method: options.method ?? "GET",
369
+ const requestSignal = createRequestSignal({
107
370
  signal: options.signal,
108
- headers: {
109
- Authorization: this.authHeaderValue,
110
- "Content-Type": "application/json",
111
- },
112
- body: options.body ? JSON.stringify(options.body) : undefined,
371
+ timeoutMs: this.requestTimeoutMs,
372
+ timeoutMessage: `[ando-sdk] ${method} ${path} timed out after ${this.requestTimeoutMs}ms`,
113
373
  });
114
- if (!response.ok) {
115
- const body = await response.text().catch(() => "");
116
- throw new AndoApiError(`[ando-sdk] ${options.method ?? "GET"} ${path} failed: ${response.status} ${body}`, response.status);
374
+ const canRetry = isRequestRetryable(method, options.headers);
375
+ const startedAt = Date.now();
376
+ try {
377
+ for (let attempt = 0;; attempt += 1) {
378
+ let status;
379
+ try {
380
+ const requestInit = createJsonRequestInit({
381
+ authHeaderValue: this.authHeaderValue,
382
+ body: options.body,
383
+ headers: options.headers,
384
+ method,
385
+ signal: requestSignal.signal,
386
+ });
387
+ const response = await this.fetchImpl(url, requestInit);
388
+ status = response.status;
389
+ if (!response.ok) {
390
+ const bodyText = await response.text().catch(() => "");
391
+ const body = parseErrorBody(bodyText);
392
+ const details = getErrorDetails(body);
393
+ throw new AndoApiError({
394
+ body,
395
+ bodyText,
396
+ code: details.code,
397
+ method,
398
+ path,
399
+ requestId: details.requestId,
400
+ status: response.status,
401
+ });
402
+ }
403
+ const data = (await response.json());
404
+ emitDiagnostic(this.diagnostics, {
405
+ type: "http",
406
+ method,
407
+ path,
408
+ status,
409
+ ok: true,
410
+ durationMs: Date.now() - startedAt,
411
+ });
412
+ return data;
413
+ }
414
+ catch (error) {
415
+ const retryDelay = TRANSIENT_API_RETRY_DELAYS_MS[attempt];
416
+ if (canRetry &&
417
+ retryDelay != null &&
418
+ !requestSignal.signal?.aborted &&
419
+ isRetryableRequestError(error)) {
420
+ await waitForRetryDelay(retryDelay, requestSignal.signal);
421
+ continue;
422
+ }
423
+ emitDiagnostic(this.diagnostics, {
424
+ type: "http",
425
+ method,
426
+ path,
427
+ status,
428
+ ok: false,
429
+ durationMs: Date.now() - startedAt,
430
+ error,
431
+ });
432
+ throw error;
433
+ }
434
+ }
117
435
  }
118
- return (await response.json());
436
+ finally {
437
+ requestSignal.cleanup();
438
+ }
439
+ }
440
+ fetchPublicJson(path, options = {}) {
441
+ return this.fetchJson(publicApiPathForConfiguredBase(this.baseUrl, path), options);
119
442
  }
120
443
  encodeMessageCursor(message) {
121
444
  const cursor = {
@@ -131,6 +454,73 @@ class AndoClient {
131
454
  }
132
455
  return data;
133
456
  }
457
+ async searchMessages(params, signal) {
458
+ return (0, agent_endpoints_1.fetchSearchMessages)((path, options) => this.fetchPublicJson(path, options), params, signal);
459
+ }
460
+ async searchMembers(params, signal) {
461
+ return (0, agent_endpoints_1.fetchSearchMembers)((path, options) => this.fetchPublicJson(path, options), params, signal);
462
+ }
463
+ async searchConversations(params, signal) {
464
+ return (0, agent_endpoints_1.fetchSearchConversations)((path, options) => this.fetchPublicJson(path, options), params, signal);
465
+ }
466
+ async searchClipboard(params, signal) {
467
+ return (0, agent_endpoints_1.fetchSearchClipboard)((path, options) => this.fetchPublicJson(path, options), params, signal);
468
+ }
469
+ async searchClipboards(params, signal) {
470
+ return this.searchClipboard(params, signal);
471
+ }
472
+ async searchCalls(params, signal) {
473
+ return (0, agent_endpoints_1.fetchSearchCalls)((path, options) => this.fetchPublicJson(path, options), params, signal);
474
+ }
475
+ async searchTasks(params, signal) {
476
+ return (0, agent_endpoints_1.fetchSearchTasks)((path, options) => this.fetchPublicJson(path, options), params, signal);
477
+ }
478
+ async getConversationMessageResult(messageId, signal) {
479
+ return (0, agent_endpoints_1.fetchConversationMessageResult)((path, options) => this.fetchPublicJson(path, options), messageId, signal);
480
+ }
481
+ async getMember(memberId, signal) {
482
+ return (0, agent_endpoints_1.fetchMember)((path, options) => this.fetchPublicJson(path, options), memberId, signal);
483
+ }
484
+ async getClipboard(clipboardId, signal) {
485
+ return (0, agent_endpoints_1.fetchClipboard)((path, options) => this.fetchPublicJson(path, options), clipboardId, signal);
486
+ }
487
+ async getCall(callId, signal) {
488
+ return (0, agent_endpoints_1.fetchCall)((path, options) => this.fetchPublicJson(path, options), callId, signal);
489
+ }
490
+ async getCallTranscript(params, signal) {
491
+ return (0, agent_endpoints_1.fetchCallTranscript)((path, options) => this.fetchPublicJson(path, options), params, signal);
492
+ }
493
+ async getConversationMessageResults(params, signal) {
494
+ return (0, agent_endpoints_1.fetchConversationMessageResults)((path, options) => this.fetchPublicJson(path, options), params, signal);
495
+ }
496
+ async listConversationMessages(params, signal) {
497
+ return this.getConversationMessageResults(params, signal);
498
+ }
499
+ async listThreadReplyResults(params, signal) {
500
+ return (0, agent_endpoints_1.fetchThreadReplyResults)((path, options) => this.fetchPublicJson(path, options), params, signal);
501
+ }
502
+ async listThreadReplies(params, signal) {
503
+ return this.listThreadReplyResults(params, signal);
504
+ }
505
+ async recordTaskUpdate(params, signal) {
506
+ return (0, agent_endpoints_1.fetchRecordTaskUpdate)((path, options) => this.fetchPublicJson(path, options), {
507
+ ...params,
508
+ idempotencyKey: requireIdempotencyKey("recordTaskUpdate", params.idempotencyKey),
509
+ }, signal);
510
+ }
511
+ async getTask(taskId, signal) {
512
+ return (0, agent_endpoints_1.fetchTask)((path, options) => this.fetchPublicJson(path, options), taskId, signal);
513
+ }
514
+ async createConversationMessage(input, signal) {
515
+ const response = await (0, agent_endpoints_1.fetchCreateConversationMessage)((path, options) => this.fetchPublicJson(path, options), {
516
+ input,
517
+ idempotencyKey: requireIdempotencyKey("createConversationMessage", input.idempotencyKey),
518
+ }, signal);
519
+ if (!response.success) {
520
+ throw new Error("[ando-sdk] createConversationMessage returned an empty response");
521
+ }
522
+ return response.data;
523
+ }
134
524
  async getOptionalConversationMessage(messageId, signal) {
135
525
  try {
136
526
  return await this.getConversationMessage(messageId, signal);
@@ -165,6 +555,40 @@ class AndoClient {
165
555
  },
166
556
  });
167
557
  }
558
+ async getOptionalConversationMessages(conversationId, query, signal) {
559
+ try {
560
+ return await this.getConversationMessages(conversationId, query, signal);
561
+ }
562
+ catch (error) {
563
+ if (isOptionalContextHistoryError(error)) {
564
+ return {
565
+ items: [],
566
+ pageInfo: {
567
+ hasNextPage: false,
568
+ hasPreviousPage: false,
569
+ },
570
+ };
571
+ }
572
+ throw error;
573
+ }
574
+ }
575
+ async getOptionalThreadReplies(messageId, query, signal) {
576
+ try {
577
+ return await this.getThreadReplies(messageId, query, signal);
578
+ }
579
+ catch (error) {
580
+ if (isOptionalContextHistoryError(error)) {
581
+ return {
582
+ items: [],
583
+ pageInfo: {
584
+ hasNextPage: false,
585
+ hasPreviousPage: false,
586
+ },
587
+ };
588
+ }
589
+ throw error;
590
+ }
591
+ }
168
592
  async getMe(signal) {
169
593
  const { data } = await this.fetchJson("/auth/me", {
170
594
  signal,
@@ -183,15 +607,31 @@ class AndoClient {
183
607
  },
184
608
  });
185
609
  }
186
- async getMessageContext(messageId, signal) {
610
+ async getConversation(conversationId, signal) {
611
+ let cursor;
612
+ do {
613
+ const response = await this.getMyMemberships({
614
+ cursor,
615
+ limit: 200,
616
+ signal,
617
+ });
618
+ const match = response.items.find((membership) => membership.conversation.id === conversationId);
619
+ if (match) {
620
+ return match.conversation;
621
+ }
622
+ cursor = response.pageInfo.hasNextPage ? response.pageInfo.nextCursor : undefined;
623
+ } while (cursor);
624
+ return null;
625
+ }
626
+ async getMessageContext(messageId, signal, options = {}) {
187
627
  const message = await this.getConversationMessage(messageId, signal);
188
628
  const currentText = buildMessageText(message);
189
- const isThreadReply = message.thread_root_id != null;
190
- const replyThreadRootId = message.thread_root_id ?? message.id;
629
+ const replyThreadRootId = options.threadRootId ?? message.thread_root_id ?? message.id;
630
+ const isThreadReply = replyThreadRootId !== message.id;
191
631
  if (isThreadReply) {
192
632
  const [threadRootMessage, threadReplyHistory] = await Promise.all([
193
633
  this.getOptionalConversationMessage(replyThreadRootId, signal),
194
- this.getThreadReplies(replyThreadRootId, {
634
+ this.getOptionalThreadReplies(replyThreadRootId, {
195
635
  limit: DEFAULT_THREAD_HISTORY_LIMIT,
196
636
  before: this.encodeMessageCursor(message),
197
637
  }, signal),
@@ -213,12 +653,12 @@ class AndoClient {
213
653
  const [conversationHistory, threadReplyHistory] = await Promise.all([
214
654
  // Conversation pagination is fetched newest-first under the hood and then
215
655
  // reversed for display, so `after` pages further back in history here.
216
- this.getConversationMessages(message.conversation_id, {
656
+ this.getOptionalConversationMessages(message.conversation_id, {
217
657
  limit: DEFAULT_HISTORY_LIMIT,
218
658
  after: this.encodeMessageCursor(message),
219
659
  }, signal),
220
660
  message.replies_count > 0
221
- ? this.getThreadReplies(message.id, {
661
+ ? this.getOptionalThreadReplies(message.id, {
222
662
  limit: DEFAULT_THREAD_HISTORY_LIMIT,
223
663
  }, signal)
224
664
  : Promise.resolve(null),
@@ -237,12 +677,23 @@ class AndoClient {
237
677
  : null,
238
678
  };
239
679
  }
680
+ async openRealtimeConnection(request, signal) {
681
+ return this.fetchJson(publicApiPathForConfiguredBase(this.baseUrl, "/v1/realtime/connections"), {
682
+ method: "POST",
683
+ signal,
684
+ body: request,
685
+ });
686
+ }
240
687
  async postMessage(input) {
241
688
  const response = await this.fetchJson(`/conversations/${input.conversationId}/messages`, {
242
689
  method: "POST",
690
+ headers: {
691
+ "Idempotency-Key": requireIdempotencyKey("postMessage", input.idempotencyKey),
692
+ },
243
693
  body: {
244
694
  markdown_content: input.markdownContent,
245
695
  thread_root_id: input.threadRootId ?? null,
696
+ replied_to_message_id: input.repliedToMessageId ?? null,
246
697
  call_root_id: input.callRootId ?? null,
247
698
  },
248
699
  });
@@ -251,6 +702,15 @@ class AndoClient {
251
702
  }
252
703
  return response.data;
253
704
  }
705
+ async addReaction(messageId, emojiText, signal) {
706
+ return this.fetchJson(`/conversation-messages/${encodeURIComponent(messageId)}/reactions`, {
707
+ method: "POST",
708
+ signal,
709
+ body: {
710
+ emoji_text: emojiText,
711
+ },
712
+ });
713
+ }
254
714
  async getMemberGroupIdByMemberId(memberId, signal) {
255
715
  let cursor;
256
716
  do {
@@ -265,21 +725,29 @@ class AndoClient {
265
725
  if (match) {
266
726
  return match.id;
267
727
  }
268
- cursor = response.pageInfo.hasNextPage
269
- ? response.pageInfo.nextCursor
270
- : undefined;
728
+ cursor = response.pageInfo.hasNextPage ? response.pageInfo.nextCursor : undefined;
271
729
  } while (cursor);
272
730
  return null;
273
731
  }
274
- async createWebSocketToken(room, signal) {
275
- return this.fetchJson("/tokens/ws", {
276
- method: "POST",
277
- signal,
278
- body: {
279
- room,
280
- },
281
- });
282
- }
283
732
  }
284
733
  exports.AndoClient = AndoClient;
734
+ exports.ANDO_PUBLIC_API_CLIENT_METHODS = {
735
+ createConversationMessage: "createConversationMessage",
736
+ getCall: "getCall",
737
+ getCallTranscript: "getCallTranscript",
738
+ getClipboard: "getClipboard",
739
+ getConversationMessage: "getConversationMessageResult",
740
+ getMember: "getMember",
741
+ getTask: "getTask",
742
+ listConversationMessages: "listConversationMessages",
743
+ listThreadReplies: "listThreadReplies",
744
+ openRealtimeConnection: "openRealtimeConnection",
745
+ recordTaskUpdate: "recordTaskUpdate",
746
+ searchCalls: "searchCalls",
747
+ searchClipboards: "searchClipboards",
748
+ searchConversations: "searchConversations",
749
+ searchMembers: "searchMembers",
750
+ searchMessages: "searchMessages",
751
+ searchTasks: "searchTasks",
752
+ };
285
753
  //# sourceMappingURL=client.js.map