@alter-ai/alter-sdk 0.5.0 → 0.7.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.cjs CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  ActorType: () => ActorType,
35
35
  AlterSDKError: () => AlterSDKError,
36
36
  AlterVault: () => AlterVault,
37
+ AuthResult: () => AuthResult,
37
38
  BackendError: () => BackendError,
38
39
  ConnectConfigError: () => ConnectConfigError,
39
40
  ConnectDeniedError: () => ConnectDeniedError,
@@ -41,12 +42,13 @@ __export(index_exports, {
41
42
  ConnectResult: () => ConnectResult,
42
43
  ConnectSession: () => ConnectSession,
43
44
  ConnectTimeoutError: () => ConnectTimeoutError,
44
- ConnectionDeletedError: () => ConnectionDeletedError,
45
- ConnectionExpiredError: () => ConnectionExpiredError,
46
- ConnectionInfo: () => ConnectionInfo,
47
- ConnectionListResult: () => ConnectionListResult,
48
- ConnectionNotFoundError: () => ConnectionNotFoundError,
49
- ConnectionRevokedError: () => ConnectionRevokedError,
45
+ CredentialRevokedError: () => CredentialRevokedError,
46
+ GrantDeletedError: () => GrantDeletedError,
47
+ GrantExpiredError: () => GrantExpiredError,
48
+ GrantInfo: () => GrantInfo,
49
+ GrantListResult: () => GrantListResult,
50
+ GrantNotFoundError: () => GrantNotFoundError,
51
+ GrantRevokedError: () => GrantRevokedError,
50
52
  HttpMethod: () => HttpMethod,
51
53
  NetworkError: () => NetworkError,
52
54
  PolicyViolationError: () => PolicyViolationError,
@@ -90,30 +92,38 @@ var ReAuthRequiredError = class extends BackendError {
90
92
  this.name = "ReAuthRequiredError";
91
93
  }
92
94
  };
93
- var ConnectionExpiredError = class extends ReAuthRequiredError {
95
+ var GrantExpiredError = class extends ReAuthRequiredError {
94
96
  constructor(message, details) {
95
97
  super(message, details);
96
- this.name = "ConnectionExpiredError";
98
+ this.name = "GrantExpiredError";
97
99
  }
98
100
  };
99
- var ConnectionRevokedError = class extends ReAuthRequiredError {
100
- connectionId;
101
- constructor(message, connectionId, details) {
101
+ var GrantRevokedError = class extends ReAuthRequiredError {
102
+ grantId;
103
+ constructor(message, grantId, details) {
102
104
  super(message, details);
103
- this.name = "ConnectionRevokedError";
104
- this.connectionId = connectionId;
105
+ this.name = "GrantRevokedError";
106
+ this.grantId = grantId;
105
107
  }
106
108
  };
107
- var ConnectionDeletedError = class extends ReAuthRequiredError {
109
+ var CredentialRevokedError = class extends ReAuthRequiredError {
110
+ grantId;
111
+ constructor(message, grantId, details) {
112
+ super(message, details);
113
+ this.name = "CredentialRevokedError";
114
+ this.grantId = grantId;
115
+ }
116
+ };
117
+ var GrantDeletedError = class extends ReAuthRequiredError {
108
118
  constructor(message, details) {
109
119
  super(message, details);
110
- this.name = "ConnectionDeletedError";
120
+ this.name = "GrantDeletedError";
111
121
  }
112
122
  };
113
- var ConnectionNotFoundError = class extends BackendError {
123
+ var GrantNotFoundError = class extends BackendError {
114
124
  constructor(message, details) {
115
125
  super(message, details);
116
- this.name = "ConnectionNotFoundError";
126
+ this.name = "GrantNotFoundError";
117
127
  }
118
128
  };
119
129
  var PolicyViolationError = class extends BackendError {
@@ -159,12 +169,12 @@ var ProviderAPIError = class extends AlterSDKError {
159
169
  }
160
170
  };
161
171
  var ScopeReauthRequiredError = class extends ProviderAPIError {
162
- connectionId;
172
+ grantId;
163
173
  providerId;
164
- constructor(message, connectionId, providerId, statusCode, responseBody, details) {
174
+ constructor(message, grantId, providerId, statusCode, responseBody, details) {
165
175
  super(message, statusCode, responseBody, details);
166
176
  this.name = "ScopeReauthRequiredError";
167
- this.connectionId = connectionId;
177
+ this.grantId = grantId;
168
178
  this.providerId = providerId;
169
179
  }
170
180
  };
@@ -185,7 +195,6 @@ var TimeoutError = class extends NetworkError {
185
195
  var ActorType = /* @__PURE__ */ ((ActorType2) => {
186
196
  ActorType2["BACKEND_SERVICE"] = "backend_service";
187
197
  ActorType2["AI_AGENT"] = "ai_agent";
188
- ActorType2["MCP_SERVER"] = "mcp_server";
189
198
  return ActorType2;
190
199
  })(ActorType || {});
191
200
  var TokenResponse = class _TokenResponse {
@@ -197,8 +206,8 @@ var TokenResponse = class _TokenResponse {
197
206
  expiresAt;
198
207
  /** OAuth scopes granted */
199
208
  scopes;
200
- /** Connection ID that provided this token */
201
- connectionId;
209
+ /** Grant ID that provided this token */
210
+ grantId;
202
211
  /** Provider ID (google, github, etc.) */
203
212
  providerId;
204
213
  /** HTTP header name for credential injection (e.g., "Authorization", "X-API-Key") */
@@ -214,8 +223,8 @@ var TokenResponse = class _TokenResponse {
214
223
  this.expiresIn = data.expires_in ?? null;
215
224
  this.expiresAt = data.expires_at ? _TokenResponse.parseExpiresAt(data.expires_at) : null;
216
225
  this.scopes = data.scopes ?? [];
217
- this.connectionId = data.connection_id;
218
- this.providerId = data.provider_id ?? "";
226
+ this.grantId = data.grant_id;
227
+ this.providerId = data.provider_id ?? null;
219
228
  this.injectionHeader = data.injection_header ?? "Authorization";
220
229
  this.injectionFormat = data.injection_format ?? "Bearer {token}";
221
230
  if (data.additional_credentials) {
@@ -270,14 +279,15 @@ var TokenResponse = class _TokenResponse {
270
279
  expires_in: this.expiresIn,
271
280
  expires_at: this.expiresAt?.toISOString() ?? null,
272
281
  scopes: this.scopes,
273
- connection_id: this.connectionId
282
+ grant_id: this.grantId,
283
+ provider_id: this.providerId
274
284
  };
275
285
  }
276
286
  /**
277
287
  * Custom string representation — EXCLUDES access_token for security.
278
288
  */
279
289
  toString() {
280
- return `TokenResponse(connection_id=${this.connectionId}, token_type=${this.tokenType}, scopes=[${this.scopes.join(", ")}])`;
290
+ return `TokenResponse(grant_id=${this.grantId}, token_type=${this.tokenType}, scopes=[${this.scopes.join(", ")}])`;
281
291
  }
282
292
  /**
283
293
  * Custom Node.js inspect output — EXCLUDES access_token for security.
@@ -286,8 +296,8 @@ var TokenResponse = class _TokenResponse {
286
296
  return this.toString();
287
297
  }
288
298
  };
289
- var ConnectionInfo = class {
290
- id;
299
+ var GrantInfo = class {
300
+ grantId;
291
301
  providerId;
292
302
  scopes;
293
303
  accountIdentifier;
@@ -298,7 +308,7 @@ var ConnectionInfo = class {
298
308
  createdAt;
299
309
  lastUsedAt;
300
310
  constructor(data) {
301
- this.id = data.id;
311
+ this.grantId = data.grant_id;
302
312
  this.providerId = data.provider_id;
303
313
  this.scopes = data.scopes ?? [];
304
314
  this.accountIdentifier = data.account_identifier ?? null;
@@ -312,7 +322,7 @@ var ConnectionInfo = class {
312
322
  }
313
323
  toJSON() {
314
324
  return {
315
- id: this.id,
325
+ grant_id: this.grantId,
316
326
  provider_id: this.providerId,
317
327
  scopes: this.scopes,
318
328
  account_identifier: this.accountIdentifier,
@@ -325,7 +335,7 @@ var ConnectionInfo = class {
325
335
  };
326
336
  }
327
337
  toString() {
328
- return `ConnectionInfo(id=${this.id}, provider=${this.providerId}, status=${this.status})`;
338
+ return `GrantInfo(grant_id=${this.grantId}, provider=${this.providerId}, status=${this.status})`;
329
339
  }
330
340
  };
331
341
  var ConnectSession = class {
@@ -352,46 +362,91 @@ var ConnectSession = class {
352
362
  return `ConnectSession(url=${this.connectUrl}, expires_in=${this.expiresIn})`;
353
363
  }
354
364
  };
355
- var ConnectionListResult = class {
356
- connections;
365
+ var GrantListResult = class {
366
+ grants;
357
367
  total;
358
368
  limit;
359
369
  offset;
360
370
  hasMore;
361
371
  constructor(data) {
362
- this.connections = data.connections;
372
+ this.grants = data.grants;
363
373
  this.total = data.total;
364
374
  this.limit = data.limit;
365
375
  this.offset = data.offset;
366
376
  this.hasMore = data.has_more;
367
377
  Object.freeze(this);
368
378
  }
379
+ /**
380
+ * Custom JSON serialization — snake_case wire format.
381
+ */
382
+ toJSON() {
383
+ return {
384
+ grants: this.grants.map((g) => g.toJSON()),
385
+ total: this.total,
386
+ limit: this.limit,
387
+ offset: this.offset,
388
+ has_more: this.hasMore
389
+ };
390
+ }
369
391
  };
370
392
  var ConnectResult = class {
371
- connectionId;
393
+ grantId;
372
394
  providerId;
373
395
  accountIdentifier;
374
396
  scopes;
375
- connectionPolicy;
397
+ grantPolicy;
376
398
  constructor(data) {
377
- this.connectionId = data.connection_id;
399
+ this.grantId = data.grant_id;
378
400
  this.providerId = data.provider_id;
379
401
  this.accountIdentifier = data.account_identifier ?? null;
380
402
  this.scopes = data.scopes ?? [];
381
- this.connectionPolicy = data.connection_policy ?? null;
403
+ const rawPolicy = data.grant_policy;
404
+ if (rawPolicy) {
405
+ const raw = rawPolicy;
406
+ this.grantPolicy = Object.freeze({
407
+ expiresAt: raw["expires_at"] !== void 0 ? raw["expires_at"] : rawPolicy.expiresAt ?? null,
408
+ createdBy: raw["created_by"] !== void 0 ? raw["created_by"] : rawPolicy.createdBy ?? null,
409
+ createdAt: raw["created_at"] !== void 0 ? raw["created_at"] : rawPolicy.createdAt ?? null
410
+ });
411
+ } else {
412
+ this.grantPolicy = null;
413
+ }
382
414
  Object.freeze(this);
383
415
  }
384
416
  toJSON() {
385
417
  return {
386
- connection_id: this.connectionId,
418
+ grant_id: this.grantId,
387
419
  provider_id: this.providerId,
388
420
  account_identifier: this.accountIdentifier,
389
421
  scopes: this.scopes,
390
- connection_policy: this.connectionPolicy
422
+ grant_policy: this.grantPolicy ? {
423
+ expires_at: this.grantPolicy.expiresAt ?? null,
424
+ created_by: this.grantPolicy.createdBy ?? null,
425
+ created_at: this.grantPolicy.createdAt ?? null
426
+ } : null
391
427
  };
392
428
  }
393
429
  toString() {
394
- return `ConnectResult(connection_id=${this.connectionId}, provider=${this.providerId})`;
430
+ return `ConnectResult(grant_id=${this.grantId}, provider=${this.providerId})`;
431
+ }
432
+ };
433
+ var AuthResult = class {
434
+ userToken;
435
+ userInfo;
436
+ constructor(data) {
437
+ this.userToken = data.user_token;
438
+ this.userInfo = data.user_info ?? {};
439
+ Object.freeze(this);
440
+ }
441
+ toJSON() {
442
+ return {
443
+ user_token: this.userToken,
444
+ user_info: this.userInfo
445
+ };
446
+ }
447
+ toString() {
448
+ const sub = this.userInfo?.sub ?? "unknown";
449
+ return `AuthResult(sub=${sub})`;
395
450
  }
396
451
  };
397
452
  var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
@@ -401,10 +456,11 @@ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
401
456
  "x-api-key",
402
457
  "x-auth-token",
403
458
  "x-amz-date",
404
- "x-amz-content-sha256"
459
+ "x-amz-content-sha256",
460
+ "x-amz-security-token"
405
461
  ]);
406
462
  var APICallAuditLog = class {
407
- connectionId;
463
+ grantId;
408
464
  providerId;
409
465
  method;
410
466
  url;
@@ -423,22 +479,28 @@ var APICallAuditLog = class {
423
479
  threadId;
424
480
  /** Tool invocation ID for actor tracking */
425
481
  toolCallId;
482
+ /** Specific tool being invoked (e.g., "read_calendar") */
483
+ toolId;
484
+ /** Tool bundle identifier (e.g., "google-calendar-mcp-server") */
485
+ toolBundleId;
426
486
  constructor(data) {
427
- this.connectionId = data.connectionId;
428
- this.providerId = data.providerId;
487
+ this.grantId = data.grant_id;
488
+ this.providerId = data.provider_id;
429
489
  this.method = data.method;
430
490
  this.url = data.url;
431
- this.requestHeaders = data.requestHeaders ?? null;
432
- this.requestBody = data.requestBody ?? null;
433
- this.responseStatus = data.responseStatus;
434
- this.responseHeaders = data.responseHeaders ?? null;
435
- this.responseBody = data.responseBody ?? null;
436
- this.latencyMs = data.latencyMs;
491
+ this.requestHeaders = data.request_headers ?? null;
492
+ this.requestBody = data.request_body ?? null;
493
+ this.responseStatus = data.response_status;
494
+ this.responseHeaders = data.response_headers ?? null;
495
+ this.responseBody = data.response_body ?? null;
496
+ this.latencyMs = data.latency_ms;
437
497
  this.timestamp = /* @__PURE__ */ new Date();
438
498
  this.reason = data.reason ?? null;
439
- this.runId = data.runId ?? null;
440
- this.threadId = data.threadId ?? null;
441
- this.toolCallId = data.toolCallId ?? null;
499
+ this.runId = data.run_id ?? null;
500
+ this.threadId = data.thread_id ?? null;
501
+ this.toolCallId = data.tool_call_id ?? null;
502
+ this.toolId = data.tool_id ?? null;
503
+ this.toolBundleId = data.tool_bundle_id ?? null;
442
504
  }
443
505
  /**
444
506
  * Sanitize sensitive data before sending.
@@ -447,7 +509,7 @@ var APICallAuditLog = class {
447
509
  */
448
510
  sanitize() {
449
511
  const sanitized = {
450
- connection_id: this.connectionId,
512
+ grant_id: this.grantId,
451
513
  provider_id: this.providerId,
452
514
  method: this.method,
453
515
  url: this.url,
@@ -460,7 +522,9 @@ var APICallAuditLog = class {
460
522
  reason: this.reason,
461
523
  run_id: this.runId,
462
524
  thread_id: this.threadId,
463
- tool_call_id: this.toolCallId
525
+ tool_call_id: this.toolCallId,
526
+ tool_id: this.toolId,
527
+ tool_bundle_id: this.toolBundleId
464
528
  };
465
529
  return sanitized;
466
530
  }
@@ -480,6 +544,7 @@ var import_node_crypto = require("crypto");
480
544
  var ALGORITHM = "AWS4-HMAC-SHA256";
481
545
  var AWS_HOST_RE = /^(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
482
546
  var S3_VIRTUAL_HOST_RE = /^[^.]+\.s3\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
547
+ var VPCE_HOST_RE = /^vpce-[a-z0-9-]+\.(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.vpce\.amazonaws\.com$/;
483
548
  function hmacSha256(key, message) {
484
549
  return (0, import_node_crypto.createHmac)("sha256", key).update(message, "utf8").digest();
485
550
  }
@@ -502,6 +567,10 @@ function detectServiceAndRegion(hostname) {
502
567
  if (s3m?.groups) {
503
568
  return { service: "s3", region: s3m.groups.region };
504
569
  }
570
+ const vpcem = VPCE_HOST_RE.exec(lower);
571
+ if (vpcem?.groups) {
572
+ return { service: vpcem.groups.service, region: vpcem.groups.region };
573
+ }
505
574
  return { service: null, region: null };
506
575
  }
507
576
  function deriveSigningKey(secretKey, dateStamp, region, service) {
@@ -536,18 +605,21 @@ function canonicalQueryString(query) {
536
605
  ]);
537
606
  }
538
607
  }
539
- sorted.sort((a, b) => {
608
+ const sigv4Encode = (s) => encodeURIComponent(s).replace(
609
+ /[!'()*]/g,
610
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
611
+ );
612
+ const encoded = sorted.map(
613
+ ([k, v]) => [sigv4Encode(k), sigv4Encode(v)]
614
+ );
615
+ encoded.sort((a, b) => {
540
616
  if (a[0] < b[0]) return -1;
541
617
  if (a[0] > b[0]) return 1;
542
618
  if (a[1] < b[1]) return -1;
543
619
  if (a[1] > b[1]) return 1;
544
620
  return 0;
545
621
  });
546
- const sigv4Encode = (s) => encodeURIComponent(s).replace(
547
- /[!'()*]/g,
548
- (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
549
- );
550
- return sorted.map(([k, v]) => `${sigv4Encode(k)}=${sigv4Encode(v)}`).join("&");
622
+ return encoded.map(([k, v]) => `${k}=${v}`).join("&");
551
623
  }
552
624
  function canonicalHeadersAndSigned(headers) {
553
625
  const canonical = {};
@@ -566,11 +638,9 @@ function signAwsRequest(opts) {
566
638
  const parsed = new URL(opts.url);
567
639
  const hostname = parsed.hostname;
568
640
  let { region, service } = opts;
569
- if (region == null || service == null) {
570
- const detected = detectServiceAndRegion(hostname);
571
- if (region == null) region = detected.region;
572
- if (service == null) service = detected.service;
573
- }
641
+ const detected = detectServiceAndRegion(hostname);
642
+ if (detected.region != null) region = detected.region;
643
+ if (detected.service != null) service = detected.service;
574
644
  if (!region) {
575
645
  throw new Error(
576
646
  `Cannot determine AWS region from URL '${opts.url}'. Pass region explicitly via additional_credentials.`
@@ -653,7 +723,7 @@ function _extractAdditionalCredentials(token) {
653
723
  return _additionalCredsStore.get(token);
654
724
  }
655
725
  var _fetch;
656
- var SDK_VERSION = "0.5.0";
726
+ var SDK_VERSION = "0.7.0";
657
727
  var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
658
728
  var HTTP_FORBIDDEN = 403;
659
729
  var HTTP_NOT_FOUND = 404;
@@ -765,6 +835,7 @@ var AlterVault = class _AlterVault {
765
835
  #actorVersion;
766
836
  #clientType;
767
837
  #framework;
838
+ #toolBundleId;
768
839
  constructor(options) {
769
840
  _AlterVault.#validateInitParams(
770
841
  options.apiKey,
@@ -776,13 +847,14 @@ var AlterVault = class _AlterVault {
776
847
  ["actorName", options.actorName],
777
848
  ["actorVersion", options.actorVersion],
778
849
  ["clientType", options.clientType],
779
- ["framework", options.framework]
850
+ ["framework", options.framework],
851
+ ["toolBundleId", options.toolBundleId]
780
852
  ];
781
853
  for (const [name, value] of actorStrings) {
782
854
  _AlterVault.#validateActorString(value, name);
783
855
  }
784
856
  this.#hmacKey = (0, import_node_crypto2.createHmac)("sha256", options.apiKey).update("alter-signing-v1").digest();
785
- this.baseUrl = (process.env.ALTER_BASE_URL ?? "https://backend.alterai.dev").replace(/\/+$/, "");
857
+ this.baseUrl = (process.env.ALTER_BASE_URL ?? "https://backend.alterauth.com").replace(/\/+$/, "");
786
858
  const timeoutMs = options.timeout ?? 3e4;
787
859
  this.#actorType = options.actorType;
788
860
  this.#actorIdentifier = options.actorIdentifier;
@@ -791,6 +863,7 @@ var AlterVault = class _AlterVault {
791
863
  this.#clientType = options.clientType;
792
864
  this.#userTokenGetter = options.userTokenGetter ?? null;
793
865
  this.#framework = options.framework;
866
+ this.#toolBundleId = options.toolBundleId;
794
867
  if (!_fetch) {
795
868
  _fetch = globalThis.fetch;
796
869
  }
@@ -838,7 +911,7 @@ var AlterVault = class _AlterVault {
838
911
  }
839
912
  if (!actorType) {
840
913
  throw new AlterSDKError(
841
- "actor_type is required (use ActorType.AI_AGENT, ActorType.MCP_SERVER, or ActorType.BACKEND_SERVICE)"
914
+ "actor_type is required (use ActorType.AI_AGENT or ActorType.BACKEND_SERVICE)"
842
915
  );
843
916
  }
844
917
  const validValues = Object.values(ActorType);
@@ -867,7 +940,8 @@ var AlterVault = class _AlterVault {
867
940
  "X-Alter-Actor-Name": this.#actorName,
868
941
  "X-Alter-Actor-Version": this.#actorVersion,
869
942
  "X-Alter-Client-Type": this.#clientType,
870
- "X-Alter-Framework": this.#framework
943
+ "X-Alter-Framework": this.#framework,
944
+ "X-Alter-Tool-Bundle-ID": this.#toolBundleId
871
945
  };
872
946
  for (const [key, value] of Object.entries(actorHeaders)) {
873
947
  if (value) {
@@ -879,21 +953,24 @@ var AlterVault = class _AlterVault {
879
953
  /**
880
954
  * Compute HMAC-SHA256 signature headers for an Alter backend request.
881
955
  *
882
- * String-to-sign format: METHOD\nPATH_WITH_SORTED_QUERY\nTIMESTAMP\nCONTENT_HASH
956
+ * String-to-sign format: METHOD\nPATH_WITH_SORTED_QUERY\nTIMESTAMP\nNONCE\nCONTENT_HASH
883
957
  *
884
958
  * The path should include sorted query parameters if present (e.g. "/sdk/endpoint?a=1&b=2").
885
- * Currently all SDKbackend calls are POSTs without query params, so the path is clean.
959
+ * Currently all SDK->backend calls are POSTs without query params, so the path is clean.
886
960
  */
887
961
  #computeHmacHeaders(method, path, body) {
888
962
  const timestamp = String(Math.floor(Date.now() / 1e3));
963
+ const nonce = (0, import_node_crypto2.randomBytes)(16).toString("hex");
889
964
  const contentHash = (0, import_node_crypto2.createHash)("sha256").update(body ?? "").digest("hex");
890
965
  const stringToSign = `${method.toUpperCase()}
891
966
  ${path}
892
967
  ${timestamp}
968
+ ${nonce}
893
969
  ${contentHash}`;
894
970
  const signature = (0, import_node_crypto2.createHmac)("sha256", this.#hmacKey).update(stringToSign).digest("hex");
895
971
  return {
896
972
  "X-Alter-Timestamp": timestamp,
973
+ "X-Alter-Nonce": nonce,
897
974
  "X-Alter-Content-SHA256": contentHash,
898
975
  "X-Alter-Signature": signature
899
976
  };
@@ -901,7 +978,7 @@ ${contentHash}`;
901
978
  /**
902
979
  * Build per-request actor headers for instance tracking.
903
980
  */
904
- #getActorRequestHeaders(runId, threadId, toolCallId) {
981
+ #getActorRequestHeaders(runId, threadId, toolCallId, toolId, toolBundleId) {
905
982
  const headers = {};
906
983
  if (this.#cachedActorId) {
907
984
  headers["X-Alter-Actor-ID"] = this.#cachedActorId;
@@ -931,6 +1008,24 @@ ${contentHash}`;
931
1008
  );
932
1009
  }
933
1010
  }
1011
+ if (toolId) {
1012
+ if (toolId.length <= MAX_TRACKING_ID_LENGTH && SAFE_HEADER_PATTERN.test(toolId)) {
1013
+ headers["X-Alter-Tool-ID"] = toolId;
1014
+ } else {
1015
+ console.warn(
1016
+ "Invalid tool_id (too long or invalid chars), skipping"
1017
+ );
1018
+ }
1019
+ }
1020
+ if (toolBundleId) {
1021
+ if (toolBundleId.length <= MAX_TRACKING_ID_LENGTH && SAFE_HEADER_PATTERN.test(toolBundleId)) {
1022
+ headers["X-Alter-Tool-Bundle-ID"] = toolBundleId;
1023
+ } else {
1024
+ console.warn(
1025
+ "Invalid tool_bundle_id (too long or invalid chars), skipping"
1026
+ );
1027
+ }
1028
+ }
934
1029
  return headers;
935
1030
  }
936
1031
  /**
@@ -982,9 +1077,20 @@ ${contentHash}`;
982
1077
  }
983
1078
  return null;
984
1079
  }
1080
+ /**
1081
+ * Parse JSON from response, unwrapping FastAPI's HTTPException envelope.
1082
+ *
1083
+ * FastAPI wraps `HTTPException.detail` in `{"detail": ...}`. When the
1084
+ * inner detail is an object we unwrap it so callers can access error
1085
+ * fields (`error`, `message`, `details`) directly.
1086
+ */
985
1087
  static async #safeParseJson(response) {
986
1088
  try {
987
- return await response.clone().json();
1089
+ const data = await response.clone().json();
1090
+ if ("detail" in data && typeof data.detail === "object" && data.detail !== null && !Array.isArray(data.detail)) {
1091
+ return data.detail;
1092
+ }
1093
+ return data;
988
1094
  } catch {
989
1095
  try {
990
1096
  const text = await response.clone().text();
@@ -1003,38 +1109,69 @@ ${contentHash}`;
1003
1109
  }
1004
1110
  if (response.status === HTTP_FORBIDDEN) {
1005
1111
  const errorData = await _AlterVault.#safeParseJson(response);
1006
- if (errorData.error === "connection_expired") {
1007
- throw new ConnectionExpiredError(
1008
- errorData.message ?? "Connection expired per TTL policy",
1112
+ const errorCode = errorData.error;
1113
+ if (errorCode === "grant_expired") {
1114
+ throw new GrantExpiredError(
1115
+ errorData.message ?? "Grant expired per TTL policy",
1009
1116
  errorData.details
1010
1117
  );
1011
1118
  }
1119
+ if (errorCode === "grant_revoked") {
1120
+ throw new GrantRevokedError(
1121
+ errorData.message ?? "Grant has been revoked. User must re-authorize.",
1122
+ errorData.grant_id
1123
+ );
1124
+ }
1125
+ if (errorCode === "credential_revoked") {
1126
+ throw new CredentialRevokedError(
1127
+ errorData.message ?? "Credential has been revoked. User must re-authorize.",
1128
+ errorData.grant_id
1129
+ );
1130
+ }
1131
+ if (errorData.error === "scope_mismatch") {
1132
+ const details = errorData.details ?? {};
1133
+ throw new ScopeReauthRequiredError(
1134
+ errorData.message ?? "Grant is missing required scopes",
1135
+ details.grant_id ?? void 0,
1136
+ details.provider_id ?? void 0,
1137
+ response.status,
1138
+ JSON.stringify(errorData),
1139
+ details
1140
+ );
1141
+ }
1012
1142
  throw new PolicyViolationError(
1013
1143
  errorData.message ?? "Access denied by policy",
1014
- errorData.error,
1144
+ errorCode,
1015
1145
  errorData.details
1016
1146
  );
1017
1147
  }
1018
1148
  if (response.status === HTTP_GONE) {
1019
1149
  const errorData = await _AlterVault.#safeParseJson(response);
1020
- throw new ConnectionDeletedError(
1021
- errorData.message ?? "Connection has been deleted. A new connection_id will be issued on re-authorization.",
1150
+ throw new GrantDeletedError(
1151
+ errorData.message ?? "Grant has been deleted. A new grant_id will be issued on re-authorization.",
1022
1152
  errorData
1023
1153
  );
1024
1154
  }
1025
1155
  if (response.status === HTTP_NOT_FOUND) {
1026
1156
  const errorData = await _AlterVault.#safeParseJson(response);
1027
- throw new ConnectionNotFoundError(
1028
- errorData.message ?? "OAuth connection not found for the given connection_id",
1157
+ throw new GrantNotFoundError(
1158
+ errorData.message ?? "OAuth grant not found for the given grant_id",
1029
1159
  errorData
1030
1160
  );
1031
1161
  }
1032
1162
  if (response.status === HTTP_BAD_REQUEST || response.status === HTTP_BAD_GATEWAY) {
1033
1163
  const errorData = await _AlterVault.#safeParseJson(response);
1034
- if (errorData.error === "connection_revoked") {
1035
- throw new ConnectionRevokedError(
1036
- errorData.message ?? "Connection has been revoked. User must re-authorize.",
1037
- errorData.connection_id,
1164
+ if (errorData.error === "grant_revoked") {
1165
+ throw new GrantRevokedError(
1166
+ errorData.message ?? "Grant has been revoked. User must re-authorize.",
1167
+ errorData.grant_id,
1168
+ errorData
1169
+ );
1170
+ }
1171
+ if (errorData.error === "credential_revoked") {
1172
+ throw new CredentialRevokedError(
1173
+ errorData.message ?? "Underlying credential has been revoked. User must re-authorize.",
1174
+ errorData.grant_id,
1038
1175
  errorData
1039
1176
  );
1040
1177
  }
@@ -1084,11 +1221,13 @@ ${contentHash}`;
1084
1221
  }
1085
1222
  return token;
1086
1223
  }
1087
- async #getToken(connectionId, reason, requestMetadata, runId, threadId, toolCallId, provider, account) {
1224
+ async #getToken(grantId, reason, requestMetadata, runId, threadId, toolCallId, toolId, provider, account, toolBundleId) {
1088
1225
  const actorHeaders = this.#getActorRequestHeaders(
1089
1226
  runId,
1090
1227
  threadId,
1091
- toolCallId
1228
+ toolCallId,
1229
+ toolId,
1230
+ toolBundleId
1092
1231
  );
1093
1232
  let response;
1094
1233
  let tokenBody;
@@ -1105,7 +1244,7 @@ ${contentHash}`;
1105
1244
  }
1106
1245
  } else {
1107
1246
  tokenBody = {
1108
- connection_id: connectionId,
1247
+ grant_id: grantId,
1109
1248
  reason: reason ?? null,
1110
1249
  request: requestMetadata ?? null
1111
1250
  };
@@ -1133,7 +1272,7 @@ ${contentHash}`;
1133
1272
  }
1134
1273
  throw new BackendError(
1135
1274
  `Failed to retrieve token: ${error instanceof Error ? error.message : String(error)}`,
1136
- { connection_id: connectionId, error: String(error) }
1275
+ { grant_id: grantId, error: String(error) }
1137
1276
  );
1138
1277
  }
1139
1278
  this.#cacheActorIdFromResponse(response);
@@ -1145,13 +1284,13 @@ ${contentHash}`;
1145
1284
  if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(tokenResponse.injectionHeader)) {
1146
1285
  throw new BackendError(
1147
1286
  `Backend returned invalid injection_header: ${tokenResponse.injectionHeader}`,
1148
- { connectionId: String(connectionId) }
1287
+ { grantId: String(grantId) }
1149
1288
  );
1150
1289
  }
1151
1290
  if (/[\r\n\x00]/.test(tokenResponse.injectionFormat)) {
1152
1291
  throw new BackendError(
1153
1292
  `Backend returned invalid injection_format (contains control characters)`,
1154
- { connectionId: String(connectionId) }
1293
+ { grantId: String(grantId) }
1155
1294
  );
1156
1295
  }
1157
1296
  _storeAccessToken(tokenResponse, typedData.access_token);
@@ -1169,20 +1308,22 @@ ${contentHash}`;
1169
1308
  async #logApiCall(params) {
1170
1309
  try {
1171
1310
  const auditLog = new APICallAuditLog({
1172
- connectionId: params.connectionId,
1173
- providerId: params.providerId,
1311
+ grant_id: params.grantId,
1312
+ provider_id: params.providerId,
1174
1313
  method: params.method,
1175
1314
  url: params.url,
1176
- requestHeaders: params.requestHeaders,
1177
- requestBody: params.requestBody,
1178
- responseStatus: params.responseStatus,
1179
- responseHeaders: params.responseHeaders,
1180
- responseBody: params.responseBody,
1181
- latencyMs: params.latencyMs,
1315
+ request_headers: params.requestHeaders,
1316
+ request_body: params.requestBody,
1317
+ response_status: params.responseStatus,
1318
+ response_headers: params.responseHeaders,
1319
+ response_body: params.responseBody,
1320
+ latency_ms: params.latencyMs,
1182
1321
  reason: params.reason,
1183
- runId: params.runId,
1184
- threadId: params.threadId,
1185
- toolCallId: params.toolCallId
1322
+ run_id: params.runId,
1323
+ thread_id: params.threadId,
1324
+ tool_call_id: params.toolCallId,
1325
+ tool_id: params.toolId,
1326
+ tool_bundle_id: params.toolBundleId
1186
1327
  });
1187
1328
  const sanitized = auditLog.sanitize();
1188
1329
  const actorHeaders = this.#getActorRequestHeaders(params.runId);
@@ -1258,7 +1399,7 @@ ${contentHash}`;
1258
1399
  * 4. Logs the call for audit (fire-and-forget)
1259
1400
  * 5. Returns the raw response
1260
1401
  */
1261
- async request(connectionId, method, url, options) {
1402
+ async request(grantId, method, url, options) {
1262
1403
  if (this.#closed) {
1263
1404
  throw new AlterSDKError(
1264
1405
  "SDK instance has been closed. Create a new AlterVault instance to make requests."
@@ -1266,13 +1407,13 @@ ${contentHash}`;
1266
1407
  }
1267
1408
  const provider = options?.provider;
1268
1409
  const account = options?.account;
1269
- if (!connectionId && !provider) {
1270
- throw new AlterSDKError("Provide connectionId or options.provider");
1410
+ if (!grantId && !provider) {
1411
+ throw new AlterSDKError("Provide grantId or options.provider");
1271
1412
  }
1272
- if (connectionId && provider) {
1273
- throw new AlterSDKError("Cannot provide both connectionId and options.provider");
1413
+ if (grantId && provider) {
1414
+ throw new AlterSDKError("Cannot provide both grantId and options.provider");
1274
1415
  }
1275
- const effectiveConnectionId = connectionId ?? null;
1416
+ const effectiveGrantId = grantId ?? null;
1276
1417
  let currentUrl = url;
1277
1418
  const runId = options?.runId ?? (0, import_node_crypto2.randomUUID)();
1278
1419
  const methodStr = String(method).toUpperCase();
@@ -1313,14 +1454,16 @@ ${contentHash}`;
1313
1454
  }
1314
1455
  }
1315
1456
  const { tokenResponse, scopeMismatch } = await this.#getToken(
1316
- effectiveConnectionId,
1457
+ effectiveGrantId,
1317
1458
  options?.reason,
1318
1459
  { method: methodStr, url: currentUrl },
1319
1460
  runId,
1320
1461
  options?.threadId,
1321
1462
  options?.toolCallId,
1463
+ options?.toolId,
1322
1464
  provider,
1323
- account
1465
+ account,
1466
+ options?.toolBundleId
1324
1467
  );
1325
1468
  const requestHeaders = options?.extraHeaders ? { ...options.extraHeaders } : {};
1326
1469
  const accessToken = _extractAccessToken(tokenResponse);
@@ -1339,7 +1482,7 @@ ${contentHash}`;
1339
1482
  if (!additionalCreds.secret_key) {
1340
1483
  throw new BackendError(
1341
1484
  "AWS SigV4 credential is missing secret_key in additional_credentials. Re-store the credential with both Access Key ID and Secret Access Key.",
1342
- { connection_id: effectiveConnectionId }
1485
+ { grant_id: effectiveGrantId }
1343
1486
  );
1344
1487
  }
1345
1488
  const awsHeaders = signAwsRequest({
@@ -1406,7 +1549,7 @@ ${contentHash}`;
1406
1549
  throw new TimeoutError(
1407
1550
  `Provider API request timed out: ${error instanceof Error ? error.message : String(error)}`,
1408
1551
  {
1409
- connection_id: effectiveConnectionId,
1552
+ grant_id: effectiveGrantId,
1410
1553
  method: methodStr,
1411
1554
  url: currentUrl
1412
1555
  }
@@ -1415,7 +1558,7 @@ ${contentHash}`;
1415
1558
  throw new NetworkError(
1416
1559
  `Failed to call provider API: ${error instanceof Error ? error.message : String(error)}`,
1417
1560
  {
1418
- connection_id: effectiveConnectionId,
1561
+ grant_id: effectiveGrantId,
1419
1562
  method: methodStr,
1420
1563
  url: currentUrl,
1421
1564
  error: String(error)
@@ -1444,8 +1587,8 @@ ${contentHash}`;
1444
1587
  responseHeaders[key] = value;
1445
1588
  });
1446
1589
  this.#scheduleAuditLog({
1447
- connectionId: tokenResponse.connectionId,
1448
- providerId: tokenResponse.providerId || effectiveConnectionId || "",
1590
+ grantId: tokenResponse.grantId,
1591
+ providerId: tokenResponse.providerId || effectiveGrantId || "",
1449
1592
  method: methodStr,
1450
1593
  url: auditUrl,
1451
1594
  requestHeaders: auditHeaders,
@@ -1457,18 +1600,20 @@ ${contentHash}`;
1457
1600
  reason: options?.reason ?? null,
1458
1601
  runId,
1459
1602
  threadId: options?.threadId ?? null,
1460
- toolCallId: options?.toolCallId ?? null
1603
+ toolCallId: options?.toolCallId ?? null,
1604
+ toolId: options?.toolId ?? null,
1605
+ toolBundleId: options?.toolBundleId ?? null
1461
1606
  });
1462
1607
  if (response.status >= HTTP_CLIENT_ERROR_START) {
1463
1608
  if (response.status === HTTP_FORBIDDEN && scopeMismatch) {
1464
1609
  throw new ScopeReauthRequiredError(
1465
- "Provider API returned 403 and the connection's scopes don't match the provider config. The user needs to re-authorize to grant updated permissions.",
1466
- tokenResponse.connectionId,
1467
- tokenResponse.providerId,
1610
+ "Provider API returned 403 and the grant's scopes don't match the provider config. The user needs to re-authorize to grant updated permissions.",
1611
+ tokenResponse.grantId,
1612
+ tokenResponse.providerId ?? void 0,
1468
1613
  response.status,
1469
1614
  responseBody,
1470
1615
  {
1471
- connection_id: tokenResponse.connectionId,
1616
+ grant_id: tokenResponse.grantId,
1472
1617
  provider_id: tokenResponse.providerId,
1473
1618
  method: methodStr,
1474
1619
  url: currentUrl
@@ -1480,7 +1625,7 @@ ${contentHash}`;
1480
1625
  response.status,
1481
1626
  responseBody,
1482
1627
  {
1483
- connection_id: effectiveConnectionId,
1628
+ grant_id: effectiveGrantId,
1484
1629
  method: methodStr,
1485
1630
  url: currentUrl
1486
1631
  }
@@ -1489,12 +1634,12 @@ ${contentHash}`;
1489
1634
  return response;
1490
1635
  }
1491
1636
  /**
1492
- * List OAuth connections for this app.
1637
+ * List OAuth grants for this app.
1493
1638
  *
1494
- * Returns paginated connection metadata (no tokens).
1639
+ * Returns paginated grant metadata (no tokens).
1495
1640
  * Useful for discovering which services a user has connected.
1496
1641
  */
1497
- async listConnections(options) {
1642
+ async listGrants(options) {
1498
1643
  if (this.#closed) {
1499
1644
  throw new AlterSDKError(
1500
1645
  "SDK instance has been closed. Create a new AlterVault instance to make requests."
@@ -1512,12 +1657,12 @@ ${contentHash}`;
1512
1657
  listBody.user_token = await this.#getUserToken();
1513
1658
  } catch (err) {
1514
1659
  console.warn(
1515
- "user_token_getter failed in listConnections, falling back to un-scoped listing:",
1660
+ "user_token_getter failed in listGrants, falling back to un-scoped listing:",
1516
1661
  err instanceof Error ? err.message : String(err)
1517
1662
  );
1518
1663
  }
1519
1664
  }
1520
- const listPath = "/sdk/oauth/connections/list";
1665
+ const listPath = "/sdk/oauth/grants/list";
1521
1666
  const listBodyStr = JSON.stringify(listBody);
1522
1667
  const listHmac = this.#computeHmacHeaders("POST", listPath, listBodyStr);
1523
1668
  try {
@@ -1539,19 +1684,19 @@ ${contentHash}`;
1539
1684
  );
1540
1685
  }
1541
1686
  throw new AlterSDKError(
1542
- `Failed to list connections: ${error instanceof Error ? error.message : String(error)}`
1687
+ `Failed to list grants: ${error instanceof Error ? error.message : String(error)}`
1543
1688
  );
1544
1689
  }
1545
1690
  this.#cacheActorIdFromResponse(response);
1546
1691
  await this.#handleErrorResponse(response);
1547
1692
  const data = await response.json();
1548
- const connections = data.connections.map(
1549
- (c) => new ConnectionInfo(
1550
- c
1693
+ const grants = data.grants.map(
1694
+ (g) => new GrantInfo(
1695
+ g
1551
1696
  )
1552
1697
  );
1553
- return new ConnectionListResult({
1554
- connections,
1698
+ return new GrantListResult({
1699
+ grants,
1555
1700
  total: data.total,
1556
1701
  limit: data.limit,
1557
1702
  offset: data.offset,
@@ -1577,10 +1722,10 @@ ${contentHash}`;
1577
1722
  allowed_origin: options.allowedOrigin ?? null,
1578
1723
  metadata: options.metadata ?? null
1579
1724
  };
1580
- if (options.connectionPolicy) {
1581
- sessionBody.connection_policy = {
1582
- max_ttl_seconds: options.connectionPolicy.maxTtlSeconds ?? null,
1583
- default_ttl_seconds: options.connectionPolicy.defaultTtlSeconds ?? null
1725
+ if (options.grantPolicy) {
1726
+ sessionBody.grant_policy = {
1727
+ max_ttl_seconds: options.grantPolicy.maxTtlSeconds ?? null,
1728
+ default_ttl_seconds: options.grantPolicy.defaultTtlSeconds ?? null
1584
1729
  };
1585
1730
  }
1586
1731
  if (this.#userTokenGetter) {
@@ -1647,7 +1792,7 @@ ${contentHash}`;
1647
1792
  const openBrowser = options.openBrowser ?? true;
1648
1793
  const session = await this.createConnectSession({
1649
1794
  allowedProviders: options.providers,
1650
- connectionPolicy: options.connectionPolicy
1795
+ grantPolicy: options.grantPolicy
1651
1796
  });
1652
1797
  if (openBrowser) {
1653
1798
  try {
@@ -1678,14 +1823,14 @@ ${contentHash}`;
1678
1823
  const pollResult = await this.#pollSession(session.sessionToken);
1679
1824
  const pollStatus = pollResult.status;
1680
1825
  if (pollStatus === "completed") {
1681
- const connectionsData = pollResult.connections ?? [];
1682
- return connectionsData.map(
1683
- (conn) => new ConnectResult({
1684
- connection_id: conn.connection_id ?? "",
1685
- provider_id: conn.provider_id ?? "",
1686
- account_identifier: conn.account_identifier ?? null,
1687
- scopes: conn.scopes ?? [],
1688
- connection_policy: conn.connection_policy ?? null
1826
+ const grantsData = pollResult.grants ?? [];
1827
+ return grantsData.map(
1828
+ (grant) => new ConnectResult({
1829
+ grant_id: grant.grant_id ?? "",
1830
+ provider_id: grant.provider_id ?? "",
1831
+ account_identifier: grant.account_identifier ?? null,
1832
+ scopes: grant.scopes ?? [],
1833
+ grant_policy: grant.grant_policy ?? null
1689
1834
  })
1690
1835
  );
1691
1836
  }
@@ -1712,12 +1857,133 @@ ${contentHash}`;
1712
1857
  "Connect session expired before OAuth was completed"
1713
1858
  );
1714
1859
  }
1860
+ if (pollStatus !== "pending") {
1861
+ throw new ConnectFlowError(
1862
+ `Unexpected poll status from server: ${pollStatus}`,
1863
+ { status: pollStatus }
1864
+ );
1865
+ }
1715
1866
  }
1716
1867
  throw new ConnectTimeoutError(
1717
1868
  `OAuth flow did not complete within ${timeout} seconds. The user may not have finished authorizing in the browser.`,
1718
1869
  { timeout }
1719
1870
  );
1720
1871
  }
1872
+ /**
1873
+ * Trigger IDP login for end user via browser.
1874
+ *
1875
+ * Opens the app's configured IDP login page in the user's default browser.
1876
+ * Polls for completion and returns the user's IDP JWT token.
1877
+ *
1878
+ * After authenticate() completes, the returned userToken is automatically
1879
+ * used for subsequent request() calls that use identity resolution.
1880
+ *
1881
+ * @param options - Optional configuration (timeout in milliseconds, default 300000 = 5 min)
1882
+ * @returns AuthResult with userToken and userInfo
1883
+ * @throws ConnectTimeoutError if authentication times out
1884
+ * @throws AlterSDKError if session creation or authentication fails
1885
+ */
1886
+ async authenticate(options) {
1887
+ if (this.#closed) {
1888
+ throw new AlterSDKError(
1889
+ "SDK instance has been closed. Create a new AlterVault instance to make requests."
1890
+ );
1891
+ }
1892
+ const timeoutMs = options?.timeout ?? 3e5;
1893
+ const actorHeaders = this.#getActorRequestHeaders();
1894
+ const sessionPath = "/sdk/auth/session";
1895
+ const sessionBodyStr = JSON.stringify({});
1896
+ const sessionHmac = this.#computeHmacHeaders("POST", sessionPath, sessionBodyStr);
1897
+ let sessionResp;
1898
+ try {
1899
+ sessionResp = await this.#alterClient.post(sessionPath, {
1900
+ body: sessionBodyStr,
1901
+ headers: { ...actorHeaders, ...sessionHmac, "Content-Type": "application/json" }
1902
+ });
1903
+ } catch (error) {
1904
+ if (_AlterVault.#isTimeoutOrAbortError(error)) {
1905
+ throw new TimeoutError(
1906
+ `Request to Alter Vault backend timed out: ${error instanceof Error ? error.message : String(error)}`,
1907
+ { base_url: this.baseUrl }
1908
+ );
1909
+ }
1910
+ if (error instanceof TypeError) {
1911
+ throw new NetworkError(
1912
+ `Failed to connect to Alter Vault backend: ${error.message}`,
1913
+ { base_url: this.baseUrl }
1914
+ );
1915
+ }
1916
+ throw new AlterSDKError(
1917
+ `Failed to create auth session: ${error instanceof Error ? error.message : String(error)}`
1918
+ );
1919
+ }
1920
+ this.#cacheActorIdFromResponse(sessionResp);
1921
+ await this.#handleErrorResponse(sessionResp);
1922
+ const sessionData = await sessionResp.json();
1923
+ try {
1924
+ const openModule = await import("open");
1925
+ const openFn = openModule.default;
1926
+ if (openFn) {
1927
+ await openFn(sessionData.auth_url);
1928
+ } else {
1929
+ console.log(
1930
+ `Open this URL to authenticate: ${sessionData.auth_url}`
1931
+ );
1932
+ }
1933
+ } catch {
1934
+ console.log(
1935
+ `Open this URL to authenticate: ${sessionData.auth_url}`
1936
+ );
1937
+ }
1938
+ const startTime = Date.now();
1939
+ while (true) {
1940
+ if (Date.now() - startTime > timeoutMs) {
1941
+ throw new ConnectTimeoutError(
1942
+ "Authentication timed out",
1943
+ { timeout: timeoutMs }
1944
+ );
1945
+ }
1946
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
1947
+ const pollPath = "/sdk/auth/poll";
1948
+ const pollBodyStr = JSON.stringify({ session_token: sessionData.session_token });
1949
+ const pollHmac = this.#computeHmacHeaders("POST", pollPath, pollBodyStr);
1950
+ let pollResp;
1951
+ try {
1952
+ pollResp = await this.#alterClient.post(pollPath, {
1953
+ body: pollBodyStr,
1954
+ headers: { ...actorHeaders, ...pollHmac, "Content-Type": "application/json" }
1955
+ });
1956
+ } catch {
1957
+ continue;
1958
+ }
1959
+ this.#cacheActorIdFromResponse(pollResp);
1960
+ if (!pollResp.ok) {
1961
+ continue;
1962
+ }
1963
+ const pollData = await pollResp.json();
1964
+ if (pollData.status === "completed") {
1965
+ const userToken = pollData.user_token;
1966
+ if (!userToken) {
1967
+ throw new AlterSDKError(
1968
+ "Authentication completed but no user token was returned by the IDP"
1969
+ );
1970
+ }
1971
+ this.#userTokenGetter = () => userToken;
1972
+ return new AuthResult({
1973
+ user_token: userToken,
1974
+ user_info: pollData.user_info
1975
+ });
1976
+ }
1977
+ if (pollData.status === "error") {
1978
+ throw new AlterSDKError(
1979
+ pollData.error_message ?? "Authentication failed"
1980
+ );
1981
+ }
1982
+ if (pollData.status === "expired") {
1983
+ throw new AlterSDKError("Authentication session expired");
1984
+ }
1985
+ }
1986
+ }
1721
1987
  /**
1722
1988
  * Poll the Connect session for completion status (INTERNAL).
1723
1989
  *
@@ -1869,6 +2135,7 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
1869
2135
  ActorType,
1870
2136
  AlterSDKError,
1871
2137
  AlterVault,
2138
+ AuthResult,
1872
2139
  BackendError,
1873
2140
  ConnectConfigError,
1874
2141
  ConnectDeniedError,
@@ -1876,12 +2143,13 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
1876
2143
  ConnectResult,
1877
2144
  ConnectSession,
1878
2145
  ConnectTimeoutError,
1879
- ConnectionDeletedError,
1880
- ConnectionExpiredError,
1881
- ConnectionInfo,
1882
- ConnectionListResult,
1883
- ConnectionNotFoundError,
1884
- ConnectionRevokedError,
2146
+ CredentialRevokedError,
2147
+ GrantDeletedError,
2148
+ GrantExpiredError,
2149
+ GrantInfo,
2150
+ GrantListResult,
2151
+ GrantNotFoundError,
2152
+ GrantRevokedError,
1885
2153
  HttpMethod,
1886
2154
  NetworkError,
1887
2155
  PolicyViolationError,