@andocorp/sdk 0.1.0 → 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 (42) hide show
  1. package/README.md +26 -4
  2. package/dist/agent-endpoints.d.ts +89 -0
  3. package/dist/agent-endpoints.d.ts.map +1 -0
  4. package/dist/agent-endpoints.js +229 -0
  5. package/dist/agent-endpoints.js.map +1 -0
  6. package/dist/agent-types.d.ts +81 -0
  7. package/dist/agent-types.d.ts.map +1 -0
  8. package/dist/agent-types.js +3 -0
  9. package/dist/agent-types.js.map +1 -0
  10. package/dist/client.d.ts +131 -10
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +523 -57
  13. package/dist/client.js.map +1 -1
  14. package/dist/generated/public-api.d.ts +856 -0
  15. package/dist/generated/public-api.d.ts.map +1 -0
  16. package/dist/generated/public-api.js +382 -0
  17. package/dist/generated/public-api.js.map +1 -0
  18. package/dist/index.d.ts +8 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +23 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/realtime-events.d.ts +9 -4
  23. package/dist/realtime-events.d.ts.map +1 -1
  24. package/dist/realtime-events.js +113 -160
  25. package/dist/realtime-events.js.map +1 -1
  26. package/dist/realtime-runtime.d.ts +5 -17
  27. package/dist/realtime-runtime.d.ts.map +1 -1
  28. package/dist/realtime-runtime.js +41 -67
  29. package/dist/realtime-runtime.js.map +1 -1
  30. package/dist/realtime-types.d.ts +58 -77
  31. package/dist/realtime-types.d.ts.map +1 -1
  32. package/dist/realtime-types.js +2 -0
  33. package/dist/realtime-types.js.map +1 -1
  34. package/dist/realtime.d.ts +6 -5
  35. package/dist/realtime.d.ts.map +1 -1
  36. package/dist/realtime.js +281 -145
  37. package/dist/realtime.js.map +1 -1
  38. package/dist/targets.d.ts.map +1 -1
  39. package/dist/targets.js.map +1 -1
  40. package/dist/types.d.ts +61 -23
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +11 -6
package/dist/client.js CHANGED
@@ -1,17 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AndoClient = exports.DEFAULT_ANDO_REALTIME_HOST = exports.DEFAULT_ANDO_BASE_URL = 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");
4
7
  exports.DEFAULT_ANDO_BASE_URL = "https://api.ando.so";
5
- exports.DEFAULT_ANDO_REALTIME_HOST = "sokachu.ando.so";
8
+ exports.DEFAULT_ANDO_PUBLIC_API_BASE_URL = "https://api.ando.so/v1";
9
+ exports.DEFAULT_ANDO_REALTIME_HOST = "realtime.ando.so";
6
10
  class AndoApiError extends Error {
7
- constructor(message, status) {
8
- 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;
9
20
  this.name = "AndoApiError";
10
- this.status = status;
21
+ this.status = params.status;
11
22
  }
12
23
  }
24
+ exports.AndoApiError = AndoApiError;
13
25
  const DEFAULT_HISTORY_LIMIT = 8;
14
26
  const DEFAULT_THREAD_HISTORY_LIMIT = 12;
27
+ const TRANSIENT_API_RETRY_DELAYS_MS = [100, 250];
15
28
  function toIsoString(value) {
16
29
  return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
17
30
  }
@@ -20,33 +33,271 @@ function isNullConversationMessageError(error) {
20
33
  error.message.includes("/conversation-messages/") &&
21
34
  error.message.includes("returned null"));
22
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
+ }
23
150
  function isDefined(value) {
24
151
  return value != null;
25
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
+ }
26
251
  function buildMessageText(message) {
27
252
  // Context generation prefers one canonical body plus lightweight
28
253
  // placeholders for non-text inputs.
29
- const body = message.markdown_content?.trim();
254
+ const body = (message.markdown_content ?? message.content)?.trim();
30
255
  if (body) {
31
256
  return body;
32
257
  }
33
258
  const imageCount = message.image_urls?.length ?? 0;
34
259
  if (imageCount > 0) {
35
- return imageCount === 1
36
- ? "[image attachment]"
37
- : `[${imageCount} image attachments]`;
260
+ return imageCount === 1 ? "[image attachment]" : `[${imageCount} image attachments]`;
38
261
  }
39
262
  const fileCount = message.files?.length ?? 0;
40
263
  if (fileCount > 0) {
41
- return fileCount === 1
42
- ? "[file attachment]"
43
- : `[${fileCount} file attachments]`;
264
+ return fileCount === 1 ? "[file attachment]" : `[${fileCount} file attachments]`;
44
265
  }
45
266
  if (message.call_id != null) {
46
267
  return "[Jam started]";
47
268
  }
48
269
  return null;
49
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
+ }
50
301
  function buildHistoryEntry(message) {
51
302
  const body = buildMessageText(message);
52
303
  if (!body) {
@@ -54,8 +305,8 @@ function buildHistoryEntry(message) {
54
305
  }
55
306
  return {
56
307
  message_id: message.id,
57
- author_id: message.author.id,
58
- author_name: message.author.display_name,
308
+ author_id: messageAuthorId(message),
309
+ author_name: messageAuthorName(message),
59
310
  body,
60
311
  created_at: toIsoString(message.created_at),
61
312
  };
@@ -67,56 +318,127 @@ function joinBodies(messages) {
67
318
  if (!body) {
68
319
  return null;
69
320
  }
70
- return `${message.author.display_name ?? message.author.id}: ${body}`;
321
+ return `${messageAuthorName(message) ?? messageAuthorId(message)}: ${body}`;
71
322
  })
72
323
  .filter(Boolean);
73
324
  return lines.length > 0 ? lines.join("\n\n") : null;
74
325
  }
75
326
  class AndoClient {
76
327
  constructor(options) {
77
- this.baseUrl =
78
- options.baseUrl?.replace(/\/+$/, "") || exports.DEFAULT_ANDO_BASE_URL;
79
- this.realtimeHost =
80
- options.realtimeHost?.replace(/\/+$/, "") || exports.DEFAULT_ANDO_REALTIME_HOST;
328
+ this.baseUrl = normalizeConfiguredHost(options.baseUrl, exports.DEFAULT_ANDO_BASE_URL);
329
+ this.realtimeHost = normalizeConfiguredHost(options.realtimeHost, exports.DEFAULT_ANDO_REALTIME_HOST);
81
330
  this.authHeaderValue = `Bearer ${options.auth.apiKey}`;
331
+ this.diagnostics = options.diagnostics;
82
332
  this.fetchImpl = options.fetch ?? fetch;
333
+ this.requestTimeoutMs = options.requestTimeoutMs;
83
334
  this.realtime = {
84
335
  subscribeMember: async (realtimeOptions) => {
85
- const { AndoRealtimeClient } = await import("./realtime.js");
86
- const realtimeClient = new AndoRealtimeClient({
87
- realtimeHost: this.realtimeHost,
88
- getMe: (signal) => this.getMe(signal),
89
- getMemberGroupIdByMemberId: (memberId, signal) => this.getMemberGroupIdByMemberId(memberId, signal),
90
- getMyMemberships: (params) => this.getMyMemberships(params),
91
- createWebSocketToken: (room, signal) => this.createWebSocketToken(room, signal),
92
- 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,
93
347
  });
94
- return realtimeClient.subscribeMember(realtimeOptions);
95
348
  },
96
349
  };
97
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
+ }
98
360
  async fetchJson(path, options = {}) {
99
361
  const url = new URL(`${this.baseUrl}${path}`);
362
+ const method = options.method ?? "GET";
100
363
  for (const [key, value] of Object.entries(options.query ?? {})) {
101
364
  if (value == null || value === "") {
102
365
  continue;
103
366
  }
104
367
  url.searchParams.set(key, String(value));
105
368
  }
106
- const response = await this.fetchImpl(url, {
107
- method: options.method ?? "GET",
369
+ const requestSignal = createRequestSignal({
108
370
  signal: options.signal,
109
- headers: {
110
- Authorization: this.authHeaderValue,
111
- "Content-Type": "application/json",
112
- },
113
- body: options.body ? JSON.stringify(options.body) : undefined,
371
+ timeoutMs: this.requestTimeoutMs,
372
+ timeoutMessage: `[ando-sdk] ${method} ${path} timed out after ${this.requestTimeoutMs}ms`,
114
373
  });
115
- if (!response.ok) {
116
- const body = await response.text().catch(() => "");
117
- 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
+ }
435
+ }
436
+ finally {
437
+ requestSignal.cleanup();
118
438
  }
119
- return (await response.json());
439
+ }
440
+ fetchPublicJson(path, options = {}) {
441
+ return this.fetchJson(publicApiPathForConfiguredBase(this.baseUrl, path), options);
120
442
  }
121
443
  encodeMessageCursor(message) {
122
444
  const cursor = {
@@ -132,6 +454,73 @@ class AndoClient {
132
454
  }
133
455
  return data;
134
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
+ }
135
524
  async getOptionalConversationMessage(messageId, signal) {
136
525
  try {
137
526
  return await this.getConversationMessage(messageId, signal);
@@ -166,6 +555,40 @@ class AndoClient {
166
555
  },
167
556
  });
168
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
+ }
169
592
  async getMe(signal) {
170
593
  const { data } = await this.fetchJson("/auth/me", {
171
594
  signal,
@@ -184,15 +607,31 @@ class AndoClient {
184
607
  },
185
608
  });
186
609
  }
187
- 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 = {}) {
188
627
  const message = await this.getConversationMessage(messageId, signal);
189
628
  const currentText = buildMessageText(message);
190
- const isThreadReply = message.thread_root_id != null;
191
- 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;
192
631
  if (isThreadReply) {
193
632
  const [threadRootMessage, threadReplyHistory] = await Promise.all([
194
633
  this.getOptionalConversationMessage(replyThreadRootId, signal),
195
- this.getThreadReplies(replyThreadRootId, {
634
+ this.getOptionalThreadReplies(replyThreadRootId, {
196
635
  limit: DEFAULT_THREAD_HISTORY_LIMIT,
197
636
  before: this.encodeMessageCursor(message),
198
637
  }, signal),
@@ -214,12 +653,12 @@ class AndoClient {
214
653
  const [conversationHistory, threadReplyHistory] = await Promise.all([
215
654
  // Conversation pagination is fetched newest-first under the hood and then
216
655
  // reversed for display, so `after` pages further back in history here.
217
- this.getConversationMessages(message.conversation_id, {
656
+ this.getOptionalConversationMessages(message.conversation_id, {
218
657
  limit: DEFAULT_HISTORY_LIMIT,
219
658
  after: this.encodeMessageCursor(message),
220
659
  }, signal),
221
660
  message.replies_count > 0
222
- ? this.getThreadReplies(message.id, {
661
+ ? this.getOptionalThreadReplies(message.id, {
223
662
  limit: DEFAULT_THREAD_HISTORY_LIMIT,
224
663
  }, signal)
225
664
  : Promise.resolve(null),
@@ -238,9 +677,19 @@ class AndoClient {
238
677
  : null,
239
678
  };
240
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
+ }
241
687
  async postMessage(input) {
242
688
  const response = await this.fetchJson(`/conversations/${input.conversationId}/messages`, {
243
689
  method: "POST",
690
+ headers: {
691
+ "Idempotency-Key": requireIdempotencyKey("postMessage", input.idempotencyKey),
692
+ },
244
693
  body: {
245
694
  markdown_content: input.markdownContent,
246
695
  thread_root_id: input.threadRootId ?? null,
@@ -253,6 +702,15 @@ class AndoClient {
253
702
  }
254
703
  return response.data;
255
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
+ }
256
714
  async getMemberGroupIdByMemberId(memberId, signal) {
257
715
  let cursor;
258
716
  do {
@@ -267,21 +725,29 @@ class AndoClient {
267
725
  if (match) {
268
726
  return match.id;
269
727
  }
270
- cursor = response.pageInfo.hasNextPage
271
- ? response.pageInfo.nextCursor
272
- : undefined;
728
+ cursor = response.pageInfo.hasNextPage ? response.pageInfo.nextCursor : undefined;
273
729
  } while (cursor);
274
730
  return null;
275
731
  }
276
- async createWebSocketToken(room, signal) {
277
- return this.fetchJson("/tokens/ws", {
278
- method: "POST",
279
- signal,
280
- body: {
281
- room,
282
- },
283
- });
284
- }
285
732
  }
286
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
+ };
287
753
  //# sourceMappingURL=client.js.map