@alter-ai/alter-sdk 0.6.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,7 +279,7 @@ 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,
274
283
  provider_id: this.providerId
275
284
  };
276
285
  }
@@ -278,7 +287,7 @@ var TokenResponse = class _TokenResponse {
278
287
  * Custom string representation — EXCLUDES access_token for security.
279
288
  */
280
289
  toString() {
281
- 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(", ")}])`;
282
291
  }
283
292
  /**
284
293
  * Custom Node.js inspect output — EXCLUDES access_token for security.
@@ -287,8 +296,8 @@ var TokenResponse = class _TokenResponse {
287
296
  return this.toString();
288
297
  }
289
298
  };
290
- var ConnectionInfo = class {
291
- connectionId;
299
+ var GrantInfo = class {
300
+ grantId;
292
301
  providerId;
293
302
  scopes;
294
303
  accountIdentifier;
@@ -299,7 +308,7 @@ var ConnectionInfo = class {
299
308
  createdAt;
300
309
  lastUsedAt;
301
310
  constructor(data) {
302
- this.connectionId = data.connection_id;
311
+ this.grantId = data.grant_id;
303
312
  this.providerId = data.provider_id;
304
313
  this.scopes = data.scopes ?? [];
305
314
  this.accountIdentifier = data.account_identifier ?? null;
@@ -313,7 +322,7 @@ var ConnectionInfo = class {
313
322
  }
314
323
  toJSON() {
315
324
  return {
316
- connection_id: this.connectionId,
325
+ grant_id: this.grantId,
317
326
  provider_id: this.providerId,
318
327
  scopes: this.scopes,
319
328
  account_identifier: this.accountIdentifier,
@@ -326,7 +335,7 @@ var ConnectionInfo = class {
326
335
  };
327
336
  }
328
337
  toString() {
329
- return `ConnectionInfo(connection_id=${this.connectionId}, provider=${this.providerId}, status=${this.status})`;
338
+ return `GrantInfo(grant_id=${this.grantId}, provider=${this.providerId}, status=${this.status})`;
330
339
  }
331
340
  };
332
341
  var ConnectSession = class {
@@ -353,60 +362,91 @@ var ConnectSession = class {
353
362
  return `ConnectSession(url=${this.connectUrl}, expires_in=${this.expiresIn})`;
354
363
  }
355
364
  };
356
- var ConnectionListResult = class {
357
- connections;
365
+ var GrantListResult = class {
366
+ grants;
358
367
  total;
359
368
  limit;
360
369
  offset;
361
370
  hasMore;
362
371
  constructor(data) {
363
- this.connections = data.connections;
372
+ this.grants = data.grants;
364
373
  this.total = data.total;
365
374
  this.limit = data.limit;
366
375
  this.offset = data.offset;
367
376
  this.hasMore = data.has_more;
368
377
  Object.freeze(this);
369
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
+ }
370
391
  };
371
392
  var ConnectResult = class {
372
- connectionId;
393
+ grantId;
373
394
  providerId;
374
395
  accountIdentifier;
375
396
  scopes;
376
- connectionPolicy;
397
+ grantPolicy;
377
398
  constructor(data) {
378
- this.connectionId = data.connection_id;
399
+ this.grantId = data.grant_id;
379
400
  this.providerId = data.provider_id;
380
401
  this.accountIdentifier = data.account_identifier ?? null;
381
402
  this.scopes = data.scopes ?? [];
382
- const rawPolicy = data.connection_policy;
403
+ const rawPolicy = data.grant_policy;
383
404
  if (rawPolicy) {
384
405
  const raw = rawPolicy;
385
- this.connectionPolicy = Object.freeze({
406
+ this.grantPolicy = Object.freeze({
386
407
  expiresAt: raw["expires_at"] !== void 0 ? raw["expires_at"] : rawPolicy.expiresAt ?? null,
387
408
  createdBy: raw["created_by"] !== void 0 ? raw["created_by"] : rawPolicy.createdBy ?? null,
388
409
  createdAt: raw["created_at"] !== void 0 ? raw["created_at"] : rawPolicy.createdAt ?? null
389
410
  });
390
411
  } else {
391
- this.connectionPolicy = null;
412
+ this.grantPolicy = null;
392
413
  }
393
414
  Object.freeze(this);
394
415
  }
395
416
  toJSON() {
396
417
  return {
397
- connection_id: this.connectionId,
418
+ grant_id: this.grantId,
398
419
  provider_id: this.providerId,
399
420
  account_identifier: this.accountIdentifier,
400
421
  scopes: this.scopes,
401
- connection_policy: this.connectionPolicy ? {
402
- expires_at: this.connectionPolicy.expiresAt ?? null,
403
- created_by: this.connectionPolicy.createdBy ?? null,
404
- created_at: this.connectionPolicy.createdAt ?? null
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
405
426
  } : null
406
427
  };
407
428
  }
408
429
  toString() {
409
- 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})`;
410
450
  }
411
451
  };
412
452
  var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
@@ -420,7 +460,7 @@ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
420
460
  "x-amz-security-token"
421
461
  ]);
422
462
  var APICallAuditLog = class {
423
- connectionId;
463
+ grantId;
424
464
  providerId;
425
465
  method;
426
466
  url;
@@ -439,22 +479,28 @@ var APICallAuditLog = class {
439
479
  threadId;
440
480
  /** Tool invocation ID for actor tracking */
441
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;
442
486
  constructor(data) {
443
- this.connectionId = data.connectionId;
444
- this.providerId = data.providerId;
487
+ this.grantId = data.grant_id;
488
+ this.providerId = data.provider_id;
445
489
  this.method = data.method;
446
490
  this.url = data.url;
447
- this.requestHeaders = data.requestHeaders ?? null;
448
- this.requestBody = data.requestBody ?? null;
449
- this.responseStatus = data.responseStatus;
450
- this.responseHeaders = data.responseHeaders ?? null;
451
- this.responseBody = data.responseBody ?? null;
452
- 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;
453
497
  this.timestamp = /* @__PURE__ */ new Date();
454
498
  this.reason = data.reason ?? null;
455
- this.runId = data.runId ?? null;
456
- this.threadId = data.threadId ?? null;
457
- 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;
458
504
  }
459
505
  /**
460
506
  * Sanitize sensitive data before sending.
@@ -463,7 +509,7 @@ var APICallAuditLog = class {
463
509
  */
464
510
  sanitize() {
465
511
  const sanitized = {
466
- connection_id: this.connectionId,
512
+ grant_id: this.grantId,
467
513
  provider_id: this.providerId,
468
514
  method: this.method,
469
515
  url: this.url,
@@ -476,7 +522,9 @@ var APICallAuditLog = class {
476
522
  reason: this.reason,
477
523
  run_id: this.runId,
478
524
  thread_id: this.threadId,
479
- tool_call_id: this.toolCallId
525
+ tool_call_id: this.toolCallId,
526
+ tool_id: this.toolId,
527
+ tool_bundle_id: this.toolBundleId
480
528
  };
481
529
  return sanitized;
482
530
  }
@@ -675,7 +723,7 @@ function _extractAdditionalCredentials(token) {
675
723
  return _additionalCredsStore.get(token);
676
724
  }
677
725
  var _fetch;
678
- var SDK_VERSION = "0.6.0";
726
+ var SDK_VERSION = "0.7.0";
679
727
  var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
680
728
  var HTTP_FORBIDDEN = 403;
681
729
  var HTTP_NOT_FOUND = 404;
@@ -787,6 +835,7 @@ var AlterVault = class _AlterVault {
787
835
  #actorVersion;
788
836
  #clientType;
789
837
  #framework;
838
+ #toolBundleId;
790
839
  constructor(options) {
791
840
  _AlterVault.#validateInitParams(
792
841
  options.apiKey,
@@ -798,13 +847,14 @@ var AlterVault = class _AlterVault {
798
847
  ["actorName", options.actorName],
799
848
  ["actorVersion", options.actorVersion],
800
849
  ["clientType", options.clientType],
801
- ["framework", options.framework]
850
+ ["framework", options.framework],
851
+ ["toolBundleId", options.toolBundleId]
802
852
  ];
803
853
  for (const [name, value] of actorStrings) {
804
854
  _AlterVault.#validateActorString(value, name);
805
855
  }
806
856
  this.#hmacKey = (0, import_node_crypto2.createHmac)("sha256", options.apiKey).update("alter-signing-v1").digest();
807
- 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(/\/+$/, "");
808
858
  const timeoutMs = options.timeout ?? 3e4;
809
859
  this.#actorType = options.actorType;
810
860
  this.#actorIdentifier = options.actorIdentifier;
@@ -813,6 +863,7 @@ var AlterVault = class _AlterVault {
813
863
  this.#clientType = options.clientType;
814
864
  this.#userTokenGetter = options.userTokenGetter ?? null;
815
865
  this.#framework = options.framework;
866
+ this.#toolBundleId = options.toolBundleId;
816
867
  if (!_fetch) {
817
868
  _fetch = globalThis.fetch;
818
869
  }
@@ -860,7 +911,7 @@ var AlterVault = class _AlterVault {
860
911
  }
861
912
  if (!actorType) {
862
913
  throw new AlterSDKError(
863
- "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)"
864
915
  );
865
916
  }
866
917
  const validValues = Object.values(ActorType);
@@ -889,7 +940,8 @@ var AlterVault = class _AlterVault {
889
940
  "X-Alter-Actor-Name": this.#actorName,
890
941
  "X-Alter-Actor-Version": this.#actorVersion,
891
942
  "X-Alter-Client-Type": this.#clientType,
892
- "X-Alter-Framework": this.#framework
943
+ "X-Alter-Framework": this.#framework,
944
+ "X-Alter-Tool-Bundle-ID": this.#toolBundleId
893
945
  };
894
946
  for (const [key, value] of Object.entries(actorHeaders)) {
895
947
  if (value) {
@@ -901,21 +953,24 @@ var AlterVault = class _AlterVault {
901
953
  /**
902
954
  * Compute HMAC-SHA256 signature headers for an Alter backend request.
903
955
  *
904
- * 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
905
957
  *
906
958
  * The path should include sorted query parameters if present (e.g. "/sdk/endpoint?a=1&b=2").
907
- * 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.
908
960
  */
909
961
  #computeHmacHeaders(method, path, body) {
910
962
  const timestamp = String(Math.floor(Date.now() / 1e3));
963
+ const nonce = (0, import_node_crypto2.randomBytes)(16).toString("hex");
911
964
  const contentHash = (0, import_node_crypto2.createHash)("sha256").update(body ?? "").digest("hex");
912
965
  const stringToSign = `${method.toUpperCase()}
913
966
  ${path}
914
967
  ${timestamp}
968
+ ${nonce}
915
969
  ${contentHash}`;
916
970
  const signature = (0, import_node_crypto2.createHmac)("sha256", this.#hmacKey).update(stringToSign).digest("hex");
917
971
  return {
918
972
  "X-Alter-Timestamp": timestamp,
973
+ "X-Alter-Nonce": nonce,
919
974
  "X-Alter-Content-SHA256": contentHash,
920
975
  "X-Alter-Signature": signature
921
976
  };
@@ -923,7 +978,7 @@ ${contentHash}`;
923
978
  /**
924
979
  * Build per-request actor headers for instance tracking.
925
980
  */
926
- #getActorRequestHeaders(runId, threadId, toolCallId) {
981
+ #getActorRequestHeaders(runId, threadId, toolCallId, toolId, toolBundleId) {
927
982
  const headers = {};
928
983
  if (this.#cachedActorId) {
929
984
  headers["X-Alter-Actor-ID"] = this.#cachedActorId;
@@ -953,6 +1008,24 @@ ${contentHash}`;
953
1008
  );
954
1009
  }
955
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
+ }
956
1029
  return headers;
957
1030
  }
958
1031
  /**
@@ -1004,9 +1077,20 @@ ${contentHash}`;
1004
1077
  }
1005
1078
  return null;
1006
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
+ */
1007
1087
  static async #safeParseJson(response) {
1008
1088
  try {
1009
- 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;
1010
1094
  } catch {
1011
1095
  try {
1012
1096
  const text = await response.clone().text();
@@ -1025,38 +1109,69 @@ ${contentHash}`;
1025
1109
  }
1026
1110
  if (response.status === HTTP_FORBIDDEN) {
1027
1111
  const errorData = await _AlterVault.#safeParseJson(response);
1028
- if (errorData.error === "connection_expired") {
1029
- throw new ConnectionExpiredError(
1030
- 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",
1031
1116
  errorData.details
1032
1117
  );
1033
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
+ }
1034
1142
  throw new PolicyViolationError(
1035
1143
  errorData.message ?? "Access denied by policy",
1036
- errorData.error,
1144
+ errorCode,
1037
1145
  errorData.details
1038
1146
  );
1039
1147
  }
1040
1148
  if (response.status === HTTP_GONE) {
1041
1149
  const errorData = await _AlterVault.#safeParseJson(response);
1042
- throw new ConnectionDeletedError(
1043
- 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.",
1044
1152
  errorData
1045
1153
  );
1046
1154
  }
1047
1155
  if (response.status === HTTP_NOT_FOUND) {
1048
1156
  const errorData = await _AlterVault.#safeParseJson(response);
1049
- throw new ConnectionNotFoundError(
1050
- 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",
1051
1159
  errorData
1052
1160
  );
1053
1161
  }
1054
1162
  if (response.status === HTTP_BAD_REQUEST || response.status === HTTP_BAD_GATEWAY) {
1055
1163
  const errorData = await _AlterVault.#safeParseJson(response);
1056
- if (errorData.error === "connection_revoked") {
1057
- throw new ConnectionRevokedError(
1058
- errorData.message ?? "Connection has been revoked. User must re-authorize.",
1059
- 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,
1060
1175
  errorData
1061
1176
  );
1062
1177
  }
@@ -1106,11 +1221,13 @@ ${contentHash}`;
1106
1221
  }
1107
1222
  return token;
1108
1223
  }
1109
- async #getToken(connectionId, reason, requestMetadata, runId, threadId, toolCallId, provider, account) {
1224
+ async #getToken(grantId, reason, requestMetadata, runId, threadId, toolCallId, toolId, provider, account, toolBundleId) {
1110
1225
  const actorHeaders = this.#getActorRequestHeaders(
1111
1226
  runId,
1112
1227
  threadId,
1113
- toolCallId
1228
+ toolCallId,
1229
+ toolId,
1230
+ toolBundleId
1114
1231
  );
1115
1232
  let response;
1116
1233
  let tokenBody;
@@ -1127,7 +1244,7 @@ ${contentHash}`;
1127
1244
  }
1128
1245
  } else {
1129
1246
  tokenBody = {
1130
- connection_id: connectionId,
1247
+ grant_id: grantId,
1131
1248
  reason: reason ?? null,
1132
1249
  request: requestMetadata ?? null
1133
1250
  };
@@ -1155,7 +1272,7 @@ ${contentHash}`;
1155
1272
  }
1156
1273
  throw new BackendError(
1157
1274
  `Failed to retrieve token: ${error instanceof Error ? error.message : String(error)}`,
1158
- { connection_id: connectionId, error: String(error) }
1275
+ { grant_id: grantId, error: String(error) }
1159
1276
  );
1160
1277
  }
1161
1278
  this.#cacheActorIdFromResponse(response);
@@ -1167,13 +1284,13 @@ ${contentHash}`;
1167
1284
  if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(tokenResponse.injectionHeader)) {
1168
1285
  throw new BackendError(
1169
1286
  `Backend returned invalid injection_header: ${tokenResponse.injectionHeader}`,
1170
- { connectionId: String(connectionId) }
1287
+ { grantId: String(grantId) }
1171
1288
  );
1172
1289
  }
1173
1290
  if (/[\r\n\x00]/.test(tokenResponse.injectionFormat)) {
1174
1291
  throw new BackendError(
1175
1292
  `Backend returned invalid injection_format (contains control characters)`,
1176
- { connectionId: String(connectionId) }
1293
+ { grantId: String(grantId) }
1177
1294
  );
1178
1295
  }
1179
1296
  _storeAccessToken(tokenResponse, typedData.access_token);
@@ -1191,20 +1308,22 @@ ${contentHash}`;
1191
1308
  async #logApiCall(params) {
1192
1309
  try {
1193
1310
  const auditLog = new APICallAuditLog({
1194
- connectionId: params.connectionId,
1195
- providerId: params.providerId,
1311
+ grant_id: params.grantId,
1312
+ provider_id: params.providerId,
1196
1313
  method: params.method,
1197
1314
  url: params.url,
1198
- requestHeaders: params.requestHeaders,
1199
- requestBody: params.requestBody,
1200
- responseStatus: params.responseStatus,
1201
- responseHeaders: params.responseHeaders,
1202
- responseBody: params.responseBody,
1203
- 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,
1204
1321
  reason: params.reason,
1205
- runId: params.runId,
1206
- threadId: params.threadId,
1207
- 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
1208
1327
  });
1209
1328
  const sanitized = auditLog.sanitize();
1210
1329
  const actorHeaders = this.#getActorRequestHeaders(params.runId);
@@ -1280,7 +1399,7 @@ ${contentHash}`;
1280
1399
  * 4. Logs the call for audit (fire-and-forget)
1281
1400
  * 5. Returns the raw response
1282
1401
  */
1283
- async request(connectionId, method, url, options) {
1402
+ async request(grantId, method, url, options) {
1284
1403
  if (this.#closed) {
1285
1404
  throw new AlterSDKError(
1286
1405
  "SDK instance has been closed. Create a new AlterVault instance to make requests."
@@ -1288,13 +1407,13 @@ ${contentHash}`;
1288
1407
  }
1289
1408
  const provider = options?.provider;
1290
1409
  const account = options?.account;
1291
- if (!connectionId && !provider) {
1292
- throw new AlterSDKError("Provide connectionId or options.provider");
1410
+ if (!grantId && !provider) {
1411
+ throw new AlterSDKError("Provide grantId or options.provider");
1293
1412
  }
1294
- if (connectionId && provider) {
1295
- 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");
1296
1415
  }
1297
- const effectiveConnectionId = connectionId ?? null;
1416
+ const effectiveGrantId = grantId ?? null;
1298
1417
  let currentUrl = url;
1299
1418
  const runId = options?.runId ?? (0, import_node_crypto2.randomUUID)();
1300
1419
  const methodStr = String(method).toUpperCase();
@@ -1335,14 +1454,16 @@ ${contentHash}`;
1335
1454
  }
1336
1455
  }
1337
1456
  const { tokenResponse, scopeMismatch } = await this.#getToken(
1338
- effectiveConnectionId,
1457
+ effectiveGrantId,
1339
1458
  options?.reason,
1340
1459
  { method: methodStr, url: currentUrl },
1341
1460
  runId,
1342
1461
  options?.threadId,
1343
1462
  options?.toolCallId,
1463
+ options?.toolId,
1344
1464
  provider,
1345
- account
1465
+ account,
1466
+ options?.toolBundleId
1346
1467
  );
1347
1468
  const requestHeaders = options?.extraHeaders ? { ...options.extraHeaders } : {};
1348
1469
  const accessToken = _extractAccessToken(tokenResponse);
@@ -1361,7 +1482,7 @@ ${contentHash}`;
1361
1482
  if (!additionalCreds.secret_key) {
1362
1483
  throw new BackendError(
1363
1484
  "AWS SigV4 credential is missing secret_key in additional_credentials. Re-store the credential with both Access Key ID and Secret Access Key.",
1364
- { connection_id: effectiveConnectionId }
1485
+ { grant_id: effectiveGrantId }
1365
1486
  );
1366
1487
  }
1367
1488
  const awsHeaders = signAwsRequest({
@@ -1428,7 +1549,7 @@ ${contentHash}`;
1428
1549
  throw new TimeoutError(
1429
1550
  `Provider API request timed out: ${error instanceof Error ? error.message : String(error)}`,
1430
1551
  {
1431
- connection_id: effectiveConnectionId,
1552
+ grant_id: effectiveGrantId,
1432
1553
  method: methodStr,
1433
1554
  url: currentUrl
1434
1555
  }
@@ -1437,7 +1558,7 @@ ${contentHash}`;
1437
1558
  throw new NetworkError(
1438
1559
  `Failed to call provider API: ${error instanceof Error ? error.message : String(error)}`,
1439
1560
  {
1440
- connection_id: effectiveConnectionId,
1561
+ grant_id: effectiveGrantId,
1441
1562
  method: methodStr,
1442
1563
  url: currentUrl,
1443
1564
  error: String(error)
@@ -1466,8 +1587,8 @@ ${contentHash}`;
1466
1587
  responseHeaders[key] = value;
1467
1588
  });
1468
1589
  this.#scheduleAuditLog({
1469
- connectionId: tokenResponse.connectionId,
1470
- providerId: tokenResponse.providerId || effectiveConnectionId || "",
1590
+ grantId: tokenResponse.grantId,
1591
+ providerId: tokenResponse.providerId || effectiveGrantId || "",
1471
1592
  method: methodStr,
1472
1593
  url: auditUrl,
1473
1594
  requestHeaders: auditHeaders,
@@ -1479,18 +1600,20 @@ ${contentHash}`;
1479
1600
  reason: options?.reason ?? null,
1480
1601
  runId,
1481
1602
  threadId: options?.threadId ?? null,
1482
- toolCallId: options?.toolCallId ?? null
1603
+ toolCallId: options?.toolCallId ?? null,
1604
+ toolId: options?.toolId ?? null,
1605
+ toolBundleId: options?.toolBundleId ?? null
1483
1606
  });
1484
1607
  if (response.status >= HTTP_CLIENT_ERROR_START) {
1485
1608
  if (response.status === HTTP_FORBIDDEN && scopeMismatch) {
1486
1609
  throw new ScopeReauthRequiredError(
1487
- "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.",
1488
- tokenResponse.connectionId,
1489
- 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,
1490
1613
  response.status,
1491
1614
  responseBody,
1492
1615
  {
1493
- connection_id: tokenResponse.connectionId,
1616
+ grant_id: tokenResponse.grantId,
1494
1617
  provider_id: tokenResponse.providerId,
1495
1618
  method: methodStr,
1496
1619
  url: currentUrl
@@ -1502,7 +1625,7 @@ ${contentHash}`;
1502
1625
  response.status,
1503
1626
  responseBody,
1504
1627
  {
1505
- connection_id: effectiveConnectionId,
1628
+ grant_id: effectiveGrantId,
1506
1629
  method: methodStr,
1507
1630
  url: currentUrl
1508
1631
  }
@@ -1511,12 +1634,12 @@ ${contentHash}`;
1511
1634
  return response;
1512
1635
  }
1513
1636
  /**
1514
- * List OAuth connections for this app.
1637
+ * List OAuth grants for this app.
1515
1638
  *
1516
- * Returns paginated connection metadata (no tokens).
1639
+ * Returns paginated grant metadata (no tokens).
1517
1640
  * Useful for discovering which services a user has connected.
1518
1641
  */
1519
- async listConnections(options) {
1642
+ async listGrants(options) {
1520
1643
  if (this.#closed) {
1521
1644
  throw new AlterSDKError(
1522
1645
  "SDK instance has been closed. Create a new AlterVault instance to make requests."
@@ -1534,12 +1657,12 @@ ${contentHash}`;
1534
1657
  listBody.user_token = await this.#getUserToken();
1535
1658
  } catch (err) {
1536
1659
  console.warn(
1537
- "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:",
1538
1661
  err instanceof Error ? err.message : String(err)
1539
1662
  );
1540
1663
  }
1541
1664
  }
1542
- const listPath = "/sdk/oauth/connections/list";
1665
+ const listPath = "/sdk/oauth/grants/list";
1543
1666
  const listBodyStr = JSON.stringify(listBody);
1544
1667
  const listHmac = this.#computeHmacHeaders("POST", listPath, listBodyStr);
1545
1668
  try {
@@ -1561,19 +1684,19 @@ ${contentHash}`;
1561
1684
  );
1562
1685
  }
1563
1686
  throw new AlterSDKError(
1564
- `Failed to list connections: ${error instanceof Error ? error.message : String(error)}`
1687
+ `Failed to list grants: ${error instanceof Error ? error.message : String(error)}`
1565
1688
  );
1566
1689
  }
1567
1690
  this.#cacheActorIdFromResponse(response);
1568
1691
  await this.#handleErrorResponse(response);
1569
1692
  const data = await response.json();
1570
- const connections = data.connections.map(
1571
- (c) => new ConnectionInfo(
1572
- c
1693
+ const grants = data.grants.map(
1694
+ (g) => new GrantInfo(
1695
+ g
1573
1696
  )
1574
1697
  );
1575
- return new ConnectionListResult({
1576
- connections,
1698
+ return new GrantListResult({
1699
+ grants,
1577
1700
  total: data.total,
1578
1701
  limit: data.limit,
1579
1702
  offset: data.offset,
@@ -1599,10 +1722,10 @@ ${contentHash}`;
1599
1722
  allowed_origin: options.allowedOrigin ?? null,
1600
1723
  metadata: options.metadata ?? null
1601
1724
  };
1602
- if (options.connectionPolicy) {
1603
- sessionBody.connection_policy = {
1604
- max_ttl_seconds: options.connectionPolicy.maxTtlSeconds ?? null,
1605
- 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
1606
1729
  };
1607
1730
  }
1608
1731
  if (this.#userTokenGetter) {
@@ -1669,7 +1792,7 @@ ${contentHash}`;
1669
1792
  const openBrowser = options.openBrowser ?? true;
1670
1793
  const session = await this.createConnectSession({
1671
1794
  allowedProviders: options.providers,
1672
- connectionPolicy: options.connectionPolicy
1795
+ grantPolicy: options.grantPolicy
1673
1796
  });
1674
1797
  if (openBrowser) {
1675
1798
  try {
@@ -1700,14 +1823,14 @@ ${contentHash}`;
1700
1823
  const pollResult = await this.#pollSession(session.sessionToken);
1701
1824
  const pollStatus = pollResult.status;
1702
1825
  if (pollStatus === "completed") {
1703
- const connectionsData = pollResult.connections ?? [];
1704
- return connectionsData.map(
1705
- (conn) => new ConnectResult({
1706
- connection_id: conn.connection_id ?? "",
1707
- provider_id: conn.provider_id ?? "",
1708
- account_identifier: conn.account_identifier ?? null,
1709
- scopes: conn.scopes ?? [],
1710
- 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
1711
1834
  })
1712
1835
  );
1713
1836
  }
@@ -1734,12 +1857,133 @@ ${contentHash}`;
1734
1857
  "Connect session expired before OAuth was completed"
1735
1858
  );
1736
1859
  }
1860
+ if (pollStatus !== "pending") {
1861
+ throw new ConnectFlowError(
1862
+ `Unexpected poll status from server: ${pollStatus}`,
1863
+ { status: pollStatus }
1864
+ );
1865
+ }
1737
1866
  }
1738
1867
  throw new ConnectTimeoutError(
1739
1868
  `OAuth flow did not complete within ${timeout} seconds. The user may not have finished authorizing in the browser.`,
1740
1869
  { timeout }
1741
1870
  );
1742
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
+ }
1743
1987
  /**
1744
1988
  * Poll the Connect session for completion status (INTERNAL).
1745
1989
  *
@@ -1891,6 +2135,7 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
1891
2135
  ActorType,
1892
2136
  AlterSDKError,
1893
2137
  AlterVault,
2138
+ AuthResult,
1894
2139
  BackendError,
1895
2140
  ConnectConfigError,
1896
2141
  ConnectDeniedError,
@@ -1898,12 +2143,13 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
1898
2143
  ConnectResult,
1899
2144
  ConnectSession,
1900
2145
  ConnectTimeoutError,
1901
- ConnectionDeletedError,
1902
- ConnectionExpiredError,
1903
- ConnectionInfo,
1904
- ConnectionListResult,
1905
- ConnectionNotFoundError,
1906
- ConnectionRevokedError,
2146
+ CredentialRevokedError,
2147
+ GrantDeletedError,
2148
+ GrantExpiredError,
2149
+ GrantInfo,
2150
+ GrantListResult,
2151
+ GrantNotFoundError,
2152
+ GrantRevokedError,
1907
2153
  HttpMethod,
1908
2154
  NetworkError,
1909
2155
  PolicyViolationError,