@alfe.ai/agent-api-client 0.14.0 → 0.16.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/index.js CHANGED
@@ -19,7 +19,14 @@ function firstFrame(err) {
19
19
  return frame ? ` (${frame.trim()})` : "";
20
20
  }
21
21
  function buildLine(plugin, tool, kind, message, frame = "") {
22
- return `[ERROR] alfe-tool plugin=${plugin} tool=${tool} ${kind}: ${message.replace(/\s+/g, " ").trim()}${frame}`.slice(0, 480);
22
+ const safeToken = (value) => value.replace(/[^A-Za-z0-9_.@/-]+/g, "_").slice(0, 80);
23
+ const stripControls = (value) => Array.from(value, (character) => {
24
+ const code = character.charCodeAt(0);
25
+ return code < 32 || code >= 127 && code <= 159 ? " " : character;
26
+ }).join("");
27
+ const oneLine = stripControls(message).replace(/\s+/g, " ").trim();
28
+ const safeFrame = stripControls(frame).replace(/\s+/g, " ");
29
+ return `[ERROR] alfe-tool plugin=${safeToken(plugin)} tool=${safeToken(tool)} ${kind}: ${oneLine}${safeFrame}`.slice(0, 480);
23
30
  }
24
31
  function wrapExecute(tool, opts) {
25
32
  const execute = tool.execute;
@@ -56,7 +63,6 @@ function installToolErrorCapture(api, options) {
56
63
  try {
57
64
  const markedApi = api;
58
65
  if (markedApi[INSTALLED_MARKER]) return;
59
- markedApi[INSTALLED_MARKER] = true;
60
66
  const emit = options.emit ?? ((line) => {
61
67
  process.stderr.write(`${line}\n`);
62
68
  });
@@ -65,24 +71,29 @@ function installToolErrorCapture(api, options) {
65
71
  emit
66
72
  };
67
73
  const original = api.registerTool.bind(api);
68
- api.registerTool = (...args) => {
74
+ const wrappedRegisterTool = (...args) => {
75
+ let preparedArgs = args;
69
76
  try {
70
77
  const [first, ...rest] = args;
71
78
  if (typeof first === "function") {
72
79
  const factory = first;
73
80
  const wrappedFactory = (...fa) => {
74
81
  const tool = factory(...fa);
75
- if (typeof tool === "object" && tool !== null) wrapExecute(tool, opts);
82
+ if (typeof tool === "object" && tool !== null) try {
83
+ wrapExecute(tool, opts);
84
+ } catch {}
76
85
  return tool;
77
86
  };
78
- return original(wrappedFactory, ...rest);
87
+ preparedArgs = [wrappedFactory, ...rest];
79
88
  }
80
89
  if (typeof first === "object" && first !== null) wrapExecute(first, opts);
81
- return original(first, ...rest);
82
90
  } catch {
83
- return original(...args);
91
+ preparedArgs = args;
84
92
  }
93
+ return original(...preparedArgs);
85
94
  };
95
+ api.registerTool = wrappedRegisterTool;
96
+ markedApi[INSTALLED_MARKER] = true;
86
97
  } catch {}
87
98
  }
88
99
  //#endregion
@@ -121,6 +132,10 @@ const RETRYABLE_STATUS = new Set([
121
132
  504
122
133
  ]);
123
134
  const RETRY_DELAY_MS = 500;
135
+ function isSafeRetryMethod(method) {
136
+ const normalized = (method ?? "GET").toUpperCase();
137
+ return normalized === "GET" || normalized === "HEAD" || normalized === "OPTIONS";
138
+ }
124
139
  function sleep(ms) {
125
140
  return new Promise((resolve) => {
126
141
  setTimeout(resolve, ms);
@@ -132,6 +147,12 @@ function isRetryableNetworkError(err) {
132
147
  if (err.name === "TypeError") return true;
133
148
  return false;
134
149
  }
150
+ /** Whether a completed request may be retried/polled without masking a real client error. */
151
+ function isTransientRequestError(err) {
152
+ if (isRetryableNetworkError(err)) return true;
153
+ const status = err?.status;
154
+ return typeof status === "number" && RETRYABLE_STATUS.has(status);
155
+ }
135
156
  var AgentApiTransport = class {
136
157
  apiKey;
137
158
  apiUrl;
@@ -144,16 +165,17 @@ var AgentApiTransport = class {
144
165
  * `Content-Type: application/json` and parses a `{ data: T }` envelope,
145
166
  * neither of which fits a raw-audio flow (voice TTS/STT), so those go
146
167
  * through this instead. Auth (Bearer), the request budget, and the single
147
- * retry on transient 5xx / network errors are kept in sync with
148
- * `request()`. Retries fire only on statuses produced BEFORE the route
149
- * handler runs (authorizer-timeout 500 + LB 502/503/504), so re-issuing a
150
- * POST does not risk a duplicate side effect.
168
+ * retry policy on transient 5xx / network errors is kept in sync with
169
+ * `request()`. Safe read methods retry once by default; mutation methods do
170
+ * not, because a response can be lost after a handler or provider call has
171
+ * already succeeded.
151
172
  */
152
- async rawRequest(path, init) {
173
+ async rawRequest(path, init, extra) {
153
174
  const url = `${this.apiUrl}${path}`;
154
175
  init.headers.set("Authorization", `Bearer ${this.apiKey}`);
176
+ const maxAttempts = extra?.retry ?? isSafeRetryMethod(init.method) ? 2 : 1;
155
177
  let lastError;
156
- for (let attempt = 1; attempt <= 2; attempt++) try {
178
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
157
179
  const res = await fetch(url, {
158
180
  method: init.method,
159
181
  headers: init.headers,
@@ -164,7 +186,7 @@ var AgentApiTransport = class {
164
186
  const errorBody = await res.text();
165
187
  const error = new Error(formatErrorMessage(res.status, errorBody));
166
188
  error.status = res.status;
167
- if (attempt === 1 && RETRYABLE_STATUS.has(res.status)) {
189
+ if (attempt < maxAttempts && RETRYABLE_STATUS.has(res.status)) {
168
190
  lastError = error;
169
191
  await sleep(RETRY_DELAY_MS);
170
192
  continue;
@@ -173,7 +195,7 @@ var AgentApiTransport = class {
173
195
  }
174
196
  return res;
175
197
  } catch (err) {
176
- if (attempt === 1 && isRetryableNetworkError(err)) {
198
+ if (attempt < maxAttempts && isRetryableNetworkError(err)) {
177
199
  lastError = err;
178
200
  await sleep(RETRY_DELAY_MS);
179
201
  continue;
@@ -186,8 +208,11 @@ var AgentApiTransport = class {
186
208
  * @param extra.timeoutMs Per-request abort timeout (default REQUEST_TIMEOUT_MS).
187
209
  * Long endpoints (image generation) pass a larger value so the gateway's
188
210
  * own timeout wins with a readable status instead of a client-side abort.
189
- * @param extra.retry Whether to retry once on transient failures (default
190
- * true). Expensive/non-idempotent endpoints pass false.
211
+ * @param extra.retry Whether to retry once on transient failures. Safe reads
212
+ * (GET/HEAD/OPTIONS) default to true; mutations default to false. Set true
213
+ * only when the endpoint's server-side contract is explicitly idempotent.
214
+ * @param extra.signal Optional caller cancellation combined with the client's
215
+ * own timeout budget. Aborting either signal cancels the request.
191
216
  */
192
217
  async request(path, options, extra) {
193
218
  const url = `${this.apiUrl}${path}`;
@@ -195,13 +220,13 @@ var AgentApiTransport = class {
195
220
  headers.set("Authorization", `Bearer ${this.apiKey}`);
196
221
  headers.set("Content-Type", "application/json");
197
222
  const timeoutMs = extra?.timeoutMs ?? 2e4;
198
- const maxAttempts = extra?.retry === false ? 1 : 2;
223
+ const maxAttempts = extra?.retry ?? isSafeRetryMethod(options?.method) ? 2 : 1;
199
224
  let lastError;
200
225
  for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
201
226
  const res = await fetch(url, {
202
227
  ...options,
203
228
  headers,
204
- signal: AbortSignal.timeout(timeoutMs)
229
+ signal: extra?.signal ? AbortSignal.any([extra.signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs)
205
230
  });
206
231
  if (!res.ok) {
207
232
  const errorBody = await res.text();
@@ -370,8 +395,9 @@ var ConnectCredentialsApi = class extends ApiBase {
370
395
  * selector arg (e.g. `xeroTenantId`) on every credential-touching tool
371
396
  * and look up the matching account by that selector at dispatch time.
372
397
  *
373
- * Returned `accounts[i].accountIdentifier` is the Xero tenantId the
374
- * stable cross-session identifier the LLM should pass.
398
+ * `xeroTenantId` is the model-facing organisation selector. The separate
399
+ * `accountIdentifier` is the Connect persistence key used for refresh and
400
+ * may be an email; never substitute one for the other.
375
401
  */
376
402
  async getXeroAccounts() {
377
403
  return { accounts: (await this.transport.request("/agent/connect/xero/accounts")).accounts.map((a) => ({
@@ -381,21 +407,21 @@ var ConnectCredentialsApi = class extends ApiBase {
381
407
  connectedAt: a.connectedAt,
382
408
  accessToken: a.accessToken,
383
409
  accessTokenExpiresAt: a.accessTokenExpiresAt ?? "",
384
- xeroTenantId: a.xeroTenantId ?? a.accountIdentifier
410
+ xeroTenantId: a.xeroTenantId ?? ""
385
411
  })) };
386
412
  }
387
413
  async refreshXeroToken() {
388
- return this.transport.request("/agent/connect/xero/refresh", { method: "POST" });
414
+ return this.transport.request("/agent/connect/xero/refresh", { method: "POST" }, { retry: true });
389
415
  }
390
416
  /**
391
- * Pattern A: refresh a specific Xero connection by its `accountIdentifier`
392
- * (the Xero `tenantId`). The legacy `refreshXeroToken()` only refreshes
393
- * the *primary* connection, which is wrong for multi-tenant Xero where
394
- * each tenant has its own non-interchangeable access token.
417
+ * Refresh a specific Xero Connection by its exact `accountIdentifier` from
418
+ * `getXeroAccounts()`. Do not substitute `xeroTenantId`: current Xero OAuth
419
+ * rows may use the account email as their persistence key even when a sole
420
+ * organisation tenant ID is available in provider metadata.
395
421
  */
396
- async refreshXeroAccountToken(xeroTenantId) {
397
- const path = `/agent/connect/xero/accounts/${encodeURIComponent(xeroTenantId)}/refresh`;
398
- const raw = await this.transport.request(path, { method: "POST" });
422
+ async refreshXeroAccountToken(accountIdentifier) {
423
+ const path = `/agent/connect/xero/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;
424
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
399
425
  return {
400
426
  accessToken: raw.accessToken,
401
427
  accessTokenExpiresAt: raw.accessTokenExpiresAt ?? "",
@@ -457,7 +483,7 @@ var ConnectCredentialsApi = class extends ApiBase {
457
483
  };
458
484
  }
459
485
  async refreshAtlassianToken() {
460
- return this.transport.request("/agent/connect/atlassian/refresh", { method: "POST" });
486
+ return this.transport.request("/agent/connect/atlassian/refresh", { method: "POST" }, { retry: true });
461
487
  }
462
488
  /**
463
489
  * Pattern A: multi-account / multi-site credential fetch for Atlassian.
@@ -513,7 +539,7 @@ var ConnectCredentialsApi = class extends ApiBase {
513
539
  */
514
540
  async refreshAtlassianAccountToken(accountIdentifier) {
515
541
  const path = `/agent/connect/atlassian/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;
516
- const raw = await this.transport.request(path, { method: "POST" });
542
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
517
543
  return {
518
544
  accessToken: raw.accessToken,
519
545
  accessTokenExpiresAt: raw.accessTokenExpiresAt ?? "",
@@ -555,7 +581,26 @@ var ConnectCredentialsApi = class extends ApiBase {
555
581
  })) };
556
582
  }
557
583
  async refreshMYOBToken() {
558
- return this.transport.request("/agent/connect/myob/refresh", { method: "POST" });
584
+ return this.transport.request("/agent/connect/myob/refresh", { method: "POST" }, { retry: true });
585
+ }
586
+ /**
587
+ * Pattern A: refresh one MYOB Connection by its stable
588
+ * `accountIdentifier` (the MYOB business id returned by
589
+ * `getMYOBAccounts()`).
590
+ *
591
+ * MYOB refresh tokens belong to individual Connection rows. A
592
+ * multi-business client must use this method instead of refreshing the
593
+ * primary Connection and copying that access token into every cached
594
+ * business client.
595
+ */
596
+ async refreshMYOBAccountToken(accountIdentifier) {
597
+ const path = `/agent/connect/myob/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;
598
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
599
+ return {
600
+ accessToken: raw.accessToken,
601
+ accessTokenExpiresAt: raw.accessTokenExpiresAt ?? "",
602
+ expiresAt: raw.expiresAt ?? ""
603
+ };
559
604
  }
560
605
  /**
561
606
  * @deprecated Returns a single primary credential blob. Use
@@ -596,7 +641,7 @@ var ConnectCredentialsApi = class extends ApiBase {
596
641
  */
597
642
  async refreshSalesforceAccountToken(orgId) {
598
643
  const path = `/agent/connect/salesforce/accounts/${encodeURIComponent(orgId)}/refresh`;
599
- const raw = await this.transport.request(path, { method: "POST" });
644
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
600
645
  return {
601
646
  accessToken: raw.accessToken,
602
647
  accessTokenExpiresAt: raw.accessTokenExpiresAt ?? "",
@@ -626,9 +671,6 @@ var ConnectCredentialsApi = class extends ApiBase {
626
671
  connectedAt: a.connectedAt,
627
672
  accessToken: a.accessToken ?? "",
628
673
  accessTokenExpiresAt: a.accessTokenExpiresAt ?? "",
629
- refreshToken: a.refreshToken ?? "",
630
- clientId: a.clientId ?? "",
631
- clientSecret: a.clientSecret ?? "",
632
674
  email: a.email ?? a.accountIdentifier,
633
675
  microsoftTenantId: a.microsoftTenantId ?? "",
634
676
  workspaceDomain: a.workspaceDomain ?? ""
@@ -650,7 +692,7 @@ var ConnectCredentialsApi = class extends ApiBase {
650
692
  */
651
693
  async refreshMicrosoftAccountToken(accountIdentifier) {
652
694
  const path = `/agent/connect/microsoft/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;
653
- const raw = await this.transport.request(path, { method: "POST" });
695
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
654
696
  return {
655
697
  accessToken: raw.accessToken,
656
698
  accessTokenExpiresAt: raw.accessTokenExpiresAt ?? "",
@@ -751,6 +793,7 @@ var ConnectCredentialsApi = class extends ApiBase {
751
793
  const accounts = [];
752
794
  for (const row of raw.accounts) {
753
795
  const rowToken = row.accessToken ?? "";
796
+ const rowAccountIdentifier = row.accountIdentifier ?? "";
754
797
  for (const a of row.availableAccounts ?? []) {
755
798
  const id = a.ctidTraderAccountId != null ? String(a.ctidTraderAccountId) : a.accountId != null ? String(a.accountId) : "";
756
799
  if (id.length === 0 || seen.has(id)) continue;
@@ -762,7 +805,8 @@ var ConnectCredentialsApi = class extends ApiBase {
762
805
  isLive,
763
806
  ...a.brokerName != null ? { brokerName: a.brokerName } : {},
764
807
  ...a.accountNumber != null ? { accountNumber: String(a.accountNumber) } : {},
765
- accessToken: rowToken
808
+ accessToken: rowToken,
809
+ accountIdentifier: rowAccountIdentifier
766
810
  });
767
811
  }
768
812
  }
@@ -773,6 +817,31 @@ var ConnectCredentialsApi = class extends ApiBase {
773
817
  };
774
818
  }
775
819
  /**
820
+ * Pattern A: refresh a specific cTrader grant by its stable
821
+ * `accountIdentifier` (`ctid:<userId>` from `getCTraderAccounts()`).
822
+ *
823
+ * cTrader access tokens live ~30 days; the `getCTraderAccounts()` /
824
+ * credentials reads serve the STORED token without refreshing, so refresh is
825
+ * the consumer's job. `@alfe.ai/ctrader-mcp` calls this when the cTrader Open
826
+ * API rejects an account-auth with `CH_ACCESS_TOKEN_INVALID`, then re-runs
827
+ * the socket handshake with the returned `accessToken`.
828
+ *
829
+ * Refreshing one grant rotates the single OAuth token that covers EVERY
830
+ * trading account under that login. cTrader's refresh token itself does not
831
+ * expire but may rotate on refresh (`rotatesRefreshToken: true`); connect
832
+ * persists the rotated refresh token server-side, so the caller only needs
833
+ * the new `accessToken`. Mirrors `refreshXeroAccountToken`.
834
+ */
835
+ async refreshCTraderAccount(accountIdentifier) {
836
+ const path = `/agent/connect/ctrader/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;
837
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
838
+ return {
839
+ accessToken: raw.accessToken,
840
+ accessTokenExpiresAt: raw.accessTokenExpiresAt ?? "",
841
+ expiresAt: raw.expiresAt ?? ""
842
+ };
843
+ }
844
+ /**
776
845
  * @deprecated Returns a single primary credential blob. Use
777
846
  * `getShopifyAccounts()` for the multi-account shape required by Pattern A
778
847
  * (`@alfe.ai/shopify-mcp` keys per-shop on the myshopify domain).
@@ -874,7 +943,7 @@ var ConnectCredentialsApi = class extends ApiBase {
874
943
  */
875
944
  async refreshSocialAccount(provider, accountIdentifier) {
876
945
  const path = `/agent/connect/${encodeURIComponent(provider)}/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;
877
- const raw = await this.transport.request(path, { method: "POST" });
946
+ const raw = await this.transport.request(path, { method: "POST" }, { retry: true });
878
947
  return {
879
948
  accountIdentifier: raw.accountIdentifier,
880
949
  accessToken: raw.accessToken,
@@ -940,11 +1009,8 @@ var IdentityApi = class extends ApiBase {
940
1009
  body: JSON.stringify(args)
941
1010
  });
942
1011
  }
943
- async unmergeIdentity(identityId, args) {
944
- return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/unmerge`, {
945
- method: "POST",
946
- body: JSON.stringify(args)
947
- });
1012
+ async unmergeIdentity(identityId) {
1013
+ return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/unmerge`, { method: "POST" });
948
1014
  }
949
1015
  async addIdentityNote(identityId, args) {
950
1016
  return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/notes`, {
@@ -961,6 +1027,7 @@ var IdentityApi = class extends ApiBase {
961
1027
  async getIdentityChangelog(identityId, args) {
962
1028
  const qs = new URLSearchParams();
963
1029
  if (args?.limit) qs.set("limit", String(args.limit));
1030
+ if (args?.cursor) qs.set("cursor", args.cursor);
964
1031
  const query = qs.toString();
965
1032
  return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/changelog${query ? `?${query}` : ""}`);
966
1033
  }
@@ -1037,8 +1104,9 @@ var ImagesApi = class extends ApiBase {
1037
1104
  let job;
1038
1105
  try {
1039
1106
  job = await this.transport.request(`/agent/images/${jobId}`);
1040
- } catch {
1041
- continue;
1107
+ } catch (error) {
1108
+ if (isTransientRequestError(error)) continue;
1109
+ throw error;
1042
1110
  }
1043
1111
  if (job.status === "completed") {
1044
1112
  if (!job.imageUrl) throw new Error("Image generation completed without a URL");
@@ -1111,6 +1179,8 @@ var IntegrationsApi = class extends ApiBase {
1111
1179
  * Knowledge resource methods (org/team/project scoped docs, profiles,
1112
1180
  * change requests + RAG search) for the Agent API client.
1113
1181
  */
1182
+ /** Matches services/knowledge's maximum indexed document size. */
1183
+ const MAX_KNOWLEDGE_DOCUMENT_BYTES = 2 * 1024 * 1024;
1114
1184
  var KnowledgeApi = class extends ApiBase {
1115
1185
  /**
1116
1186
  * Semantic search across the agent's member scopes. Fan-out is gated
@@ -1199,16 +1269,18 @@ var KnowledgeApi = class extends ApiBase {
1199
1269
  * from `services/org`, then fetches the bytes directly from S3 (the one
1200
1270
  * legitimate raw fetch in a plugin — same pattern as sync).
1201
1271
  */
1202
- async readScopeDoc(scopeType, scopeId, filePath) {
1272
+ async readScopeDoc(scopeType, scopeId, filePath, opts) {
1273
+ const maxBytes = opts?.maxBytes ?? 2097152;
1274
+ if (!Number.isInteger(maxBytes) || maxBytes < 1 || maxBytes > 2097152) throw new RangeError(`maxBytes must be an integer from 1 to ${String(MAX_KNOWLEDGE_DOCUMENT_BYTES)}`);
1203
1275
  const { downloadUrl } = await this.transport.request(`/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/download/${encodeFilePath(filePath)}`);
1204
1276
  const res = await fetch(downloadUrl, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
1205
1277
  if (!res.ok) {
1206
- await res.text();
1278
+ await res.body?.cancel().catch(() => void 0);
1207
1279
  throw new Error(`Doc download failed (${String(res.status)})`);
1208
1280
  }
1209
1281
  return {
1210
1282
  filePath,
1211
- text: await res.text()
1283
+ text: await readBoundedUtf8(res, maxBytes)
1212
1284
  };
1213
1285
  }
1214
1286
  /**
@@ -1242,6 +1314,52 @@ var KnowledgeApi = class extends ApiBase {
1242
1314
  return { filePath: presign.filePath };
1243
1315
  }
1244
1316
  };
1317
+ async function readBoundedUtf8(response, maxBytes) {
1318
+ const declaredLength = response.headers.get("content-length");
1319
+ if (declaredLength !== null && /^\d+$/u.test(declaredLength) && Number(declaredLength) > maxBytes) {
1320
+ await response.body?.cancel().catch(() => void 0);
1321
+ throw documentTooLargeError(maxBytes);
1322
+ }
1323
+ if (response.body === null) return "";
1324
+ const reader = response.body.getReader();
1325
+ const chunks = [];
1326
+ let total = 0;
1327
+ let complete = false;
1328
+ try {
1329
+ while (!complete) {
1330
+ const { done, value } = await reader.read();
1331
+ if (done) {
1332
+ complete = true;
1333
+ continue;
1334
+ }
1335
+ total += value.byteLength;
1336
+ if (total > maxBytes) {
1337
+ await reader.cancel().catch(() => void 0);
1338
+ throw documentTooLargeError(maxBytes);
1339
+ }
1340
+ chunks.push(value);
1341
+ }
1342
+ } finally {
1343
+ reader.releaseLock();
1344
+ }
1345
+ const bytes = new Uint8Array(total);
1346
+ let offset = 0;
1347
+ for (const chunk of chunks) {
1348
+ bytes.set(chunk, offset);
1349
+ offset += chunk.byteLength;
1350
+ }
1351
+ try {
1352
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
1353
+ } catch {
1354
+ throw new Error("Knowledge document is not valid UTF-8 text");
1355
+ }
1356
+ }
1357
+ function documentTooLargeError(maxBytes) {
1358
+ const error = /* @__PURE__ */ new Error(`Knowledge document exceeds the ${String(maxBytes)} byte read limit`);
1359
+ error.name = "KnowledgeDocumentTooLargeError";
1360
+ error.code = "KNOWLEDGE_DOCUMENT_TOO_LARGE";
1361
+ return error;
1362
+ }
1245
1363
  //#endregion
1246
1364
  //#region src/domains/memory.ts
1247
1365
  /**
@@ -1416,23 +1534,23 @@ var RemoteApi = class extends ApiBase {
1416
1534
  * Web/image/news search methods (services/search) for the Agent API client.
1417
1535
  */
1418
1536
  var SearchApi = class extends ApiBase {
1419
- async searchWeb(params) {
1537
+ async searchWeb(params, options) {
1420
1538
  return this.transport.request("/agent/search/web", {
1421
1539
  method: "POST",
1422
1540
  body: JSON.stringify(params)
1423
- });
1541
+ }, { signal: options?.signal });
1424
1542
  }
1425
- async searchImages(params) {
1543
+ async searchImages(params, options) {
1426
1544
  return this.transport.request("/agent/search/images", {
1427
1545
  method: "POST",
1428
1546
  body: JSON.stringify(params)
1429
- });
1547
+ }, { signal: options?.signal });
1430
1548
  }
1431
- async searchNews(params) {
1549
+ async searchNews(params, options) {
1432
1550
  return this.transport.request("/agent/search/news", {
1433
1551
  method: "POST",
1434
1552
  body: JSON.stringify(params)
1435
- });
1553
+ }, { signal: options?.signal });
1436
1554
  }
1437
1555
  /** Search news across the selected provider's corpus. → POST /agent/news/search */
1438
1556
  async newsSearch(params) {
@@ -1583,8 +1701,9 @@ var SelfApi = class extends ApiBase {
1583
1701
  let job;
1584
1702
  try {
1585
1703
  job = await this.transport.request(`/agent/avatar/${jobId}`);
1586
- } catch {
1587
- continue;
1704
+ } catch (error) {
1705
+ if (isTransientRequestError(error)) continue;
1706
+ throw error;
1588
1707
  }
1589
1708
  if (job.status === "completed") {
1590
1709
  if (!job.agent) throw new Error("Avatar generation completed without an agent");
@@ -1729,7 +1848,11 @@ var SyncApi = class extends ApiBase {
1729
1848
  return this.transport.request(`/agent/sync/files/${encodeFilePath(filePath)}`, { method: "DELETE" });
1730
1849
  }
1731
1850
  async sharedListFiles(args) {
1732
- return this.transport.request(`/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}`);
1851
+ const params = new URLSearchParams();
1852
+ if (args.limit !== void 0) params.set("limit", String(args.limit));
1853
+ if (args.cursor) params.set("cursor", args.cursor);
1854
+ const query = params.toString();
1855
+ return this.transport.request(`/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}${query ? `?${query}` : ""}`);
1733
1856
  }
1734
1857
  async sharedDownloadUrl(args) {
1735
1858
  return this.transport.request(`/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/download/${encodeFilePath(args.filePath)}`);
@@ -1762,11 +1885,11 @@ var TeamsApi = class extends ApiBase {
1762
1885
  */
1763
1886
  var WorkspaceApi = class extends ApiBase {
1764
1887
  /**
1765
- * GET /agents/me/workspace — workspace config for the authenticated agent
1888
+ * GET /agent/workspace — workspace config for the authenticated agent
1766
1889
  * (template assignment, default model, org roster).
1767
1890
  */
1768
1891
  async getWorkspace() {
1769
- return this.transport.request("/agents/me/workspace");
1892
+ return this.transport.request("/agent/workspace");
1770
1893
  }
1771
1894
  /**
1772
1895
  * GET /templates/{key}/files — persona/workspace file contents for a
@@ -1775,7 +1898,30 @@ var WorkspaceApi = class extends ApiBase {
1775
1898
  */
1776
1899
  async getTemplateFiles(templateKey, opts) {
1777
1900
  const query = opts?.version !== void 0 ? `?version=${String(opts.version)}` : "";
1778
- return this.transport.request(`/templates/${encodeURIComponent(templateKey)}/files${query}`);
1901
+ return this.transport.request(`/agent/templates/${encodeURIComponent(templateKey)}/files${query}`);
1902
+ }
1903
+ };
1904
+ //#endregion
1905
+ //#region src/domains/webhooks.ts
1906
+ /** Agent self-service webhook management methods. */
1907
+ var WebhooksApi = class extends ApiBase {
1908
+ async createWebhook(args) {
1909
+ return this.transport.request("/agent/webhooks", {
1910
+ method: "POST",
1911
+ body: JSON.stringify(args)
1912
+ }, { retry: false });
1913
+ }
1914
+ async listWebhooks() {
1915
+ return (await this.transport.request("/agent/webhooks")).webhooks;
1916
+ }
1917
+ async deleteWebhook(webhookId) {
1918
+ return this.transport.request(`/agent/webhooks/${encodeURIComponent(webhookId)}`, { method: "DELETE" });
1919
+ }
1920
+ async rotateWebhookSecret(webhookId) {
1921
+ return this.transport.request(`/agent/webhooks/${encodeURIComponent(webhookId)}/rotate`, { method: "POST" }, { retry: false });
1922
+ }
1923
+ async listWebhookDeliveries(webhookId) {
1924
+ return (await this.transport.request(`/agent/webhooks/${encodeURIComponent(webhookId)}/deliveries`)).deliveries;
1779
1925
  }
1780
1926
  };
1781
1927
  //#endregion
@@ -1810,7 +1956,8 @@ applyMixins(AgentApiClient, [
1810
1956
  RemoteApi,
1811
1957
  SelfApi,
1812
1958
  VoiceApi,
1813
- ImagesApi
1959
+ ImagesApi,
1960
+ WebhooksApi
1814
1961
  ]);
1815
1962
  //#endregion
1816
1963
  export { AgentApiClient, installToolErrorCapture };